Skip to content

Exif fixes #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 23, 2017
Merged
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
@@ -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;
@@ -56,6 +57,7 @@ public class CropImageActivity extends MonitoredActivity {
private int maxX;
private int maxY;
private int exifRotation;
private int exifScale;
private boolean saveAsPng;

private Uri sourceUri;
@@ -64,7 +66,7 @@ public class CropImageActivity extends MonitoredActivity {
private boolean isSaving;

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

@@ -75,7 +77,7 @@ public void onCreate(Bundle icicle) {
setupViews();

loadInput();
if (rotateBitmap == null) {
if (srcBitmap == null) {
finish();
return;
}
@@ -120,6 +122,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);
@@ -132,15 +135,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);
@@ -151,6 +171,9 @@ private void loadInput() {
CropUtil.closeSilently(is);
}
}
else {
Log.e("Source URI is null");
}
}

private int calculateBitmapSampleSize(Uri bitmapUri) throws IOException {
@@ -192,7 +215,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() {
@@ -219,13 +242,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);

@@ -297,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();
}
@@ -322,20 +345,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));

@@ -345,11 +368,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
@@ -371,8 +403,8 @@ private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {

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

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

setResultUri(saveUri);
}

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

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
@@ -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;
@@ -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) {