xref: /aosp_15_r20/cts/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.graphics.cts;
17 
18 import static android.graphics.cts.utils.LeakTest.runNotLeakingTest;
19 
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertSame;
26 import static org.junit.Assert.assertThrows;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 import static org.junit.Assume.assumeNoException;
30 import static org.junit.Assume.assumeNotNull;
31 import static org.junit.Assume.assumeTrue;
32 
33 import android.content.res.Resources;
34 import android.graphics.Bitmap;
35 import android.graphics.Bitmap.CompressFormat;
36 import android.graphics.Bitmap.Config;
37 import android.graphics.BitmapFactory;
38 import android.graphics.Canvas;
39 import android.graphics.Color;
40 import android.graphics.ColorSpace;
41 import android.graphics.ColorSpace.Named;
42 import android.graphics.ImageDecoder;
43 import android.graphics.ImageFormat;
44 import android.graphics.LinearGradient;
45 import android.graphics.Matrix;
46 import android.graphics.Paint;
47 import android.graphics.Picture;
48 import android.graphics.Shader;
49 import android.hardware.HardwareBuffer;
50 import android.os.Parcel;
51 import android.os.StrictMode;
52 import android.util.DisplayMetrics;
53 import android.view.Surface;
54 
55 import androidx.test.InstrumentationRegistry;
56 import androidx.test.filters.LargeTest;
57 import androidx.test.filters.SmallTest;
58 
59 import com.android.compatibility.common.util.BitmapUtils;
60 import com.android.compatibility.common.util.ColorUtils;
61 import com.android.compatibility.common.util.WidgetTestUtils;
62 
63 import junitparams.JUnitParamsRunner;
64 import junitparams.Parameters;
65 
66 import org.junit.Before;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 
70 import java.io.ByteArrayOutputStream;
71 import java.io.IOException;
72 import java.io.OutputStream;
73 import java.nio.ByteBuffer;
74 import java.nio.CharBuffer;
75 import java.nio.IntBuffer;
76 import java.nio.ShortBuffer;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.HashSet;
80 import java.util.List;
81 
82 @SmallTest
83 @RunWith(JUnitParamsRunner.class)
84 public class BitmapTest {
85     // small alpha values cause color values to be pre-multiplied down, losing accuracy
86     private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253);
87     private static final int PREMUL_ROUNDED_COLOR = Color.argb(2, 255, 255, 255);
88     private static final int PREMUL_STORED_COLOR = Color.argb(2, 2, 2, 2);
89 
90     private static final BitmapFactory.Options HARDWARE_OPTIONS = createHardwareBitmapOptions();
91 
92     static {
93         System.loadLibrary("ctsgraphics_jni");
94     }
95 
96     private Resources mRes;
97     private Bitmap mBitmap;
98     private BitmapFactory.Options mOptions;
99 
getRgbColorSpaces()100     public static List<ColorSpace> getRgbColorSpaces() {
101         List<ColorSpace> rgbColorSpaces;
102         rgbColorSpaces = new ArrayList<ColorSpace>();
103         for (ColorSpace.Named e : ColorSpace.Named.values()) {
104             ColorSpace cs = ColorSpace.get(e);
105             if (cs.getModel() != ColorSpace.Model.RGB) {
106                 continue;
107             }
108             if (((ColorSpace.Rgb) cs).getTransferParameters() == null) {
109                 continue;
110             }
111             rgbColorSpaces.add(cs);
112         }
113         return rgbColorSpaces;
114     }
115 
116     @Before
setup()117     public void setup() {
118         mRes = InstrumentationRegistry.getTargetContext().getResources();
119         mOptions = new BitmapFactory.Options();
120         mOptions.inScaled = false;
121         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
122     }
123 
124     @Test(expected=IllegalStateException.class)
testCompressRecycled()125     public void testCompressRecycled() {
126         mBitmap.recycle();
127         mBitmap.compress(CompressFormat.JPEG, 0, null);
128     }
129 
130     @Test(expected=NullPointerException.class)
testCompressNullStream()131     public void testCompressNullStream() {
132         mBitmap.compress(CompressFormat.JPEG, 0, null);
133     }
134 
135     @Test(expected=IllegalArgumentException.class)
testCompressQualityTooLow()136     public void testCompressQualityTooLow() {
137         mBitmap.compress(CompressFormat.JPEG, -1, new ByteArrayOutputStream());
138     }
139 
140     @Test(expected=IllegalArgumentException.class)
testCompressQualityTooHigh()141     public void testCompressQualityTooHigh() {
142         mBitmap.compress(CompressFormat.JPEG, 101, new ByteArrayOutputStream());
143     }
144 
compressFormats()145     private static Object[] compressFormats() {
146         return CompressFormat.values();
147     }
148 
149     @Test
150     @Parameters(method = "compressFormats")
testCompress(CompressFormat format)151     public void testCompress(CompressFormat format) {
152         assertTrue(mBitmap.compress(format, 50, new ByteArrayOutputStream()));
153     }
154 
decodeBytes(byte[] bytes)155     private Bitmap decodeBytes(byte[] bytes) {
156         ByteBuffer buffer = ByteBuffer.wrap(bytes);
157         ImageDecoder.Source src = ImageDecoder.createSource(buffer);
158         try {
159             return ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
160                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
161             });
162         } catch (IOException e) {
163             fail("Failed to decode with " + e);
164             return null;
165         }
166     }
167 
168     // There are three color components and
169     // each should be within a square difference of 15 * 15.
170     private static final int MSE_MARGIN = 3 * (15 * 15);
171 
172     @Test
testCompressWebpLossy()173     public void testCompressWebpLossy() {
174         // For qualities < 100, WEBP performs a lossy decode.
175         byte[] last = null;
176         Bitmap lastBitmap = null;
177         for (int quality : new int[] { 25, 50, 80, 99 }) {
178             ByteArrayOutputStream webp = new ByteArrayOutputStream();
179             assertTrue(mBitmap.compress(CompressFormat.WEBP, quality, webp));
180             byte[] webpCompressed = webp.toByteArray();
181 
182 
183             ByteArrayOutputStream webpLossy = new ByteArrayOutputStream();
184             assertTrue(mBitmap.compress(CompressFormat.WEBP_LOSSY, quality, webpLossy));
185             byte[] webpLossyCompressed = webpLossy.toByteArray();
186 
187             assertTrue("Compression did not match at quality " + quality,
188                     Arrays.equals(webpCompressed, webpLossyCompressed));
189 
190             Bitmap result = decodeBytes(webpCompressed);
191             if (last != null) {
192                 // Higher quality will generally result in a larger file.
193                 assertTrue(webpCompressed.length > last.length);
194                 if (!BitmapUtils.compareBitmapsMse(lastBitmap, result, MSE_MARGIN, true, false)) {
195                     fail("Bad comparison for quality " + quality);
196                 }
197             }
198             last = webpCompressed;
199             lastBitmap = result;
200         }
201     }
202 
203     @Test
204     @Parameters({ "0", "50", "80", "99", "100" })
testCompressWebpLossless(int quality)205     public void testCompressWebpLossless(int quality) {
206         ByteArrayOutputStream webp = new ByteArrayOutputStream();
207         assertTrue(mBitmap.compress(CompressFormat.WEBP_LOSSLESS, quality, webp));
208         byte[] webpCompressed = webp.toByteArray();
209         Bitmap result = decodeBytes(webpCompressed);
210 
211         assertTrue("WEBP_LOSSLESS did not losslessly compress at quality " + quality,
212                 BitmapUtils.compareBitmaps(mBitmap, result));
213     }
214 
215     @Test
testCompressWebp100MeansLossless()216     public void testCompressWebp100MeansLossless() {
217         ByteArrayOutputStream webp = new ByteArrayOutputStream();
218         assertTrue(mBitmap.compress(CompressFormat.WEBP, 100, webp));
219         byte[] webpCompressed = webp.toByteArray();
220         Bitmap result = decodeBytes(webpCompressed);
221         assertTrue("WEBP_LOSSLESS did not losslessly compress at quality 100",
222                 BitmapUtils.compareBitmaps(mBitmap, result));
223     }
224 
225     @Test
226     @Parameters(method = "compressFormats")
testCompressAlpha8Fails(CompressFormat format)227     public void testCompressAlpha8Fails(CompressFormat format) {
228         Bitmap bitmap = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
229         assertFalse("Incorrectly compressed ALPHA_8 to " + format,
230                 bitmap.compress(format, 50, new ByteArrayOutputStream()));
231 
232         if (format == CompressFormat.WEBP) {
233             // Skip the native test, since the NDK just has equivalents for
234             // WEBP_LOSSY and WEBP_LOSSLESS.
235             return;
236         }
237 
238         byte[] storage = new byte[16 * 1024];
239         OutputStream stream = new ByteArrayOutputStream();
240         assertFalse("Incorrectly compressed ALPHA_8 with the NDK to " + format,
241                 nCompress(bitmap, nativeCompressFormat(format), 50, stream, storage));
242     }
243 
244     @Test(expected=IllegalStateException.class)
testCopyRecycled()245     public void testCopyRecycled() {
246         mBitmap.recycle();
247         mBitmap.copy(Config.RGB_565, false);
248     }
249 
250     @Test
testCopy()251     public void testCopy() {
252         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
253         Bitmap bitmap = mBitmap.copy(Config.ARGB_8888, false);
254         WidgetTestUtils.assertEquals(mBitmap, bitmap);
255     }
256 
257     @Test
testCopyConfigs()258     public void testCopyConfigs() {
259         Config[] supportedConfigs = new Config[] {
260                 Config.ALPHA_8, Config.RGB_565, Config.ARGB_8888, Config.RGBA_F16,
261         };
262         for (Config src : supportedConfigs) {
263             for (Config dst : supportedConfigs) {
264                 Bitmap srcBitmap = Bitmap.createBitmap(1, 1, src);
265                 srcBitmap.eraseColor(Color.WHITE);
266                 Bitmap dstBitmap = srcBitmap.copy(dst, false);
267                 assertNotNull("Should support copying from " + src + " to " + dst,
268                         dstBitmap);
269                 if (Config.ALPHA_8 == dst || Config.ALPHA_8 == src) {
270                     // Color will be opaque but color information will be lost.
271                     assertEquals("Color should be black when copying from " + src + " to "
272                             + dst, Color.BLACK, dstBitmap.getPixel(0, 0));
273                 } else {
274                     assertEquals("Color should be preserved when copying from " + src + " to "
275                             + dst, Color.WHITE, dstBitmap.getPixel(0, 0));
276                 }
277             }
278         }
279     }
280 
281     @Test(expected=IllegalArgumentException.class)
testCopyMutableHwBitmap()282     public void testCopyMutableHwBitmap() {
283         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
284         mBitmap.copy(Config.HARDWARE, true);
285     }
286 
287     @Test(expected=RuntimeException.class)
testCopyPixelsToBufferUnsupportedBufferClass()288     public void testCopyPixelsToBufferUnsupportedBufferClass() {
289         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
290 
291         mBitmap.copyPixelsToBuffer(CharBuffer.allocate(pixSize));
292     }
293 
294     @Test(expected=RuntimeException.class)
testCopyPixelsToBufferBufferTooSmall()295     public void testCopyPixelsToBufferBufferTooSmall() {
296         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
297         final int tooSmall = pixSize / 2;
298 
299         mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall));
300     }
301 
302     @Test
testCopyPixelsToBuffer()303     public void testCopyPixelsToBuffer() {
304         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
305 
306         ByteBuffer byteBuf = ByteBuffer.allocate(pixSize);
307         assertEquals(0, byteBuf.position());
308         mBitmap.copyPixelsToBuffer(byteBuf);
309         assertEquals(pixSize, byteBuf.position());
310 
311         ShortBuffer shortBuf = ShortBuffer.allocate(pixSize);
312         assertEquals(0, shortBuf.position());
313         mBitmap.copyPixelsToBuffer(shortBuf);
314         assertEquals(pixSize >> 1, shortBuf.position());
315 
316         IntBuffer intBuf1 = IntBuffer.allocate(pixSize);
317         assertEquals(0, intBuf1.position());
318         mBitmap.copyPixelsToBuffer(intBuf1);
319         assertEquals(pixSize >> 2, intBuf1.position());
320 
321         Bitmap bitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
322                 mBitmap.getConfig());
323         intBuf1.position(0); // copyPixelsToBuffer adjusted the position, so rewind to start
324         bitmap.copyPixelsFromBuffer(intBuf1);
325         IntBuffer intBuf2 = IntBuffer.allocate(pixSize);
326         bitmap.copyPixelsToBuffer(intBuf2);
327 
328         assertEquals(pixSize >> 2, intBuf2.position());
329         assertEquals(intBuf1.position(), intBuf2.position());
330         int size = intBuf1.position();
331         intBuf1.position(0);
332         intBuf2.position(0);
333         for (int i = 0; i < size; i++) {
334             assertEquals("mismatching pixels at position " + i, intBuf1.get(), intBuf2.get());
335         }
336     }
337 
338     @Test
testCreateBitmap1()339     public void testCreateBitmap1() {
340         int[] colors = createColors(100);
341         Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565);
342         assertFalse(bitmap.isMutable());
343         Bitmap ret = Bitmap.createBitmap(bitmap);
344         assertNotNull(ret);
345         assertFalse(ret.isMutable());
346         assertEquals(10, ret.getWidth());
347         assertEquals(10, ret.getHeight());
348         assertEquals(Config.RGB_565, ret.getConfig());
349         assertEquals(ANDROID_BITMAP_FORMAT_RGB_565, nGetFormat(ret));
350     }
351 
352     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeX()353     public void testCreateBitmapNegativeX() {
354         Bitmap.createBitmap(mBitmap, -100, 50, 50, 200);
355     }
356 
357     @Test
testCreateBitmap2()358     public void testCreateBitmap2() {
359         // special case: output bitmap is equal to the input bitmap
360         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
361         assertFalse(mBitmap.isMutable()); // createBitmap w/ colors should be immutable
362         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100);
363         assertNotNull(ret);
364         assertFalse(ret.isMutable()); // createBitmap from subset should be immutable
365         assertTrue(mBitmap.equals(ret));
366 
367         //normal case
368         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
369         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50);
370         assertNotNull(ret);
371         assertFalse(mBitmap.equals(ret));
372         assertEquals(ANDROID_BITMAP_FORMAT_RGBA_8888, nGetFormat(mBitmap));
373     }
374 
375     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeXY()376     public void testCreateBitmapNegativeXY() {
377         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
378 
379         // abnormal case: x and/or y less than 0
380         Bitmap.createBitmap(mBitmap, -1, -1, 10, 10, null, false);
381     }
382 
383     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeWidthHeight()384     public void testCreateBitmapNegativeWidthHeight() {
385         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
386 
387         // abnormal case: width and/or height less than 0
388         Bitmap.createBitmap(mBitmap, 1, 1, -10, -10, null, false);
389     }
390 
391     @Test(expected=IllegalArgumentException.class)
testCreateBitmapXRegionTooWide()392     public void testCreateBitmapXRegionTooWide() {
393         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
394 
395         // abnormal case: (x + width) bigger than source bitmap's width
396         Bitmap.createBitmap(mBitmap, 10, 10, 95, 50, null, false);
397     }
398 
399     @Test(expected=IllegalArgumentException.class)
testCreateBitmapYRegionTooTall()400     public void testCreateBitmapYRegionTooTall() {
401         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
402 
403         // abnormal case: (y + height) bigger than source bitmap's height
404         Bitmap.createBitmap(mBitmap, 10, 10, 50, 95, null, false);
405     }
406 
407     @Test(expected=IllegalArgumentException.class)
testCreateMutableBitmapWithHardwareConfig()408     public void testCreateMutableBitmapWithHardwareConfig() {
409         Bitmap.createBitmap(100, 100, Config.HARDWARE);
410     }
411 
412     @Test
testCreateBitmap3()413     public void testCreateBitmap3() {
414         // special case: output bitmap is equal to the input bitmap
415         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
416         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100, null, false);
417         assertNotNull(ret);
418         assertFalse(ret.isMutable()); // subset should be immutable
419         assertTrue(mBitmap.equals(ret));
420 
421         // normal case
422         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
423         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50, new Matrix(), true);
424         assertTrue(ret.isMutable());
425         assertNotNull(ret);
426         assertFalse(mBitmap.equals(ret));
427     }
428 
429     @Test
testCreateBitmapFromHardwareBitmap()430     public void testCreateBitmapFromHardwareBitmap() {
431         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
432                 HARDWARE_OPTIONS);
433         assertEquals(Config.HARDWARE, hardwareBitmap.getConfig());
434 
435         Bitmap ret = Bitmap.createBitmap(hardwareBitmap, 0, 0, 96, 96, null, false);
436         assertEquals(Config.HARDWARE, ret.getConfig());
437         assertFalse(ret.isMutable());
438     }
439 
440     @Test
testCreateBitmap4()441     public void testCreateBitmap4() {
442         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
443         assertNotNull(ret);
444         assertTrue(ret.isMutable());
445         assertEquals(100, ret.getWidth());
446         assertEquals(200, ret.getHeight());
447         assertEquals(Config.RGB_565, ret.getConfig());
448     }
449 
verify2x2BitmapContents(int[] expected, Bitmap observed)450     private static void verify2x2BitmapContents(int[] expected, Bitmap observed) {
451         ColorUtils.verifyColor(expected[0], observed.getPixel(0, 0));
452         ColorUtils.verifyColor(expected[1], observed.getPixel(1, 0));
453         ColorUtils.verifyColor(expected[2], observed.getPixel(0, 1));
454         ColorUtils.verifyColor(expected[3], observed.getPixel(1, 1));
455     }
456 
457     @Test
testCreateBitmap_matrix()458     public void testCreateBitmap_matrix() {
459         int[] colorArray = new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK };
460         Bitmap src = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
461         assertTrue(src.isMutable());
462         src.setPixels(colorArray,0, 2, 0, 0, 2, 2);
463 
464         // baseline
465         verify2x2BitmapContents(colorArray, src);
466 
467         // null
468         Bitmap dst = Bitmap.createBitmap(src, 0, 0, 2, 2, null, false);
469         assertTrue(dst.isMutable());
470         verify2x2BitmapContents(colorArray, dst);
471 
472         // identity matrix
473         Matrix matrix = new Matrix();
474         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
475         assertTrue(dst.isMutable());
476         verify2x2BitmapContents(colorArray, dst);
477 
478         // big scale - only red visible
479         matrix.setScale(10, 10);
480         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
481         assertTrue(dst.isMutable());
482         verify2x2BitmapContents(new int[] { Color.RED, Color.RED, Color.RED, Color.RED }, dst);
483 
484         // rotation
485         matrix.setRotate(90);
486         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
487         assertTrue(dst.isMutable());
488         verify2x2BitmapContents(
489                 new int[] { Color.BLUE, Color.RED, Color.BLACK, Color.GREEN }, dst);
490     }
491 
492     @Test(expected=IllegalArgumentException.class)
testCreateBitmapFromColorsNegativeWidthHeight()493     public void testCreateBitmapFromColorsNegativeWidthHeight() {
494         int[] colors = createColors(100);
495 
496         // abnormal case: width and/or height less than 0
497         Bitmap.createBitmap(colors, 0, 100, -1, 100, Config.RGB_565);
498     }
499 
500     @Test(expected=IllegalArgumentException.class)
testCreateBitmapFromColorsIllegalStride()501     public void testCreateBitmapFromColorsIllegalStride() {
502         int[] colors = createColors(100);
503 
504         // abnormal case: stride less than width and bigger than -width
505         Bitmap.createBitmap(colors, 10, 10, 100, 100, Config.RGB_565);
506     }
507 
508     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsNegativeOffset()509     public void testCreateBitmapFromColorsNegativeOffset() {
510         int[] colors = createColors(100);
511 
512         // abnormal case: offset less than 0
513         Bitmap.createBitmap(colors, -10, 100, 100, 100, Config.RGB_565);
514     }
515 
516     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsOffsetTooLarge()517     public void testCreateBitmapFromColorsOffsetTooLarge() {
518         int[] colors = createColors(100);
519 
520         // abnormal case: (offset + width) bigger than colors' length
521         Bitmap.createBitmap(colors, 10, 100, 100, 100, Config.RGB_565);
522     }
523 
524     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsScalnlineTooLarge()525     public void testCreateBitmapFromColorsScalnlineTooLarge() {
526         int[] colors = createColors(100);
527 
528         // abnormal case: (lastScanline + width) bigger than colors' length
529         Bitmap.createBitmap(colors, 10, 100, 50, 100, Config.RGB_565);
530     }
531 
532     @Test
testCreateBitmap6()533     public void testCreateBitmap6() {
534         int[] colors = createColors(100);
535 
536         // normal case
537         Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565);
538         assertNotNull(ret);
539         assertFalse(ret.isMutable());
540         assertEquals(10, ret.getWidth());
541         assertEquals(5, ret.getHeight());
542         assertEquals(Config.RGB_565, ret.getConfig());
543     }
544 
545     @Test
testCreateBitmap_displayMetrics_mutable()546     public void testCreateBitmap_displayMetrics_mutable() {
547         DisplayMetrics metrics =
548                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
549 
550         Bitmap bitmap;
551         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
552         assertTrue(bitmap.isMutable());
553         assertEquals(metrics.densityDpi, bitmap.getDensity());
554 
555         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
556         assertTrue(bitmap.isMutable());
557         assertEquals(metrics.densityDpi, bitmap.getDensity());
558 
559         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true);
560         assertTrue(bitmap.isMutable());
561         assertEquals(metrics.densityDpi, bitmap.getDensity());
562 
563         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true, ColorSpace.get(
564                 ColorSpace.Named.SRGB));
565 
566         assertTrue(bitmap.isMutable());
567         assertEquals(metrics.densityDpi, bitmap.getDensity());
568 
569         int[] colors = createColors(100);
570         bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
571         assertNotNull(bitmap);
572         assertFalse(bitmap.isMutable());
573 
574         bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
575         assertNotNull(bitmap);
576         assertFalse(bitmap.isMutable());
577     }
578 
579     @Test
testCreateBitmap_noDisplayMetrics_mutable()580     public void testCreateBitmap_noDisplayMetrics_mutable() {
581         Bitmap bitmap;
582         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
583         assertTrue(bitmap.isMutable());
584 
585         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true);
586         assertTrue(bitmap.isMutable());
587 
588         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB));
589         assertTrue(bitmap.isMutable());
590     }
591 
592     @Test
testCreateBitmap_displayMetrics_immutable()593     public void testCreateBitmap_displayMetrics_immutable() {
594         DisplayMetrics metrics =
595                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
596         int[] colors = createColors(100);
597 
598         Bitmap bitmap;
599         bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
600         assertFalse(bitmap.isMutable());
601         assertEquals(metrics.densityDpi, bitmap.getDensity());
602 
603         bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
604         assertFalse(bitmap.isMutable());
605         assertEquals(metrics.densityDpi, bitmap.getDensity());
606     }
607 
608     @Test
testCreateBitmap_noDisplayMetrics_immutable()609     public void testCreateBitmap_noDisplayMetrics_immutable() {
610         int[] colors = createColors(100);
611         Bitmap bitmap;
612         bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888);
613         assertFalse(bitmap.isMutable());
614 
615         bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888);
616         assertFalse(bitmap.isMutable());
617     }
618 
619     @Test
testCreateBitmap_Picture_immutable()620     public void testCreateBitmap_Picture_immutable() {
621         Picture picture = new Picture();
622         Canvas canvas = picture.beginRecording(200, 100);
623 
624         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
625 
626         p.setColor(0x88FF0000);
627         canvas.drawCircle(50, 50, 40, p);
628 
629         p.setColor(Color.GREEN);
630         p.setTextSize(30);
631         canvas.drawText("Pictures", 60, 60, p);
632         picture.endRecording();
633 
634         Bitmap bitmap;
635         bitmap = Bitmap.createBitmap(picture);
636         assertFalse(bitmap.isMutable());
637 
638         bitmap = Bitmap.createBitmap(picture, 100, 100, Config.HARDWARE);
639         assertFalse(bitmap.isMutable());
640         assertNotNull(bitmap.getColorSpace());
641 
642         bitmap = Bitmap.createBitmap(picture, 100, 100, Config.ARGB_8888);
643         assertFalse(bitmap.isMutable());
644     }
645 
646     @Test
testCreateScaledBitmap()647     public void testCreateScaledBitmap() {
648         mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565);
649         assertTrue(mBitmap.isMutable());
650         Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false);
651         assertNotNull(ret);
652         assertEquals(50, ret.getWidth());
653         assertEquals(100, ret.getHeight());
654         assertTrue(ret.isMutable());
655     }
656 
657     @Test
testWrapHardwareBufferSucceeds()658     public void testWrapHardwareBufferSucceeds() {
659         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
660             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
661             assertNotNull(bitmap);
662             bitmap.recycle();
663         }
664     }
665 
666     @Test
testWrapHardwareBufferMissingGpuUsageFails()667     public void testWrapHardwareBufferMissingGpuUsageFails() {
668         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
669             HardwareBuffer.USAGE_CPU_WRITE_RARELY)) {
670             assertThrows(IllegalArgumentException.class, () -> {
671                 Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
672             });
673         }
674     }
675 
676     @Test
testWrapHardwareBufferWithProtectedUsageFails()677     public void testWrapHardwareBufferWithProtectedUsageFails() {
678         long usage = HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
679                 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
680                 | HardwareBuffer.USAGE_COMPOSER_OVERLAY
681                 | HardwareBuffer.USAGE_PROTECTED_CONTENT;
682 
683         assumeTrue("Creating a protected HW buffer is not supported",
684                 HardwareBuffer.isSupported(512, 512, HardwareBuffer.RGBA_8888, 1, usage));
685         try (HardwareBuffer hwBuffer =
686                 HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1, usage)) {
687             assertThrows(IllegalArgumentException.class, () -> {
688                 Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
689             });
690         }
691     }
692 
693     @Test(expected = IllegalArgumentException.class)
testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails()694     public void testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails() {
695         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
696             HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)) {
697             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.CIE_LAB));
698         }
699     }
700 
701     @Test
testWrapHardwareBufferFor1010102BufferSucceeds()702     public void testWrapHardwareBufferFor1010102BufferSucceeds() {
703         HardwareBuffer hwBufferMaybe = null;
704 
705         try {
706             hwBufferMaybe = HardwareBuffer.create(128, 128, HardwareBuffer.RGBA_1010102, 1,
707                     HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
708         } catch (IllegalArgumentException e) {
709             assumeNoException("Creating a 1010102 HW buffer was not supported", e);
710         }
711 
712         assumeNotNull(hwBufferMaybe);
713 
714         try (HardwareBuffer buffer = hwBufferMaybe) {
715             Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(Named.SRGB));
716             assertNotNull(bitmap);
717             bitmap.recycle();
718         }
719     }
720 
assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2)721     private void assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2) {
722         assertEquals(hwBuffer, hwBuffer2);
723         assertEquals(hwBuffer.hashCode(), hwBuffer2.hashCode());
724         assertEquals(hwBuffer.getWidth(), hwBuffer2.getWidth());
725         assertEquals(hwBuffer.getHeight(), hwBuffer2.getHeight());
726         assertEquals(hwBuffer.getFormat(), hwBuffer2.getFormat());
727         assertEquals(hwBuffer.getLayers(), hwBuffer2.getLayers());
728         assertEquals(hwBuffer.getUsage(), hwBuffer2.getUsage());
729     }
730 
731     @Test
testGetHardwareBufferMatchesWrapped()732     public void testGetHardwareBufferMatchesWrapped() {
733         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
734             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
735             assertNotNull(bitmap);
736 
737             try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
738                 assertNotNull(hwBuffer2);
739                 assertMatches(hwBuffer, hwBuffer2);
740             }
741             bitmap.recycle();
742         }
743     }
744 
parametersFor_testGetAllocationSizeWrappedBuffer()745     private static Object[] parametersFor_testGetAllocationSizeWrappedBuffer() {
746         return new Object[] {
747                 HardwareBuffer.YCBCR_420_888,
748                 HardwareBuffer.YCBCR_P010,
749                 ImageFormat.YV12,
750         };
751     }
752 
753     @Test
754     @Parameters(method = "parametersFor_testGetAllocationSizeWrappedBuffer")
testGetAllocationSizeWrappedBuffer(int format)755     public void testGetAllocationSizeWrappedBuffer(int format) {
756         final long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
757         assumeTrue(HardwareBuffer.isSupported(1, 1, format, 1, usage));
758         HardwareBuffer buffer = HardwareBuffer.create(100, 100, format, 1, usage);
759         assertNotNull("isSupported = true but allocation failed", buffer);
760         Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, null);
761         buffer.close();
762         try {
763             // We can probably assert closer to at least 100 * 100 but maybe someone has super
764             // duper good compression rates, so assume a lower bound of 2kb
765             assertTrue(bitmap.getAllocationByteCount() > 2000);
766         } finally {
767             bitmap.recycle();
768         }
769     }
770 
parametersFor_testGetHardwareBufferConfig()771     private static Object[] parametersFor_testGetHardwareBufferConfig() {
772         return new Object[] {Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565};
773     }
774 
775     @Test
776     @Parameters(method = "parametersFor_testGetHardwareBufferConfig")
testGetHardwareBufferConfig(Config config)777     public void testGetHardwareBufferConfig(Config config) {
778         Bitmap bitmap = Bitmap.createBitmap(10, 10, config);
779         bitmap = bitmap.copy(Config.HARDWARE, false);
780         if (bitmap == null) {
781             fail("Failed to copy to HARDWARE with Config " + config);
782         }
783         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
784             assertNotNull(hwBuffer);
785             assertEquals(hwBuffer.getWidth(), 10);
786             assertEquals(hwBuffer.getHeight(), 10);
787         }
788     }
789 
790     @Test
testGetHardwareBufferTwice()791     public void testGetHardwareBufferTwice() {
792         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
793         bitmap = bitmap.copy(Config.HARDWARE, false);
794         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
795             assertNotNull(hwBuffer);
796             try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
797                 assertNotNull(hwBuffer2);
798                 assertMatches(hwBuffer, hwBuffer2);
799             }
800         }
801     }
802 
803     @Test
testGetHardwareBufferMatches()804     public void testGetHardwareBufferMatches() {
805         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
806         bitmap = bitmap.copy(Config.HARDWARE, false);
807         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
808             HashSet<HardwareBuffer> set = new HashSet<HardwareBuffer>();
809             set.add(hwBuffer);
810             assertTrue(set.contains(bitmap.getHardwareBuffer()));
811         }
812     }
813 
814     @Test(expected = IllegalStateException.class)
testGetHardwareBufferNonHardware()815     public void testGetHardwareBufferNonHardware() {
816         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
817         bitmap.getHardwareBuffer();
818     }
819 
820     @Test(expected = IllegalStateException.class)
testGetHardwareBufferRecycled()821     public void testGetHardwareBufferRecycled() {
822         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
823         bitmap = bitmap.copy(Config.HARDWARE, false);
824         bitmap.recycle();
825         bitmap.getHardwareBuffer();
826     }
827 
828     @Test
testGetHardwareBufferClosed()829     public void testGetHardwareBufferClosed() {
830         HardwareBuffer hwBuffer = createTestBuffer(128, 128, false);
831         Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
832         assertNotNull(bitmap);
833 
834         hwBuffer.close();
835 
836         try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
837             assertNotNull(hwBuffer2);
838             assertFalse(hwBuffer2.isClosed());
839             assertNotEquals(hwBuffer, hwBuffer2);
840         }
841         bitmap.recycle();
842     }
843 
844     @Test
testGenerationId()845     public void testGenerationId() {
846         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
847         int genId = bitmap.getGenerationId();
848         assertEquals("not expected to change", genId, bitmap.getGenerationId());
849         bitmap.setDensity(bitmap.getDensity() + 4);
850         assertEquals("not expected to change", genId, bitmap.getGenerationId());
851         bitmap.getPixel(0, 0);
852         assertEquals("not expected to change", genId, bitmap.getGenerationId());
853 
854         int beforeGenId = bitmap.getGenerationId();
855         bitmap.eraseColor(Color.WHITE);
856         int afterGenId = bitmap.getGenerationId();
857         assertTrue("expected to increase", afterGenId > beforeGenId);
858 
859         beforeGenId = bitmap.getGenerationId();
860         bitmap.setPixel(4, 4, Color.BLUE);
861         afterGenId = bitmap.getGenerationId();
862         assertTrue("expected to increase again", afterGenId > beforeGenId);
863     }
864 
865     @Test
testDescribeContents()866     public void testDescribeContents() {
867         assertEquals(0, mBitmap.describeContents());
868     }
869 
870     @Test(expected=IllegalStateException.class)
testEraseColorOnRecycled()871     public void testEraseColorOnRecycled() {
872         mBitmap.recycle();
873 
874         mBitmap.eraseColor(0);
875     }
876 
877     @Test(expected = IllegalStateException.class)
testEraseColorLongOnRecycled()878     public void testEraseColorLongOnRecycled() {
879         mBitmap.recycle();
880 
881         mBitmap.eraseColor(Color.pack(0));
882     }
883 
884     @Test(expected=IllegalStateException.class)
testEraseColorOnImmutable()885     public void testEraseColorOnImmutable() {
886         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
887 
888         //abnormal case: bitmap is immutable
889         mBitmap.eraseColor(0);
890     }
891 
892     @Test(expected = IllegalStateException.class)
testEraseColorLongOnImmutable()893     public void testEraseColorLongOnImmutable() {
894         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
895 
896         //abnormal case: bitmap is immutable
897         mBitmap.eraseColor(Color.pack(0));
898     }
899 
900     @Test
testEraseColor()901     public void testEraseColor() {
902         // normal case
903         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
904         mBitmap.eraseColor(0xffff0000);
905         assertEquals(0xffff0000, mBitmap.getPixel(10, 10));
906         assertEquals(0xffff0000, mBitmap.getPixel(50, 50));
907     }
908 
909     @Test(expected = IllegalArgumentException.class)
testGetColorOOB()910     public void testGetColorOOB() {
911         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
912         mBitmap.getColor(-1, 0);
913     }
914 
915     @Test(expected = IllegalArgumentException.class)
testGetColorOOB2()916     public void testGetColorOOB2() {
917         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
918         mBitmap.getColor(5, -10);
919     }
920 
921     @Test(expected = IllegalArgumentException.class)
testGetColorOOB3()922     public void testGetColorOOB3() {
923         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
924         mBitmap.getColor(100, 10);
925     }
926 
927     @Test(expected = IllegalArgumentException.class)
testGetColorOOB4()928     public void testGetColorOOB4() {
929         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
930         mBitmap.getColor(99, 1000);
931     }
932 
933     @Test(expected = IllegalStateException.class)
testGetColorRecycled()934     public void testGetColorRecycled() {
935         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
936         mBitmap.recycle();
937         mBitmap.getColor(0, 0);
938     }
939 
940     @Test(expected = IllegalStateException.class)
testGetColorHardware()941     public void testGetColorHardware() {
942         BitmapFactory.Options options = new BitmapFactory.Options();
943         options.inPreferredConfig = Bitmap.Config.HARDWARE;
944         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
945         mBitmap.getColor(50, 50);
946 
947     }
948 
clamp(float f)949     private static float clamp(float f) {
950         return clamp(f, 0.0f, 1.0f);
951     }
952 
clamp(float f, float min, float max)953     private static float clamp(float f, float min, float max) {
954         return Math.min(Math.max(f, min), max);
955     }
956 
957     @Test
testGetColor()958     public void testGetColor() {
959         final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
960         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
961         for (Config config : new Config[] { Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565 }) {
962             for (ColorSpace bitmapColorSpace : rgbColorSpaces) {
963                 mBitmap = Bitmap.createBitmap(1, 1, config, /*hasAlpha*/ false,
964                         bitmapColorSpace);
965                 bitmapColorSpace = mBitmap.getColorSpace();
966                 for (ColorSpace eraseColorSpace : rgbColorSpaces) {
967                     for (long wideGamutLong : new long[] {
968                             Color.pack(1.0f, 0.0f, 0.0f, 1.0f, eraseColorSpace),
969                             Color.pack(0.0f, 1.0f, 0.0f, 1.0f, eraseColorSpace),
970                             Color.pack(0.0f, 0.0f, 1.0f, 1.0f, eraseColorSpace)}) {
971                         mBitmap.eraseColor(wideGamutLong);
972 
973                         Color result = mBitmap.getColor(0, 0);
974                         if (mBitmap.getColorSpace().equals(sRGB)) {
975                             assertEquals(mBitmap.getPixel(0, 0), result.toArgb());
976                         }
977                         if (eraseColorSpace.equals(bitmapColorSpace)) {
978                             final Color wideGamutColor = Color.valueOf(wideGamutLong);
979                             ColorUtils.verifyColor("Erasing to Bitmap's ColorSpace "
980                                     + bitmapColorSpace, wideGamutColor, result, .001f);
981 
982                         } else {
983                             Color convertedColor = Color.valueOf(
984                                     Color.convert(wideGamutLong, bitmapColorSpace));
985                             if (mBitmap.getConfig() != Config.RGBA_F16) {
986                                 // It's possible that we have to clip to fit into the Config.
987                                 convertedColor = Color.valueOf(
988                                         clamp(convertedColor.red()),
989                                         clamp(convertedColor.green()),
990                                         clamp(convertedColor.blue()),
991                                         convertedColor.alpha(),
992                                         bitmapColorSpace);
993                             }
994                             ColorUtils.verifyColor("Bitmap(Config: " + mBitmap.getConfig()
995                                     + ", ColorSpace: " + bitmapColorSpace
996                                     + ") erasing to " + Color.valueOf(wideGamutLong),
997                                     convertedColor, result, .03f);
998                         }
999                     }
1000                 }
1001             }
1002         }
1003     }
1004 
1005     private static class ARGB {
1006         public float alpha;
1007         public float red;
1008         public float green;
1009         public float blue;
ARGB(float alpha, float red, float green, float blue)1010         ARGB(float alpha, float red, float green, float blue) {
1011             this.alpha = alpha;
1012             this.red = red;
1013             this.green = green;
1014             this.blue = blue;
1015         }
1016     };
1017 
1018     @Test
testEraseColorLong()1019     public void testEraseColorLong() {
1020         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
1021         for (Config config : new Config[]{Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16}) {
1022             mBitmap = Bitmap.createBitmap(100, 100, config);
1023             // pack SRGB colors into ColorLongs.
1024             for (int color : new int[]{ Color.RED, Color.BLUE, Color.GREEN, Color.BLACK,
1025                     Color.WHITE, Color.TRANSPARENT }) {
1026                 if (config.equals(Config.RGB_565) && Float.compare(Color.alpha(color), 1.0f) != 0) {
1027                     // 565 doesn't support alpha.
1028                     continue;
1029                 }
1030                 mBitmap.eraseColor(Color.pack(color));
1031                 // The Bitmap is either SRGB or SRGBLinear (F16). getPixel(), which retrieves the
1032                 // color in SRGB, should match exactly.
1033                 ColorUtils.verifyColor("Config " + config + " mismatch at 10, 10 ",
1034                         color, mBitmap.getPixel(10, 10), 0);
1035                 ColorUtils.verifyColor("Config " + config + " mismatch at 50, 50 ",
1036                         color, mBitmap.getPixel(50, 50), 0);
1037             }
1038 
1039             // Use arbitrary colors in various ColorSpaces. getPixel() should approximately match
1040             // the SRGB version of the color.
1041             for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
1042                                           new ARGB(1.0f, .3f, .6f, .9f),
1043                                           new ARGB(0.5f, .2f, .8f, .7f) }) {
1044                 if (config.equals(Config.RGB_565) && Float.compare(color.alpha, 1.0f) != 0) {
1045                     continue;
1046                 }
1047                 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
1048                 for (ColorSpace cs : rgbColorSpaces) {
1049                     long longColor = Color.convert(srgbColor, cs);
1050                     mBitmap.eraseColor(longColor);
1051                     // These tolerances were chosen by trial and error. It is expected that
1052                     // some conversions do not round-trip perfectly.
1053                     int tolerance = 1;
1054                     if (config.equals(Config.RGB_565)) {
1055                         tolerance = 4;
1056                     } else if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) {
1057                         tolerance = 3;
1058                     }
1059 
1060                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
1061                             + ", mismatch at 10, 10 ", srgbColor, mBitmap.getPixel(10, 10),
1062                             tolerance);
1063                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
1064                             + ", mismatch at 50, 50 ", srgbColor, mBitmap.getPixel(50, 50),
1065                             tolerance);
1066                 }
1067             }
1068         }
1069     }
1070 
1071     @Test
testEraseColorOnP3()1072     public void testEraseColorOnP3() {
1073         // Use a ColorLong with a different ColorSpace than the Bitmap. getPixel() should
1074         // approximately match the SRGB version of the color.
1075         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888, true,
1076                 ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
1077         int srgbColor = Color.argb(.5f, .3f, .6f, .7f);
1078         long acesColor = Color.convert(srgbColor, ColorSpace.get(ColorSpace.Named.ACES));
1079         mBitmap.eraseColor(acesColor);
1080         ColorUtils.verifyColor("Mismatch at 15, 15", srgbColor, mBitmap.getPixel(15, 15), 1);
1081     }
1082 
1083     @Test(expected = IllegalArgumentException.class)
testEraseColorXYZ()1084     public void testEraseColorXYZ() {
1085         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1086         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ)));
1087     }
1088 
1089     @Test(expected = IllegalArgumentException.class)
testEraseColorLAB()1090     public void testEraseColorLAB() {
1091         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1092         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB)));
1093     }
1094 
1095     @Test(expected = IllegalArgumentException.class)
testEraseColorUnknown()1096     public void testEraseColorUnknown() {
1097         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1098         mBitmap.eraseColor(-1L);
1099     }
1100 
1101     @Test(expected=IllegalStateException.class)
testExtractAlphaFromRecycled()1102     public void testExtractAlphaFromRecycled() {
1103         mBitmap.recycle();
1104 
1105         mBitmap.extractAlpha();
1106     }
1107 
1108     @Test
testExtractAlpha()1109     public void testExtractAlpha() {
1110         // normal case
1111         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1112         Bitmap ret = mBitmap.extractAlpha();
1113         assertNotNull(ret);
1114         int source = mBitmap.getPixel(10, 20);
1115         int result = ret.getPixel(10, 20);
1116         assertEquals(Color.alpha(source), Color.alpha(result));
1117         assertEquals(0xFF, Color.alpha(result));
1118     }
1119 
1120     @Test(expected=IllegalStateException.class)
testExtractAlphaWithPaintAndOffsetFromRecycled()1121     public void testExtractAlphaWithPaintAndOffsetFromRecycled() {
1122         mBitmap.recycle();
1123 
1124         mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
1125     }
1126 
1127     @Test
testExtractAlphaWithPaintAndOffset()1128     public void testExtractAlphaWithPaintAndOffset() {
1129         // normal case
1130         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1131         Bitmap ret = mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
1132         assertNotNull(ret);
1133         int source = mBitmap.getPixel(10, 20);
1134         int result = ret.getPixel(10, 20);
1135         assertEquals(Color.alpha(source), Color.alpha(result));
1136         assertEquals(0xFF, Color.alpha(result));
1137     }
1138 
1139     @Test
testGetAllocationByteCount()1140     public void testGetAllocationByteCount() {
1141         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1142         int alloc = mBitmap.getAllocationByteCount();
1143         assertEquals(mBitmap.getByteCount(), alloc);
1144 
1145         // reconfigure same size
1146         mBitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888);
1147         assertEquals(mBitmap.getByteCount(), alloc);
1148         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1149 
1150         // reconfigure different size
1151         mBitmap.reconfigure(10, 10, Bitmap.Config.ALPHA_8);
1152         assertEquals(mBitmap.getByteCount(), 100);
1153         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1154     }
1155 
1156     @Test
testGetConfig()1157     public void testGetConfig() {
1158         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1159         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1160         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1161         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
1162 
1163         assertEquals(Bitmap.Config.ALPHA_8, bm0.getConfig());
1164         assertEquals(Bitmap.Config.ARGB_8888, bm1.getConfig());
1165         assertEquals(Bitmap.Config.RGB_565, bm2.getConfig());
1166         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
1167         assertEquals(Bitmap.Config.ARGB_8888, bm3.getConfig());
1168 
1169         // Can't call Bitmap.createBitmap with Bitmap.Config.HARDWARE,
1170         // because createBitmap creates mutable bitmap and hardware bitmaps are always immutable,
1171         // so such call will throw an exception.
1172         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
1173                 HARDWARE_OPTIONS);
1174         assertEquals(Bitmap.Config.HARDWARE, hardwareBitmap.getConfig());
1175     }
1176 
1177     @Test
testGetHeight()1178     public void testGetHeight() {
1179         assertEquals(31, mBitmap.getHeight());
1180         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1181         assertEquals(200, mBitmap.getHeight());
1182     }
1183 
1184     @Test
testGetNinePatchChunk()1185     public void testGetNinePatchChunk() {
1186         assertNull(mBitmap.getNinePatchChunk());
1187     }
1188 
1189     @Test(expected=IllegalStateException.class)
testGetPixelFromRecycled()1190     public void testGetPixelFromRecycled() {
1191         mBitmap.recycle();
1192 
1193         mBitmap.getPixel(10, 16);
1194     }
1195 
1196     @Test(expected=IllegalArgumentException.class)
testGetPixelXTooLarge()1197     public void testGetPixelXTooLarge() {
1198         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1199 
1200         // abnormal case: x bigger than the source bitmap's width
1201         mBitmap.getPixel(200, 16);
1202     }
1203 
1204     @Test(expected=IllegalArgumentException.class)
testGetPixelYTooLarge()1205     public void testGetPixelYTooLarge() {
1206         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1207 
1208         // abnormal case: y bigger than the source bitmap's height
1209         mBitmap.getPixel(10, 300);
1210     }
1211 
1212     @Test
testGetPixel()1213     public void testGetPixel() {
1214         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1215 
1216         // normal case 565
1217         mBitmap.setPixel(10, 16, 0xFF << 24);
1218         assertEquals(0xFF << 24, mBitmap.getPixel(10, 16));
1219 
1220         // normal case A_8
1221         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
1222         mBitmap.setPixel(5, 5, 0xFFFFFFFF);
1223         assertEquals(0xFF000000, mBitmap.getPixel(5, 5));
1224         mBitmap.setPixel(5, 5, 0xA8A8A8A8);
1225         assertEquals(0xA8000000, mBitmap.getPixel(5, 5));
1226         mBitmap.setPixel(5, 5, 0x00000000);
1227         assertEquals(0x00000000, mBitmap.getPixel(5, 5));
1228         mBitmap.setPixel(5, 5, 0x1F000000);
1229         assertEquals(0x1F000000, mBitmap.getPixel(5, 5));
1230     }
1231 
1232     @Test
testGetRowBytes()1233     public void testGetRowBytes() {
1234         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1235         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1236         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1237         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
1238 
1239         assertEquals(100, bm0.getRowBytes());
1240         assertEquals(400, bm1.getRowBytes());
1241         assertEquals(200, bm2.getRowBytes());
1242         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
1243         assertEquals(400, bm3.getRowBytes());
1244     }
1245 
1246     @Test
testGetWidth()1247     public void testGetWidth() {
1248         assertEquals(31, mBitmap.getWidth());
1249         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1250         assertEquals(100, mBitmap.getWidth());
1251     }
1252 
1253     @Test
testHasAlpha()1254     public void testHasAlpha() {
1255         assertFalse(mBitmap.hasAlpha());
1256         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1257         assertTrue(mBitmap.hasAlpha());
1258     }
1259 
1260     @Test
testIsMutable()1261     public void testIsMutable() {
1262         assertFalse(mBitmap.isMutable());
1263         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1264         assertTrue(mBitmap.isMutable());
1265     }
1266 
1267     @Test
testIsRecycled()1268     public void testIsRecycled() {
1269         assertFalse(mBitmap.isRecycled());
1270         mBitmap.recycle();
1271         assertTrue(mBitmap.isRecycled());
1272     }
1273 
1274     @Test
testReconfigure()1275     public void testReconfigure() {
1276         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1277         int alloc = mBitmap.getAllocationByteCount();
1278 
1279         // test shrinking
1280         mBitmap.reconfigure(50, 100, Bitmap.Config.ALPHA_8);
1281         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1282         assertEquals(mBitmap.getByteCount() * 8, alloc);
1283     }
1284 
1285     @Test(expected=IllegalArgumentException.class)
testReconfigureExpanding()1286     public void testReconfigureExpanding() {
1287         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1288         mBitmap.reconfigure(101, 201, Bitmap.Config.ARGB_8888);
1289     }
1290 
1291     @Test(expected=IllegalStateException.class)
testReconfigureMutable()1292     public void testReconfigureMutable() {
1293         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1294         mBitmap.reconfigure(1, 1, Bitmap.Config.ALPHA_8);
1295     }
1296 
1297     // Used by testAlphaAndPremul.
1298     private static Config[] CONFIGS = new Config[] { Config.ALPHA_8, Config.ARGB_4444,
1299             Config.ARGB_8888, Config.RGB_565 };
1300 
1301     // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with
1302     // respect to alpha and premultiplied.
1303     @Test
testAlphaAndPremul()1304     public void testAlphaAndPremul() {
1305         boolean falseTrue[] = new boolean[] { false, true };
1306         for (Config fromConfig : CONFIGS) {
1307             for (Config toConfig : CONFIGS) {
1308                 for (boolean hasAlpha : falseTrue) {
1309                     for (boolean isPremul : falseTrue) {
1310                         Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig);
1311 
1312                         // 4444 is deprecated, and will convert to 8888. No need to
1313                         // attempt a reconfigure, which will be tested when fromConfig
1314                         // is 8888.
1315                         if (fromConfig == Config.ARGB_4444) {
1316                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1317                             break;
1318                         }
1319 
1320                         bitmap.setHasAlpha(hasAlpha);
1321                         bitmap.setPremultiplied(isPremul);
1322 
1323                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, false);
1324 
1325                         // reconfigure to a smaller size so the function will still succeed when
1326                         // going to a Config that requires more bits.
1327                         bitmap.reconfigure(1, 1, toConfig);
1328                         if (toConfig == Config.ARGB_4444) {
1329                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1330                         } else {
1331                             assertEquals(bitmap.getConfig(), toConfig);
1332                         }
1333 
1334                         // Check that the alpha and premultiplied state has not changed (unless
1335                         // we expected it to).
1336                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565);
1337                     }
1338                 }
1339             }
1340         }
1341     }
1342 
1343     /**
1344      *  Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied().
1345      *  @param bitmap Bitmap to check.
1346      *  @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based
1347      *          on what was set, but may be different from the actual return value depending on the
1348      *          Config and convertedFrom565.
1349      *  @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to
1350      *          expectedAlpha, this is based on what was set, but may be different from the actual
1351      *          return value depending on the Config.
1352      *  @param convertedFrom565 Whether bitmap was converted to its current Config by being
1353      *          reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha,
1354      *          hasAlpha() is expected to be true even if expectedAlpha is false.
1355      */
verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565)1356     private void verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul,
1357             boolean convertedFrom565) {
1358         switch (bitmap.getConfig()) {
1359             case ARGB_4444:
1360                 // This shouldn't happen, since we don't allow creating or converting
1361                 // to 4444.
1362                 assertFalse(true);
1363                 break;
1364             case RGB_565:
1365                 assertFalse(bitmap.hasAlpha());
1366                 assertFalse(bitmap.isPremultiplied());
1367                 break;
1368             case ALPHA_8:
1369                 // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through.
1370             case ARGB_8888:
1371                 // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type
1372                 // that can have alpha.
1373                 if (convertedFrom565) {
1374                     assertTrue(bitmap.hasAlpha());
1375                 } else {
1376                     assertEquals(bitmap.hasAlpha(), expectedAlpha);
1377                 }
1378 
1379                 if (bitmap.hasAlpha()) {
1380                     // ALPHA_8's premultiplied status is undefined.
1381                     if (bitmap.getConfig() != Config.ALPHA_8) {
1382                         assertEquals(bitmap.isPremultiplied(), expectedPremul);
1383                     }
1384                 } else {
1385                     // Opaque bitmap is never considered premultiplied.
1386                     assertFalse(bitmap.isPremultiplied());
1387                 }
1388                 break;
1389         }
1390     }
1391 
1392     @Test
testSetColorSpace()1393     public void testSetColorSpace() {
1394         // Use arbitrary colors and assign to various ColorSpaces.
1395         for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
1396                 new ARGB(1.0f, .3f, .6f, .9f),
1397                 new ARGB(0.5f, .2f, .8f, .7f) }) {
1398 
1399             int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
1400             for (ColorSpace cs : getRgbColorSpaces()) {
1401                 for (Config config : new Config[] {
1402                         // F16 is tested elsewhere, since it defaults to EXTENDED_SRGB, and
1403                         // many of these calls to setColorSpace would reduce the range, resulting
1404                         // in an Exception.
1405                         Config.ARGB_8888,
1406                         Config.RGB_565,
1407                 }) {
1408                     mBitmap = Bitmap.createBitmap(10, 10, config);
1409                     mBitmap.eraseColor(srgbColor);
1410                     mBitmap.setColorSpace(cs);
1411                     ColorSpace actual = mBitmap.getColorSpace();
1412                     if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
1413                         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
1414                     } else if (cs == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
1415                         assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual);
1416                     } else {
1417                         assertSame(cs, actual);
1418                     }
1419 
1420                     // This tolerance was chosen by trial and error. It is expected that
1421                     // some conversions do not round-trip perfectly.
1422                     int tolerance = 2;
1423                     Color c = Color.valueOf(color.red, color.green, color.blue, color.alpha, cs);
1424                     ColorUtils.verifyColor("Mismatch after setting the colorSpace to "
1425                             + cs.getName(), c.convert(mBitmap.getColorSpace()),
1426                             mBitmap.getColor(5, 5), tolerance);
1427                 }
1428             }
1429         }
1430     }
1431 
1432     @Test(expected = IllegalStateException.class)
testSetColorSpaceRecycled()1433     public void testSetColorSpaceRecycled() {
1434         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1435         mBitmap.recycle();
1436         mBitmap.setColorSpace(ColorSpace.get(Named.DISPLAY_P3));
1437     }
1438 
1439     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNull()1440     public void testSetColorSpaceNull() {
1441         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1442         mBitmap.setColorSpace(null);
1443     }
1444 
1445     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceXYZ()1446     public void testSetColorSpaceXYZ() {
1447         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1448         mBitmap.setColorSpace(ColorSpace.get(Named.CIE_XYZ));
1449     }
1450 
1451     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNoTransferParameters()1452     public void testSetColorSpaceNoTransferParameters() {
1453         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1454         ColorSpace cs = new ColorSpace.Rgb("NoTransferParams",
1455                 new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
1456                 ColorSpace.ILLUMINANT_D50,
1457                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f),
1458                 0, 1);
1459         mBitmap.setColorSpace(cs);
1460     }
1461 
1462     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceAlpha8()1463     public void testSetColorSpaceAlpha8() {
1464         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
1465         assertNull(mBitmap.getColorSpace());
1466         mBitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
1467     }
1468 
1469     @Test
testSetColorSpaceReducedRange()1470     public void testSetColorSpaceReducedRange() {
1471         ColorSpace aces = ColorSpace.get(Named.ACES);
1472         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, aces);
1473         try {
1474             mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1475             fail("Expected IllegalArgumentException!");
1476         } catch (IllegalArgumentException e) {
1477             assertSame(aces, mBitmap.getColorSpace());
1478         }
1479     }
1480 
1481     @Test
testSetColorSpaceNotReducedRange()1482     public void testSetColorSpaceNotReducedRange() {
1483         ColorSpace extended = ColorSpace.get(Named.EXTENDED_SRGB);
1484         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1485                 extended);
1486         mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1487         assertSame(mBitmap.getColorSpace(), extended);
1488     }
1489 
1490     @Test
testSetColorSpaceNotReducedRangeLinear()1491     public void testSetColorSpaceNotReducedRangeLinear() {
1492         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1493         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1494                 linearExtended);
1495         mBitmap.setColorSpace(ColorSpace.get(Named.LINEAR_SRGB));
1496         assertSame(mBitmap.getColorSpace(), linearExtended);
1497     }
1498 
1499     @Test
testSetColorSpaceIncreasedRange()1500     public void testSetColorSpaceIncreasedRange() {
1501         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1502                 ColorSpace.get(Named.DISPLAY_P3));
1503         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1504         mBitmap.setColorSpace(linearExtended);
1505         assertSame(mBitmap.getColorSpace(), linearExtended);
1506     }
1507 
1508     @Test
testSetConfig()1509     public void testSetConfig() {
1510         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1511         int alloc = mBitmap.getAllocationByteCount();
1512 
1513         // test shrinking
1514         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1515         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1516         assertEquals(mBitmap.getByteCount() * 2, alloc);
1517     }
1518 
1519     @Test(expected=IllegalArgumentException.class)
testSetConfigExpanding()1520     public void testSetConfigExpanding() {
1521         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1522         // test expanding
1523         mBitmap.setConfig(Bitmap.Config.ARGB_8888);
1524     }
1525 
1526     @Test(expected=IllegalStateException.class)
testSetConfigMutable()1527     public void testSetConfigMutable() {
1528         // test mutable
1529         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1530         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1531     }
1532 
1533     @Test
testSetHeight()1534     public void testSetHeight() {
1535         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1536         int alloc = mBitmap.getAllocationByteCount();
1537 
1538         // test shrinking
1539         mBitmap.setHeight(100);
1540         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1541         assertEquals(mBitmap.getByteCount() * 2, alloc);
1542     }
1543 
1544     @Test(expected=IllegalArgumentException.class)
testSetHeightExpanding()1545     public void testSetHeightExpanding() {
1546         // test expanding
1547         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1548         mBitmap.setHeight(201);
1549     }
1550 
1551     @Test(expected=IllegalStateException.class)
testSetHeightMutable()1552     public void testSetHeightMutable() {
1553         // test mutable
1554         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1555         mBitmap.setHeight(1);
1556     }
1557 
1558     @Test(expected=IllegalStateException.class)
testSetPixelOnRecycled()1559     public void testSetPixelOnRecycled() {
1560         int color = 0xff << 24;
1561 
1562         mBitmap.recycle();
1563         mBitmap.setPixel(10, 16, color);
1564     }
1565 
1566     @Test(expected=IllegalStateException.class)
testSetPixelOnImmutable()1567     public void testSetPixelOnImmutable() {
1568         int color = 0xff << 24;
1569         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1570 
1571         mBitmap.setPixel(10, 16, color);
1572     }
1573 
1574     @Test(expected=IllegalArgumentException.class)
testSetPixelXIsTooLarge()1575     public void testSetPixelXIsTooLarge() {
1576         int color = 0xff << 24;
1577         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1578 
1579         // abnormal case: x bigger than the source bitmap's width
1580         mBitmap.setPixel(200, 16, color);
1581     }
1582 
1583     @Test(expected=IllegalArgumentException.class)
testSetPixelYIsTooLarge()1584     public void testSetPixelYIsTooLarge() {
1585         int color = 0xff << 24;
1586         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1587 
1588         // abnormal case: y bigger than the source bitmap's height
1589         mBitmap.setPixel(10, 300, color);
1590     }
1591 
1592     @Test
testSetPixel()1593     public void testSetPixel() {
1594         int color = 0xff << 24;
1595         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1596 
1597         // normal case
1598         mBitmap.setPixel(10, 16, color);
1599         assertEquals(color, mBitmap.getPixel(10, 16));
1600     }
1601 
1602     @Test(expected=IllegalStateException.class)
testSetPixelsOnRecycled()1603     public void testSetPixelsOnRecycled() {
1604         int[] colors = createColors(100);
1605 
1606         mBitmap.recycle();
1607         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1608     }
1609 
1610     @Test(expected=IllegalStateException.class)
testSetPixelsOnImmutable()1611     public void testSetPixelsOnImmutable() {
1612         int[] colors = createColors(100);
1613         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1614 
1615         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1616     }
1617 
1618     @Test(expected=IllegalArgumentException.class)
testSetPixelsXYNegative()1619     public void testSetPixelsXYNegative() {
1620         int[] colors = createColors(100);
1621         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1622 
1623         // abnormal case: x and/or y less than 0
1624         mBitmap.setPixels(colors, 0, 0, -1, -1, 200, 16);
1625     }
1626 
1627     @Test(expected=IllegalArgumentException.class)
testSetPixelsWidthHeightNegative()1628     public void testSetPixelsWidthHeightNegative() {
1629         int[] colors = createColors(100);
1630         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1631 
1632         // abnormal case: width and/or height less than 0
1633         mBitmap.setPixels(colors, 0, 0, 0, 0, -1, -1);
1634     }
1635 
1636     @Test(expected=IllegalArgumentException.class)
testSetPixelsXTooHigh()1637     public void testSetPixelsXTooHigh() {
1638         int[] colors = createColors(100);
1639         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1640 
1641         // abnormal case: (x + width) bigger than the source bitmap's width
1642         mBitmap.setPixels(colors, 0, 0, 10, 10, 95, 50);
1643     }
1644 
1645     @Test(expected=IllegalArgumentException.class)
testSetPixelsYTooHigh()1646     public void testSetPixelsYTooHigh() {
1647         int[] colors = createColors(100);
1648         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1649 
1650         // abnormal case: (y + height) bigger than the source bitmap's height
1651         mBitmap.setPixels(colors, 0, 0, 10, 10, 50, 95);
1652     }
1653 
1654     @Test(expected=IllegalArgumentException.class)
testSetPixelsStrideIllegal()1655     public void testSetPixelsStrideIllegal() {
1656         int[] colors = createColors(100);
1657         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1658 
1659         // abnormal case: stride less than width and bigger than -width
1660         mBitmap.setPixels(colors, 0, 10, 10, 10, 50, 50);
1661     }
1662 
1663     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetNegative()1664     public void testSetPixelsOffsetNegative() {
1665         int[] colors = createColors(100);
1666         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1667 
1668         // abnormal case: offset less than 0
1669         mBitmap.setPixels(colors, -1, 50, 10, 10, 50, 50);
1670     }
1671 
1672     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetTooBig()1673     public void testSetPixelsOffsetTooBig() {
1674         int[] colors = createColors(100);
1675         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1676 
1677         // abnormal case: (offset + width) bigger than the length of colors
1678         mBitmap.setPixels(colors, 60, 50, 10, 10, 50, 50);
1679     }
1680 
1681     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineNegative()1682     public void testSetPixelsLastScanlineNegative() {
1683         int[] colors = createColors(100);
1684         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1685 
1686         // abnormal case: lastScanline less than 0
1687         mBitmap.setPixels(colors, 10, -50, 10, 10, 50, 50);
1688     }
1689 
1690     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineTooBig()1691     public void testSetPixelsLastScanlineTooBig() {
1692         int[] colors = createColors(100);
1693         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1694 
1695         // abnormal case: (lastScanline + width) bigger than the length of colors
1696         mBitmap.setPixels(colors, 10, 50, 10, 10, 50, 50);
1697     }
1698 
1699     @Test
testSetPixels()1700     public void testSetPixels() {
1701         int[] colors = createColors(100 * 100);
1702         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1703         mBitmap.setPixels(colors, 0, 100, 0, 0, 100, 100);
1704         int[] ret = new int[100 * 100];
1705         mBitmap.getPixels(ret, 0, 100, 0, 0, 100, 100);
1706 
1707         for(int i = 0; i < 10000; i++){
1708             assertEquals(ret[i], colors[i]);
1709         }
1710     }
1711 
verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul)1712     private void verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul) {
1713         Bitmap bitmap = Bitmap.createBitmap(1, 1, config);
1714         bitmap.setPremultiplied(true);
1715         bitmap.setPixel(0, 0, Color.TRANSPARENT);
1716         assertTrue(bitmap.isPremultiplied() == expectedPremul);
1717 
1718         bitmap.setHasAlpha(false);
1719         assertFalse(bitmap.isPremultiplied());
1720     }
1721 
1722     @Test
testSetPremultipliedSimple()1723     public void testSetPremultipliedSimple() {
1724         verifyPremultipliedBitmapConfig(Bitmap.Config.ALPHA_8, true);
1725         verifyPremultipliedBitmapConfig(Bitmap.Config.RGB_565, false);
1726         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_4444, true);
1727         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_8888, true);
1728     }
1729 
1730     @Test
testSetPremultipliedData()1731     public void testSetPremultipliedData() {
1732         // with premul, will store 2,2,2,2, so it doesn't get value correct
1733         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1734         bitmap.setPixel(0, 0, PREMUL_COLOR);
1735         assertEquals(bitmap.getPixel(0, 0), PREMUL_ROUNDED_COLOR);
1736 
1737         // read premultiplied value directly
1738         bitmap.setPremultiplied(false);
1739         assertEquals(bitmap.getPixel(0, 0), PREMUL_STORED_COLOR);
1740 
1741         // value can now be stored/read correctly
1742         bitmap.setPixel(0, 0, PREMUL_COLOR);
1743         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1744 
1745         // verify with array methods
1746         int testArray[] = new int[] { PREMUL_COLOR };
1747         bitmap.setPixels(testArray, 0, 1, 0, 0, 1, 1);
1748         bitmap.getPixels(testArray, 0, 1, 0, 0, 1, 1);
1749         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1750     }
1751 
1752     @Test
testPremultipliedCanvas()1753     public void testPremultipliedCanvas() {
1754         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1755         bitmap.setHasAlpha(true);
1756         bitmap.setPremultiplied(false);
1757         assertFalse(bitmap.isPremultiplied());
1758 
1759         Canvas c = new Canvas();
1760         try {
1761             c.drawBitmap(bitmap, 0, 0, null);
1762             fail("canvas should fail with exception");
1763         } catch (RuntimeException e) {
1764         }
1765     }
1766 
getBitmapRawInt(Bitmap bitmap)1767     private int getBitmapRawInt(Bitmap bitmap) {
1768         IntBuffer buffer = IntBuffer.allocate(1);
1769         bitmap.copyPixelsToBuffer(buffer);
1770         return buffer.get(0);
1771     }
1772 
bitmapStoreRawInt(Bitmap bitmap, int value)1773     private void bitmapStoreRawInt(Bitmap bitmap, int value) {
1774         IntBuffer buffer = IntBuffer.allocate(1);
1775         buffer.put(0, value);
1776         bitmap.copyPixelsFromBuffer(buffer);
1777     }
1778 
1779     @Test
testSetPremultipliedToBuffer()1780     public void testSetPremultipliedToBuffer() {
1781         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1782         bitmap.setPixel(0, 0, PREMUL_COLOR);
1783         int storedPremul = getBitmapRawInt(bitmap);
1784 
1785         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1786         bitmap.setPremultiplied(false);
1787         bitmap.setPixel(0, 0, PREMUL_STORED_COLOR);
1788 
1789         assertEquals(getBitmapRawInt(bitmap), storedPremul);
1790     }
1791 
1792     @Test
testSetPremultipliedFromBuffer()1793     public void testSetPremultipliedFromBuffer() {
1794         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1795         bitmap.setPremultiplied(false);
1796         bitmap.setPixel(0, 0, PREMUL_COLOR);
1797         int rawTestColor = getBitmapRawInt(bitmap);
1798 
1799         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1800         bitmap.setPremultiplied(false);
1801         bitmapStoreRawInt(bitmap, rawTestColor);
1802         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1803     }
1804 
1805     @Test
testSetWidth()1806     public void testSetWidth() {
1807         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1808         int alloc = mBitmap.getAllocationByteCount();
1809 
1810         // test shrinking
1811         mBitmap.setWidth(50);
1812         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1813         assertEquals(mBitmap.getByteCount() * 2, alloc);
1814     }
1815 
1816     @Test(expected=IllegalArgumentException.class)
testSetWidthExpanding()1817     public void testSetWidthExpanding() {
1818         // test expanding
1819         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1820 
1821         mBitmap.setWidth(101);
1822     }
1823 
1824     @Test(expected=IllegalStateException.class)
testSetWidthMutable()1825     public void testSetWidthMutable() {
1826         // test mutable
1827         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1828 
1829         mBitmap.setWidth(1);
1830     }
1831 
1832     @Test(expected=IllegalStateException.class)
testWriteToParcelRecycled()1833     public void testWriteToParcelRecycled() {
1834         mBitmap.recycle();
1835 
1836         mBitmap.writeToParcel(null, 0);
1837     }
1838 
1839     @Test
testWriteToParcel()1840     public void testWriteToParcel() {
1841         // abnormal case: failed to unparcel Bitmap
1842         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1843         Parcel p = Parcel.obtain();
1844         mBitmap.writeToParcel(p, 0);
1845 
1846         try {
1847             Bitmap.CREATOR.createFromParcel(p);
1848             fail("shouldn't come to here");
1849         } catch(RuntimeException e){
1850         }
1851 
1852         p.recycle();
1853         // normal case
1854         p = Parcel.obtain();
1855         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1856         mBitmap.writeToParcel(p, 0);
1857         p.setDataPosition(0);
1858         assertTrue(mBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1859 
1860         p.recycle();
1861     }
1862 
1863     /**
1864      * Although not specified as required behavior, it's something that some apps appear
1865      * to rely upon when sending bitmaps between themselves
1866      */
1867     @Test
testWriteToParcelPreserveMutability()1868     public void testWriteToParcelPreserveMutability() {
1869         Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1870         assertTrue(source.isMutable());
1871         Parcel p = Parcel.obtain();
1872         source.writeToParcel(p, 0);
1873         p.setDataPosition(0);
1874         Bitmap result = Bitmap.CREATOR.createFromParcel(p);
1875         p.recycle();
1876         assertTrue(result.isMutable());
1877     }
1878 
1879     @Test
testWriteToParcelPreserveImmutability()1880     public void testWriteToParcelPreserveImmutability() {
1881         // Kinda silly way to create an immutable bitmap but it works
1882         Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888)
1883                 .copy(Config.ARGB_8888, false);
1884         assertFalse(source.isMutable());
1885         Parcel p = Parcel.obtain();
1886         source.writeToParcel(p, 0);
1887         p.setDataPosition(0);
1888         Bitmap result = Bitmap.CREATOR.createFromParcel(p);
1889         p.recycle();
1890         assertFalse(result.isMutable());
1891     }
1892 
1893     @Test
testWriteHwBitmapToParcel()1894     public void testWriteHwBitmapToParcel() {
1895         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1896         Parcel p = Parcel.obtain();
1897         mBitmap.writeToParcel(p, 0);
1898         p.setDataPosition(0);
1899         Bitmap expectedBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot);
1900         assertTrue(expectedBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1901 
1902         p.recycle();
1903     }
1904 
1905     @Test
testParcelF16ColorSpace()1906     public void testParcelF16ColorSpace() {
1907         for (ColorSpace.Named e : new ColorSpace.Named[] {
1908                 ColorSpace.Named.EXTENDED_SRGB,
1909                 ColorSpace.Named.LINEAR_EXTENDED_SRGB,
1910                 ColorSpace.Named.PRO_PHOTO_RGB,
1911                 ColorSpace.Named.DISPLAY_P3
1912         }) {
1913             final ColorSpace cs = ColorSpace.get(e);
1914             Bitmap b = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, cs);
1915             assertSame(cs, b.getColorSpace());
1916 
1917             Parcel p = Parcel.obtain();
1918             b.writeToParcel(p, 0);
1919             p.setDataPosition(0);
1920             Bitmap unparceled = Bitmap.CREATOR.createFromParcel(p);
1921             assertSame(cs, unparceled.getColorSpace());
1922         }
1923     }
1924 
1925     @Test
testGetScaledHeight1()1926     public void testGetScaledHeight1() {
1927         int dummyDensity = 5;
1928         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1929         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity);
1930         assertNotNull(ret);
1931         assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity));
1932     }
1933 
1934     @Test
testGetScaledHeight2()1935     public void testGetScaledHeight2() {
1936         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1937         DisplayMetrics metrics =
1938                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1939         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi);
1940         assertEquals(scaledHeight, ret.getScaledHeight(metrics));
1941     }
1942 
1943     @Test
testGetScaledHeight3()1944     public void testGetScaledHeight3() {
1945         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1946         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1947         Canvas mCanvas = new Canvas(mMutableBitmap);
1948         // set Density
1949         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1950         int scaledHeight = scaleFromDensity(
1951                 ret.getHeight(), ret.getDensity(), mCanvas.getDensity());
1952         assertEquals(scaledHeight, ret.getScaledHeight(mCanvas));
1953     }
1954 
1955     @Test
testGetScaledWidth1()1956     public void testGetScaledWidth1() {
1957         int dummyDensity = 5;
1958         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1959         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity);
1960         assertNotNull(ret);
1961         assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity));
1962     }
1963 
1964     @Test
testGetScaledWidth2()1965     public void testGetScaledWidth2() {
1966         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1967         DisplayMetrics metrics =
1968                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1969         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi);
1970         assertEquals(scaledWidth, ret.getScaledWidth(metrics));
1971     }
1972 
1973     @Test
testGetScaledWidth3()1974     public void testGetScaledWidth3() {
1975         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1976         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1977         Canvas mCanvas = new Canvas(mMutableBitmap);
1978         // set Density
1979         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1980         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(),  mCanvas.getDensity());
1981         assertEquals(scaledWidth, ret.getScaledWidth(mCanvas));
1982     }
1983 
1984     @Test
testSameAs_simpleSuccess()1985     public void testSameAs_simpleSuccess() {
1986         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1987         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1988         bitmap1.eraseColor(Color.BLACK);
1989         bitmap2.eraseColor(Color.BLACK);
1990         assertTrue(bitmap1.sameAs(bitmap2));
1991         assertTrue(bitmap2.sameAs(bitmap1));
1992     }
1993 
1994     @Test
testSameAs_simpleFail()1995     public void testSameAs_simpleFail() {
1996         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1997         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1998         bitmap1.eraseColor(Color.BLACK);
1999         bitmap2.eraseColor(Color.BLACK);
2000         bitmap2.setPixel(20, 10, Color.WHITE);
2001         assertFalse(bitmap1.sameAs(bitmap2));
2002         assertFalse(bitmap2.sameAs(bitmap1));
2003     }
2004 
2005     @Test
testSameAs_reconfigure()2006     public void testSameAs_reconfigure() {
2007         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2008         Bitmap bitmap2 = Bitmap.createBitmap(150, 150, Config.ARGB_8888);
2009         bitmap2.reconfigure(100, 100, Config.ARGB_8888); // now same size, so should be same
2010         bitmap1.eraseColor(Color.BLACK);
2011         bitmap2.eraseColor(Color.BLACK);
2012         assertTrue(bitmap1.sameAs(bitmap2));
2013         assertTrue(bitmap2.sameAs(bitmap1));
2014     }
2015 
2016     @Test
testSameAs_config()2017     public void testSameAs_config() {
2018         Bitmap bitmap1 = Bitmap.createBitmap(100, 200, Config.RGB_565);
2019         Bitmap bitmap2 = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
2020 
2021         // both bitmaps can represent black perfectly
2022         bitmap1.eraseColor(Color.BLACK);
2023         bitmap2.eraseColor(Color.BLACK);
2024 
2025         // but not same due to config
2026         assertFalse(bitmap1.sameAs(bitmap2));
2027         assertFalse(bitmap2.sameAs(bitmap1));
2028     }
2029 
2030     @Test
testSameAs_width()2031     public void testSameAs_width() {
2032         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2033         Bitmap bitmap2 = Bitmap.createBitmap(101, 100, Config.ARGB_8888);
2034         bitmap1.eraseColor(Color.BLACK);
2035         bitmap2.eraseColor(Color.BLACK);
2036         assertFalse(bitmap1.sameAs(bitmap2));
2037         assertFalse(bitmap2.sameAs(bitmap1));
2038     }
2039 
2040     @Test
testSameAs_height()2041     public void testSameAs_height() {
2042         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2043         Bitmap bitmap2 = Bitmap.createBitmap(102, 100, Config.ARGB_8888);
2044         bitmap1.eraseColor(Color.BLACK);
2045         bitmap2.eraseColor(Color.BLACK);
2046         assertFalse(bitmap1.sameAs(bitmap2));
2047         assertFalse(bitmap2.sameAs(bitmap1));
2048     }
2049 
2050     @Test
testSameAs_opaque()2051     public void testSameAs_opaque() {
2052         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2053         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2054         bitmap1.eraseColor(Color.BLACK);
2055         bitmap2.eraseColor(Color.BLACK);
2056         bitmap1.setHasAlpha(true);
2057         bitmap2.setHasAlpha(false);
2058         assertFalse(bitmap1.sameAs(bitmap2));
2059         assertFalse(bitmap2.sameAs(bitmap1));
2060     }
2061 
2062     @Test
testSameAs_hardware()2063     public void testSameAs_hardware() {
2064         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2065         Bitmap bitmap2 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2066         Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
2067         Bitmap bitmap4 = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2068         assertTrue(bitmap1.sameAs(bitmap2));
2069         assertTrue(bitmap2.sameAs(bitmap1));
2070         assertFalse(bitmap1.sameAs(bitmap3));
2071         assertFalse(bitmap1.sameAs(bitmap4));
2072     }
2073 
2074     @Test
testSameAs_wrappedHardwareBuffer()2075     public void testSameAs_wrappedHardwareBuffer() {
2076         try (HardwareBuffer hwBufferA = createTestBuffer(512, 512, true);
2077              HardwareBuffer hwBufferB = createTestBuffer(512, 512, true);
2078              HardwareBuffer hwBufferC = createTestBuffer(512, 512, true);) {
2079             // Fill buffer C with generated data
2080             nFillRgbaHwBuffer(hwBufferC);
2081 
2082             // Create the test bitmaps
2083             Bitmap bitmap1 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
2084             Bitmap bitmap2 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
2085             Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
2086             Bitmap bitmap4 = Bitmap.wrapHardwareBuffer(hwBufferB, ColorSpace.get(Named.SRGB));
2087             Bitmap bitmap5 = Bitmap.wrapHardwareBuffer(hwBufferC, ColorSpace.get(Named.SRGB));
2088 
2089             // Run the compare-a-thon
2090             assertTrue(bitmap1.sameAs(bitmap2));  // SAME UNDERLYING BUFFER
2091             assertTrue(bitmap2.sameAs(bitmap1));  // SAME UNDERLYING BUFFER
2092             assertFalse(bitmap1.sameAs(bitmap3)); // HW vs. NON-HW
2093             assertTrue(bitmap1.sameAs(bitmap4));  // DIFFERENT BUFFERS, SAME CONTENT
2094             assertFalse(bitmap1.sameAs(bitmap5)); // DIFFERENT BUFFERS, DIFFERENT CONTENT
2095         }
2096     }
2097 
2098     @Test(expected=IllegalStateException.class)
testHardwareGetPixel()2099     public void testHardwareGetPixel() {
2100         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2101         bitmap.getPixel(0, 0);
2102     }
2103 
2104     @Test(expected=IllegalStateException.class)
testHardwareGetPixels()2105     public void testHardwareGetPixels() {
2106         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2107         bitmap.getPixels(new int[5], 0, 5, 0, 0, 5, 1);
2108     }
2109 
2110     @Test
testGetConfigOnRecycled()2111     public void testGetConfigOnRecycled() {
2112         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2113         bitmap1.recycle();
2114         assertEquals(Config.HARDWARE, bitmap1.getConfig());
2115         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2116         bitmap2.recycle();
2117         assertEquals(Config.ARGB_8888, bitmap2.getConfig());
2118     }
2119 
2120     @Test(expected = IllegalStateException.class)
testHardwareSetWidth()2121     public void testHardwareSetWidth() {
2122         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2123         bitmap.setWidth(30);
2124     }
2125 
2126     @Test(expected = IllegalStateException.class)
testHardwareSetHeight()2127     public void testHardwareSetHeight() {
2128         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2129         bitmap.setHeight(30);
2130     }
2131 
2132     @Test(expected = IllegalStateException.class)
testHardwareSetConfig()2133     public void testHardwareSetConfig() {
2134         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2135         bitmap.setConfig(Config.ARGB_8888);
2136     }
2137 
2138     @Test(expected = IllegalStateException.class)
testHardwareReconfigure()2139     public void testHardwareReconfigure() {
2140         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2141         bitmap.reconfigure(30, 30, Config.ARGB_8888);
2142     }
2143 
2144     @Test(expected = IllegalStateException.class)
testHardwareSetPixels()2145     public void testHardwareSetPixels() {
2146         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2147         bitmap.setPixels(new int[10], 0, 1, 0, 0, 1, 1);
2148     }
2149 
2150     @Test(expected = IllegalStateException.class)
testHardwareSetPixel()2151     public void testHardwareSetPixel() {
2152         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2153         bitmap.setPixel(1, 1, 0);
2154     }
2155 
2156     @Test(expected = IllegalStateException.class)
testHardwareEraseColor()2157     public void testHardwareEraseColor() {
2158         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2159         bitmap.eraseColor(0);
2160     }
2161 
2162     @Test(expected = IllegalStateException.class)
testHardwareEraseColorLong()2163     public void testHardwareEraseColorLong() {
2164         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2165         bitmap.eraseColor(Color.pack(0));
2166     }
2167 
2168     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsToBuffer()2169     public void testHardwareCopyPixelsToBuffer() {
2170         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2171         ByteBuffer byteBuf = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight());
2172         bitmap.copyPixelsToBuffer(byteBuf);
2173     }
2174 
2175     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsFromBuffer()2176     public void testHardwareCopyPixelsFromBuffer() {
2177         IntBuffer intBuf1 = IntBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight());
2178         assertEquals(0, intBuf1.position());
2179         mBitmap.copyPixelsToBuffer(intBuf1);
2180         Bitmap hwBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2181         hwBitmap.copyPixelsFromBuffer(intBuf1);
2182     }
2183 
2184     @Test
testUseMetadataAfterRecycle()2185     public void testUseMetadataAfterRecycle() {
2186         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
2187         bitmap.recycle();
2188         assertEquals(10, bitmap.getWidth());
2189         assertEquals(20, bitmap.getHeight());
2190         assertEquals(Config.RGB_565, bitmap.getConfig());
2191     }
2192 
2193     @Test
testCopyHWBitmapInStrictMode()2194     public void testCopyHWBitmapInStrictMode() {
2195         strictModeTest(()->{
2196             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2197             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2198             hwBitmap.copy(Config.ARGB_8888, false);
2199         });
2200     }
2201 
2202     @Test
testCreateScaledFromHWInStrictMode()2203     public void testCreateScaledFromHWInStrictMode() {
2204         strictModeTest(()->{
2205             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2206             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2207             Bitmap.createScaledBitmap(hwBitmap, 200, 200, false);
2208         });
2209     }
2210 
2211     @Test
testExtractAlphaFromHWInStrictMode()2212     public void testExtractAlphaFromHWInStrictMode() {
2213         strictModeTest(()->{
2214             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2215             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2216             hwBitmap.extractAlpha();
2217         });
2218     }
2219 
2220     @Test
testCompressInStrictMode()2221     public void testCompressInStrictMode() {
2222         strictModeTest(()->{
2223             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2224             bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream());
2225         });
2226     }
2227 
2228     @Test
testParcelHWInStrictMode()2229     public void testParcelHWInStrictMode() {
2230         strictModeTest(()->{
2231             mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2232             Bitmap hwBitmap = mBitmap.copy(Config.HARDWARE, false);
2233             hwBitmap.writeToParcel(Parcel.obtain(), 0);
2234         });
2235     }
2236 
2237     @Test
testSameAsFirstHWInStrictMode()2238     public void testSameAsFirstHWInStrictMode() {
2239         strictModeTest(()->{
2240             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2241             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2242             hwBitmap.sameAs(bitmap);
2243         });
2244     }
2245 
2246     @Test
testSameAsSecondHWInStrictMode()2247     public void testSameAsSecondHWInStrictMode() {
2248         strictModeTest(()->{
2249             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2250             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2251             bitmap.sameAs(hwBitmap);
2252         });
2253     }
2254 
2255     @Test
testNdkAccessAfterRecycle()2256     public void testNdkAccessAfterRecycle() {
2257         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
2258         Bitmap hardware = bitmap.copy(Config.HARDWARE, false);
2259         nValidateBitmapInfo(bitmap, 10, 20, true);
2260         nValidateBitmapInfo(hardware, 10, 20, true);
2261 
2262         bitmap.recycle();
2263         hardware.recycle();
2264 
2265         nValidateBitmapInfo(bitmap, 10, 20, true);
2266         nValidateBitmapInfo(hardware, 10, 20, true);
2267         nValidateNdkAccessFails(bitmap);
2268     }
2269 
2270     @Test
bitmapIsMutable()2271     public void bitmapIsMutable() {
2272         Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2273         assertTrue("CreateBitmap w/ params should be mutable", b.isMutable());
2274         assertTrue("CreateBitmap from bitmap should be mutable",
2275                 Bitmap.createBitmap(b).isMutable());
2276     }
2277 
2278     @Test
2279     @LargeTest
testHardwareBitmapNotLeaking()2280     public void testHardwareBitmapNotLeaking() {
2281         BitmapFactory.Options opts = new BitmapFactory.Options();
2282         opts.inPreferredConfig = Config.HARDWARE;
2283         opts.inScaled = false;
2284 
2285         runNotLeakingTest(() -> {
2286             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2287             assertNotNull(bitmap);
2288             // Make sure nothing messed with the bitmap
2289             assertEquals(128, bitmap.getWidth());
2290             assertEquals(128, bitmap.getHeight());
2291             assertEquals(Config.HARDWARE, bitmap.getConfig());
2292             bitmap.recycle();
2293         });
2294     }
2295 
2296     @Test
2297     @LargeTest
testWrappedHardwareBufferBitmapNotLeaking()2298     public void testWrappedHardwareBufferBitmapNotLeaking() {
2299         final ColorSpace colorSpace = ColorSpace.get(Named.SRGB);
2300         try (HardwareBuffer hwBuffer = createTestBuffer(1024, 512, false)) {
2301             runNotLeakingTest(() -> {
2302                 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, colorSpace);
2303                 assertNotNull(bitmap);
2304                 // Make sure nothing messed with the bitmap
2305                 assertEquals(1024, bitmap.getWidth());
2306                 assertEquals(512, bitmap.getHeight());
2307                 assertEquals(Config.HARDWARE, bitmap.getConfig());
2308                 bitmap.recycle();
2309             });
2310         }
2311     }
2312 
2313     @Test
2314     @LargeTest
testDrawingHardwareBitmapNotLeaking()2315     public void testDrawingHardwareBitmapNotLeaking() {
2316         BitmapFactory.Options opts = new BitmapFactory.Options();
2317         opts.inPreferredConfig = Config.HARDWARE;
2318         opts.inScaled = false;
2319         RenderTarget renderTarget = RenderTarget.create();
2320         renderTarget.setDefaultSize(128, 128);
2321         final Surface surface = renderTarget.getSurface();
2322 
2323         runNotLeakingTest(() -> {
2324             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2325             assertNotNull(bitmap);
2326             // Make sure nothing messed with the bitmap
2327             assertEquals(128, bitmap.getWidth());
2328             assertEquals(128, bitmap.getHeight());
2329             assertEquals(Config.HARDWARE, bitmap.getConfig());
2330             Canvas canvas = surface.lockHardwareCanvas();
2331             canvas.drawBitmap(bitmap, 0, 0, null);
2332             surface.unlockCanvasAndPost(canvas);
2333             bitmap.recycle();
2334         });
2335 
2336         surface.release();
2337         renderTarget.destroy();
2338     }
2339 
2340     @Test
testWrapHardwareBufferHoldsReference()2341     public void testWrapHardwareBufferHoldsReference() {
2342         Bitmap bitmap;
2343         // Create hardware-buffer and wrap it in a Bitmap
2344         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
2345             // Fill buffer with colors (x, y, 42, 255)
2346             nFillRgbaHwBuffer(hwBuffer);
2347             bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2348         }
2349 
2350         // Buffer is closed at this point. Ensure bitmap still works by drawing it
2351         assertEquals(128, bitmap.getWidth());
2352         assertEquals(128, bitmap.getHeight());
2353         assertEquals(Config.HARDWARE, bitmap.getConfig());
2354 
2355         // Copy bitmap to target bitmap we can read from
2356         Bitmap dstBitmap = bitmap.copy(Config.ARGB_8888, false);
2357         bitmap.recycle();
2358 
2359         // Ensure that the bitmap has valid contents
2360         int pixel = dstBitmap.getPixel(0, 0);
2361         assertEquals(255 << 24 | 42, pixel);
2362         dstBitmap.recycle();
2363     }
2364 
2365     @Test
testWrapHardwareBufferPreservesColors()2366     public void testWrapHardwareBufferPreservesColors() {
2367         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
2368             // Fill buffer with colors (x, y, 42, 255)
2369             nFillRgbaHwBuffer(hwBuffer);
2370 
2371             // Create HW bitmap from this buffer
2372             Bitmap srcBitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2373             assertNotNull(srcBitmap);
2374 
2375             // Copy it to target non-HW bitmap
2376             Bitmap dstBitmap = srcBitmap.copy(Config.ARGB_8888, false);
2377             srcBitmap.recycle();
2378 
2379             // Ensure all colors are as expected (matches the nFillRgbaHwBuffer call used above).
2380             for (int y = 0; y < 128; ++y) {
2381                 for (int x = 0; x < 128; ++x) {
2382                     int pixel = dstBitmap.getPixel(x, y);
2383                     short a = 255;
2384                     short r = (short) (x % 255);
2385                     short g = (short) (y % 255);
2386                     short b = 42;
2387                     assertEquals(a << 24 | r << 16 | g << 8 | b, pixel);
2388                 }
2389             }
2390             dstBitmap.recycle();
2391         }
2392     }
2393 
compressToPng(Bitmap bitmap)2394     private static byte[] compressToPng(Bitmap bitmap) {
2395         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2396             assertTrue("Failed to encode a Bitmap with Config " + bitmap.getConfig()
2397                     + " and ColorSpace " + bitmap.getColorSpace() + "!",
2398                     bitmap.compress(CompressFormat.PNG, 100, stream));
2399             return stream.toByteArray();
2400         } catch (IOException e) {
2401             fail("Failed to compress with " + e);
2402             return null;
2403         }
2404     }
2405 
parametersForTestAsShared()2406     private static Object[] parametersForTestAsShared() {
2407         return Utils.crossProduct(Config.values(), getRgbColorSpaces().toArray(new Object[0]));
2408     }
2409 
2410     @Test
2411     @Parameters(method = "parametersForTestAsShared")
testAsShared(Config config, ColorSpace colorSpace)2412     public void testAsShared(Config config, ColorSpace colorSpace) {
2413         Bitmap original = Bitmap.createBitmap(10, 10,
2414                 config == Config.HARDWARE ? Config.ARGB_8888 : config, true /*hasAlpha*/,
2415                 colorSpace);
2416         drawGradient(original);
2417 
2418         if (config == Config.HARDWARE) {
2419             original = original.copy(Config.HARDWARE, false /*mutable*/);
2420         }
2421 
2422         // There's no visible way to test that the memory is allocated in shared memory, but we can
2423         // verify that the Bitmaps look the same.
2424         Bitmap shared = original.asShared();
2425         assertNotNull(shared);
2426 
2427         if (config == Config.HARDWARE) {
2428             int expectedFormat = nGetFormat(original);
2429             assertEquals(expectedFormat, configToFormat(shared.getConfig()));
2430 
2431             // There's no public way to look at the pixels in the HARDWARE Bitmap, but if we
2432             // compress each as a lossless PNG, they should look identical.
2433             byte[] origBytes = compressToPng(original);
2434             byte[] sharedBytes = compressToPng(shared);
2435             assertTrue(Arrays.equals(origBytes, sharedBytes));
2436         } else {
2437             assertSame(original.getConfig(), shared.getConfig());
2438             assertTrue(shared.sameAs(original));
2439         }
2440         assertSame(original.getColorSpace(), shared.getColorSpace());
2441 
2442         // The Bitmap is already in shared memory, so no work is done.
2443         Bitmap shared2 = shared.asShared();
2444         assertSame(shared, shared2);
2445 
2446         original.recycle();
2447         shared.recycle();
2448         shared2.recycle();
2449     }
2450 
2451     @Test
testAsSharedRecycled()2452     public void testAsSharedRecycled() {
2453         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2454         bitmap.recycle();
2455         assertThrows(IllegalStateException.class, bitmap::asShared);
2456     }
2457 
2458     @Test
testAsSharedDensity()2459     public void testAsSharedDensity() {
2460         DisplayMetrics metrics =
2461                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
2462         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2463         for (int density : new int[] { Bitmap.DENSITY_NONE, metrics.densityDpi,
2464                 DisplayMetrics.DENSITY_HIGH, DisplayMetrics.DENSITY_DEVICE_STABLE,
2465                 DisplayMetrics.DENSITY_MEDIUM }) {
2466             bitmap.setDensity(density);
2467             Bitmap shared = bitmap.asShared();
2468             assertEquals(density, shared.getDensity());
2469             shared.recycle();
2470         }
2471     }
2472 
2473     @Test
2474     @Parameters({"true", "false"})
testAsSharedImageDecoder(boolean mutable)2475     public void testAsSharedImageDecoder(boolean mutable) {
2476         Resources res = InstrumentationRegistry.getTargetContext().getResources();
2477         ImageDecoder.Source source = ImageDecoder.createSource(res.getAssets(),
2478                 "grayscale-16bit-linearSrgb.png");
2479         try {
2480             Bitmap bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, s) -> {
2481                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SHARED_MEMORY);
2482                 if (mutable) decoder.setMutableRequired(true);
2483             });
2484 
2485             Bitmap shared = bitmap.asShared();
2486             if (mutable) {
2487                 // bitmap is mutable, so asShared must make a copy.
2488                 assertNotEquals(bitmap, shared);
2489                 assertTrue(bitmap.sameAs(shared));
2490             } else {
2491                 // bitmap is already immutable and in shared memory, so asShared will return
2492                 // itself.
2493                 assertSame(bitmap, shared);
2494             }
2495         } catch (IOException e) {
2496             fail("Failed to decode with " + e);
2497         }
2498     }
2499 
2500     @Test
testNdkFormats()2501     public void testNdkFormats() {
2502         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2503             Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
2504             assertNotNull(bm);
2505             int nativeFormat = nGetFormat(bm);
2506             assertEquals("Config: " + pair.config, pair.format, nativeFormat);
2507         }
2508     }
2509 
2510     @Test
testNdkFormatsHardware()2511     public void testNdkFormatsHardware() {
2512         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2513             Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
2514             bm = bm.copy(Bitmap.Config.HARDWARE, false);
2515 
2516             // ALPHA_8 may not be supported in HARDWARE.
2517             if (bm == null) {
2518                 assertEquals(Bitmap.Config.ALPHA_8, pair.config);
2519                 continue;
2520             }
2521 
2522             int nativeFormat = nGetFormat(bm);
2523             // We allow everything to fall back to 8888
2524             if (nativeFormat != ANDROID_BITMAP_FORMAT_RGBA_8888) {
2525                 assertEquals("Config: " + pair.config, pair.format, nativeFormat);
2526             }
2527         }
2528     }
2529 
2530     @Test
testNullBitmapNdk()2531     public void testNullBitmapNdk() {
2532         Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
2533         nTestNullBitmap(bitmap);
2534     }
2535 
parametersForTestNdkInfo()2536     private Object[] parametersForTestNdkInfo() {
2537         return new Object[] {
2538             new Object[] { Config.ALPHA_8,   ANDROID_BITMAP_FORMAT_A_8  },
2539             new Object[] { Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888 },
2540             new Object[] { Config.RGB_565,   ANDROID_BITMAP_FORMAT_RGB_565 },
2541             new Object[] { Config.RGBA_F16,  ANDROID_BITMAP_FORMAT_RGBA_F16 },
2542             new Object[] { Config.RGBA_1010102,  ANDROID_BITMAP_FORMAT_RGBA_1010102 },
2543         };
2544     }
2545 
2546     @Test
2547     @Parameters(method = "parametersForTestNdkInfo")
testNdkInfo(Config config, final int expectedFormat)2548     public void testNdkInfo(Config config, final int expectedFormat) {
2549         // Arbitrary width and height.
2550         final int width = 13;
2551         final int height = 7;
2552         boolean[] trueFalse = new boolean[] { true, false };
2553         for (boolean hasAlpha : trueFalse) {
2554             for (boolean premultiplied : trueFalse) {
2555                 Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha);
2556                 bm.setPremultiplied(premultiplied);
2557                 nTestInfo(bm, expectedFormat, width, height, bm.hasAlpha(),
2558                         bm.isPremultiplied(), false);
2559                 Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false);
2560                 assertNotNull(hwBitmap);
2561                 // Formats that are not supported by gralloc fall back to 8888.
2562                 // Check what the HWB format is and compare against that
2563                 int tempExpectedFormat = expectedFormat;
2564                 HardwareBuffer buffer = hwBitmap.getHardwareBuffer();
2565                 if (buffer.getFormat() == HardwareBuffer.RGBA_8888) {
2566                     tempExpectedFormat = ANDROID_BITMAP_FORMAT_RGBA_8888;
2567                 }
2568                 nTestInfo(hwBitmap, tempExpectedFormat, width, height, hwBitmap.hasAlpha(),
2569                         hwBitmap.isPremultiplied(), true);
2570                 hwBitmap.recycle();
2571                 bm.recycle();
2572             }
2573         }
2574     }
2575 
2576     @Test
testNdkDataSpaceF16Extended()2577     public void testNdkDataSpaceF16Extended() {
2578         // In RGBA_F16 we force EXTENDED in these cases.
2579         for (ColorSpace colorSpace : new ColorSpace[] {
2580                 ColorSpace.get(Named.SRGB),
2581                 ColorSpace.get(Named.EXTENDED_SRGB),
2582         }) {
2583             Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace);
2584             assertNotNull(bm);
2585 
2586             assertEquals(ColorSpace.get(Named.EXTENDED_SRGB), bm.getColorSpace());
2587             assertEquals(DataSpace.ADATASPACE_SCRGB, nGetDataSpace(bm));
2588         }
2589 
2590         for (ColorSpace colorSpace : new ColorSpace[] {
2591                 ColorSpace.get(Named.LINEAR_SRGB),
2592                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2593         }) {
2594             Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace);
2595             assertNotNull(bm);
2596 
2597             assertEquals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), bm.getColorSpace());
2598             assertEquals(DataSpace.ADATASPACE_SCRGB_LINEAR, nGetDataSpace(bm));
2599         }
2600     }
2601 
2602     @Test
testNdkDataSpaceNonExtended()2603     public void testNdkDataSpaceNonExtended() {
2604         // In 565 and 8888, these force non-extended.
2605         for (ColorSpace colorSpace : new ColorSpace[] {
2606                 ColorSpace.get(Named.SRGB),
2607                 ColorSpace.get(Named.EXTENDED_SRGB),
2608         }) {
2609             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) {
2610                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2611                 assertNotNull(bm);
2612 
2613                 assertEquals(ColorSpace.get(Named.SRGB), bm.getColorSpace());
2614                 assertEquals(DataSpace.ADATASPACE_SRGB, nGetDataSpace(bm));
2615             }
2616         }
2617 
2618         for (ColorSpace colorSpace : new ColorSpace[] {
2619                 ColorSpace.get(Named.LINEAR_SRGB),
2620                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2621         }) {
2622             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) {
2623                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2624                 assertNotNull(bm);
2625 
2626                 assertEquals(ColorSpace.get(Named.LINEAR_SRGB), bm.getColorSpace());
2627                 assertEquals(DataSpace.ADATASPACE_SRGB_LINEAR, nGetDataSpace(bm));
2628             }
2629         }
2630     }
2631 
2632     @Test
testNdkDataSpace()2633     public void testNdkDataSpace() {
2634         // DataSpace.ADATASPACEs that do not depend on the Config.
2635         for (ColorSpace colorSpace : new ColorSpace[] {
2636                 // These have corresponding DataSpace.ADATASPACEs that are independent of the Config
2637                 ColorSpace.get(Named.DISPLAY_P3),
2638                 ColorSpace.get(Named.BT2020),
2639                 ColorSpace.get(Named.ADOBE_RGB),
2640                 ColorSpace.get(Named.BT709),
2641                 ColorSpace.get(Named.DCI_P3),
2642 
2643                 // These have no public ADATASPACE.
2644                 ColorSpace.get(Named.ACES),
2645                 ColorSpace.get(Named.ACESCG),
2646                 ColorSpace.get(Named.NTSC_1953),
2647                 ColorSpace.get(Named.PRO_PHOTO_RGB),
2648                 ColorSpace.get(Named.SMPTE_C),
2649         }) {
2650             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16 }) {
2651                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2652                 assertNotNull(bm);
2653 
2654                 int dataSpace = nGetDataSpace(bm);
2655                 assertEquals("Bitmap with " + c + " and " + bm.getColorSpace()
2656                         + " has unexpected data space", colorSpace.getDataSpace(),
2657                         dataSpace);
2658             }
2659         }
2660     }
2661 
2662     @Test
testNdkDataSpaceAlpha8()2663     public void testNdkDataSpaceAlpha8() {
2664         // ALPHA_8 doesn't support ColorSpaces
2665         Bitmap bm = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
2666         assertNotNull(bm);
2667         assertNull(bm.getColorSpace());
2668         int dataSpace = nGetDataSpace(bm);
2669         assertEquals(DataSpace.ADATASPACE_UNKNOWN, dataSpace);
2670     }
2671 
2672     @Test
testNdkDataSpaceNullBitmap()2673     public void testNdkDataSpaceNullBitmap() {
2674         assertEquals(DataSpace.ADATASPACE_UNKNOWN, nGetDataSpace(null));
2675     }
2676 
nGetDataSpace(Bitmap bm)2677     private static native int nGetDataSpace(Bitmap bm);
2678 
2679     // These match the NDK APIs.
2680     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_JPEG = 0;
2681     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_PNG = 1;
2682     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY = 3;
2683     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS = 4;
2684 
nativeCompressFormat(CompressFormat format)2685     private int nativeCompressFormat(CompressFormat format) {
2686         switch (format) {
2687             case JPEG:
2688                 return ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
2689             case PNG:
2690                 return ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
2691             case WEBP_LOSSY:
2692                 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY;
2693             case WEBP_LOSSLESS:
2694                 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS;
2695             default:
2696                 fail("format " + format + " has no corresponding native compress format!");
2697                 return -1;
2698         }
2699     }
2700 
parametersForNdkCompress()2701     private static Object[] parametersForNdkCompress() {
2702         // Skip WEBP, which has no corresponding native compress format.
2703         Object[] formats = new Object[] {
2704                 CompressFormat.JPEG,
2705                 CompressFormat.PNG,
2706                 CompressFormat.WEBP_LOSSY,
2707                 CompressFormat.WEBP_LOSSLESS,
2708         };
2709         // These are the ColorSpaces with corresponding ADataSpaces
2710         Object[] colorSpaces = new Object[] {
2711                 ColorSpace.get(Named.SRGB),
2712                 ColorSpace.get(Named.EXTENDED_SRGB),
2713                 ColorSpace.get(Named.LINEAR_SRGB),
2714                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2715 
2716                 ColorSpace.get(Named.DISPLAY_P3),
2717                 ColorSpace.get(Named.DCI_P3),
2718                 ColorSpace.get(Named.BT2020),
2719                 ColorSpace.get(Named.BT709),
2720                 ColorSpace.get(Named.ADOBE_RGB),
2721         };
2722 
2723         Object[] configs = new Object[] {
2724                 Config.ARGB_8888,
2725                 Config.RGB_565,
2726                 Config.RGBA_F16,
2727         };
2728 
2729         return crossProduct(formats, colorSpaces, configs);
2730     }
2731 
crossProduct(Object[] a, Object[] b, Object[] c)2732     private static Object[] crossProduct(Object[] a, Object[] b, Object[] c) {
2733         final int length = a.length * b.length * c.length;
2734         Object[] ret = new Object[length];
2735         for (int i = 0; i < a.length; i++) {
2736             for (int j = 0; j < b.length; j++) {
2737                 for (int k = 0; k < c.length; k++) {
2738                     int index = i * (b.length * c.length) + j * c.length + k;
2739                     assertNull(ret[index]);
2740                     ret[index] = new Object[] { a[i], b[j], c[k] };
2741                 }
2742             }
2743         }
2744         return ret;
2745     }
2746 
isSrgb(ColorSpace cs)2747     private static boolean isSrgb(ColorSpace cs) {
2748         return cs == ColorSpace.get(Named.SRGB)
2749                 || cs == ColorSpace.get(Named.EXTENDED_SRGB)
2750                 || cs == ColorSpace.get(Named.LINEAR_SRGB)
2751                 || cs == ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
2752     }
2753 
2754     // Helper method for populating a Bitmap with interesting pixels for comparison.
drawGradient(Bitmap bitmap)2755     private static void drawGradient(Bitmap bitmap) {
2756         // Use different colors and alphas.
2757         Canvas canvas = new Canvas(bitmap);
2758         ColorSpace cs = bitmap.getColorSpace();
2759         if (cs == null) {
2760             assertSame(Config.ALPHA_8, bitmap.getConfig());
2761             cs = ColorSpace.get(ColorSpace.Named.SRGB);
2762         }
2763         long color0 = Color.pack(0, 0, 1, 1, cs);
2764         long color1 = Color.pack(1, 0, 0, 0, cs);
2765         LinearGradient gradient = new LinearGradient(0, 0, 10, 10, color0, color1,
2766                 Shader.TileMode.CLAMP);
2767         Paint paint = new Paint();
2768         paint.setShader(gradient);
2769         canvas.drawPaint(paint);
2770     }
2771 
2772     @Test
2773     @Parameters(method = "parametersForNdkCompress")
testNdkCompress(CompressFormat format, ColorSpace cs, Config config)2774     public void testNdkCompress(CompressFormat format, ColorSpace cs, Config config)
2775             throws IOException {
2776         // Verify that ndk compress behaves the same as Bitmap#compress
2777         Bitmap bitmap = Bitmap.createBitmap(10, 10, config, true /* hasAlpha */, cs);
2778         assertNotNull(bitmap);
2779 
2780         {
2781             drawGradient(bitmap);
2782         }
2783 
2784         byte[] storage = new byte[16 * 1024];
2785         for (int quality : new int[] { 50, 80, 100 }) {
2786             byte[] expected = null;
2787             try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2788                 assertTrue("Failed to encode a Bitmap with " + cs + " to " + format + " at quality "
2789                         + quality + " from Java API", bitmap.compress(format, quality, stream));
2790                 expected = stream.toByteArray();
2791             }
2792 
2793             try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2794                 boolean success = nCompress(bitmap, nativeCompressFormat(format),
2795                         quality, stream, storage);
2796                 assertTrue("Failed to encode pixels with " + cs + " to " + format + " at quality "
2797                         + quality + " from NDK API", success);
2798                 byte[] actual = stream.toByteArray();
2799 
2800                 if (isSrgb(cs)) {
2801                     if (!Arrays.equals(expected, actual)) {
2802                         fail("NDK compression did not match for " + cs + " and format " + format
2803                                 + " at quality " + quality);
2804                     }
2805                 } else {
2806                     // The byte arrays will match exactly for SRGB and its variants, because those
2807                     // are treated specially. For the others, there are some small differences
2808                     // between Skia's and ColorSpace's values that result in the ICC profiles being
2809                     // written slightly differently. They should still look the same, though.
2810                     Bitmap expectedBitmap = decodeBytes(expected);
2811                     Bitmap actualBitmap = decodeBytes(actual);
2812                     boolean matched = BitmapUtils.compareBitmapsMse(expectedBitmap, actualBitmap,
2813                               5, true, false);
2814                     expectedBitmap.recycle();
2815                     actualBitmap.recycle();
2816                     assertTrue("NDK compression did not match for " + cs + " and format " + format
2817                                 + " at quality " + quality, matched);
2818                 }
2819             }
2820         }
2821     }
2822 
2823     @Test
testNdkCompressBadParameter()2824     public void testNdkCompressBadParameter() throws IOException {
2825         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2826             nTestNdkCompressBadParameter(mBitmap, stream, new byte[16 * 1024]);
2827         }
2828     }
2829 
nCompress(Bitmap bitmap, int format, int quality, OutputStream stream, byte[] storage)2830     private static native boolean nCompress(Bitmap bitmap, int format, int quality,
2831             OutputStream stream, byte[] storage);
nTestNdkCompressBadParameter(Bitmap bitmap, OutputStream stream, byte[] storage)2832     private static native void nTestNdkCompressBadParameter(Bitmap bitmap,
2833             OutputStream stream, byte[] storage);
2834 
strictModeTest(Runnable runnable)2835     private void strictModeTest(Runnable runnable) {
2836         StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
2837         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
2838                 .detectCustomSlowCalls().penaltyDeath().build());
2839         try {
2840             runnable.run();
2841             fail("Shouldn't reach it");
2842         } catch (RuntimeException expected){
2843             // expect to receive StrictModeViolation
2844         } finally {
2845             StrictMode.setThreadPolicy(originalPolicy);
2846         }
2847     }
2848 
nValidateBitmapInfo(Bitmap bitmap, int width, int height, boolean is565)2849     private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
2850             boolean is565);
nValidateNdkAccessFails(Bitmap bitmap)2851     private static native void nValidateNdkAccessFails(Bitmap bitmap);
2852 
nFillRgbaHwBuffer(HardwareBuffer hwBuffer)2853     private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
nTestNullBitmap(Bitmap bitmap)2854     private static native void nTestNullBitmap(Bitmap bitmap);
2855 
2856     private static final int ANDROID_BITMAP_FORMAT_NONE = 0;
2857     static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
2858     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
2859     private static final int ANDROID_BITMAP_FORMAT_A_8 = 8;
2860     private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9;
2861     private static final int ANDROID_BITMAP_FORMAT_RGBA_1010102 = 10;
2862 
2863     private static class ConfigToFormat {
2864         public final Config config;
2865         public final int format;
2866 
ConfigToFormat(Config c, int f)2867         ConfigToFormat(Config c, int f) {
2868             this.config = c;
2869             this.format = f;
2870         }
2871     }
2872 
configToFormat(Config config)2873     private static int configToFormat(Config config) {
2874         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2875             if (config == pair.config) {
2876                 return pair.format;
2877             }
2878         }
2879         return ANDROID_BITMAP_FORMAT_NONE;
2880     }
2881 
2882     private static final ConfigToFormat[] CONFIG_TO_FORMAT = new ConfigToFormat[] {
2883         new ConfigToFormat(Bitmap.Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888),
2884         // ARGB_4444 is deprecated, and createBitmap converts to 8888.
2885         new ConfigToFormat(Bitmap.Config.ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_8888),
2886         new ConfigToFormat(Bitmap.Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565),
2887         new ConfigToFormat(Bitmap.Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8),
2888         new ConfigToFormat(Bitmap.Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16),
2889         new ConfigToFormat(Bitmap.Config.RGBA_1010102, ANDROID_BITMAP_FORMAT_RGBA_1010102),
2890     };
2891 
nGetFormat(Bitmap bitmap)2892     static native int nGetFormat(Bitmap bitmap);
2893 
nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height, boolean hasAlpha, boolean premultiplied, boolean hardware)2894     private static native void nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height,
2895             boolean hasAlpha, boolean premultiplied, boolean hardware);
2896 
createTestBuffer(int width, int height, boolean cpuAccess)2897     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
2898         long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
2899         if (cpuAccess) {
2900             usage |= HardwareBuffer.USAGE_CPU_WRITE_RARELY;
2901         }
2902         // We can assume that RGBA_8888 format is supported for every platform.
2903         HardwareBuffer hwBuffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
2904                 1, usage);
2905         return hwBuffer;
2906     }
2907 
scaleFromDensity(int size, int sdensity, int tdensity)2908     private static int scaleFromDensity(int size, int sdensity, int tdensity) {
2909         if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) {
2910             return size;
2911         }
2912 
2913         // Scale by tdensity / sdensity, rounding up.
2914         return ((size * tdensity) + (sdensity >> 1)) / sdensity;
2915     }
2916 
createColors(int size)2917     private static int[] createColors(int size) {
2918         int[] colors = new int[size];
2919 
2920         for (int i = 0; i < size; i++) {
2921             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
2922         }
2923 
2924         return colors;
2925     }
2926 
createHardwareBitmapOptions()2927     private static BitmapFactory.Options createHardwareBitmapOptions() {
2928         BitmapFactory.Options options = new BitmapFactory.Options();
2929         options.inPreferredConfig = Config.HARDWARE;
2930         return options;
2931     }
2932 
2933     @Test
testCopyAlpha8ToHardware()2934     public void testCopyAlpha8ToHardware() {
2935         Bitmap bm = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
2936         assertNotNull(bm);
2937         Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false /* mutable */);
2938         // Some devices may not support ALPHA_8 + HARDWARE
2939         if (hwBitmap != null) {
2940             assertNull(hwBitmap.getColorSpace());
2941         }
2942 
2943         bm.recycle();
2944     }
2945 }
2946