/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.webpsupport;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Override;
import android.app.Instrumentation;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.os.Environment;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.InstrumentationTestCase;
import android.os.Build;
import com.facebook.common.internal.ByteStreams;
import com.facebook.common.internal.Throwables;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.core.ImagePipelineFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class WebpBitmapFactoryTest extends InstrumentationTestCase {
private Instrumentation mInstrumentation;
private WebpBitmapFactoryImpl mWebpBitmapFactory;
@Override
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mWebpBitmapFactory = new WebpBitmapFactoryImpl();
ImagePipelineConfig.Builder configBuilder =
ImagePipelineConfig.newBuilder(mInstrumentation.getContext())
.experiment().setWebpBitmapFactory(mWebpBitmapFactory);
ImagePipelineFactory.initialize(configBuilder.build());
}
private FileDescriptor getImageFileDescriptor(String path) {
try {
File file = File.createTempFile("reqsquare", "tmp");
byte[] data = ByteStreams.toByteArray(getTestImageInputStream(path));
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
return new FileInputStream(file).getFD();
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
private InputStream getTestImageInputStream(String path) {
try {
return mInstrumentation.getContext().getResources().getAssets().open(path);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
/**
* Return input stream for jpeg
* @return InputStream instance
*/
private InputStream getTestJpegInputStream() {
return getTestImageInputStream("redsquare.jpg");
}
/**
* Return input stream for lossless webp
* @return InputStream instance
*/
private InputStream getTestWebpInputStream() {
return getTestImageInputStream("redsquare.webp");
}
@Test
public void testJpegFallback() throws Throwable {
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestJpegInputStream(),
null,
null);
assertNotNull("Bitmap should not be null", bitmap);
assertEquals("Width should be decoded properly", 20, bitmap.getWidth());
assertEquals("Height should be decoded properly", 20, bitmap.getHeight());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// TODO 8639341 - In GB colors are not decoded exactly as they are into the image
assertEquals("Bitmap pixels should be red", 0xFFFF0100, bitmap.getPixel(5, 8));
}
}
@Test
public void testWebpDecodeStream() throws Throwable {
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
null);
assertNotNull("Bitmap should not be null", bitmap);
assertEquals("Width should be decoded properly", 20, bitmap.getWidth());
assertEquals("Height should be decoded properly", 20, bitmap.getHeight());
assertEquals("Bitmap pixels should be red", 0xFFFF0100, bitmap.getPixel(5, 8));
// Alternatively, load image manually adb pull /mnt/sdcard/resulthooked.jpg
bitmap.compress(
Bitmap.CompressFormat.JPEG,
90,
new FileOutputStream(Environment.getExternalStorageDirectory() + "/resulthooked.jpg"));
}
@Test
public void testWebpJustDecodeBounds() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
assertNull("Bitmap should be null", bitmap);
assertEquals("Width should be decoded properly", 20, options.outWidth);
assertEquals("Height should be decoded properly", 20, options.outHeight);
}
@Test
public void testInBitmap() throws Throwable {
if (!WebpBitmapFactoryImpl.IN_BITMAP_SUPPORTED) {
return;
}
Bitmap inBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = inBitmap;
final Bitmap outBitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
assertNotNull("Bitmap should not be null", outBitmap);
assertSame("Output bitmap shuold be the same as input bitmap", inBitmap, outBitmap);
assertEquals("Bitmap pixels should be red", 0xFFFF0100, outBitmap.getPixel(5, 8));
}
@Test
public void testByteArrayDecode() throws Throwable {
byte[] data = ByteStreams.toByteArray(getTestWebpInputStream());
final Bitmap bitmap = mWebpBitmapFactory.decodeByteArray(
data,
0,
data.length,
null);
testBitmapDefault(bitmap, 20, 20);
}
@Test
public void testOutMimeType() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
if (options.outMimeType != null) {
// Not all devices are able to get this info from the image
assertEquals("Mime type should be detected properly", "image/webp", options.outMimeType);
}
WebpBitmapFactoryImpl.hookDecodeStream(getTestJpegInputStream(), null, options);
assertEquals("Mime type should be detected properly", "image/jpeg", options.outMimeType);
}
@Test
public void testInTempStorage() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[128 * 1024];
Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
testBitmapDefault(bitmap, 20, 20);
}
@Test
public void testInSampleSize() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
testBitmapDefault(bitmap, 10, 10);
}
@Test
public void testOutWidthHeight() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
assertNotNull("Bitmap should not be null", bitmap);
assertEquals("Width should be scaled", 20, options.outWidth);
assertEquals("Height should be scaled", 20, options.outHeight);
}
@Test
public void testOutPadding() throws Throwable {
Rect outPadding = new Rect();
mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
outPadding,
null);
assertNotNull("Padding rect should not be null", outPadding);
assertEquals("Padding rect for webp should be not supported)", -1, outPadding.top);
assertEquals("Padding rect for webp should be not supported)", -1, outPadding.left);
assertEquals("Padding rect for webp should be not supported)", -1, outPadding.bottom);
assertEquals("Padding rect for webp should be not supported)", -1, outPadding.right);
}
@Test
public void testWebpFileDescriptorDecode() throws Throwable {
FileDescriptor fd = getImageFileDescriptor("redsquare.webp");
final Bitmap bitmap = mWebpBitmapFactory.decodeFileDescriptor(
fd,
null,
null);
testBitmapDefault(bitmap, 20, 20);
}
@Test
public void testJpegFileDescriptorDecode() throws Throwable {
FileDescriptor fd = getImageFileDescriptor("redsquare.jpg");
final Bitmap bitmap = mWebpBitmapFactory.decodeFileDescriptor(
fd,
null,
null);
testBitmapDefault(bitmap, 20, 20);
}
@Test
public void testInScaled() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inScreenDensity = 240;
options.inDensity = 480;
options.inTargetDensity = 240;
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
testBitmapDefault(bitmap, 10, 10);
}
@Test
public void testInScaled2() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inScreenDensity = 480;
options.inDensity = 480;
options.inTargetDensity = 240;
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
testBitmapDefault(bitmap, 20, 20);
}
@Test
public void testInScaled3() throws Throwable {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inScreenDensity = 240;
options.inDensity = 0;
options.inTargetDensity = 240;
final Bitmap bitmap = mWebpBitmapFactory.decodeStream(
getTestWebpInputStream(),
null,
options);
testBitmapDefault(bitmap, 20, 20);
}
private void testBitmapDefault(Bitmap bitmap, int width, int height) {
assertNotNull("Bitmap should not be null", bitmap);
assertEquals("Width should be set properly", width, bitmap.getWidth());
assertEquals("Height should be set properly", height, bitmap.getHeight());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// TODO 8639341 - In GB colors are not decoded exactly as they are into the image
assertEquals("Bitmap pixels should be red", 0xFFFF0100, bitmap.getPixel(1, 1));
}
}
}