Skip to content
This repository was archived by the owner on May 31, 2023. It is now read-only.

Commit b2879b3

Browse files
committed
Update MethodCallHandlerImpl.java
Device cameras have different native orientations (i.e. Samsung, One Plus, Sony => ROTATE_90). When cropping the bitmap, it has to be normalised before.
1 parent d635858 commit b2879b3

File tree

1 file changed

+83
-14
lines changed

1 file changed

+83
-14
lines changed

android/src/main/java/com/example/flutternativeimage/MethodCallHandlerImpl.java

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import android.content.Context;
44
import android.graphics.Bitmap;
55
import android.graphics.BitmapFactory;
6+
import android.graphics.Matrix;
67
import android.media.ExifInterface;
78
import android.util.Log;
89

10+
import androidx.annotation.Nullable;
11+
912
import java.io.ByteArrayOutputStream;
1013
import java.io.File;
1114
import java.io.FileNotFoundException;
@@ -21,6 +24,7 @@
2124

2225
public class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
2326
private final Context context;
27+
private final String LOG_TAG = "FlutterNativeImage";
2428

2529
MethodCallHandlerImpl(Context context) {
2630
this.context = context;
@@ -66,7 +70,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
6670
OutputStream outputStream = new FileOutputStream(outputFileName);
6771
bos.writeTo(outputStream);
6872

69-
copyExif(fileName, outputFileName);
73+
copyExif(fileName, outputFileName, null);
7074

7175
result.success(outputFileName);
7276
} catch (FileNotFoundException e) {
@@ -95,13 +99,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
9599
properties.put("width", options.outWidth);
96100
properties.put("height", options.outHeight);
97101

98-
int orientation = ExifInterface.ORIENTATION_UNDEFINED;
99-
try {
100-
ExifInterface exif = new ExifInterface(fileName);
101-
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
102-
} catch(IOException ex) {
103-
// EXIF could not be read from the file; ignore
104-
}
102+
int orientation = getOrientation(fileName);
105103
properties.put("orientation", orientation);
106104

107105
result.success(properties);
@@ -124,7 +122,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
124122
Bitmap.CompressFormat format = isPNG ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG;
125123
String extension = isPNG ? ".png" : ".jpg";
126124

127-
Bitmap bmp = BitmapFactory.decodeFile(fileName);
125+
Bitmap bmp = decodeBitmapWithResolvedOrientation(fileName);
128126
ByteArrayOutputStream bos = new ByteArrayOutputStream();
129127
try {
130128
bmp = Bitmap.createBitmap(bmp, originX, originY, width, height);
@@ -147,7 +145,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
147145
outputStream = new FileOutputStream(outputFileName);
148146
bos.writeTo(outputStream);
149147

150-
copyExif(fileName, outputFileName);
148+
copyExif(fileName, outputFileName, ExifInterface.ORIENTATION_NORMAL);
151149

152150
result.success(outputFileName);
153151
} catch (FileNotFoundException e) {
@@ -177,7 +175,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
177175
}
178176
}
179177

180-
private void copyExif(String filePathOri, String filePathDest) {
178+
private void copyExif(String filePathOri, String filePathDest, @Nullable Integer overwriteOrientationAttr) {
181179
try {
182180
ExifInterface oldExif = new ExifInterface(filePathOri);
183181
ExifInterface newExif = new ExifInterface(filePathDest);
@@ -201,16 +199,21 @@ private void copyExif(String filePathOri, String filePathDest) {
201199
"GPSLongitude",
202200
"GPSLongitudeRef",
203201
"Make",
204-
"Model",
205-
"Orientation");
202+
"Model");
206203
for (String attribute : attributes) {
207204
setIfNotNull(oldExif, newExif, attribute);
208205
}
209206

207+
if (overwriteOrientationAttr == null) {
208+
setIfNotNull(oldExif, newExif, "Orientation");
209+
} else {
210+
newExif.setAttribute("Orientation", String.valueOf(overwriteOrientationAttr));
211+
}
212+
210213
newExif.saveAttributes();
211214

212215
} catch (Exception ex) {
213-
Log.e("FlutterNativeImagePlugin", "Error preserving Exif data on selected image: " + ex);
216+
Log.e(LOG_TAG, "Error preserving Exif data on selected image: " + ex);
214217
}
215218
}
216219

@@ -220,6 +223,72 @@ private void setIfNotNull(ExifInterface oldExif, ExifInterface newExif, String p
220223
}
221224
}
222225

226+
/**
227+
* This method resolves the orientation of an image and returns the bitmap normalised.
228+
* @param filePath Path of the image file. Used to decode the {@link Bitmap} and camera rotation
229+
* @return {@link Bitmap} object with normalised orientation.
230+
*/
231+
private Bitmap decodeBitmapWithResolvedOrientation(String filePath) {
232+
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
233+
int orientation = getOrientation(filePath);
234+
235+
Matrix matrix = new Matrix();
236+
switch (orientation) {
237+
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
238+
matrix.setScale(-1, 1);
239+
break;
240+
case ExifInterface.ORIENTATION_ROTATE_180:
241+
matrix.setRotate(180);
242+
break;
243+
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
244+
matrix.setRotate(180);
245+
matrix.postScale(-1, 1);
246+
break;
247+
case ExifInterface.ORIENTATION_TRANSPOSE:
248+
matrix.setRotate(90);
249+
matrix.postScale(-1, 1);
250+
break;
251+
case ExifInterface.ORIENTATION_ROTATE_90:
252+
matrix.setRotate(90);
253+
break;
254+
case ExifInterface.ORIENTATION_TRANSVERSE:
255+
matrix.setRotate(-90);
256+
matrix.postScale(-1, 1);
257+
break;
258+
case ExifInterface.ORIENTATION_ROTATE_270:
259+
matrix.setRotate(-90);
260+
break;
261+
case ExifInterface.ORIENTATION_NORMAL:
262+
case ExifInterface.ORIENTATION_UNDEFINED:
263+
default:
264+
return bitmap;
265+
}
266+
267+
try {
268+
Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
269+
bitmap.recycle();
270+
return oriented;
271+
} catch (OutOfMemoryError e) {
272+
e.printStackTrace();
273+
return bitmap;
274+
}
275+
}
276+
277+
/**
278+
* Reads the orientation attribute from an image file.
279+
* @param filePath Path of the image file
280+
* @return orientation attribute read by {@link ExifInterface} or the default value {@link ExifInterface#ORIENTATION_NORMAL}
281+
*/
282+
private int getOrientation(String filePath) {
283+
try {
284+
ExifInterface exif = new ExifInterface(filePath);
285+
return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
286+
} catch(IOException ex) {
287+
// EXIF could not be read from the file; return default
288+
return ExifInterface.ORIENTATION_NORMAL;
289+
}
290+
}
291+
223292
private static String pathComponent(String filename) {
224293
int i = filename.lastIndexOf(File.separator);
225294
return (i > -1) ? filename.substring(0, i) : filename;

0 commit comments

Comments
 (0)