blob: 0dbfc1404a441f7902f2f8b75b311b0958d937f9 [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static org.assertj.core.api.Assertions.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Parcel;
import android.util.DisplayMetrics;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
public class ShadowBitmapTest {
@Test
public void shouldCreateScaledBitmap() throws Exception {
Bitmap originalBitmap = create("Original bitmap", 100, 100);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, 100, 200, false);
assertThat(shadowOf(scaledBitmap).getDescription())
.isEqualTo("Original bitmap scaled to 100 x 200");
assertThat(scaledBitmap.getWidth()).isEqualTo(100);
assertThat(scaledBitmap.getHeight()).isEqualTo(200);
scaledBitmap.getPixels(new int[20000], 0, 0, 0, 0, 100, 200);
}
@Test
public void shouldCreateActiveBitmap() throws Exception {
Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
assertThat(bitmap.isRecycled()).isFalse();
assertThat(bitmap.getPixel(0, 0)).isZero();
assertThat(bitmap.getWidth()).isEqualTo(100);
assertThat(bitmap.getHeight()).isEqualTo(200);
assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
}
@Test
public void hasAlpha() {
Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
assertThat(bitmap.hasAlpha()).isFalse();
bitmap.setHasAlpha(true);
assertThat(bitmap.hasAlpha()).isTrue();
}
@Test
@Config(minSdk = JELLY_BEAN_MR1)
public void hasMipmap() {
Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
assertThat(bitmap.hasMipMap()).isFalse();
bitmap.setHasMipMap(true);
assertThat(bitmap.hasMipMap()).isTrue();
}
@Test
public void shouldCreateBitmapWithColors() throws Exception {
int[] colors = new int[] {
Color.parseColor("#ff0000"), Color.parseColor("#00ff00"), Color.parseColor("#0000ff"),
Color.parseColor("#990000"), Color.parseColor("#009900"), Color.parseColor("#000099")
};
Bitmap bitmap = Bitmap.createBitmap(colors, 3, 2, Bitmap.Config.ARGB_8888);
assertThat(bitmap.getWidth()).isEqualTo(3);
assertThat(bitmap.getHeight()).isEqualTo(2);
assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.parseColor("#ff0000"));
assertThat(bitmap.getPixel(0, 1)).isEqualTo(Color.parseColor("#990000"));
assertThat(bitmap.getPixel(1, 0)).isEqualTo(Color.parseColor("#00ff00"));
assertThat(bitmap.getPixel(1, 1)).isEqualTo(Color.parseColor("#009900"));
assertThat(bitmap.getPixel(2, 0)).isEqualTo(Color.parseColor("#0000ff"));
assertThat(bitmap.getPixel(2, 1)).isEqualTo(Color.parseColor("#000099"));
}
@Test
public void shouldCreateBitmapFromAnotherBitmap() {
Bitmap originalBitmap = create("Original bitmap", 100, 100);
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap);
assertThat(shadowOf(newBitmap).getDescription())
.isEqualTo("Original bitmap created from Bitmap object");
}
@Test
public void shouldCreateBitmapWithMatrix() {
Bitmap originalBitmap = create("Original bitmap", 100, 100);
shadowOf(originalBitmap).setWidth(200);
shadowOf(originalBitmap).setHeight(200);
Matrix m = new Matrix();
m.postRotate(90);
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, 100, 100, m, true);
assertThat(shadowOf(newBitmap).getDescription())
.isEqualTo("Original bitmap at (0,0) with width 100 and height 100" +
" using matrix Matrix[pre=[], set={}, post=[rotate 90.0]] with filter");
}
@Test
public void shouldCreateMutableBitmap() throws Exception {
Bitmap mutableBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
assertThat(mutableBitmap.isMutable()).isTrue();
}
@Test
@Config(minSdk = JELLY_BEAN_MR1)
public void shouldCreateMutableBitmapWithDisplayMetrics() throws Exception {
final DisplayMetrics metrics = new DisplayMetrics();
metrics.densityDpi = 1000;
final Bitmap bitmap = Bitmap.createBitmap(metrics, 100, 100, Bitmap.Config.ARGB_8888);
assertThat(bitmap.isMutable()).isTrue();
assertThat(bitmap.getDensity()).isEqualTo(1000);
}
@Test
public void shouldRecycleBitmap() throws Exception {
Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
bitmap.recycle();
assertThat(bitmap.isRecycled()).isTrue();
}
@Test
public void shouldReceiveDescriptionWhenDrawingToCanvas() throws Exception {
Bitmap bitmap1 = create("Bitmap One", 100, 100);
Bitmap bitmap2 = create("Bitmap Two", 100, 100);
Canvas canvas = new Canvas(bitmap1);
canvas.drawBitmap(bitmap2, 0, 0, null);
assertThat(shadowOf(bitmap1).getDescription()).isEqualTo("Bitmap One\nBitmap Two");
}
@Test
public void shouldReceiveDescriptionWhenDrawingToCanvasWithBitmapAndMatrixAndPaint() throws Exception {
Bitmap bitmap1 = create("Bitmap One", 100, 100);
Bitmap bitmap2 = create("Bitmap Two", 100, 100);
Canvas canvas = new Canvas(bitmap1);
canvas.drawBitmap(bitmap2, new Matrix(), null);
assertThat(shadowOf(bitmap1).getDescription())
.isEqualTo("Bitmap One\nBitmap Two transformed by Matrix[pre=[], set={}, post=[]]");
}
@Test
public void shouldReceiveDescriptionWhenDrawABitmapToCanvasWithAPaintEffect() throws Exception {
Bitmap bitmap1 = create("Bitmap One", 100, 100);
Bitmap bitmap2 = create("Bitmap Two", 100, 100);
Canvas canvas = new Canvas(bitmap1);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix()));
canvas.drawBitmap(bitmap2, new Matrix(), paint);
assertThat(shadowOf(bitmap1).getDescription())
.isEqualTo("Bitmap One\n" +
"Bitmap Two with ColorMatrixColorFilter<1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0>" +
" transformed by Matrix[pre=[], set={}, post=[]]");
}
@Test
public void visualize_shouldReturnDescription() throws Exception {
Bitmap bitmap = create("Bitmap One", 100, 100);
assertThat(ShadowBitmap.visualize(bitmap))
.isEqualTo("Bitmap One");
}
@Test
public void shouldCopyBitmap() {
Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class);
Bitmap bitmapCopy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
assertThat(shadowOf(bitmapCopy).getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
assertThat(shadowOf(bitmapCopy).isMutable()).isTrue();
}
@Test(expected = NullPointerException.class)
public void rowBytesIsAccurate() {
Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
assertThat(b1.getRowBytes()).isEqualTo(40);
Bitmap b2 = Bitmap.createBitmap(10, 10, Bitmap.Config.RGB_565);
assertThat(b2.getRowBytes()).isEqualTo(20);
// Null Bitmap.Config is not allowed.
Bitmap b3 = Bitmap.createBitmap(10, 10, null);
b3.getRowBytes();
}
@Test(expected = NullPointerException.class)
@Config(minSdk = JELLY_BEAN_MR1)
public void byteCountIsAccurate() {
Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
assertThat(b1.getByteCount()).isEqualTo(400);
Bitmap b2 = Bitmap.createBitmap(10, 10, Bitmap.Config.RGB_565);
assertThat(b2.getByteCount()).isEqualTo(200);
// Null Bitmap.Config is not allowed.
Bitmap b3 = Bitmap.createBitmap(10, 10, null);
b3.getByteCount();
}
@Test
@Config(minSdk = JELLY_BEAN_MR1)
public void shouldSetDensity() {
final Bitmap bitmap = Bitmap.createBitmap(new DisplayMetrics(), 100, 100, Bitmap.Config.ARGB_8888);
bitmap.setDensity(1000);
assertThat(bitmap.getDensity()).isEqualTo(1000);
}
@Test
public void shouldSetPixel() {
Bitmap bitmap = Bitmap.createBitmap(new int[] { 1 }, 1, 1, Bitmap.Config.ARGB_8888);
shadowOf(bitmap).setMutable(true);
bitmap.setPixel(0, 0, 2);
assertThat(bitmap.getPixel(0, 0)).isEqualTo(2);
assertThat(shadowOf(bitmap).getCreatedFromColors()).isEqualTo(new int[] { 1 });
}
@Test
public void shouldSetPixel_allocateOnTheFly() {
Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
shadowOf(bitmap).setMutable(true);
bitmap.setPixel(0, 0, 2);
assertThat(bitmap.getPixel(0, 0)).isEqualTo(2);
assertThat(shadowOf(bitmap).getCreatedFromColors()).isNull();
}
@Test
public void testGetPixels() {
// Create a dummy bitmap.
Bitmap bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
for (int y = 0; y < bmp.getHeight(); ++y) {
for (int x = 0; x < bmp.getWidth(); ++x) {
bmp.setPixel(x, y, packRGB(x, y, 0));
}
}
// Use getPixels to get pixels as an array (getPixels was the missing Shadowed Function).
int[] pixels = new int[bmp.getWidth() * bmp.getHeight()];
bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
// Every entry should match regardless of accessing it by getPixel vs getPixels.
for (int y = 0; y < bmp.getHeight(); ++y) {
for (int x = 0; x < bmp.getWidth(); ++x) {
assertThat(bmp.getPixel(x, y)).isEqualTo(pixels[y * bmp.getWidth() + x]);
}
}
}
@Test(expected = IllegalStateException.class)
public void shouldThrowExceptionForSetPixelOnImmutableBitmap() {
Bitmap bitmap = Bitmap.createBitmap(new int[] { 1 }, 1, 1, Bitmap.Config.ARGB_8888);
bitmap.setPixel(0, 0, 2);
}
@Test
public void bitmapsAreReused() {
Bitmap b = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
Bitmap b1 = Bitmap.createBitmap(b, 0, 0, 10, 10);
assertThat(b1).isSameAs(b);
Bitmap b2 = Bitmap.createBitmap(b, 0, 0, 10, 10, null, false);
assertThat(b2).isSameAs(b);
Bitmap b3 = Bitmap.createScaledBitmap(b, 10, 10, false);
assertThat(b3).isSameAs(b);
}
@Test
public void equalsSizeTransformReturnsOriginal() {
Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
Bitmap b2 = Bitmap.createBitmap(b1, 0, 0, 10, 10, null, false);
assertThat(b1).isSameAs(b2);
Bitmap b3 = Bitmap.createBitmap(b1, 0, 0, 10, 10, null, true);
assertThat(b1).isSameAs(b3);
}
@Test(expected = IllegalArgumentException.class)
public void throwsExceptionForInvalidDimensions() {
Bitmap b = Bitmap.createBitmap(10, 20, Bitmap.Config.ARGB_8888);
Bitmap.createBitmap(b, 0, 0, 20, 10, null, false);
}
@Test(expected = IllegalArgumentException.class)
public void throwsExceptionForNegativeWidth() {
Bitmap.createBitmap(-100, 10, Bitmap.Config.ARGB_8888);
}
@Test(expected = IllegalArgumentException.class)
public void throwsExceptionForZeroHeight() {
Bitmap.createBitmap(100, 0, Bitmap.Config.ARGB_8888);
}
@Test
public void shouldGetPixelsFromAnyNonNullableCreatedBitmap() {
Bitmap bitmap;
int width = 10;
int height = 10;
int[] pixels = new int[width * height];
bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(bitmap);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, new Matrix(), false);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
// subset of pixels:
bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
bitmap.getPixels(pixels, 0, width, 2, 2, 3, 3);
}
@Test
public void shouldAdjustDimensionsForMatrix() {
Bitmap transformedBitmap;
int width = 10;
int height = 20;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Matrix matrix = new Matrix();
transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
assertThat(transformedBitmap.getWidth())
.isEqualTo(width);
assertThat(transformedBitmap.getHeight())
.isEqualTo(height);
matrix.setRotate(90);
transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
assertThat(transformedBitmap.getWidth())
.isEqualTo(height);
assertThat(transformedBitmap.getHeight())
.isEqualTo(width);
matrix.setScale(2, 3);
transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
assertThat(transformedBitmap.getWidth())
.isEqualTo(width * 2);
assertThat(transformedBitmap.getHeight())
.isEqualTo(height * 3);
}
@Test
public void shouldWriteToParcelAndReconstruct() {
Bitmap bitmapOriginal;
int originalWidth = 10;
int originalHeight = 10;
bitmapOriginal = Bitmap.createBitmap(originalWidth, originalHeight, Bitmap.Config.ARGB_8888);
Parcel parcel = Parcel.obtain();
bitmapOriginal.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
Bitmap bitmapReconstructed = Bitmap.CREATOR.createFromParcel(parcel);
// get reconstructed properties
int reconstructedHeight = bitmapReconstructed.getHeight();
int reconstructedWidth = bitmapReconstructed.getWidth();
//compare bitmap properties
assertThat(originalHeight).isEqualTo(reconstructedHeight);
assertThat(originalWidth).isEqualTo(reconstructedWidth);
assertThat(bitmapOriginal.getConfig()).isEqualTo(bitmapReconstructed.getConfig());
int[] pixelsOriginal = new int[originalWidth * originalHeight];
bitmapOriginal.getPixels(pixelsOriginal, 0, originalWidth, 0, 0, originalWidth, originalHeight);
int[] pixelsReconstructed = new int[reconstructedWidth * reconstructedHeight];
bitmapReconstructed.getPixels(pixelsReconstructed, 0, reconstructedWidth, 0, 0,
reconstructedWidth, reconstructedHeight);
assertThat(Arrays.equals(pixelsOriginal, pixelsReconstructed)).isTrue();
}
@Test
public void shouldCopyPixelsToBufferAndReconstruct() {
int width = 10;
int height = 10;
Bitmap bitmapOriginal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmapOriginal.setPixel(0, 0, 123);
bitmapOriginal.setPixel(1, 1, 456);
bitmapOriginal.setPixel(2, 2, 789);
int[] pixelsOriginal = new int[width * height];
bitmapOriginal.getPixels(pixelsOriginal, 0, width, 0, 0, width, height);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsToBuffer(buffer);
assertThat(buffer.position()).isEqualTo(bitmapOriginal.getByteCount());
buffer.rewind();
Bitmap bitmapReconstructed = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Set some random pixels to ensure that they're properly overwritten.
bitmapReconstructed.setPixel(1,1, 999);
bitmapReconstructed.setPixel(4,4, 999);
bitmapReconstructed.copyPixelsFromBuffer(buffer);
assertThat(buffer.position()).isEqualTo(bitmapOriginal.getByteCount());
assertThat(bitmapReconstructed.getPixel(0, 0)).isEqualTo(123);
assertThat(bitmapReconstructed.getPixel(1, 1)).isEqualTo(456);
assertThat(bitmapReconstructed.getPixel(2, 2)).isEqualTo(789);
int[] pixelsReconstructed = new int[width * height];
bitmapReconstructed.getPixels(pixelsReconstructed, 0, width, 0, 0, width, height);
assertThat(Arrays.equals(pixelsOriginal, pixelsReconstructed)).isTrue();
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsToShortBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
ShortBuffer buffer = ShortBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsToBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsToIntBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
IntBuffer buffer = IntBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsToBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsToLongBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
LongBuffer buffer = LongBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsToBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsToBufferTooSmall() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount() - 1);
bitmapOriginal.copyPixelsToBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsToBufferNonArgb8888() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsToBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsFromShortBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
ShortBuffer buffer = ShortBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsFromIntBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
IntBuffer buffer = IntBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsFromLongBuffer() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
LongBuffer buffer = LongBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsFromBufferTooSmall() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount() - 1);
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
@Test(expected = RuntimeException.class)
public void throwsExceptionCopyPixelsFromBufferNonArgb8888() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
@Test(expected = IllegalStateException.class)
public void throwsExceptionCopyPixelsFromBufferRecycled() {
Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
bitmapOriginal.recycle();
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
private static Bitmap create(String name, int width, int height) {
Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class);
shadowOf(bitmap).appendDescription(name);
return bitmap;
}
private static int packRGB(int r, int g, int b) {
return 0xff000000 | r << 16 | g << 8 | b;
}
}