Skip to content

Commit ceafe59

Browse files
committed
Merge pull request square#1021 from JohnWowUs/EXIF-Improvements
EXIFOrientation Handling improvements
2 parents 9ba8f51 + dc1caa1 commit ceafe59

File tree

3 files changed

+120
-25
lines changed

3 files changed

+120
-25
lines changed

picasso/src/main/java/com/squareup/picasso/BitmapHunter.java

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@
2828
import java.util.concurrent.Future;
2929
import java.util.concurrent.atomic.AtomicInteger;
3030

31+
import static android.media.ExifInterface.ORIENTATION_ROTATE_180;
32+
import static android.media.ExifInterface.ORIENTATION_ROTATE_270;
33+
import static android.media.ExifInterface.ORIENTATION_ROTATE_90;
34+
import static android.media.ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
35+
import static android.media.ExifInterface.ORIENTATION_FLIP_VERTICAL;
36+
import static android.media.ExifInterface.ORIENTATION_TRANSPOSE;
37+
import static android.media.ExifInterface.ORIENTATION_TRANSVERSE;
38+
3139
import static com.squareup.picasso.MemoryPolicy.shouldReadFromMemoryCache;
3240
import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY;
3341
import static com.squareup.picasso.Picasso.Priority;
@@ -84,7 +92,7 @@ class BitmapHunter implements Runnable {
8492
Future<?> future;
8593
Picasso.LoadedFrom loadedFrom;
8694
Exception exception;
87-
int exifRotation; // Determined during decoding of original resource.
95+
int exifOrientation; // Determined during decoding of original resource.
8896
int retryCount;
8997
Priority priority;
9098

@@ -208,8 +216,7 @@ Bitmap hunt() throws IOException {
208216
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
209217
if (result != null) {
210218
loadedFrom = result.getLoadedFrom();
211-
exifRotation = result.getExifOrientation();
212-
219+
exifOrientation = result.getExifOrientation();
213220
bitmap = result.getBitmap();
214221

215222
// If there was no Bitmap then we need to decode it from the stream.
@@ -228,10 +235,10 @@ Bitmap hunt() throws IOException {
228235
log(OWNER_HUNTER, VERB_DECODED, data.logId());
229236
}
230237
stats.dispatchBitmapDecoded(bitmap);
231-
if (data.needsTransformation() || exifRotation != 0) {
238+
if (data.needsTransformation() || exifOrientation != 0) {
232239
synchronized (DECODE_LOCK) {
233-
if (data.needsMatrixTransform() || exifRotation != 0) {
234-
bitmap = transformResult(data, bitmap, exifRotation);
240+
if (data.needsMatrixTransform() || exifOrientation != 0) {
241+
bitmap = transformResult(data, bitmap, exifOrientation);
235242
if (picasso.loggingEnabled) {
236243
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
237244
}
@@ -485,7 +492,7 @@ static Bitmap applyCustomTransformations(List<Transformation> transformations, B
485492
return result;
486493
}
487494

488-
static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
495+
static Bitmap transformResult(Request data, Bitmap result, int exifOrientation) {
489496
int inWidth = result.getWidth();
490497
int inHeight = result.getHeight();
491498
boolean onlyScaleDown = data.onlyScaleDown;
@@ -562,8 +569,12 @@ static Bitmap transformResult(Request data, Bitmap result, int exifRotation) {
562569
}
563570
}
564571

565-
if (exifRotation != 0) {
566-
matrix.preRotate(exifRotation);
572+
if (exifOrientation != 0) {
573+
matrix.preRotate(getExifRotation(exifOrientation));
574+
int exifTranslation = getExifTranslation(exifOrientation);
575+
if (exifTranslation != 1) {
576+
matrix.postScale(exifTranslation, 1);
577+
}
567578
}
568579

569580
Bitmap newResult =
@@ -580,4 +591,41 @@ private static boolean shouldResize(boolean onlyScaleDown, int inWidth, int inHe
580591
int targetWidth, int targetHeight) {
581592
return !onlyScaleDown || inWidth > targetWidth || inHeight > targetHeight;
582593
}
594+
595+
static int getExifRotation(int orientation) {
596+
int rotation;
597+
switch (orientation) {
598+
case ORIENTATION_ROTATE_90:
599+
case ORIENTATION_TRANSPOSE:
600+
rotation = 90;
601+
break;
602+
case ORIENTATION_ROTATE_180:
603+
case ORIENTATION_FLIP_VERTICAL:
604+
rotation = 180;
605+
break;
606+
case ORIENTATION_ROTATE_270:
607+
case ORIENTATION_TRANSVERSE:
608+
rotation = 270;
609+
break;
610+
default:
611+
rotation = 0;
612+
}
613+
return rotation;
614+
}
615+
616+
static int getExifTranslation(int orientation) {
617+
int translation;
618+
switch (orientation) {
619+
case ORIENTATION_FLIP_HORIZONTAL:
620+
case ORIENTATION_FLIP_VERTICAL:
621+
case ORIENTATION_TRANSPOSE:
622+
case ORIENTATION_TRANSVERSE:
623+
translation = -1;
624+
break;
625+
default:
626+
translation = 1;
627+
}
628+
return translation;
629+
}
583630
}
631+

picasso/src/main/java/com/squareup/picasso/FileRequestHandler.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222

2323
import static android.content.ContentResolver.SCHEME_FILE;
2424
import static android.media.ExifInterface.ORIENTATION_NORMAL;
25-
import static android.media.ExifInterface.ORIENTATION_ROTATE_180;
26-
import static android.media.ExifInterface.ORIENTATION_ROTATE_270;
27-
import static android.media.ExifInterface.ORIENTATION_ROTATE_90;
2825
import static android.media.ExifInterface.TAG_ORIENTATION;
2926
import static com.squareup.picasso.Picasso.LoadedFrom.DISK;
3027

@@ -44,16 +41,6 @@ class FileRequestHandler extends ContentStreamRequestHandler {
4441

4542
static int getFileExifRotation(Uri uri) throws IOException {
4643
ExifInterface exifInterface = new ExifInterface(uri.getPath());
47-
int orientation = exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
48-
switch (orientation) {
49-
case ORIENTATION_ROTATE_90:
50-
return 90;
51-
case ORIENTATION_ROTATE_180:
52-
return 180;
53-
case ORIENTATION_ROTATE_270:
54-
return 270;
55-
default:
56-
return 0;
57-
}
44+
return exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
5845
}
5946
}

picasso/src/test/java/com/squareup/picasso/BitmapHunterTest.java

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@
7878
import static org.mockito.MockitoAnnotations.initMocks;
7979
import static org.robolectric.Robolectric.shadowOf;
8080

81+
import static android.media.ExifInterface.ORIENTATION_ROTATE_90;
82+
import static android.media.ExifInterface.ORIENTATION_FLIP_HORIZONTAL ;
83+
import static android.media.ExifInterface.ORIENTATION_FLIP_VERTICAL;
84+
import static android.media.ExifInterface.ORIENTATION_TRANSPOSE;
85+
import static android.media.ExifInterface.ORIENTATION_TRANSVERSE;
86+
8187
@RunWith(RobolectricTestRunner.class)
8288
@Config(manifest = Config.NONE)
8389
public class BitmapHunterTest {
@@ -407,15 +413,69 @@ public class BitmapHunterTest {
407413
@Test public void exifRotation() {
408414
Request data = new Request.Builder(URI_1).rotate(-45).build();
409415
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
410-
Bitmap result = transformResult(data, source, 90);
416+
Bitmap result = transformResult(data, source, ORIENTATION_ROTATE_90);
417+
ShadowBitmap shadowBitmap = shadowOf(result);
418+
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);
419+
420+
Matrix matrix = shadowBitmap.getCreatedFromMatrix();
421+
ShadowMatrix shadowMatrix = shadowOf(matrix);
422+
assertThat(shadowMatrix.getPreOperations()).containsOnly("rotate 90.0");
423+
}
424+
425+
@Test public void exifVerticalFlip() {
426+
Request data = new Request.Builder(URI_1).rotate(-45).build();
427+
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
428+
Bitmap result = transformResult(data, source, ORIENTATION_FLIP_VERTICAL);
429+
ShadowBitmap shadowBitmap = shadowOf(result);
430+
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);
431+
432+
Matrix matrix = shadowBitmap.getCreatedFromMatrix();
433+
ShadowMatrix shadowMatrix = shadowOf(matrix);
434+
assertThat(shadowMatrix.getPostOperations()).containsOnly("scale -1.0 1.0");
435+
assertThat(shadowMatrix.getPreOperations()).containsOnly("rotate 180.0");
436+
}
437+
438+
@Test public void exifHorizontalFlip() {
439+
Request data = new Request.Builder(URI_1).rotate(-45).build();
440+
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
441+
Bitmap result = transformResult(data, source, ORIENTATION_FLIP_HORIZONTAL);
442+
ShadowBitmap shadowBitmap = shadowOf(result);
443+
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);
444+
445+
Matrix matrix = shadowBitmap.getCreatedFromMatrix();
446+
ShadowMatrix shadowMatrix = shadowOf(matrix);
447+
assertThat(shadowMatrix.getPostOperations()).containsOnly("scale -1.0 1.0");
448+
assertThat(shadowMatrix.getPreOperations()).doesNotContain("rotate 180.0");
449+
assertThat(shadowMatrix.getPreOperations()).doesNotContain("rotate 90.0");
450+
assertThat(shadowMatrix.getPreOperations()).doesNotContain("rotate 270.0");
451+
}
452+
453+
@Test public void exifTranspose() {
454+
Request data = new Request.Builder(URI_1).rotate(-45).build();
455+
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
456+
Bitmap result = transformResult(data, source, ORIENTATION_TRANSPOSE);
411457
ShadowBitmap shadowBitmap = shadowOf(result);
412458
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);
413459

414460
Matrix matrix = shadowBitmap.getCreatedFromMatrix();
415461
ShadowMatrix shadowMatrix = shadowOf(matrix);
462+
assertThat(shadowMatrix.getPostOperations()).containsOnly("scale -1.0 1.0");
416463
assertThat(shadowMatrix.getPreOperations()).containsOnly("rotate 90.0");
417464
}
418465

466+
@Test public void exifTransverse() {
467+
Request data = new Request.Builder(URI_1).rotate(-45).build();
468+
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
469+
Bitmap result = transformResult(data, source, ORIENTATION_TRANSVERSE);
470+
ShadowBitmap shadowBitmap = shadowOf(result);
471+
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);
472+
473+
Matrix matrix = shadowBitmap.getCreatedFromMatrix();
474+
ShadowMatrix shadowMatrix = shadowOf(matrix);
475+
assertThat(shadowMatrix.getPostOperations()).containsOnly("scale -1.0 1.0");
476+
assertThat(shadowMatrix.getPreOperations()).containsOnly("rotate 270.0");
477+
}
478+
419479
@Test public void keepsAspectRationWhileResizingWhenDesiredWidthIs0() {
420480
Request request = new Request.Builder(URI_1).resize(20, 0).build();
421481
Bitmap source = Bitmap.createBitmap(40, 20, ARGB_8888);
@@ -549,7 +609,7 @@ public class BitmapHunterTest {
549609
Bitmap source = Bitmap.createBitmap(10, 10, ARGB_8888);
550610
Request data = new Request.Builder(URI_1).rotate(-45).build();
551611

552-
Bitmap result = transformResult(data, source, 90);
612+
Bitmap result = transformResult(data, source, ORIENTATION_ROTATE_90);
553613

554614
ShadowBitmap shadowBitmap = shadowOf(result);
555615
assertThat(shadowBitmap.getCreatedFromBitmap()).isSameAs(source);

0 commit comments

Comments
 (0)