Skip to content
This repository has been archived by the owner on Jan 7, 2023. It is now read-only.

Exif Orientation fix #122

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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 @@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Pair;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
Expand All @@ -56,14 +57,15 @@ public class CropImageActivity extends MonitoredActivity {
private int maxX;
private int maxY;
private int exifRotation;
private int exifScale;

private Uri sourceUri;
private Uri saveUri;

private boolean isSaving;

private int sampleSize;
private RotateBitmap rotateBitmap;
private Bitmap srcBitmap;
private CropImageView imageView;
private HighlightView cropView;

Expand All @@ -74,7 +76,7 @@ public void onCreate(Bundle icicle) {
setupViews();

loadInput();
if (rotateBitmap == null) {
if (srcBitmap == null) {
finish();
return;
}
Expand Down Expand Up @@ -119,6 +121,7 @@ public void onClick(View v) {
private void loadInput() {
Intent intent = getIntent();
Bundle extras = intent.getExtras();
Bitmap initialBitmap ;

if (extras != null) {
aspectX = extras.getInt(Crop.Extra.ASPECT_X);
Expand All @@ -130,15 +133,32 @@ private void loadInput() {

sourceUri = intent.getData();
if (sourceUri != null) {
exifRotation = CropUtil.getExifRotation(CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri));

Pair<Integer,Integer> exifRotationTrans =
CropUtil.getExifRotationTranslation(
CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri));
exifRotation = exifRotationTrans.first;
exifScale = exifRotationTrans.second;
InputStream is = null;
try {
sampleSize = calculateBitmapSampleSize(sourceUri);
is = getContentResolver().openInputStream(sourceUri);
BitmapFactory.Options option = new BitmapFactory.Options();
option.inSampleSize = sampleSize;
rotateBitmap = new RotateBitmap(BitmapFactory.decodeStream(is, null, option), exifRotation);
initialBitmap = BitmapFactory.decodeStream(is, null, option);
int drawHeight = initialBitmap.getHeight();
int drawWidth = initialBitmap.getWidth();
if ((exifRotation != 0) || (exifScale != 1)) {
Matrix matrix = new Matrix() ;
if (exifRotation != 0) {
matrix.preRotate(exifRotation);
}
if (exifScale != 1) {
matrix.postScale(exifScale, 1);
}
srcBitmap = Bitmap.createBitmap(initialBitmap, 0, 0, drawWidth, drawHeight, matrix, true);
} else {
srcBitmap = initialBitmap ;
}
} catch (IOException e) {
Log.e("Error reading image: " + e.getMessage(), e);
setResultException(e);
Expand All @@ -149,6 +169,9 @@ private void loadInput() {
CropUtil.closeSilently(is);
}
}
else {
Log.e("Source URI is null");
}
}

private int calculateBitmapSampleSize(Uri bitmapUri) throws IOException {
Expand Down Expand Up @@ -190,7 +213,7 @@ private void startCrop() {
if (isFinishing()) {
return;
}
imageView.setImageRotateBitmapResetBase(rotateBitmap, true);
imageView.setImageBitmapResetBase(srcBitmap, true);
CropUtil.startBackgroundJob(this, null, getResources().getString(R.string.crop__wait),
new Runnable() {
public void run() {
Expand All @@ -217,13 +240,13 @@ public void run() {
private class Cropper {

private void makeDefault() {
if (rotateBitmap == null) {
if (srcBitmap == null) {
return;
}

HighlightView hv = new HighlightView(imageView);
final int width = rotateBitmap.getWidth();
final int height = rotateBitmap.getHeight();
final int width = srcBitmap.getWidth();
final int height = srcBitmap.getHeight();

Rect imageRect = new Rect(0, 0, width, height);

Expand Down Expand Up @@ -275,6 +298,8 @@ private void onSaveClicked() {

int outWidth = width;
int outHeight = height;
Log.e("Crop Width = " + width);
Log.e("Crop Height = " + height);
if (maxX > 0 && maxY > 0 && (width > maxX || height > maxY)) {
float ratio = (float) width / (float) height;
if ((float) maxX / (float) maxY > ratio) {
Expand All @@ -295,7 +320,7 @@ private void onSaveClicked() {
}

if (croppedImage != null) {
imageView.setImageRotateBitmapResetBase(new RotateBitmap(croppedImage, exifRotation), true);
imageView.setImageBitmapResetBase(croppedImage, true);
imageView.center();
imageView.highlightViews.clear();
}
Expand All @@ -318,22 +343,20 @@ public void run() {
}

private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {
// Release memory now
clearImageView();

Matrix matrix = new Matrix();
InputStream is = null;
Bitmap croppedImage = null;
boolean transformed = false ;
try {
is = getContentResolver().openInputStream(sourceUri);
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
final int width = decoder.getWidth();
final int height = decoder.getHeight();

if (exifRotation != 0) {
// Adjust crop area to account for image rotation
Matrix matrix = new Matrix();
matrix.setRotate(-exifRotation);

if ((exifRotation != 0) || (exifScale != 1)) {
// Adjust crop area to account for EXIF transformation so what you crop is what you get
matrix.preRotate(-exifRotation);
matrix.postScale(exifScale, 1);
RectF adjusted = new RectF();
matrix.mapRect(adjusted, new RectF(rect));

Expand All @@ -343,11 +366,20 @@ private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {
}

try {
matrix.reset();
croppedImage = decoder.decodeRegion(rect, new BitmapFactory.Options());
if (croppedImage != null && (rect.width() > outWidth || rect.height() > outHeight)) {
Matrix matrix = new Matrix();
matrix.postScale((float) outWidth / rect.width(), (float) outHeight / rect.height());
croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), matrix, true);
if ((exifRotation != 0) || (exifScale != 1)) {
transformed = true;
// Remove the EXIF transformation from the cropped image so we can dispense with it altogether
matrix.preRotate(exifRotation);
matrix.postScale(exifScale, 1);
}
if (rect.width() > outWidth || rect.height() > outHeight) {
transformed = true ;
matrix.postScale(((float)outWidth)/((float)rect.width()), ((float)outHeight)/((float)rect.height()));
}
if (transformed) {
croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), matrix, true);
}
} catch (IllegalArgumentException e) {
// Rethrow with some extra information
Expand All @@ -363,14 +395,16 @@ private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {
setResultException(e);
} finally {
CropUtil.closeSilently(is);
// Release memory now
clearImageView();
}
return croppedImage;
}

private void clearImageView() {
imageView.clear();
if (rotateBitmap != null) {
rotateBitmap.recycle();
if (srcBitmap != null) {
srcBitmap.recycle();
}
System.gc();
}
Expand All @@ -389,12 +423,6 @@ private void saveOutput(Bitmap croppedImage) {
} finally {
CropUtil.closeSilently(outputStream);
}

CropUtil.copyExifRotation(
CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri),
CropUtil.getFromMediaUri(this, getContentResolver(), saveUri)
);

setResultUri(saveUri);
}

Expand All @@ -412,8 +440,8 @@ public void run() {
@Override
protected void onDestroy() {
super.onDestroy();
if (rotateBitmap != null) {
rotateBitmap.recycle();
if (srcBitmap != null) {
srcBitmap.recycle();
}
}

Expand Down
43 changes: 31 additions & 12 deletions lib/src/main/java/com/soundcloud/android/crop/CropUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Pair;

import java.io.Closeable;
import java.io.File;
Expand All @@ -52,25 +53,43 @@ public static void closeSilently(@Nullable Closeable c) {
}
}

public static int getExifRotation(File imageFile) {
if (imageFile == null) return 0;
public static Pair<Integer,Integer> getExifRotationTranslation(File imageFile) {
int exifRotation = 0, exifScale = 1;
if (imageFile == null) return new Pair<Integer,Integer>(0,1);
try {
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
// We only recognize a subset of orientation tag values
switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return ExifInterface.ORIENTATION_UNDEFINED;
case ExifInterface.ORIENTATION_UNDEFINED:
case ExifInterface.ORIENTATION_NORMAL:
break ;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
exifScale=-1;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
exifRotation = 180;
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
exifRotation = 180;
exifScale=-1;
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
exifRotation = 90;
exifScale=-1;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
exifRotation = 90;
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
exifRotation = 270;
exifScale=-1;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
exifRotation = 270;
}
} catch (IOException e) {
Log.e("Error getting Exif data", e);
return 0;
}
return new Pair<Integer,Integer>(exifRotation,exifScale);
}

public static boolean copyExifRotation(File sourceFile, File destFile) {
Expand Down