Skip to content

Commit

Permalink
Add orientations to DownsamplerEmulatorTest.
Browse files Browse the repository at this point in the history
Android’s ExifInterface only supports writing orientations to JPEGs, so
for now that’s all I’m starting with. We should investigate other open
source image writers and considering using one that supports other 
formats.

Related to bumptech#3673, but not sufficient because there’s clearly a bug but 
all tests here still pass.
  • Loading branch information
sjudd committed Jun 1, 2019
1 parent 6d833d9 commit 41877c6
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 19 deletions.
2 changes: 2 additions & 0 deletions instrumentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ dependencies {
androidTestImplementation "androidx.test:core:${ANDROIDX_TEST_VERSION}"
androidTestImplementation "com.google.truth:truth:${TRUTH_VERSION}"
androidTestImplementation "junit:junit:${JUNIT_VERSION}"
androidTestImplementation "androidx.exifinterface:exifinterface:${ANDROID_X_VERSION}"

// Not totally clear why this is required, but it seems to be missing when tests are run on
// 4.1.2 and 4.2.0 emulators.
androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,28 @@
import android.graphics.Bitmap.Config;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import androidx.annotation.Nullable;
import android.util.DisplayMetrics;
import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.bumptech.glide.load.ImageHeaderParser;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter;
import com.bumptech.glide.load.engine.bitmap_recycle.LruArrayPool;
import com.bumptech.glide.util.Preconditions;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -347,13 +356,14 @@ private static String runScaleTest(
int initialHeight,
int targetWidth,
int targetHeight,
int exifOrientation,
DownsampleStrategy strategy,
int expectedWidth,
int expectedHeight)
throws IOException {
Downsampler downsampler = buildDownsampler();

InputStream is = openBitmapStream(format, initialWidth, initialHeight);
InputStream is = openBitmapStream(format, initialWidth, initialHeight, exifOrientation);
Options options = new Options().set(DownsampleStrategy.OPTION, strategy);
Bitmap bitmap = downsampler.decode(is, targetWidth, targetHeight, options).get();
try {
Expand All @@ -366,6 +376,8 @@ private static String runScaleTest(
+ format
+ ", strategy: "
+ strategy
+ ", orientation: "
+ exifOrientation
+ " -"
+ " Initial "
+ readableDimens(initialWidth, initialHeight)
Expand Down Expand Up @@ -397,7 +409,63 @@ private static Downsampler buildDownsampler() {
return new Downsampler(parsers, displayMetrics, bitmapPool, arrayPool);
}

private static InputStream openBitmapStream(CompressFormat format, int width, int height) {
private static InputStream openBitmapStream(
CompressFormat format, int width, int height, int exifOrientation) {
Preconditions.checkArgument(
format == CompressFormat.JPEG || exifOrientation == ExifInterface.ORIENTATION_UNDEFINED,
"Can only orient JPEGs, but asked for orientation: " + exifOrientation
+ " with format: " + format);

// TODO: support exif orientations for formats other than JPEG.
if (format == CompressFormat.JPEG) {
return openFileStream(width, height, exifOrientation);
} else {
return openInMemoryStream(format, width, height);
}
}

private static InputStream openFileStream(int width, int height, int exifOrientation) {
int rotationDegrees = TransformationUtils.getExifOrientationDegrees(exifOrientation);
if (rotationDegrees == 270 || rotationDegrees == 90) {
int temp = width;
width = height;
height = temp;
}

Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);

OutputStream os = null;
try {
File tempFile =
File.createTempFile(
"ds-" + width + "-" + height + "-" + exifOrientation,
".jpeg",
ApplicationProvider.getApplicationContext().getCacheDir());
os = new BufferedOutputStream(new FileOutputStream(tempFile));
bitmap.compress(CompressFormat.JPEG, /*quality=*/ 100, os);
os.close();

ExifInterface exifInterface = new ExifInterface(tempFile.getAbsolutePath());
exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(exifOrientation));
exifInterface.saveAttributes();

InputStream result = new BufferedInputStream(new FileInputStream(tempFile));
if (!tempFile.delete()) {
throw new IllegalStateException("Failed to delete: " + tempFile);
}
return result;
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {}
}
}
}

private static InputStream openInMemoryStream(CompressFormat format, int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(format, 100 /*quality*/, os);
Expand Down Expand Up @@ -428,7 +496,9 @@ Tester givenSquareImageWithDimensionOf(int dimension, Api... apis) {
}

Tester givenImageWithDimensionsOf(int sourceWidth, int sourceHeight, Api... apis) {
testCases.add(new TestCase(sourceWidth, sourceHeight, targetWidth, targetHeight, apis));
testCases.add(
new TestCase(
sourceWidth, sourceHeight, targetWidth, targetHeight, apis));
return this;
}

Expand Down Expand Up @@ -456,7 +526,12 @@ private static final class TestCase {
private final int targetHeight;
private final Api[] apis;

TestCase(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight, Api... apis) {
TestCase(
int sourceWidth,
int sourceHeight,
int targetWidth,
int targetHeight,
Api... apis) {
this.sourceWidth = sourceWidth;
this.sourceHeight = sourceHeight;
this.targetWidth = targetWidth;
Expand All @@ -467,7 +542,9 @@ private static final class TestCase {
List<String> test(DownsampleStrategy strategy) throws IOException {
List<String> results = new ArrayList<>();
for (Api api : apis) {
results.addAll(api.test(sourceWidth, sourceHeight, targetWidth, targetHeight, strategy));
results.addAll(
api.test(
sourceWidth, sourceHeight, targetWidth, targetHeight, strategy));
}
return results;
}
Expand Down Expand Up @@ -539,7 +616,8 @@ List<String> test(
List<String> results = new ArrayList<>();
for (Formats format : formats) {
results.addAll(
format.runTest(sourceWidth, sourceHeight, targetWidth, targetHeight, strategy));
format.runTest(
sourceWidth, sourceHeight, targetWidth, targetHeight, strategy));
}
return results;
}
Expand All @@ -549,6 +627,20 @@ static final class Formats {
private final int expectedWidth;
private final int expectedHeight;
private final CompressFormat[] formats;
private static final int[] ALL_EXIF_ORIENTATIONS =
new int[] {
ExifInterface.ORIENTATION_UNDEFINED,
ExifInterface.ORIENTATION_NORMAL,
ExifInterface.ORIENTATION_FLIP_HORIZONTAL,
ExifInterface.ORIENTATION_ROTATE_180,
ExifInterface.ORIENTATION_FLIP_VERTICAL,
ExifInterface.ORIENTATION_TRANSPOSE,
ExifInterface.ORIENTATION_ROTATE_90,
ExifInterface.ORIENTATION_TRANSVERSE,
ExifInterface.ORIENTATION_ROTATE_270 };
private static final int[] UNDEFINED_EXIF_ORIENTATIONS =
new int[] { ExifInterface.ORIENTATION_UNDEFINED };


static final class Builder {
private final CompressFormat[] formats;
Expand Down Expand Up @@ -585,18 +677,24 @@ List<String> runTest(
throws IOException {
List<String> result = new ArrayList<>();
for (CompressFormat format : formats) {
String testResult =
runScaleTest(
format,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
strategy,
expectedWidth,
expectedHeight);
if (testResult != null) {
result.add(testResult);
int[] exifOrientations =
format == CompressFormat.JPEG ? ALL_EXIF_ORIENTATIONS : UNDEFINED_EXIF_ORIENTATIONS;
for (int exifOrientation : exifOrientations) {

String testResult =
runScaleTest(
format,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
exifOrientation,
strategy,
expectedWidth,
expectedHeight);
if (testResult != null) {
result.add(testResult);
}
}
}
return result;
Expand Down

0 comments on commit 41877c6

Please sign in to comment.