• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.graphics;
2 
3 import static android.os.Build.VERSION_CODES.M;
4 import static android.os.Build.VERSION_CODES.O;
5 import static android.os.Build.VERSION_CODES.P;
6 import static android.os.Build.VERSION_CODES.Q;
7 import static androidx.test.InstrumentationRegistry.getTargetContext;
8 import static com.google.common.truth.Truth.assertThat;
9 import static com.google.common.truth.TruthJUnit.assume;
10 import static org.junit.Assert.assertThrows;
11 
12 import android.content.res.Resources;
13 import android.graphics.Bitmap.CompressFormat;
14 import android.graphics.drawable.BitmapDrawable;
15 import android.graphics.drawable.ColorDrawable;
16 import android.graphics.drawable.Drawable;
17 import android.os.Build;
18 import android.util.DisplayMetrics;
19 import androidx.test.ext.junit.runners.AndroidJUnit4;
20 import androidx.test.filters.SdkSuppress;
21 import androidx.test.platform.graphics.HardwareRendererCompat;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.ByteBuffer;
27 import java.nio.IntBuffer;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.robolectric.annotation.Config;
32 import org.robolectric.annotation.internal.DoNotInstrument;
33 import org.robolectric.testapp.R;
34 
35 /** Compatibility test for {@link Bitmap} */
36 @DoNotInstrument
37 @RunWith(AndroidJUnit4.class)
38 public class BitmapTest {
39 
40   private Resources resources;
41 
42   @Before
setUp()43   public void setUp() {
44     resources = getTargetContext().getResources();
45   }
46 
47   @Config(minSdk = P)
48   @SdkSuppress(minSdkVersion = P)
createBitmap()49   @Test public void createBitmap() {
50     assume().that(System.getProperty("robolectric.graphicsMode")).isNotEqualTo("NATIVE");
51     // Bitmap.createBitmap(Picture) requires hardware-backed bitmaps
52     HardwareRendererCompat.setDrawingEnabled(true);
53     Picture picture = new Picture();
54     Canvas canvas = picture.beginRecording(200, 100);
55 
56     Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
57 
58     p.setColor(0x88FF0000);
59     canvas.drawCircle(50, 50, 40, p);
60 
61     p.setColor(Color.GREEN);
62     p.setTextSize(30);
63     canvas.drawText("Pictures", 60, 60, p);
64     picture.endRecording();
65 
66     Bitmap bitmap = Bitmap.createBitmap(picture);
67     assertThat(bitmap).isNotNull();
68   }
69 
70   @Test
testEraseColor()71   public void testEraseColor() {
72     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
73     bitmap.eraseColor(0xffff0000);
74     assertThat(bitmap.getPixel(10, 10)).isEqualTo(0xffff0000);
75     assertThat(bitmap.getPixel(50, 50)).isEqualTo(0xffff0000);
76   }
77 
78   @Test
79   @SdkSuppress(minSdkVersion = M) // getAlpha() returns 0 on less than M
testExtractAlpha()80   public void testExtractAlpha() {
81     // normal case
82     Bitmap bitmap =
83         BitmapFactory.decodeResource(resources, R.drawable.an_image, new BitmapFactory.Options());
84     Bitmap ret = bitmap.extractAlpha();
85     int source = bitmap.getPixel(10, 20);
86     int result = ret.getPixel(10, 20);
87     assertThat(Color.alpha(result)).isEqualTo(Color.alpha(source));
88   }
89 
90   @Test
testCopy()91   public void testCopy() {
92     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
93     Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false);
94     assertThat(copy.getWidth()).isEqualTo(bitmap.getWidth());
95     assertThat(copy.getHeight()).isEqualTo(bitmap.getHeight());
96     assertThat(copy.getConfig()).isEqualTo(bitmap.getConfig());
97   }
98 
99   @Test
testCopyAndEraseColor()100   public void testCopyAndEraseColor() {
101     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
102     bitmap.eraseColor(0xffffff00);
103     assertThat(bitmap.getPixel(10, 10)).isEqualTo(0xffffff00);
104     assertThat(bitmap.getPixel(50, 50)).isEqualTo(0xffffff00);
105 
106     Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
107     assertThat(copy.getPixel(10, 10)).isEqualTo(0xffffff00);
108     assertThat(copy.getPixel(50, 50)).isEqualTo(0xffffff00);
109 
110     copy.eraseColor(0xffff0000);
111     assertThat(copy.getPixel(10, 10)).isEqualTo(0xffff0000);
112     assertThat(copy.getPixel(50, 50)).isEqualTo(0xffff0000);
113   }
114 
115   @Test
compress()116   public void compress() {
117     Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.an_image);
118 
119     ByteArrayOutputStream stm = new ByteArrayOutputStream();
120     assertThat(bitmap.compress(CompressFormat.JPEG, 0, stm)).isTrue();
121     assertThat(stm.toByteArray()).isNotEmpty();
122   }
123 
124   @Test
getConfigAfterCompress()125   public void getConfigAfterCompress() throws IOException {
126     InputStream inputStream = resources.getAssets().open("robolectric.png");
127     Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
128     Matrix matrix = new Matrix();
129     matrix.setScale(0.5f, 0.5f);
130     Bitmap scaledBitmap =
131         Bitmap.createBitmap(
132             bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, /* filter */ true);
133     assertThat(scaledBitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
134   }
135 
136   @Test
getConfigAfterCreateScaledBitmap()137   public void getConfigAfterCreateScaledBitmap() throws IOException {
138     InputStream inputStream = resources.getAssets().open("robolectric.png");
139     Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
140     Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, /* filter= */ false);
141     assertThat(scaledBitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888);
142   }
143 
144   @Test
scaledBitmap_sameAs()145   public void scaledBitmap_sameAs() {
146     Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
147     bitmap1.eraseColor(0xffff0000);
148     Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
149     bitmap2.eraseColor(0xff00ff00);
150     assertThat(bitmap1.sameAs(bitmap2)).isFalse();
151 
152     Bitmap scaled1 = Bitmap.createScaledBitmap(bitmap1, 200, 200, false);
153     Bitmap scaled2 = Bitmap.createScaledBitmap(bitmap2, 200, 200, false);
154     assertThat(scaled1.sameAs(scaled2)).isFalse();
155   }
156 
157   @Test
checkBitmapNotRecycled()158   public void checkBitmapNotRecycled() throws IOException {
159     InputStream inputStream = resources.getAssets().open("robolectric.png");
160     BitmapFactory.Options options = new BitmapFactory.Options();
161     options.inScaled = true;
162     options.inDensity = 100;
163     options.inTargetDensity = 500;
164     Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
165     assertThat(bitmap.isRecycled()).isFalse();
166   }
167 
168   @Test
decodeResource_withMutableOpt_isMutable()169   public void decodeResource_withMutableOpt_isMutable() {
170     BitmapFactory.Options opt = new BitmapFactory.Options();
171     opt.inMutable = true;
172     Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.an_image, opt);
173     assertThat(bitmap.isMutable()).isTrue();
174   }
175 
176   @Test
scaledBitmap_isMutable()177   public void scaledBitmap_isMutable() throws IOException {
178     InputStream inputStream = resources.getAssets().open("robolectric.png");
179     BitmapFactory.Options opt = new BitmapFactory.Options();
180     opt.inMutable = true;
181     Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, opt);
182     Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, false);
183     assertThat(scaledBitmap.isMutable()).isTrue();
184   }
185 
186   @Test
colorDrawable_drawToBitmap()187   public void colorDrawable_drawToBitmap() {
188     Drawable colorDrawable = new ColorDrawable(Color.RED);
189     Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
190     Canvas canvas = new Canvas(bitmap);
191     assertThat(canvas.getWidth()).isEqualTo(1);
192     assertThat(canvas.getHeight()).isEqualTo(1);
193     colorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
194     colorDrawable.draw(canvas);
195     assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED);
196   }
197 
198   @Test
drawCanvas_bitmap_sameSize()199   public void drawCanvas_bitmap_sameSize() {
200     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
201     bitmap.eraseColor(0xff00ff00);
202     Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
203     Canvas canvas = new Canvas(output);
204     canvas.drawBitmap(bitmap, 0, 0, null);
205     assertThat(bitmap.sameAs(output)).isTrue();
206   }
207 
208   @Test
drawCanvas_bitmap_centered()209   public void drawCanvas_bitmap_centered() {
210     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
211     bitmap.eraseColor(0xff00ff00);
212     Bitmap output = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
213     Canvas canvas = new Canvas(output);
214     canvas.drawBitmap(bitmap, 50, 50, null);
215     assertThat(output.getPixel(49, 49)).isEqualTo(0);
216     assertThat(output.getPixel(50, 50)).isEqualTo(0xff00ff00);
217     assertThat(output.getPixel(149, 149)).isEqualTo(0xff00ff00);
218     assertThat(output.getPixel(150, 150)).isEqualTo(0);
219   }
220 
221   @Test
drawCanvas_overflow_topLeft()222   public void drawCanvas_overflow_topLeft() {
223     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
224     bitmap.eraseColor(0xff00ff00);
225     Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
226     Canvas canvas = new Canvas(output);
227     canvas.drawBitmap(bitmap, -50, -50, null);
228     assertThat(output.getPixel(0, 0)).isEqualTo(0xff00ff00);
229     assertThat(output.getPixel(49, 49)).isEqualTo(0xff00ff00);
230     assertThat(output.getPixel(50, 50)).isEqualTo(0);
231   }
232 
233   @Test
drawCanvas_overflow_topRight()234   public void drawCanvas_overflow_topRight() {
235     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
236     bitmap.eraseColor(0xff00ff00);
237     Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
238     Canvas canvas = new Canvas(output);
239     canvas.drawBitmap(bitmap, 50, -50, null);
240     assertThat(output.getPixel(99, 0)).isEqualTo(0xff00ff00);
241     assertThat(output.getPixel(50, 49)).isEqualTo(0xff00ff00);
242     assertThat(output.getPixel(49, 50)).isEqualTo(0);
243   }
244 
245   @Test
drawCanvas_overflow_bottomLeft()246   public void drawCanvas_overflow_bottomLeft() {
247     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
248     bitmap.eraseColor(0xff00ff00);
249     Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
250     Canvas canvas = new Canvas(output);
251     canvas.drawBitmap(bitmap, -50, 50, null);
252     assertThat(output.getPixel(0, 99)).isEqualTo(0xff00ff00);
253     assertThat(output.getPixel(49, 50)).isEqualTo(0xff00ff00);
254     assertThat(output.getPixel(50, 49)).isEqualTo(0);
255   }
256 
257   @Test
drawCanvas_overflow_bottomRight()258   public void drawCanvas_overflow_bottomRight() {
259     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
260     bitmap.eraseColor(0xff00ff00);
261     Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
262     Canvas canvas = new Canvas(output);
263     canvas.drawBitmap(bitmap, 50, 50, null);
264     assertThat(output.getPixel(99, 99)).isEqualTo(0xff00ff00);
265     assertThat(output.getPixel(50, 50)).isEqualTo(0xff00ff00);
266     assertThat(output.getPixel(49, 49)).isEqualTo(0);
267   }
268 
269   @Test
createScaledBitmap_zeroWidthAndHeight_error()270   public void createScaledBitmap_zeroWidthAndHeight_error() {
271     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
272     Exception exception =
273         assertThrows(
274             IllegalArgumentException.class, () -> Bitmap.createScaledBitmap(bitmap, 0, 0, false));
275 
276     assertThat(exception).hasMessageThat().contains("width and height must be > 0");
277   }
278 
279   @Test
getBitmapPixels_strideTooLong()280   public void getBitmapPixels_strideTooLong() {
281     int[] bitmapPixels = {1, 2, 3, 4, 5, 6, 7, 8, 9};
282     Bitmap bitmap = Bitmap.createBitmap(bitmapPixels, 3, 3, Bitmap.Config.ARGB_8888);
283     int[] pixelsCopy = new int[bitmap.getHeight() * bitmap.getWidth()];
284     assertThrows(
285         ArrayIndexOutOfBoundsException.class,
286         () ->
287             bitmap.getPixels(
288                 pixelsCopy, 0, bitmap.getRowBytes(), 0, 0, bitmap.getWidth(), bitmap.getHeight()));
289   }
290 
291   @Test
eraseColor_toTransparent()292   public void eraseColor_toTransparent() {
293     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
294     bitmap.eraseColor(0);
295     assertThat(bitmap.getPixel(0, 0)).isEqualTo(0);
296   }
297 
298   @Test
reconfigure_drawPixel()299   public void reconfigure_drawPixel() {
300     Bitmap bitmap = Bitmap.createBitmap(100, 50, Bitmap.Config.ARGB_8888);
301     bitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888);
302     bitmap.setPixel(0, 99, Color.RED);
303     assertThat(bitmap.getPixel(0, 99)).isEqualTo(Color.RED);
304   }
305 
306   /**
307    * Questionable ARGB_8888 pixel values like '10' may be simplified by some graphics engines to
308    * '0'. This happens because '10' has alpha transparency '0', so the values for RGB don't matter.
309    * This happens when Java's Graphics2d is used for certain.
310    */
311   @Test
recompress_png100_samePixelss()312   public void recompress_png100_samePixelss() {
313     Bitmap applicationIconBitmap =
314         Bitmap.createBitmap(new int[] {10, 11, 12, 13}, 2, 2, Bitmap.Config.ARGB_8888);
315 
316     BitmapDrawable applicationIcon = new BitmapDrawable(resources, applicationIconBitmap);
317 
318     ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();
319     applicationIconBitmap.compress(CompressFormat.PNG, 100, outputStream1);
320 
321     Bitmap bitmap =
322         Bitmap.createBitmap(
323             applicationIcon.getIntrinsicWidth(),
324             applicationIcon.getIntrinsicHeight(),
325             Bitmap.Config.ARGB_8888);
326     Canvas canvas = new Canvas(bitmap);
327     applicationIcon.draw(canvas);
328     ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
329     bitmap.compress(CompressFormat.PNG, 100, outputStream2);
330     assertThat(outputStream2.toByteArray()).isEqualTo(outputStream1.toByteArray());
331   }
332 
333   @Test
compress_thenDecodeStream_sameAs()334   public void compress_thenDecodeStream_sameAs() {
335     Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Bitmap.Config.ARGB_8888);
336     ByteArrayOutputStream outStream = new ByteArrayOutputStream();
337     bitmap.compress(CompressFormat.PNG, /* quality= */ 100, outStream);
338     byte[] outBytes = outStream.toByteArray();
339     ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes);
340     Bitmap bitmap2 = BitmapFactory.decodeStream(inStream);
341     assertThat(bitmap.sameAs(bitmap2)).isTrue();
342   }
343 
344   @Test
compress_asJpeg_convertsTransparentToBlack()345   public void compress_asJpeg_convertsTransparentToBlack() {
346     Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Bitmap.Config.ARGB_8888);
347     ByteArrayOutputStream outStream = new ByteArrayOutputStream();
348     bitmap.compress(CompressFormat.JPEG, /* quality= */ 90, outStream);
349     byte[] outBytes = outStream.toByteArray();
350     assertThat(outBytes).isNotEmpty();
351     ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes);
352     Bitmap bitmap2 = BitmapFactory.decodeStream(inStream);
353     assertThat(bitmap2.getPixel(0, 0)).isEqualTo(Color.BLACK);
354   }
355 
356   @Test
createBitmapWithOffsetAndStride()357   public void createBitmapWithOffsetAndStride() {
358     int[] pixels = new int[10];
359     Bitmap result = Bitmap.createBitmap(pixels, 0, 2, 2, 5, Bitmap.Config.ARGB_8888);
360     assertThat(result).isNotNull();
361   }
362 
363   @Test
createBitmap_mutability()364   public void createBitmap_mutability() {
365     // Mutable constructor variants.
366     assertThat(
367             Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888)
368                 .isMutable())
369         .isTrue();
370     assertThat(
371             Bitmap.createBitmap(
372                     (DisplayMetrics) null, /* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888)
373                 .isMutable())
374         .isTrue();
375     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
376       assertThat(
377               Bitmap.createBitmap(
378                       /* width= */ 1,
379                       /* height= */ 1,
380                       Bitmap.Config.ARGB_8888,
381                       /* hasAlpha= */ true)
382                   .isMutable())
383           .isTrue();
384       assertThat(
385               Bitmap.createBitmap(
386                       /* display= */ null,
387                       /* width= */ 1,
388                       /* height= */ 1,
389                       Bitmap.Config.ARGB_8888,
390                       /* hasAlpha= */ true)
391                   .isMutable())
392           .isTrue();
393     }
394 
395     // Immutable constructor variants.
396     assertThat(
397             Bitmap.createBitmap(
398                     /* colors= */ new int[] {0},
399                     /* width= */ 1,
400                     /* height= */ 1,
401                     Bitmap.Config.ARGB_8888)
402                 .isMutable())
403         .isFalse();
404     assertThat(
405             Bitmap.createBitmap(
406                     /* colors= */ new int[] {0},
407                     /* offset= */ 0,
408                     /* stride= */ 1,
409                     /* width= */ 1,
410                     /* height= */ 1,
411                     Bitmap.Config.ARGB_8888)
412                 .isMutable())
413         .isFalse();
414     assertThat(
415             Bitmap.createBitmap(
416                     /* display= */ null,
417                     /* colors= */ new int[] {0},
418                     /* width= */ 1,
419                     /* height= */ 1,
420                     Bitmap.Config.ARGB_8888)
421                 .isMutable())
422         .isFalse();
423     assertThat(
424             Bitmap.createBitmap(
425                     /* display= */ null,
426                     /* colors= */ new int[] {0},
427                     /* offset= */ 0,
428                     /* stride= */ 1,
429                     /* width= */ 1,
430                     /* height= */ 1,
431                     Bitmap.Config.ARGB_8888)
432                 .isMutable())
433         .isFalse();
434   }
435 
436   @Test
createBitmap_hasAlpha()437   public void createBitmap_hasAlpha() {
438     // ARGB_8888 has alpha by default.
439     assertThat(
440             Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888)
441                 .hasAlpha())
442         .isTrue();
443     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
444       assertThat(
445               Bitmap.createBitmap(
446                       /* width= */ 1,
447                       /* height= */ 1,
448                       Bitmap.Config.ARGB_8888,
449                       /* hasAlpha= */ true)
450                   .hasAlpha())
451           .isTrue();
452       assertThat(
453               Bitmap.createBitmap(
454                       /* display= */ null,
455                       /* width= */ 1,
456                       /* height= */ 1,
457                       Bitmap.Config.ARGB_8888,
458                       /* hasAlpha= */ true)
459                   .hasAlpha())
460           .isTrue();
461     }
462     assertThat(
463             Bitmap.createBitmap(
464                     /* colors= */ new int[] {0},
465                     /* width= */ 1,
466                     /* height= */ 1,
467                     Bitmap.Config.ARGB_8888)
468                 .hasAlpha())
469         .isTrue();
470 
471     // Doesn't have alpha
472     assertThat(
473             Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.RGB_565).hasAlpha())
474         .isFalse();
475     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
476       assertThat(
477               Bitmap.createBitmap(
478                       /* width= */ 1,
479                       /* height= */ 1,
480                       Bitmap.Config.ARGB_8888,
481                       /* hasAlpha= */ false)
482                   .hasAlpha())
483           .isFalse();
484       assertThat(
485               Bitmap.createBitmap(
486                       /* display= */ null,
487                       /* width= */ 1,
488                       /* height= */ 1,
489                       Bitmap.Config.ARGB_8888,
490                       /* hasAlpha= */ false)
491                   .hasAlpha())
492           .isFalse();
493     }
494   }
495 
496   @Test
createBitmap_premultiplied()497   public void createBitmap_premultiplied() {
498     // ARGB_8888 has alpha by default, is premultiplied.
499     assertThat(
500             Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888)
501                 .isPremultiplied())
502         .isTrue();
503     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
504       assertThat(
505               Bitmap.createBitmap(
506                       /* width= */ 1,
507                       /* height= */ 1,
508                       Bitmap.Config.ARGB_8888,
509                       /* hasAlpha= */ true)
510                   .isPremultiplied())
511           .isTrue();
512       assertThat(
513               Bitmap.createBitmap(
514                       /* display= */ null,
515                       /* width= */ 1,
516                       /* height= */ 1,
517                       Bitmap.Config.ARGB_8888,
518                       /* hasAlpha= */ true)
519                   .isPremultiplied())
520           .isTrue();
521     }
522     assertThat(
523             Bitmap.createBitmap(
524                     /* colors= */ new int[] {0},
525                     /* width= */ 1,
526                     /* height= */ 1,
527                     Bitmap.Config.ARGB_8888)
528                 .isPremultiplied())
529         .isTrue();
530 
531     // Doesn't have alpha, is not premultiplied
532     assertThat(
533             Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.RGB_565)
534                 .isPremultiplied())
535         .isFalse();
536     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
537       assertThat(
538               Bitmap.createBitmap(
539                       /* width= */ 1,
540                       /* height= */ 1,
541                       Bitmap.Config.ARGB_8888,
542                       /* hasAlpha= */ false)
543                   .isPremultiplied())
544           .isFalse();
545       assertThat(
546               Bitmap.createBitmap(
547                       /* display= */ null,
548                       /* width= */ 1,
549                       /* height= */ 1,
550                       Bitmap.Config.ARGB_8888,
551                       /* hasAlpha= */ false)
552                   .isPremultiplied())
553           .isFalse();
554     }
555   }
556 
557   @Test
extractAlpha_isMutable()558   public void extractAlpha_isMutable() {
559     Bitmap result = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
560     Bitmap alphaBitmap = result.extractAlpha();
561     assertThat(alphaBitmap.isMutable()).isTrue();
562   }
563 
564   @Test
createBitmap_withBitmap_containsImageData()565   public void createBitmap_withBitmap_containsImageData() {
566     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
567     bitmap.eraseColor(Color.BLUE);
568     Bitmap cropped = Bitmap.createBitmap(bitmap, 0, 0, 50, 50);
569     assertThat(cropped.isMutable()).isTrue();
570     assertThat(cropped.getPixel(0, 0)).isEqualTo(Color.BLUE);
571   }
572 
573   @Test
createBitmap_withBitmap_thenCopy_isValid()574   public void createBitmap_withBitmap_thenCopy_isValid() {
575     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
576     bitmap.eraseColor(Color.BLUE);
577     Bitmap cropped = Bitmap.createBitmap(bitmap, 50, 50, 50, 50);
578     Bitmap copy = cropped.copy(Bitmap.Config.ARGB_8888, true);
579     assertThat(copy.isMutable()).isTrue();
580     assertThat(copy.getPixel(0, 0)).isEqualTo(Color.BLUE);
581   }
582 
583   @Test
copyPixelsFromBuffer_intBuffer()584   public void copyPixelsFromBuffer_intBuffer() {
585     Bitmap bitmap = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
586     IntBuffer input = IntBuffer.allocate(bitmap.getWidth() * bitmap.getHeight());
587     for (int i = 0; i < input.capacity(); i++) {
588       // IntBuffer is interpreted as ABGR. Use A=255 to avoid premultiplication.
589       input.put((0xFF << 24) | (i + 2) << 16 | (i + 1) << 8 | i);
590     }
591     input.rewind();
592     bitmap.copyPixelsFromBuffer(input);
593 
594     IntBuffer output = IntBuffer.allocate(input.capacity());
595     bitmap.copyPixelsToBuffer(output);
596 
597     input.rewind();
598     output.rewind();
599 
600     assertThat(output).isEqualTo(input);
601   }
602 
603   @Test
copyPixelsFromBuffer_byteBuffer()604   public void copyPixelsFromBuffer_byteBuffer() {
605     Bitmap bitmap = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
606     ByteBuffer input = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4);
607     for (int i = 0; i < bitmap.getWidth() * bitmap.getHeight(); i++) {
608       // ByteBuffer is interpreted as RGBA. Use A=255 to avoid premultiplication.
609       input.put((byte) i);
610       input.put((byte) (i + 1));
611       input.put((byte) (i + 2));
612       input.put((byte) 0xFF);
613     }
614     input.rewind();
615     bitmap.copyPixelsFromBuffer(input);
616 
617     ByteBuffer output = ByteBuffer.allocate(input.capacity());
618     bitmap.copyPixelsToBuffer(output);
619 
620     input.rewind();
621     output.rewind();
622 
623     assertThat(output).isEqualTo(input);
624   }
625 
626   @SdkSuppress(minSdkVersion = O)
627   @Config(minSdk = O)
628   @Test
createBitmap_colorSpace_defaultColorSpace()629   public void createBitmap_colorSpace_defaultColorSpace() {
630     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
631 
632     assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.SRGB));
633   }
634 
635   @SdkSuppress(minSdkVersion = O)
636   @Config(minSdk = O)
637   @Test
createBitmap_colorSpace_customColorSpace()638   public void createBitmap_colorSpace_customColorSpace() {
639     Bitmap bitmap =
640         Bitmap.createBitmap(
641             100, 100, Bitmap.Config.ARGB_8888, true, ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
642 
643     assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
644   }
645 
646   @SdkSuppress(minSdkVersion = Q)
647   @Config(minSdk = Q)
648   @Test
setColorSpace()649   public void setColorSpace() {
650     Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
651     bitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
652 
653     assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
654   }
655 
656   @Test
bitmapDrawable_mutate()657   public void bitmapDrawable_mutate() {
658     BitmapDrawable drawable1 = (BitmapDrawable) resources.getDrawable(R.drawable.an_image);
659     BitmapDrawable drawable2 = (BitmapDrawable) resources.getDrawable(R.drawable.an_image);
660 
661     Drawable mutated1 = drawable1.mutate();
662     Drawable mutated2 = drawable2.mutate();
663     mutated1.setAlpha(100);
664     mutated1.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
665     mutated2.setAlpha(200);
666     mutated2.setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
667     assertThat(mutated1.getAlpha()).isEqualTo(100);
668     // ColorFilter is part of the Drawable paint, so BLUE is overridden by RED.
669     assertThat(mutated1.getColorFilter())
670         .isEqualTo(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN));
671     assertThat(mutated2.getAlpha()).isEqualTo(200);
672     assertThat(mutated2.getColorFilter())
673         .isEqualTo(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN));
674   }
675 
676   @Test
null_bitmapConfig_throwsNPE()677   public void null_bitmapConfig_throwsNPE() {
678     assertThrows(NullPointerException.class, () -> Bitmap.createBitmap(100, 100, null));
679   }
680 }
681