Skip to content
This repository was archived by the owner on May 31, 2023. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.util.Log;

import androidx.annotation.Nullable;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
Expand All @@ -21,6 +24,7 @@

public class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
private final Context context;
private final String LOG_TAG = "FlutterNativeImage";

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

copyExif(fileName, outputFileName);
copyExif(fileName, outputFileName, null);

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

int orientation = ExifInterface.ORIENTATION_UNDEFINED;
try {
ExifInterface exif = new ExifInterface(fileName);
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
} catch(IOException ex) {
// EXIF could not be read from the file; ignore
}
int orientation = getOrientation(fileName);
properties.put("orientation", orientation);

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

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

copyExif(fileName, outputFileName);
copyExif(fileName, outputFileName, ExifInterface.ORIENTATION_NORMAL);

result.success(outputFileName);
} catch (FileNotFoundException e) {
Expand Down Expand Up @@ -177,7 +175,7 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) {
}
}

private void copyExif(String filePathOri, String filePathDest) {
private void copyExif(String filePathOri, String filePathDest, @Nullable Integer overwriteOrientationAttr) {
try {
ExifInterface oldExif = new ExifInterface(filePathOri);
ExifInterface newExif = new ExifInterface(filePathDest);
Expand All @@ -201,16 +199,21 @@ private void copyExif(String filePathOri, String filePathDest) {
"GPSLongitude",
"GPSLongitudeRef",
"Make",
"Model",
"Orientation");
"Model");
for (String attribute : attributes) {
setIfNotNull(oldExif, newExif, attribute);
}

if (overwriteOrientationAttr == null) {
setIfNotNull(oldExif, newExif, "Orientation");
} else {
newExif.setAttribute("Orientation", String.valueOf(overwriteOrientationAttr));
}

newExif.saveAttributes();

} catch (Exception ex) {
Log.e("FlutterNativeImagePlugin", "Error preserving Exif data on selected image: " + ex);
Log.e(LOG_TAG, "Error preserving Exif data on selected image: " + ex);
}
}

Expand All @@ -220,6 +223,72 @@ private void setIfNotNull(ExifInterface oldExif, ExifInterface newExif, String p
}
}

/**
* This method resolves the orientation of an image and returns the bitmap normalised.
* @param filePath Path of the image file. Used to decode the {@link Bitmap} and camera rotation
* @return {@link Bitmap} object with normalised orientation.
*/
private Bitmap decodeBitmapWithResolvedOrientation(String filePath) {
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
int orientation = getOrientation(filePath);

Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
matrix.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
matrix.setRotate(180);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
matrix.setRotate(90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
matrix.setRotate(-90);
matrix.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(-90);
break;
case ExifInterface.ORIENTATION_NORMAL:
case ExifInterface.ORIENTATION_UNDEFINED:
default:
return bitmap;
}

try {
Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
return oriented;
} catch (OutOfMemoryError e) {
e.printStackTrace();
return bitmap;
}
}

/**
* Reads the orientation attribute from an image file.
* @param filePath Path of the image file
* @return orientation attribute read by {@link ExifInterface} or the default value {@link ExifInterface#ORIENTATION_NORMAL}
*/
private int getOrientation(String filePath) {
try {
ExifInterface exif = new ExifInterface(filePath);
return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
} catch(IOException ex) {
// EXIF could not be read from the file; return default
return ExifInterface.ORIENTATION_NORMAL;
}
}

private static String pathComponent(String filename) {
int i = filename.lastIndexOf(File.separator);
return (i > -1) ? filename.substring(0, i) : filename;
Expand Down