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