xref: /aosp_15_r20/external/robolectric/robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java (revision e6ba16074e6af37d123cb567d575f496bf0a58ee)
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.O;
4 import static android.os.Build.VERSION_CODES.S;
5 import static com.google.common.truth.Truth.assertThat;
6 import static org.robolectric.Shadows.shadowOf;
7 
8 import android.graphics.Bitmap;
9 import android.graphics.Canvas;
10 import android.graphics.Color;
11 import android.graphics.ColorMatrix;
12 import android.graphics.ColorMatrixColorFilter;
13 import android.graphics.Matrix;
14 import android.graphics.Paint;
15 import android.graphics.Rect;
16 import android.os.Parcel;
17 import android.util.DisplayMetrics;
18 import androidx.test.ext.junit.runners.AndroidJUnit4;
19 import java.io.ByteArrayOutputStream;
20 import java.nio.ByteBuffer;
21 import java.nio.IntBuffer;
22 import java.nio.LongBuffer;
23 import java.nio.ShortBuffer;
24 import java.util.Arrays;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.robolectric.Shadows;
28 import org.robolectric.annotation.Config;
29 import org.robolectric.annotation.GraphicsMode;
30 import org.robolectric.annotation.GraphicsMode.Mode;
31 import org.robolectric.shadow.api.Shadow;
32 
33 @RunWith(AndroidJUnit4.class)
34 @GraphicsMode(Mode.LEGACY)
35 public class ShadowBitmapTest {
36   @Test
shouldCreateScaledBitmap()37   public void shouldCreateScaledBitmap() {
38     Bitmap originalBitmap = create("Original bitmap");
39     Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, 100, 200, false);
40     assertThat(shadowOf(scaledBitmap).getDescription())
41         .isEqualTo("Original bitmap scaled to 100 x 200");
42     assertThat(scaledBitmap.getWidth()).isEqualTo(100);
43     assertThat(scaledBitmap.getHeight()).isEqualTo(200);
44     scaledBitmap.getPixels(new int[20000], 0, 0, 0, 0, 100, 200);
45   }
46 
47   @Test
createScaledBitmap_succeedForLargeBitmapWithFilter()48   public void createScaledBitmap_succeedForLargeBitmapWithFilter() {
49     createScaledBitmap_succeedForLargeBitmap(true);
50   }
51 
52   @Test
createScaledBitmap_succeedForLargeBitmapWithoutFilter()53   public void createScaledBitmap_succeedForLargeBitmapWithoutFilter() {
54     createScaledBitmap_succeedForLargeBitmap(false);
55   }
56 
57   @Test
createScaledBitmap_modifiesPixelsWithFilter()58   public void createScaledBitmap_modifiesPixelsWithFilter() {
59     createScaledBitmap_modifiesPixels(true);
60   }
61 
62   @Test
createScaledBitmap_modifiesPixelsWithoutFilter()63   public void createScaledBitmap_modifiesPixelsWithoutFilter() {
64     createScaledBitmap_modifiesPixels(false);
65   }
66 
67   @Test
createScaledBitmap_expectedUpSizeWithFilter()68   public void createScaledBitmap_expectedUpSizeWithFilter() {
69     createScaledBitmap_expectedUpSize(true);
70   }
71 
72   @Test
createScaledBitmap_expectedUpSizeWithoutFilter()73   public void createScaledBitmap_expectedUpSizeWithoutFilter() {
74     createScaledBitmap_expectedUpSize(false);
75   }
76 
77   @Test
createScaledBitmap_expectedDownSizeWithFilter()78   public void createScaledBitmap_expectedDownSizeWithFilter() {
79     createScaledBitmap_expectedDownSize(true);
80   }
81 
82   @Test
createScaledBitmap_expectedDownSizeWithoutFilter()83   public void createScaledBitmap_expectedDownSizeWithoutFilter() {
84     createScaledBitmap_expectedDownSize(false);
85   }
86 
87   @Test
createScaledBitmap_drawOnScaledWithFilter()88   public void createScaledBitmap_drawOnScaledWithFilter() {
89     createScaledBitmap_drawOnScaled(true);
90   }
91 
92   @Test
createScaledBitmap_drawOnScaledWithoutFilter()93   public void createScaledBitmap_drawOnScaledWithoutFilter() {
94     createScaledBitmap_drawOnScaled(false);
95   }
96 
97   @Test
shouldCreateActiveBitmap()98   public void shouldCreateActiveBitmap() {
99     Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
100     assertThat(bitmap.isRecycled()).isFalse();
101     assertThat(bitmap.getPixel(0, 0)).isEqualTo(0);
102     assertThat(bitmap.getWidth()).isEqualTo(100);
103     assertThat(bitmap.getHeight()).isEqualTo(200);
104     assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
105   }
106 
107   @Test
hasAlpha()108   public void hasAlpha() {
109     Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
110     assertThat(bitmap.hasAlpha()).isTrue();
111     bitmap.setHasAlpha(false);
112     assertThat(bitmap.hasAlpha()).isFalse();
113   }
114 
115   @Test
hasMipmap()116   public void hasMipmap() {
117     Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
118     assertThat(bitmap.hasMipMap()).isFalse();
119     bitmap.setHasMipMap(true);
120     assertThat(bitmap.hasMipMap()).isTrue();
121   }
122 
123   @Test
getAllocationByteCount()124   public void getAllocationByteCount() {
125     Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
126     assertThat(bitmap.getAllocationByteCount()).isGreaterThan(0);
127   }
128 
129   @Test
shouldCreateBitmapWithColors()130   public void shouldCreateBitmapWithColors() {
131     int[] colors =
132         new int[] {
133           Color.parseColor("#ff0000"), Color.parseColor("#00ff00"), Color.parseColor("#0000ff"),
134           Color.parseColor("#990000"), Color.parseColor("#009900"), Color.parseColor("#000099")
135         };
136     Bitmap bitmap = Bitmap.createBitmap(colors, 3, 2, Bitmap.Config.ARGB_8888);
137     assertThat(bitmap.getWidth()).isEqualTo(3);
138     assertThat(bitmap.getHeight()).isEqualTo(2);
139     assertThat(bitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
140     assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.parseColor("#ff0000"));
141     assertThat(bitmap.getPixel(0, 1)).isEqualTo(Color.parseColor("#990000"));
142     assertThat(bitmap.getPixel(1, 0)).isEqualTo(Color.parseColor("#00ff00"));
143     assertThat(bitmap.getPixel(1, 1)).isEqualTo(Color.parseColor("#009900"));
144     assertThat(bitmap.getPixel(2, 0)).isEqualTo(Color.parseColor("#0000ff"));
145     assertThat(bitmap.getPixel(2, 1)).isEqualTo(Color.parseColor("#000099"));
146   }
147 
148   @Test
shouldCreateBitmapWithMatrix()149   public void shouldCreateBitmapWithMatrix() {
150     Bitmap originalBitmap = create("Original bitmap");
151     ((ShadowLegacyBitmap) Shadow.extract(originalBitmap)).setWidth(200);
152     ((ShadowLegacyBitmap) Shadow.extract(originalBitmap)).setHeight(200);
153     Matrix m = new Matrix();
154     m.postRotate(90);
155     Bitmap newBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, 100, 50, m, true);
156 
157     ShadowLegacyBitmap shadowBitmap = Shadow.extract(newBitmap);
158     assertThat(shadowBitmap.getDescription())
159         .isEqualTo(
160             "Original bitmap at (0,0) with width 100 and height 50"
161                 + " using matrix Matrix[pre=[], set={}, post=[rotate 90.0]] with filter");
162     assertThat(shadowBitmap.getCreatedFromBitmap()).isEqualTo(originalBitmap);
163     assertThat(shadowBitmap.getCreatedFromX()).isEqualTo(0);
164     assertThat(shadowBitmap.getCreatedFromY()).isEqualTo(0);
165     assertThat(shadowBitmap.getCreatedFromWidth()).isEqualTo(100);
166     assertThat(shadowBitmap.getCreatedFromHeight()).isEqualTo(50);
167     assertThat(shadowBitmap.getCreatedFromMatrix()).isEqualTo(m);
168     assertThat(shadowBitmap.getCreatedFromFilter()).isEqualTo(true);
169     assertThat(shadowBitmap.getWidth()).isEqualTo(50);
170     assertThat(shadowBitmap.getHeight()).isEqualTo(100);
171   }
172 
173   @Test
shouldCreateMutableBitmap()174   public void shouldCreateMutableBitmap() {
175     Bitmap mutableBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
176     assertThat(mutableBitmap.isMutable()).isTrue();
177   }
178 
179   @Test
shouldCreateMutableBitmapWithDisplayMetrics()180   public void shouldCreateMutableBitmapWithDisplayMetrics() {
181     final DisplayMetrics metrics = new DisplayMetrics();
182     metrics.densityDpi = 1000;
183 
184     final Bitmap bitmap = Bitmap.createBitmap(metrics, 100, 100, Bitmap.Config.ARGB_8888);
185     assertThat(bitmap.isMutable()).isTrue();
186     assertThat(bitmap.getDensity()).isEqualTo(1000);
187   }
188 
189   @Test
shouldRecycleBitmap()190   public void shouldRecycleBitmap() {
191     Bitmap bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
192     bitmap.recycle();
193     assertThat(bitmap.isRecycled()).isTrue();
194   }
195 
196   @Test
shouldReceiveDescriptionWhenDrawingToCanvas()197   public void shouldReceiveDescriptionWhenDrawingToCanvas() {
198     Bitmap bitmap1 = create("Bitmap One");
199     Bitmap bitmap2 = create("Bitmap Two");
200 
201     Canvas canvas = new Canvas(bitmap1);
202     canvas.drawBitmap(bitmap2, 0, 0, null);
203 
204     assertThat(shadowOf(bitmap1).getDescription()).isEqualTo("Bitmap One\nBitmap Two");
205   }
206 
207   @Test
shouldReceiveDescriptionWhenDrawingToCanvasWithBitmapAndMatrixAndPaint()208   public void shouldReceiveDescriptionWhenDrawingToCanvasWithBitmapAndMatrixAndPaint() {
209     Bitmap bitmap1 = create("Bitmap One");
210     Bitmap bitmap2 = create("Bitmap Two");
211 
212     Canvas canvas = new Canvas(bitmap1);
213     canvas.drawBitmap(bitmap2, new Matrix(), null);
214 
215     assertThat(shadowOf(bitmap1).getDescription())
216         .isEqualTo("Bitmap One\nBitmap Two transformed by Matrix[pre=[], set={}, post=[]]");
217   }
218 
219   @Test
shouldReceiveDescriptionWhenDrawABitmapToCanvasWithAPaintEffect()220   public void shouldReceiveDescriptionWhenDrawABitmapToCanvasWithAPaintEffect() {
221     Bitmap bitmap1 = create("Bitmap One");
222     Bitmap bitmap2 = create("Bitmap Two");
223 
224     Canvas canvas = new Canvas(bitmap1);
225     Paint paint = new Paint();
226     paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix()));
227     canvas.drawBitmap(bitmap2, new Matrix(), paint);
228 
229     assertThat(shadowOf(bitmap1).getDescription())
230         .isEqualTo(
231             "Bitmap One\n"
232                 + "Bitmap Two with ColorMatrixColorFilter"
233                 + " transformed by Matrix[pre=[], set={}, post=[]]");
234   }
235 
236   @Test
visualize_shouldReturnDescription()237   public void visualize_shouldReturnDescription() {
238     Bitmap bitmap = create("Bitmap One");
239     assertThat(ShadowBitmap.visualize(bitmap)).isEqualTo("Bitmap One");
240   }
241 
242   @Test
shouldCopyBitmap()243   public void shouldCopyBitmap() {
244     Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class);
245     Bitmap bitmapCopy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
246     assertThat(bitmapCopy.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
247     assertThat(bitmapCopy.isMutable()).isTrue();
248   }
249 
250   @Test(expected = NullPointerException.class)
rowBytesIsAccurate()251   public void rowBytesIsAccurate() {
252     Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
253     assertThat(b1.getRowBytes()).isEqualTo(40);
254     Bitmap b2 = Bitmap.createBitmap(10, 10, Bitmap.Config.RGB_565);
255     assertThat(b2.getRowBytes()).isEqualTo(20);
256 
257     // Null Bitmap.Config is not allowed.
258     Bitmap b3 = Bitmap.createBitmap(10, 10, null);
259     b3.getRowBytes();
260   }
261 
262   @Test(expected = NullPointerException.class)
byteCountIsAccurate()263   public void byteCountIsAccurate() {
264     Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
265     assertThat(b1.getByteCount()).isEqualTo(400);
266     Bitmap b2 = Bitmap.createBitmap(10, 10, Bitmap.Config.RGB_565);
267     assertThat(b2.getByteCount()).isEqualTo(200);
268 
269     // Null Bitmap.Config is not allowed.
270     Bitmap b3 = Bitmap.createBitmap(10, 10, null);
271     b3.getByteCount();
272   }
273 
274   @Test
shouldSetDensity()275   public void shouldSetDensity() {
276     final Bitmap bitmap =
277         Bitmap.createBitmap(new DisplayMetrics(), 100, 100, Bitmap.Config.ARGB_8888);
278     bitmap.setDensity(1000);
279     assertThat(bitmap.getDensity()).isEqualTo(1000);
280   }
281 
282   @Test
shouldSetPixel()283   public void shouldSetPixel() {
284     Bitmap bitmap = Bitmap.createBitmap(new int[] {1}, 1, 1, Bitmap.Config.ARGB_8888);
285     shadowOf(bitmap).setMutable(true);
286     bitmap.setPixel(0, 0, 2);
287     assertThat(bitmap.getPixel(0, 0)).isEqualTo(2);
288     assertThat(shadowOf(bitmap).getCreatedFromColors()).isEqualTo(new int[] {1});
289   }
290 
291   @Test
shouldSetPixel_allocateOnTheFly()292   public void shouldSetPixel_allocateOnTheFly() {
293     Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
294     shadowOf(bitmap).setMutable(true);
295     bitmap.setPixel(0, 0, 2);
296     assertThat(bitmap.getPixel(0, 0)).isEqualTo(2);
297     assertThat(shadowOf(bitmap).getCreatedFromColors()).isNull();
298   }
299 
300   @Test
testGetPixels()301   public void testGetPixels() {
302     // Create a dummy bitmap.
303     Bitmap bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
304     for (int y = 0; y < bmp.getHeight(); ++y) {
305       for (int x = 0; x < bmp.getWidth(); ++x) {
306         bmp.setPixel(x, y, packRGB(x, y, 0));
307       }
308     }
309 
310     // Use getPixels to get pixels as an array (getPixels was the missing Shadowed Function).
311     int[] pixels = new int[bmp.getWidth() * bmp.getHeight()];
312     bmp.getPixels(pixels, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());
313 
314     // Every entry should match regardless of accessing it by getPixel vs getPixels.
315     for (int y = 0; y < bmp.getHeight(); ++y) {
316       for (int x = 0; x < bmp.getWidth(); ++x) {
317         assertThat(bmp.getPixel(x, y)).isEqualTo(pixels[y * bmp.getWidth() + x]);
318       }
319     }
320   }
321 
322   @Test(expected = IllegalStateException.class)
shouldThrowExceptionForSetPixelOnImmutableBitmap()323   public void shouldThrowExceptionForSetPixelOnImmutableBitmap() {
324     Bitmap bitmap = Bitmap.createBitmap(new int[] {1}, 1, 1, Bitmap.Config.ARGB_8888);
325     bitmap.setPixel(0, 0, 2);
326   }
327 
328   @Test(expected = IllegalStateException.class)
shouldThrowExceptionForSetPixelsOnImmutableBitmap()329   public void shouldThrowExceptionForSetPixelsOnImmutableBitmap() {
330     Bitmap bitmap = Bitmap.createBitmap(new int[] {1}, 1, 1, Bitmap.Config.ARGB_8888);
331     bitmap.setPixels(new int[] {1}, 0, 0, 0, 0, 1, 1);
332   }
333 
334   @Test
bitmapsAreReused()335   public void bitmapsAreReused() {
336     Bitmap b = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
337     Bitmap b1 = Bitmap.createBitmap(b, 0, 0, 10, 10);
338     assertThat(b1).isSameInstanceAs(b);
339     Bitmap b2 = Bitmap.createBitmap(b, 0, 0, 10, 10, null, false);
340     assertThat(b2).isSameInstanceAs(b);
341     Bitmap b3 = Bitmap.createScaledBitmap(b, 10, 10, false);
342     assertThat(b3).isSameInstanceAs(b);
343   }
344 
345   @Test
equalsSizeTransformReturnsOriginal()346   public void equalsSizeTransformReturnsOriginal() {
347     Bitmap b1 = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
348     Bitmap b2 = Bitmap.createBitmap(b1, 0, 0, 10, 10, null, false);
349     assertThat(b1).isSameInstanceAs(b2);
350     Bitmap b3 = Bitmap.createBitmap(b1, 0, 0, 10, 10, null, true);
351     assertThat(b1).isSameInstanceAs(b3);
352   }
353 
354   @Test(expected = IllegalArgumentException.class)
throwsExceptionForInvalidDimensions()355   public void throwsExceptionForInvalidDimensions() {
356     Bitmap b = Bitmap.createBitmap(10, 20, Bitmap.Config.ARGB_8888);
357     Bitmap.createBitmap(b, 0, 0, 20, 10, null, false);
358   }
359 
360   @Test(expected = IllegalArgumentException.class)
throwsExceptionForNegativeWidth()361   public void throwsExceptionForNegativeWidth() {
362     Bitmap.createBitmap(-100, 10, Bitmap.Config.ARGB_8888);
363   }
364 
365   @Test(expected = IllegalArgumentException.class)
throwsExceptionForZeroHeight()366   public void throwsExceptionForZeroHeight() {
367     Bitmap.createBitmap(100, 0, Bitmap.Config.ARGB_8888);
368   }
369 
370   @Test
shouldGetPixelsFromAnyNonNullableCreatedBitmap()371   public void shouldGetPixelsFromAnyNonNullableCreatedBitmap() {
372     Bitmap bitmap;
373     int width = 10;
374     int height = 10;
375 
376     int[] pixels = new int[width * height];
377     bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
378     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
379 
380     bitmap = Bitmap.createBitmap(bitmap);
381     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
382 
383     bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
384     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
385 
386     bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, new Matrix(), false);
387     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
388 
389     bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
390     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
391 
392     bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
393     bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
394   }
395 
396   @Test
shouldGetPixelsFromSubsetOfBitmap()397   public void shouldGetPixelsFromSubsetOfBitmap() {
398     int width = 10;
399     int height = 10;
400     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
401     int offset = 7;
402     int subWidth = 3;
403     int subHeight = 4;
404     int x = 2;
405     int y = 5;
406 
407     // Fill a region of the bitmap with increasing redness.
408     int r = 0;
409     for (int y0 = y; y0 < y + subHeight; y0++) {
410       for (int x0 = x; x0 < x + subWidth; x0++) {
411         bitmap.setPixel(x0, y0, packRGB(r++, 0, 0));
412       }
413     }
414 
415     // Get the pixels from that region.
416     int[] pixels = new int[offset + subWidth * subHeight];
417     bitmap.getPixels(pixels, offset, subWidth, x, y, subWidth, subHeight);
418 
419     // Verify that pixels contains the expected colors.
420     r = 0;
421     int index = offset;
422     for (int y0 = 0; y0 < subHeight; y0++) {
423       for (int x0 = 0; x0 < subWidth; x0++) {
424         assertThat(pixels[index++]).isEqualTo(packRGB(r++, 0, 0));
425       }
426     }
427   }
428 
429   @Test
shouldAdjustDimensionsForMatrix()430   public void shouldAdjustDimensionsForMatrix() {
431     Bitmap transformedBitmap;
432     int width = 10;
433     int height = 20;
434 
435     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
436     Matrix matrix = new Matrix();
437     transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
438     assertThat(transformedBitmap.getWidth()).isEqualTo(width);
439     assertThat(transformedBitmap.getHeight()).isEqualTo(height);
440 
441     matrix.setRotate(90);
442     transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
443     assertThat(transformedBitmap.getWidth()).isEqualTo(height);
444     assertThat(transformedBitmap.getHeight()).isEqualTo(width);
445 
446     matrix.setScale(2, 3);
447     transformedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
448     assertThat(transformedBitmap.getWidth()).isEqualTo(width * 2);
449     assertThat(transformedBitmap.getHeight()).isEqualTo(height * 3);
450   }
451 
452   @Test
shouldWriteToParcelAndReconstruct()453   public void shouldWriteToParcelAndReconstruct() {
454     Bitmap bitmapOriginal;
455     int originalWidth = 10;
456     int originalHeight = 10;
457 
458     bitmapOriginal = Bitmap.createBitmap(originalWidth, originalHeight, Bitmap.Config.ARGB_8888);
459 
460     Parcel parcel = Parcel.obtain();
461     bitmapOriginal.writeToParcel(parcel, 0);
462 
463     parcel.setDataPosition(0);
464 
465     Bitmap bitmapReconstructed = Bitmap.CREATOR.createFromParcel(parcel);
466 
467     // get reconstructed properties
468     int reconstructedHeight = bitmapReconstructed.getHeight();
469     int reconstructedWidth = bitmapReconstructed.getWidth();
470 
471     // compare bitmap properties
472     assertThat(originalHeight).isEqualTo(reconstructedHeight);
473     assertThat(originalWidth).isEqualTo(reconstructedWidth);
474     assertThat(bitmapOriginal.getConfig()).isEqualTo(bitmapReconstructed.getConfig());
475 
476     int[] pixelsOriginal = new int[originalWidth * originalHeight];
477     bitmapOriginal.getPixels(pixelsOriginal, 0, originalWidth, 0, 0, originalWidth, originalHeight);
478 
479     int[] pixelsReconstructed = new int[reconstructedWidth * reconstructedHeight];
480     bitmapReconstructed.getPixels(
481         pixelsReconstructed, 0, reconstructedWidth, 0, 0, reconstructedWidth, reconstructedHeight);
482 
483     assertThat(Arrays.equals(pixelsOriginal, pixelsReconstructed)).isTrue();
484   }
485 
486   @Test
shouldCopyPixelsToBufferAndReconstruct()487   public void shouldCopyPixelsToBufferAndReconstruct() {
488     int width = 10;
489     int height = 10;
490 
491     Bitmap bitmapOriginal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
492     bitmapOriginal.setPixel(0, 0, 123);
493     bitmapOriginal.setPixel(1, 1, 456);
494     bitmapOriginal.setPixel(2, 2, 789);
495     int[] pixelsOriginal = new int[width * height];
496     bitmapOriginal.getPixels(pixelsOriginal, 0, width, 0, 0, width, height);
497 
498     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
499     bitmapOriginal.copyPixelsToBuffer(buffer);
500     assertThat(buffer.position()).isEqualTo(bitmapOriginal.getByteCount());
501 
502     buffer.rewind();
503     Bitmap bitmapReconstructed = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
504     // Set some random pixels to ensure that they're properly overwritten.
505     bitmapReconstructed.setPixel(1, 1, 999);
506     bitmapReconstructed.setPixel(4, 4, 999);
507     bitmapReconstructed.copyPixelsFromBuffer(buffer);
508     assertThat(buffer.position()).isEqualTo(bitmapOriginal.getByteCount());
509 
510     assertThat(bitmapReconstructed.getPixel(0, 0)).isEqualTo(123);
511     assertThat(bitmapReconstructed.getPixel(1, 1)).isEqualTo(456);
512     assertThat(bitmapReconstructed.getPixel(2, 2)).isEqualTo(789);
513 
514     int[] pixelsReconstructed = new int[width * height];
515     bitmapReconstructed.getPixels(pixelsReconstructed, 0, width, 0, 0, width, height);
516     assertThat(Arrays.equals(pixelsOriginal, pixelsReconstructed)).isTrue();
517   }
518 
519   @Test
compress_shouldLessThanBeforeForWebp()520   public void compress_shouldLessThanBeforeForWebp() {
521     Bitmap bitmap = Bitmap.createBitmap(400, 200, Bitmap.Config.ARGB_8888);
522     ByteArrayOutputStream stream = new ByteArrayOutputStream();
523     bitmap.compress(Bitmap.CompressFormat.WEBP, 75, stream);
524     byte[] compressedImageByteArray = stream.toByteArray();
525     assertThat(compressedImageByteArray.length).isLessThan(bitmap.getByteCount());
526   }
527 
528   @Test
compress_shouldSucceedForNullPixelData()529   public void compress_shouldSucceedForNullPixelData() {
530     Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class);
531     ShadowLegacyBitmap shadowBitmap = Shadow.extract(bitmap);
532     shadowBitmap.setWidth(100);
533     shadowBitmap.setHeight(100);
534     ByteArrayOutputStream stream = new ByteArrayOutputStream();
535     bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
536   }
537 
538   @Config(sdk = O)
539   @Test
getBytesPerPixel_O()540   public void getBytesPerPixel_O() {
541     assertThat(ShadowLegacyBitmap.getBytesPerPixel(Bitmap.Config.RGBA_F16)).isEqualTo(8);
542   }
543 
544   @Test
getBytesPerPixel_preO()545   public void getBytesPerPixel_preO() {
546     assertThat(ShadowLegacyBitmap.getBytesPerPixel(Bitmap.Config.ARGB_8888)).isEqualTo(4);
547     assertThat(ShadowLegacyBitmap.getBytesPerPixel(Bitmap.Config.RGB_565)).isEqualTo(2);
548     assertThat(ShadowLegacyBitmap.getBytesPerPixel(Bitmap.Config.ARGB_4444)).isEqualTo(2);
549     assertThat(ShadowLegacyBitmap.getBytesPerPixel(Bitmap.Config.ALPHA_8)).isEqualTo(1);
550   }
551 
552   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsToShortBuffer()553   public void throwsExceptionCopyPixelsToShortBuffer() {
554     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
555     ShortBuffer buffer = ShortBuffer.allocate(bitmapOriginal.getByteCount());
556     bitmapOriginal.copyPixelsToBuffer(buffer);
557   }
558 
559   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsToLongBuffer()560   public void throwsExceptionCopyPixelsToLongBuffer() {
561     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
562     LongBuffer buffer = LongBuffer.allocate(bitmapOriginal.getByteCount());
563     bitmapOriginal.copyPixelsToBuffer(buffer);
564   }
565 
566   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsToBufferTooSmall()567   public void throwsExceptionCopyPixelsToBufferTooSmall() {
568     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
569     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount() - 1);
570     bitmapOriginal.copyPixelsToBuffer(buffer);
571   }
572 
573   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsToBufferNonArgb8888()574   public void throwsExceptionCopyPixelsToBufferNonArgb8888() {
575     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
576     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
577     bitmapOriginal.copyPixelsToBuffer(buffer);
578   }
579 
580   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsFromShortBuffer()581   public void throwsExceptionCopyPixelsFromShortBuffer() {
582     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
583     ShortBuffer buffer = ShortBuffer.allocate(bitmapOriginal.getByteCount());
584     bitmapOriginal.copyPixelsFromBuffer(buffer);
585   }
586 
587   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsFromIntBufferTooSmall()588   public void throwsExceptionCopyPixelsFromIntBufferTooSmall() {
589     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
590     IntBuffer buffer =
591         IntBuffer.allocate(bitmapOriginal.getWidth() * bitmapOriginal.getHeight() - 1);
592     bitmapOriginal.copyPixelsFromBuffer(buffer);
593   }
594 
595   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsFromLongBuffer()596   public void throwsExceptionCopyPixelsFromLongBuffer() {
597     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
598     LongBuffer buffer = LongBuffer.allocate(bitmapOriginal.getByteCount());
599     bitmapOriginal.copyPixelsFromBuffer(buffer);
600   }
601 
602   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsFromBufferTooSmall()603   public void throwsExceptionCopyPixelsFromBufferTooSmall() {
604     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
605     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount() - 1);
606     bitmapOriginal.copyPixelsFromBuffer(buffer);
607   }
608 
609   @Test(expected = RuntimeException.class)
throwsExceptionCopyPixelsFromBufferNonArgb8888()610   public void throwsExceptionCopyPixelsFromBufferNonArgb8888() {
611     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_4444);
612     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
613     bitmapOriginal.copyPixelsFromBuffer(buffer);
614   }
615 
616   @Test(expected = IllegalStateException.class)
throwsExceptionCopyPixelsFromBufferRecycled()617   public void throwsExceptionCopyPixelsFromBufferRecycled() {
618     Bitmap bitmapOriginal = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
619     ByteBuffer buffer = ByteBuffer.allocate(bitmapOriginal.getByteCount());
620     bitmapOriginal.recycle();
621     bitmapOriginal.copyPixelsFromBuffer(buffer);
622   }
623 
624   @Test
reconfigure_withArgb8888Bitmap_validDimensionsAndConfig_doesNotThrow()625   public void reconfigure_withArgb8888Bitmap_validDimensionsAndConfig_doesNotThrow() {
626     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
627     original.reconfigure(100, 100, Bitmap.Config.ARGB_8888);
628   }
629 
630   @Config(sdk = O)
631   @Test(expected = IllegalStateException.class)
reconfigure_withHardwareBitmap_validDimensionsAndConfig_throws()632   public void reconfigure_withHardwareBitmap_validDimensionsAndConfig_throws() {
633     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
634     original.setConfig(Bitmap.Config.HARDWARE);
635     original.reconfigure(100, 100, Bitmap.Config.ARGB_8888);
636   }
637 
638   @Test
isPremultiplied_argb888_defaultsTrue()639   public void isPremultiplied_argb888_defaultsTrue() {
640     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
641 
642     assertThat(original.isPremultiplied()).isTrue();
643   }
644 
645   @Test
isPremultiplied_argb888_noAlpha_defaultsFalse()646   public void isPremultiplied_argb888_noAlpha_defaultsFalse() {
647     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
648     original.setHasAlpha(false);
649 
650     assertThat(original.isPremultiplied()).isFalse();
651   }
652 
653   @Test
isPremultiplied_rgb565_defaultsFalse()654   public void isPremultiplied_rgb565_defaultsFalse() {
655     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);
656 
657     assertThat(original.isPremultiplied()).isFalse();
658   }
659 
660   @Test
setPremultiplied_argb888_isFalse()661   public void setPremultiplied_argb888_isFalse() {
662     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
663     original.setPremultiplied(false);
664 
665     assertThat(original.isPremultiplied()).isFalse();
666   }
667 
668   @Test
sameAs_bitmapsDifferentWidth()669   public void sameAs_bitmapsDifferentWidth() {
670     Bitmap original1 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
671     Bitmap original2 = Bitmap.createBitmap(101, 100, Bitmap.Config.ARGB_8888);
672     assertThat(original1.sameAs(original2)).isFalse();
673   }
674 
675   @Test
sameAs_bitmapsDifferentHeight()676   public void sameAs_bitmapsDifferentHeight() {
677     Bitmap original1 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
678     Bitmap original2 = Bitmap.createBitmap(100, 101, Bitmap.Config.ARGB_8888);
679     assertThat(original1.sameAs(original2)).isFalse();
680   }
681 
682   @Test
sameAs_bitmapsDifferentConfig()683   public void sameAs_bitmapsDifferentConfig() {
684     Bitmap original1 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
685     Bitmap original2 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444);
686     assertThat(original1.sameAs(original2)).isFalse();
687   }
688 
689   @Test
sameAs_bitmapsDifferentPixels()690   public void sameAs_bitmapsDifferentPixels() {
691     int[] pixels1 = new int[] {0, 1, 2, 3};
692     Bitmap original1 = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
693     original1.setPixels(pixels1, 0, 1, 0, 0, 2, 2);
694 
695     int[] pixels2 = new int[] {3, 2, 1, 0};
696     Bitmap original2 = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
697     original2.setPixels(pixels2, 0, 1, 0, 0, 2, 2);
698     assertThat(original1.sameAs(original2)).isFalse();
699   }
700 
701   @Test
sameAs_bitmapsSamePixels()702   public void sameAs_bitmapsSamePixels() {
703     int[] pixels = new int[] {0, 1, 2, 3};
704     Bitmap original1 = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
705     original1.setPixels(pixels, 0, 1, 0, 0, 2, 2);
706 
707     Bitmap original2 = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
708     original2.setPixels(pixels, 0, 1, 0, 0, 2, 2);
709     assertThat(original1.sameAs(original2)).isTrue();
710   }
711 
712   @Test
extractAlpha()713   public void extractAlpha() {
714     int[] pixels = new int[] {0xFF123456, 0x00123456, 0x88999999, 0x12345678};
715     Bitmap bitmap = Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888);
716     bitmap.setPixels(
717         pixels,
718         /* offset= */ 0,
719         /* stride= */ 2,
720         /* x= */ 0,
721         /* y= */ 0,
722         /* width= */ 2,
723         /* height= */ 2);
724 
725     Bitmap alpha = bitmap.extractAlpha();
726 
727     assertThat(alpha.getPixel(0, 0)).isEqualTo(0xFF000000);
728     assertThat(alpha.getPixel(1, 0)).isEqualTo(0x00000000);
729     assertThat(alpha.getPixel(0, 1)).isEqualTo(0x88000000);
730     assertThat(alpha.getPixel(1, 1)).isEqualTo(0x12000000);
731   }
732 
733   @Test
extractAlpha_withArgs()734   public void extractAlpha_withArgs() {
735     int[] pixels = new int[] {0xFF123456, 0x00123456, 0x88999999, 0x12345678};
736     Bitmap bitmap = Bitmap.createBitmap(/* width= */ 2, /* height= */ 2, Bitmap.Config.ARGB_8888);
737     bitmap.setPixels(
738         pixels,
739         /* offset= */ 0,
740         /* stride= */ 2,
741         /* x= */ 0,
742         /* y= */ 0,
743         /* width= */ 2,
744         /* height= */ 2);
745 
746     Bitmap alpha = bitmap.extractAlpha(/* paint= */ null, /* offsetXY= */ new int[2]);
747 
748     assertThat(alpha.getPixel(0, 0)).isEqualTo(0xFF000000);
749     assertThat(alpha.getPixel(1, 0)).isEqualTo(0x00000000);
750     assertThat(alpha.getPixel(0, 1)).isEqualTo(0x88000000);
751     assertThat(alpha.getPixel(1, 1)).isEqualTo(0x12000000);
752   }
753 
754   @Test
eraseColor_clearsDescription()755   public void eraseColor_clearsDescription() {
756     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
757     Bitmap scaled = Bitmap.createScaledBitmap(original, 200, 200, false);
758     scaled.eraseColor(Color.TRANSPARENT);
759     String description = Shadows.shadowOf(scaled).getDescription();
760     assertThat(description).isEqualTo("Bitmap (200, 200)");
761     scaled.eraseColor(Color.BLUE);
762     description = Shadows.shadowOf(scaled).getDescription();
763     assertThat(description).isEqualTo("Bitmap (200, 200) erased with 0xff0000ff");
764   }
765 
766   @Config(minSdk = S)
767   @Test
asShared_shouldReturnImmutableInstance()768   public void asShared_shouldReturnImmutableInstance() {
769     Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
770 
771     assertThat(original.asShared().isMutable()).isFalse();
772   }
773 
create(String name)774   private static Bitmap create(String name) {
775     Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class);
776     shadowOf(bitmap).appendDescription(name);
777     return bitmap;
778   }
779 
packRGB(int r, int g, int b)780   private static int packRGB(int r, int g, int b) {
781     return 0xff000000 | r << 16 | g << 8 | b;
782   }
783 
createScaledBitmap_succeedForLargeBitmap(boolean filter)784   private void createScaledBitmap_succeedForLargeBitmap(boolean filter) {
785     Bitmap bitmap = Bitmap.createBitmap(100000, 10, Bitmap.Config.ARGB_8888);
786     Bitmap.createScaledBitmap(bitmap, 480000, 48, filter);
787   }
788 
createScaledBitmap_modifiesPixels(boolean filter)789   private void createScaledBitmap_modifiesPixels(boolean filter) {
790     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
791     bitmap.eraseColor(Color.BLUE);
792     Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, filter);
793     assertThat(scaledBitmap.getPixel(0, 0)).isEqualTo(Color.BLUE);
794   }
795 
createScaledBitmap_expectedUpSize(boolean filter)796   private void createScaledBitmap_expectedUpSize(boolean filter) {
797     Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
798     Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 32, 32, filter);
799     ShadowLegacyBitmap shadowBitmap = Shadow.extract(scaledBitmap);
800     assertThat(shadowBitmap.getBufferedImage().getWidth()).isEqualTo(32);
801     assertThat(shadowBitmap.getBufferedImage().getHeight()).isEqualTo(32);
802   }
803 
createScaledBitmap_expectedDownSize(boolean filter)804   private void createScaledBitmap_expectedDownSize(boolean filter) {
805     Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
806     Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 10, 10, filter);
807     ShadowLegacyBitmap shadowBitmap = Shadow.extract(scaledBitmap);
808     assertThat(shadowBitmap.getBufferedImage().getWidth()).isEqualTo(10);
809     assertThat(shadowBitmap.getBufferedImage().getHeight()).isEqualTo(10);
810   }
811 
createScaledBitmap_drawOnScaled(boolean filter)812   private void createScaledBitmap_drawOnScaled(boolean filter) {
813     Bitmap original = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
814     Bitmap scaled = Bitmap.createScaledBitmap(original, 32, 32, filter);
815     Canvas canvas = new Canvas(scaled);
816     Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
817     p.setColor(Color.BLACK);
818     canvas.drawRect(new Rect(0, 0, 32, 32), p);
819   }
820 }
821