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         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
679                 HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
680                         | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
681                         | HardwareBuffer.USAGE_COMPOSER_OVERLAY
682                         | HardwareBuffer.USAGE_PROTECTED_CONTENT)) {
683             assertThrows(IllegalArgumentException.class, () -> {
684                 Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
685             });
686         }
687     }
688 
689     @Test(expected = IllegalArgumentException.class)
testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails()690     public void testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails() {
691         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
692             HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)) {
693             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.CIE_LAB));
694         }
695     }
696 
697     @Test
testWrapHardwareBufferFor1010102BufferSucceeds()698     public void testWrapHardwareBufferFor1010102BufferSucceeds() {
699         HardwareBuffer hwBufferMaybe = null;
700 
701         try {
702             hwBufferMaybe = HardwareBuffer.create(128, 128, HardwareBuffer.RGBA_1010102, 1,
703                     HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
704         } catch (IllegalArgumentException e) {
705             assumeNoException("Creating a 1010102 HW buffer was not supported", e);
706         }
707 
708         assumeNotNull(hwBufferMaybe);
709 
710         try (HardwareBuffer buffer = hwBufferMaybe) {
711             Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(Named.SRGB));
712             assertNotNull(bitmap);
713             bitmap.recycle();
714         }
715     }
716 
assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2)717     private void assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2) {
718         assertEquals(hwBuffer, hwBuffer2);
719         assertEquals(hwBuffer.hashCode(), hwBuffer2.hashCode());
720         assertEquals(hwBuffer.getWidth(), hwBuffer2.getWidth());
721         assertEquals(hwBuffer.getHeight(), hwBuffer2.getHeight());
722         assertEquals(hwBuffer.getFormat(), hwBuffer2.getFormat());
723         assertEquals(hwBuffer.getLayers(), hwBuffer2.getLayers());
724         assertEquals(hwBuffer.getUsage(), hwBuffer2.getUsage());
725     }
726 
727     @Test
testGetHardwareBufferMatchesWrapped()728     public void testGetHardwareBufferMatchesWrapped() {
729         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
730             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
731             assertNotNull(bitmap);
732 
733             try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
734                 assertNotNull(hwBuffer2);
735                 assertMatches(hwBuffer, hwBuffer2);
736             }
737             bitmap.recycle();
738         }
739     }
740 
parametersFor_testGetAllocationSizeWrappedBuffer()741     private static Object[] parametersFor_testGetAllocationSizeWrappedBuffer() {
742         return new Object[] {
743                 HardwareBuffer.YCBCR_420_888,
744                 HardwareBuffer.YCBCR_P010,
745                 ImageFormat.YV12,
746         };
747     }
748 
749     @Test
750     @Parameters(method = "parametersFor_testGetAllocationSizeWrappedBuffer")
testGetAllocationSizeWrappedBuffer(int format)751     public void testGetAllocationSizeWrappedBuffer(int format) {
752         final long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
753         assumeTrue(HardwareBuffer.isSupported(1, 1, format, 1, usage));
754         HardwareBuffer buffer = HardwareBuffer.create(100, 100, format, 1, usage);
755         assertNotNull("isSupported = true but allocation failed", buffer);
756         Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, null);
757         buffer.close();
758         try {
759             // We can probably assert closer to at least 100 * 100 but maybe someone has super
760             // duper good compression rates, so assume a lower bound of 2kb
761             assertTrue(bitmap.getAllocationByteCount() > 2000);
762         } finally {
763             bitmap.recycle();
764         }
765     }
766 
parametersFor_testGetHardwareBufferConfig()767     private static Object[] parametersFor_testGetHardwareBufferConfig() {
768         return new Object[] {Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565};
769     }
770 
771     @Test
772     @Parameters(method = "parametersFor_testGetHardwareBufferConfig")
testGetHardwareBufferConfig(Config config)773     public void testGetHardwareBufferConfig(Config config) {
774         Bitmap bitmap = Bitmap.createBitmap(10, 10, config);
775         bitmap = bitmap.copy(Config.HARDWARE, false);
776         if (bitmap == null) {
777             fail("Failed to copy to HARDWARE with Config " + config);
778         }
779         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
780             assertNotNull(hwBuffer);
781             assertEquals(hwBuffer.getWidth(), 10);
782             assertEquals(hwBuffer.getHeight(), 10);
783         }
784     }
785 
786     @Test
testGetHardwareBufferTwice()787     public void testGetHardwareBufferTwice() {
788         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
789         bitmap = bitmap.copy(Config.HARDWARE, false);
790         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
791             assertNotNull(hwBuffer);
792             try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
793                 assertNotNull(hwBuffer2);
794                 assertMatches(hwBuffer, hwBuffer2);
795             }
796         }
797     }
798 
799     @Test
testGetHardwareBufferMatches()800     public void testGetHardwareBufferMatches() {
801         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
802         bitmap = bitmap.copy(Config.HARDWARE, false);
803         try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) {
804             HashSet<HardwareBuffer> set = new HashSet<HardwareBuffer>();
805             set.add(hwBuffer);
806             assertTrue(set.contains(bitmap.getHardwareBuffer()));
807         }
808     }
809 
810     @Test(expected = IllegalStateException.class)
testGetHardwareBufferNonHardware()811     public void testGetHardwareBufferNonHardware() {
812         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
813         bitmap.getHardwareBuffer();
814     }
815 
816     @Test(expected = IllegalStateException.class)
testGetHardwareBufferRecycled()817     public void testGetHardwareBufferRecycled() {
818         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
819         bitmap = bitmap.copy(Config.HARDWARE, false);
820         bitmap.recycle();
821         bitmap.getHardwareBuffer();
822     }
823 
824     @Test
testGetHardwareBufferClosed()825     public void testGetHardwareBufferClosed() {
826         HardwareBuffer hwBuffer = createTestBuffer(128, 128, false);
827         Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
828         assertNotNull(bitmap);
829 
830         hwBuffer.close();
831 
832         try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) {
833             assertNotNull(hwBuffer2);
834             assertFalse(hwBuffer2.isClosed());
835             assertNotEquals(hwBuffer, hwBuffer2);
836         }
837         bitmap.recycle();
838     }
839 
840     @Test
testGenerationId()841     public void testGenerationId() {
842         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
843         int genId = bitmap.getGenerationId();
844         assertEquals("not expected to change", genId, bitmap.getGenerationId());
845         bitmap.setDensity(bitmap.getDensity() + 4);
846         assertEquals("not expected to change", genId, bitmap.getGenerationId());
847         bitmap.getPixel(0, 0);
848         assertEquals("not expected to change", genId, bitmap.getGenerationId());
849 
850         int beforeGenId = bitmap.getGenerationId();
851         bitmap.eraseColor(Color.WHITE);
852         int afterGenId = bitmap.getGenerationId();
853         assertTrue("expected to increase", afterGenId > beforeGenId);
854 
855         beforeGenId = bitmap.getGenerationId();
856         bitmap.setPixel(4, 4, Color.BLUE);
857         afterGenId = bitmap.getGenerationId();
858         assertTrue("expected to increase again", afterGenId > beforeGenId);
859     }
860 
861     @Test
testDescribeContents()862     public void testDescribeContents() {
863         assertEquals(0, mBitmap.describeContents());
864     }
865 
866     @Test(expected=IllegalStateException.class)
testEraseColorOnRecycled()867     public void testEraseColorOnRecycled() {
868         mBitmap.recycle();
869 
870         mBitmap.eraseColor(0);
871     }
872 
873     @Test(expected = IllegalStateException.class)
testEraseColorLongOnRecycled()874     public void testEraseColorLongOnRecycled() {
875         mBitmap.recycle();
876 
877         mBitmap.eraseColor(Color.pack(0));
878     }
879 
880     @Test(expected=IllegalStateException.class)
testEraseColorOnImmutable()881     public void testEraseColorOnImmutable() {
882         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
883 
884         //abnormal case: bitmap is immutable
885         mBitmap.eraseColor(0);
886     }
887 
888     @Test(expected = IllegalStateException.class)
testEraseColorLongOnImmutable()889     public void testEraseColorLongOnImmutable() {
890         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
891 
892         //abnormal case: bitmap is immutable
893         mBitmap.eraseColor(Color.pack(0));
894     }
895 
896     @Test
testEraseColor()897     public void testEraseColor() {
898         // normal case
899         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
900         mBitmap.eraseColor(0xffff0000);
901         assertEquals(0xffff0000, mBitmap.getPixel(10, 10));
902         assertEquals(0xffff0000, mBitmap.getPixel(50, 50));
903     }
904 
905     @Test(expected = IllegalArgumentException.class)
testGetColorOOB()906     public void testGetColorOOB() {
907         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
908         mBitmap.getColor(-1, 0);
909     }
910 
911     @Test(expected = IllegalArgumentException.class)
testGetColorOOB2()912     public void testGetColorOOB2() {
913         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
914         mBitmap.getColor(5, -10);
915     }
916 
917     @Test(expected = IllegalArgumentException.class)
testGetColorOOB3()918     public void testGetColorOOB3() {
919         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
920         mBitmap.getColor(100, 10);
921     }
922 
923     @Test(expected = IllegalArgumentException.class)
testGetColorOOB4()924     public void testGetColorOOB4() {
925         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
926         mBitmap.getColor(99, 1000);
927     }
928 
929     @Test(expected = IllegalStateException.class)
testGetColorRecycled()930     public void testGetColorRecycled() {
931         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
932         mBitmap.recycle();
933         mBitmap.getColor(0, 0);
934     }
935 
936     @Test(expected = IllegalStateException.class)
testGetColorHardware()937     public void testGetColorHardware() {
938         BitmapFactory.Options options = new BitmapFactory.Options();
939         options.inPreferredConfig = Bitmap.Config.HARDWARE;
940         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
941         mBitmap.getColor(50, 50);
942 
943     }
944 
clamp(float f)945     private static float clamp(float f) {
946         return clamp(f, 0.0f, 1.0f);
947     }
948 
clamp(float f, float min, float max)949     private static float clamp(float f, float min, float max) {
950         return Math.min(Math.max(f, min), max);
951     }
952 
953     @Test
testGetColor()954     public void testGetColor() {
955         final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
956         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
957         for (Config config : new Config[] { Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565 }) {
958             for (ColorSpace bitmapColorSpace : rgbColorSpaces) {
959                 mBitmap = Bitmap.createBitmap(1, 1, config, /*hasAlpha*/ false,
960                         bitmapColorSpace);
961                 bitmapColorSpace = mBitmap.getColorSpace();
962                 for (ColorSpace eraseColorSpace : rgbColorSpaces) {
963                     for (long wideGamutLong : new long[] {
964                             Color.pack(1.0f, 0.0f, 0.0f, 1.0f, eraseColorSpace),
965                             Color.pack(0.0f, 1.0f, 0.0f, 1.0f, eraseColorSpace),
966                             Color.pack(0.0f, 0.0f, 1.0f, 1.0f, eraseColorSpace)}) {
967                         mBitmap.eraseColor(wideGamutLong);
968 
969                         Color result = mBitmap.getColor(0, 0);
970                         if (mBitmap.getColorSpace().equals(sRGB)) {
971                             assertEquals(mBitmap.getPixel(0, 0), result.toArgb());
972                         }
973                         if (eraseColorSpace.equals(bitmapColorSpace)) {
974                             final Color wideGamutColor = Color.valueOf(wideGamutLong);
975                             ColorUtils.verifyColor("Erasing to Bitmap's ColorSpace "
976                                     + bitmapColorSpace, wideGamutColor, result, .001f);
977 
978                         } else {
979                             Color convertedColor = Color.valueOf(
980                                     Color.convert(wideGamutLong, bitmapColorSpace));
981                             if (mBitmap.getConfig() != Config.RGBA_F16) {
982                                 // It's possible that we have to clip to fit into the Config.
983                                 convertedColor = Color.valueOf(
984                                         clamp(convertedColor.red()),
985                                         clamp(convertedColor.green()),
986                                         clamp(convertedColor.blue()),
987                                         convertedColor.alpha(),
988                                         bitmapColorSpace);
989                             }
990                             ColorUtils.verifyColor("Bitmap(Config: " + mBitmap.getConfig()
991                                     + ", ColorSpace: " + bitmapColorSpace
992                                     + ") erasing to " + Color.valueOf(wideGamutLong),
993                                     convertedColor, result, .03f);
994                         }
995                     }
996                 }
997             }
998         }
999     }
1000 
1001     private static class ARGB {
1002         public float alpha;
1003         public float red;
1004         public float green;
1005         public float blue;
ARGB(float alpha, float red, float green, float blue)1006         ARGB(float alpha, float red, float green, float blue) {
1007             this.alpha = alpha;
1008             this.red = red;
1009             this.green = green;
1010             this.blue = blue;
1011         }
1012     };
1013 
1014     @Test
testEraseColorLong()1015     public void testEraseColorLong() {
1016         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
1017         for (Config config : new Config[]{Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16}) {
1018             mBitmap = Bitmap.createBitmap(100, 100, config);
1019             // pack SRGB colors into ColorLongs.
1020             for (int color : new int[]{ Color.RED, Color.BLUE, Color.GREEN, Color.BLACK,
1021                     Color.WHITE, Color.TRANSPARENT }) {
1022                 if (config.equals(Config.RGB_565) && Float.compare(Color.alpha(color), 1.0f) != 0) {
1023                     // 565 doesn't support alpha.
1024                     continue;
1025                 }
1026                 mBitmap.eraseColor(Color.pack(color));
1027                 // The Bitmap is either SRGB or SRGBLinear (F16). getPixel(), which retrieves the
1028                 // color in SRGB, should match exactly.
1029                 ColorUtils.verifyColor("Config " + config + " mismatch at 10, 10 ",
1030                         color, mBitmap.getPixel(10, 10), 0);
1031                 ColorUtils.verifyColor("Config " + config + " mismatch at 50, 50 ",
1032                         color, mBitmap.getPixel(50, 50), 0);
1033             }
1034 
1035             // Use arbitrary colors in various ColorSpaces. getPixel() should approximately match
1036             // the SRGB version of the color.
1037             for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
1038                                           new ARGB(1.0f, .3f, .6f, .9f),
1039                                           new ARGB(0.5f, .2f, .8f, .7f) }) {
1040                 if (config.equals(Config.RGB_565) && Float.compare(color.alpha, 1.0f) != 0) {
1041                     continue;
1042                 }
1043                 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
1044                 for (ColorSpace cs : rgbColorSpaces) {
1045                     long longColor = Color.convert(srgbColor, cs);
1046                     mBitmap.eraseColor(longColor);
1047                     // These tolerances were chosen by trial and error. It is expected that
1048                     // some conversions do not round-trip perfectly.
1049                     int tolerance = 1;
1050                     if (config.equals(Config.RGB_565)) {
1051                         tolerance = 4;
1052                     } else if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) {
1053                         tolerance = 3;
1054                     }
1055 
1056                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
1057                             + ", mismatch at 10, 10 ", srgbColor, mBitmap.getPixel(10, 10),
1058                             tolerance);
1059                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
1060                             + ", mismatch at 50, 50 ", srgbColor, mBitmap.getPixel(50, 50),
1061                             tolerance);
1062                 }
1063             }
1064         }
1065     }
1066 
1067     @Test
testEraseColorOnP3()1068     public void testEraseColorOnP3() {
1069         // Use a ColorLong with a different ColorSpace than the Bitmap. getPixel() should
1070         // approximately match the SRGB version of the color.
1071         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888, true,
1072                 ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
1073         int srgbColor = Color.argb(.5f, .3f, .6f, .7f);
1074         long acesColor = Color.convert(srgbColor, ColorSpace.get(ColorSpace.Named.ACES));
1075         mBitmap.eraseColor(acesColor);
1076         ColorUtils.verifyColor("Mismatch at 15, 15", srgbColor, mBitmap.getPixel(15, 15), 1);
1077     }
1078 
1079     @Test(expected = IllegalArgumentException.class)
testEraseColorXYZ()1080     public void testEraseColorXYZ() {
1081         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1082         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ)));
1083     }
1084 
1085     @Test(expected = IllegalArgumentException.class)
testEraseColorLAB()1086     public void testEraseColorLAB() {
1087         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1088         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB)));
1089     }
1090 
1091     @Test(expected = IllegalArgumentException.class)
testEraseColorUnknown()1092     public void testEraseColorUnknown() {
1093         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1094         mBitmap.eraseColor(-1L);
1095     }
1096 
1097     @Test(expected=IllegalStateException.class)
testExtractAlphaFromRecycled()1098     public void testExtractAlphaFromRecycled() {
1099         mBitmap.recycle();
1100 
1101         mBitmap.extractAlpha();
1102     }
1103 
1104     @Test
testExtractAlpha()1105     public void testExtractAlpha() {
1106         // normal case
1107         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1108         Bitmap ret = mBitmap.extractAlpha();
1109         assertNotNull(ret);
1110         int source = mBitmap.getPixel(10, 20);
1111         int result = ret.getPixel(10, 20);
1112         assertEquals(Color.alpha(source), Color.alpha(result));
1113         assertEquals(0xFF, Color.alpha(result));
1114     }
1115 
1116     @Test(expected=IllegalStateException.class)
testExtractAlphaWithPaintAndOffsetFromRecycled()1117     public void testExtractAlphaWithPaintAndOffsetFromRecycled() {
1118         mBitmap.recycle();
1119 
1120         mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
1121     }
1122 
1123     @Test
testExtractAlphaWithPaintAndOffset()1124     public void testExtractAlphaWithPaintAndOffset() {
1125         // normal case
1126         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1127         Bitmap ret = mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
1128         assertNotNull(ret);
1129         int source = mBitmap.getPixel(10, 20);
1130         int result = ret.getPixel(10, 20);
1131         assertEquals(Color.alpha(source), Color.alpha(result));
1132         assertEquals(0xFF, Color.alpha(result));
1133     }
1134 
1135     @Test
testGetAllocationByteCount()1136     public void testGetAllocationByteCount() {
1137         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1138         int alloc = mBitmap.getAllocationByteCount();
1139         assertEquals(mBitmap.getByteCount(), alloc);
1140 
1141         // reconfigure same size
1142         mBitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888);
1143         assertEquals(mBitmap.getByteCount(), alloc);
1144         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1145 
1146         // reconfigure different size
1147         mBitmap.reconfigure(10, 10, Bitmap.Config.ALPHA_8);
1148         assertEquals(mBitmap.getByteCount(), 100);
1149         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1150     }
1151 
1152     @Test
testGetConfig()1153     public void testGetConfig() {
1154         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1155         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1156         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1157         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
1158 
1159         assertEquals(Bitmap.Config.ALPHA_8, bm0.getConfig());
1160         assertEquals(Bitmap.Config.ARGB_8888, bm1.getConfig());
1161         assertEquals(Bitmap.Config.RGB_565, bm2.getConfig());
1162         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
1163         assertEquals(Bitmap.Config.ARGB_8888, bm3.getConfig());
1164 
1165         // Can't call Bitmap.createBitmap with Bitmap.Config.HARDWARE,
1166         // because createBitmap creates mutable bitmap and hardware bitmaps are always immutable,
1167         // so such call will throw an exception.
1168         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
1169                 HARDWARE_OPTIONS);
1170         assertEquals(Bitmap.Config.HARDWARE, hardwareBitmap.getConfig());
1171     }
1172 
1173     @Test
testGetHeight()1174     public void testGetHeight() {
1175         assertEquals(31, mBitmap.getHeight());
1176         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1177         assertEquals(200, mBitmap.getHeight());
1178     }
1179 
1180     @Test
testGetNinePatchChunk()1181     public void testGetNinePatchChunk() {
1182         assertNull(mBitmap.getNinePatchChunk());
1183     }
1184 
1185     @Test(expected=IllegalStateException.class)
testGetPixelFromRecycled()1186     public void testGetPixelFromRecycled() {
1187         mBitmap.recycle();
1188 
1189         mBitmap.getPixel(10, 16);
1190     }
1191 
1192     @Test(expected=IllegalArgumentException.class)
testGetPixelXTooLarge()1193     public void testGetPixelXTooLarge() {
1194         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1195 
1196         // abnormal case: x bigger than the source bitmap's width
1197         mBitmap.getPixel(200, 16);
1198     }
1199 
1200     @Test(expected=IllegalArgumentException.class)
testGetPixelYTooLarge()1201     public void testGetPixelYTooLarge() {
1202         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1203 
1204         // abnormal case: y bigger than the source bitmap's height
1205         mBitmap.getPixel(10, 300);
1206     }
1207 
1208     @Test
testGetPixel()1209     public void testGetPixel() {
1210         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1211 
1212         // normal case 565
1213         mBitmap.setPixel(10, 16, 0xFF << 24);
1214         assertEquals(0xFF << 24, mBitmap.getPixel(10, 16));
1215 
1216         // normal case A_8
1217         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
1218         mBitmap.setPixel(5, 5, 0xFFFFFFFF);
1219         assertEquals(0xFF000000, mBitmap.getPixel(5, 5));
1220         mBitmap.setPixel(5, 5, 0xA8A8A8A8);
1221         assertEquals(0xA8000000, mBitmap.getPixel(5, 5));
1222         mBitmap.setPixel(5, 5, 0x00000000);
1223         assertEquals(0x00000000, mBitmap.getPixel(5, 5));
1224         mBitmap.setPixel(5, 5, 0x1F000000);
1225         assertEquals(0x1F000000, mBitmap.getPixel(5, 5));
1226     }
1227 
1228     @Test
testGetRowBytes()1229     public void testGetRowBytes() {
1230         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
1231         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1232         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1233         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
1234 
1235         assertEquals(100, bm0.getRowBytes());
1236         assertEquals(400, bm1.getRowBytes());
1237         assertEquals(200, bm2.getRowBytes());
1238         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
1239         assertEquals(400, bm3.getRowBytes());
1240     }
1241 
1242     @Test
testGetWidth()1243     public void testGetWidth() {
1244         assertEquals(31, mBitmap.getWidth());
1245         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1246         assertEquals(100, mBitmap.getWidth());
1247     }
1248 
1249     @Test
testHasAlpha()1250     public void testHasAlpha() {
1251         assertFalse(mBitmap.hasAlpha());
1252         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1253         assertTrue(mBitmap.hasAlpha());
1254     }
1255 
1256     @Test
testIsMutable()1257     public void testIsMutable() {
1258         assertFalse(mBitmap.isMutable());
1259         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1260         assertTrue(mBitmap.isMutable());
1261     }
1262 
1263     @Test
testIsRecycled()1264     public void testIsRecycled() {
1265         assertFalse(mBitmap.isRecycled());
1266         mBitmap.recycle();
1267         assertTrue(mBitmap.isRecycled());
1268     }
1269 
1270     @Test
testReconfigure()1271     public void testReconfigure() {
1272         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1273         int alloc = mBitmap.getAllocationByteCount();
1274 
1275         // test shrinking
1276         mBitmap.reconfigure(50, 100, Bitmap.Config.ALPHA_8);
1277         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1278         assertEquals(mBitmap.getByteCount() * 8, alloc);
1279     }
1280 
1281     @Test(expected=IllegalArgumentException.class)
testReconfigureExpanding()1282     public void testReconfigureExpanding() {
1283         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1284         mBitmap.reconfigure(101, 201, Bitmap.Config.ARGB_8888);
1285     }
1286 
1287     @Test(expected=IllegalStateException.class)
testReconfigureMutable()1288     public void testReconfigureMutable() {
1289         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1290         mBitmap.reconfigure(1, 1, Bitmap.Config.ALPHA_8);
1291     }
1292 
1293     // Used by testAlphaAndPremul.
1294     private static Config[] CONFIGS = new Config[] { Config.ALPHA_8, Config.ARGB_4444,
1295             Config.ARGB_8888, Config.RGB_565 };
1296 
1297     // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with
1298     // respect to alpha and premultiplied.
1299     @Test
testAlphaAndPremul()1300     public void testAlphaAndPremul() {
1301         boolean falseTrue[] = new boolean[] { false, true };
1302         for (Config fromConfig : CONFIGS) {
1303             for (Config toConfig : CONFIGS) {
1304                 for (boolean hasAlpha : falseTrue) {
1305                     for (boolean isPremul : falseTrue) {
1306                         Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig);
1307 
1308                         // 4444 is deprecated, and will convert to 8888. No need to
1309                         // attempt a reconfigure, which will be tested when fromConfig
1310                         // is 8888.
1311                         if (fromConfig == Config.ARGB_4444) {
1312                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1313                             break;
1314                         }
1315 
1316                         bitmap.setHasAlpha(hasAlpha);
1317                         bitmap.setPremultiplied(isPremul);
1318 
1319                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, false);
1320 
1321                         // reconfigure to a smaller size so the function will still succeed when
1322                         // going to a Config that requires more bits.
1323                         bitmap.reconfigure(1, 1, toConfig);
1324                         if (toConfig == Config.ARGB_4444) {
1325                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1326                         } else {
1327                             assertEquals(bitmap.getConfig(), toConfig);
1328                         }
1329 
1330                         // Check that the alpha and premultiplied state has not changed (unless
1331                         // we expected it to).
1332                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565);
1333                     }
1334                 }
1335             }
1336         }
1337     }
1338 
1339     /**
1340      *  Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied().
1341      *  @param bitmap Bitmap to check.
1342      *  @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based
1343      *          on what was set, but may be different from the actual return value depending on the
1344      *          Config and convertedFrom565.
1345      *  @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to
1346      *          expectedAlpha, this is based on what was set, but may be different from the actual
1347      *          return value depending on the Config.
1348      *  @param convertedFrom565 Whether bitmap was converted to its current Config by being
1349      *          reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha,
1350      *          hasAlpha() is expected to be true even if expectedAlpha is false.
1351      */
verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565)1352     private void verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul,
1353             boolean convertedFrom565) {
1354         switch (bitmap.getConfig()) {
1355             case ARGB_4444:
1356                 // This shouldn't happen, since we don't allow creating or converting
1357                 // to 4444.
1358                 assertFalse(true);
1359                 break;
1360             case RGB_565:
1361                 assertFalse(bitmap.hasAlpha());
1362                 assertFalse(bitmap.isPremultiplied());
1363                 break;
1364             case ALPHA_8:
1365                 // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through.
1366             case ARGB_8888:
1367                 // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type
1368                 // that can have alpha.
1369                 if (convertedFrom565) {
1370                     assertTrue(bitmap.hasAlpha());
1371                 } else {
1372                     assertEquals(bitmap.hasAlpha(), expectedAlpha);
1373                 }
1374 
1375                 if (bitmap.hasAlpha()) {
1376                     // ALPHA_8's premultiplied status is undefined.
1377                     if (bitmap.getConfig() != Config.ALPHA_8) {
1378                         assertEquals(bitmap.isPremultiplied(), expectedPremul);
1379                     }
1380                 } else {
1381                     // Opaque bitmap is never considered premultiplied.
1382                     assertFalse(bitmap.isPremultiplied());
1383                 }
1384                 break;
1385         }
1386     }
1387 
1388     @Test
testSetColorSpace()1389     public void testSetColorSpace() {
1390         // Use arbitrary colors and assign to various ColorSpaces.
1391         for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
1392                 new ARGB(1.0f, .3f, .6f, .9f),
1393                 new ARGB(0.5f, .2f, .8f, .7f) }) {
1394 
1395             int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
1396             for (ColorSpace cs : getRgbColorSpaces()) {
1397                 for (Config config : new Config[] {
1398                         // F16 is tested elsewhere, since it defaults to EXTENDED_SRGB, and
1399                         // many of these calls to setColorSpace would reduce the range, resulting
1400                         // in an Exception.
1401                         Config.ARGB_8888,
1402                         Config.RGB_565,
1403                 }) {
1404                     mBitmap = Bitmap.createBitmap(10, 10, config);
1405                     mBitmap.eraseColor(srgbColor);
1406                     mBitmap.setColorSpace(cs);
1407                     ColorSpace actual = mBitmap.getColorSpace();
1408                     if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
1409                         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
1410                     } else if (cs == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
1411                         assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual);
1412                     } else {
1413                         assertSame(cs, actual);
1414                     }
1415 
1416                     // This tolerance was chosen by trial and error. It is expected that
1417                     // some conversions do not round-trip perfectly.
1418                     int tolerance = 2;
1419                     Color c = Color.valueOf(color.red, color.green, color.blue, color.alpha, cs);
1420                     ColorUtils.verifyColor("Mismatch after setting the colorSpace to "
1421                             + cs.getName(), c.convert(mBitmap.getColorSpace()),
1422                             mBitmap.getColor(5, 5), tolerance);
1423                 }
1424             }
1425         }
1426     }
1427 
1428     @Test(expected = IllegalStateException.class)
testSetColorSpaceRecycled()1429     public void testSetColorSpaceRecycled() {
1430         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1431         mBitmap.recycle();
1432         mBitmap.setColorSpace(ColorSpace.get(Named.DISPLAY_P3));
1433     }
1434 
1435     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNull()1436     public void testSetColorSpaceNull() {
1437         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1438         mBitmap.setColorSpace(null);
1439     }
1440 
1441     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceXYZ()1442     public void testSetColorSpaceXYZ() {
1443         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1444         mBitmap.setColorSpace(ColorSpace.get(Named.CIE_XYZ));
1445     }
1446 
1447     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNoTransferParameters()1448     public void testSetColorSpaceNoTransferParameters() {
1449         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1450         ColorSpace cs = new ColorSpace.Rgb("NoTransferParams",
1451                 new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
1452                 ColorSpace.ILLUMINANT_D50,
1453                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f),
1454                 0, 1);
1455         mBitmap.setColorSpace(cs);
1456     }
1457 
1458     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceAlpha8()1459     public void testSetColorSpaceAlpha8() {
1460         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
1461         assertNull(mBitmap.getColorSpace());
1462         mBitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
1463     }
1464 
1465     @Test
testSetColorSpaceReducedRange()1466     public void testSetColorSpaceReducedRange() {
1467         ColorSpace aces = ColorSpace.get(Named.ACES);
1468         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, aces);
1469         try {
1470             mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1471             fail("Expected IllegalArgumentException!");
1472         } catch (IllegalArgumentException e) {
1473             assertSame(aces, mBitmap.getColorSpace());
1474         }
1475     }
1476 
1477     @Test
testSetColorSpaceNotReducedRange()1478     public void testSetColorSpaceNotReducedRange() {
1479         ColorSpace extended = ColorSpace.get(Named.EXTENDED_SRGB);
1480         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1481                 extended);
1482         mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1483         assertSame(mBitmap.getColorSpace(), extended);
1484     }
1485 
1486     @Test
testSetColorSpaceNotReducedRangeLinear()1487     public void testSetColorSpaceNotReducedRangeLinear() {
1488         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1489         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1490                 linearExtended);
1491         mBitmap.setColorSpace(ColorSpace.get(Named.LINEAR_SRGB));
1492         assertSame(mBitmap.getColorSpace(), linearExtended);
1493     }
1494 
1495     @Test
testSetColorSpaceIncreasedRange()1496     public void testSetColorSpaceIncreasedRange() {
1497         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1498                 ColorSpace.get(Named.DISPLAY_P3));
1499         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1500         mBitmap.setColorSpace(linearExtended);
1501         assertSame(mBitmap.getColorSpace(), linearExtended);
1502     }
1503 
1504     @Test
testSetConfig()1505     public void testSetConfig() {
1506         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1507         int alloc = mBitmap.getAllocationByteCount();
1508 
1509         // test shrinking
1510         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1511         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1512         assertEquals(mBitmap.getByteCount() * 2, alloc);
1513     }
1514 
1515     @Test(expected=IllegalArgumentException.class)
testSetConfigExpanding()1516     public void testSetConfigExpanding() {
1517         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1518         // test expanding
1519         mBitmap.setConfig(Bitmap.Config.ARGB_8888);
1520     }
1521 
1522     @Test(expected=IllegalStateException.class)
testSetConfigMutable()1523     public void testSetConfigMutable() {
1524         // test mutable
1525         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1526         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1527     }
1528 
1529     @Test
testSetHeight()1530     public void testSetHeight() {
1531         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1532         int alloc = mBitmap.getAllocationByteCount();
1533 
1534         // test shrinking
1535         mBitmap.setHeight(100);
1536         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1537         assertEquals(mBitmap.getByteCount() * 2, alloc);
1538     }
1539 
1540     @Test(expected=IllegalArgumentException.class)
testSetHeightExpanding()1541     public void testSetHeightExpanding() {
1542         // test expanding
1543         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1544         mBitmap.setHeight(201);
1545     }
1546 
1547     @Test(expected=IllegalStateException.class)
testSetHeightMutable()1548     public void testSetHeightMutable() {
1549         // test mutable
1550         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1551         mBitmap.setHeight(1);
1552     }
1553 
1554     @Test(expected=IllegalStateException.class)
testSetPixelOnRecycled()1555     public void testSetPixelOnRecycled() {
1556         int color = 0xff << 24;
1557 
1558         mBitmap.recycle();
1559         mBitmap.setPixel(10, 16, color);
1560     }
1561 
1562     @Test(expected=IllegalStateException.class)
testSetPixelOnImmutable()1563     public void testSetPixelOnImmutable() {
1564         int color = 0xff << 24;
1565         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1566 
1567         mBitmap.setPixel(10, 16, color);
1568     }
1569 
1570     @Test(expected=IllegalArgumentException.class)
testSetPixelXIsTooLarge()1571     public void testSetPixelXIsTooLarge() {
1572         int color = 0xff << 24;
1573         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1574 
1575         // abnormal case: x bigger than the source bitmap's width
1576         mBitmap.setPixel(200, 16, color);
1577     }
1578 
1579     @Test(expected=IllegalArgumentException.class)
testSetPixelYIsTooLarge()1580     public void testSetPixelYIsTooLarge() {
1581         int color = 0xff << 24;
1582         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1583 
1584         // abnormal case: y bigger than the source bitmap's height
1585         mBitmap.setPixel(10, 300, color);
1586     }
1587 
1588     @Test
testSetPixel()1589     public void testSetPixel() {
1590         int color = 0xff << 24;
1591         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1592 
1593         // normal case
1594         mBitmap.setPixel(10, 16, color);
1595         assertEquals(color, mBitmap.getPixel(10, 16));
1596     }
1597 
1598     @Test(expected=IllegalStateException.class)
testSetPixelsOnRecycled()1599     public void testSetPixelsOnRecycled() {
1600         int[] colors = createColors(100);
1601 
1602         mBitmap.recycle();
1603         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1604     }
1605 
1606     @Test(expected=IllegalStateException.class)
testSetPixelsOnImmutable()1607     public void testSetPixelsOnImmutable() {
1608         int[] colors = createColors(100);
1609         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1610 
1611         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1612     }
1613 
1614     @Test(expected=IllegalArgumentException.class)
testSetPixelsXYNegative()1615     public void testSetPixelsXYNegative() {
1616         int[] colors = createColors(100);
1617         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1618 
1619         // abnormal case: x and/or y less than 0
1620         mBitmap.setPixels(colors, 0, 0, -1, -1, 200, 16);
1621     }
1622 
1623     @Test(expected=IllegalArgumentException.class)
testSetPixelsWidthHeightNegative()1624     public void testSetPixelsWidthHeightNegative() {
1625         int[] colors = createColors(100);
1626         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1627 
1628         // abnormal case: width and/or height less than 0
1629         mBitmap.setPixels(colors, 0, 0, 0, 0, -1, -1);
1630     }
1631 
1632     @Test(expected=IllegalArgumentException.class)
testSetPixelsXTooHigh()1633     public void testSetPixelsXTooHigh() {
1634         int[] colors = createColors(100);
1635         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1636 
1637         // abnormal case: (x + width) bigger than the source bitmap's width
1638         mBitmap.setPixels(colors, 0, 0, 10, 10, 95, 50);
1639     }
1640 
1641     @Test(expected=IllegalArgumentException.class)
testSetPixelsYTooHigh()1642     public void testSetPixelsYTooHigh() {
1643         int[] colors = createColors(100);
1644         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1645 
1646         // abnormal case: (y + height) bigger than the source bitmap's height
1647         mBitmap.setPixels(colors, 0, 0, 10, 10, 50, 95);
1648     }
1649 
1650     @Test(expected=IllegalArgumentException.class)
testSetPixelsStrideIllegal()1651     public void testSetPixelsStrideIllegal() {
1652         int[] colors = createColors(100);
1653         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1654 
1655         // abnormal case: stride less than width and bigger than -width
1656         mBitmap.setPixels(colors, 0, 10, 10, 10, 50, 50);
1657     }
1658 
1659     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetNegative()1660     public void testSetPixelsOffsetNegative() {
1661         int[] colors = createColors(100);
1662         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1663 
1664         // abnormal case: offset less than 0
1665         mBitmap.setPixels(colors, -1, 50, 10, 10, 50, 50);
1666     }
1667 
1668     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetTooBig()1669     public void testSetPixelsOffsetTooBig() {
1670         int[] colors = createColors(100);
1671         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1672 
1673         // abnormal case: (offset + width) bigger than the length of colors
1674         mBitmap.setPixels(colors, 60, 50, 10, 10, 50, 50);
1675     }
1676 
1677     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineNegative()1678     public void testSetPixelsLastScanlineNegative() {
1679         int[] colors = createColors(100);
1680         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1681 
1682         // abnormal case: lastScanline less than 0
1683         mBitmap.setPixels(colors, 10, -50, 10, 10, 50, 50);
1684     }
1685 
1686     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineTooBig()1687     public void testSetPixelsLastScanlineTooBig() {
1688         int[] colors = createColors(100);
1689         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1690 
1691         // abnormal case: (lastScanline + width) bigger than the length of colors
1692         mBitmap.setPixels(colors, 10, 50, 10, 10, 50, 50);
1693     }
1694 
1695     @Test
testSetPixels()1696     public void testSetPixels() {
1697         int[] colors = createColors(100 * 100);
1698         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1699         mBitmap.setPixels(colors, 0, 100, 0, 0, 100, 100);
1700         int[] ret = new int[100 * 100];
1701         mBitmap.getPixels(ret, 0, 100, 0, 0, 100, 100);
1702 
1703         for(int i = 0; i < 10000; i++){
1704             assertEquals(ret[i], colors[i]);
1705         }
1706     }
1707 
verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul)1708     private void verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul) {
1709         Bitmap bitmap = Bitmap.createBitmap(1, 1, config);
1710         bitmap.setPremultiplied(true);
1711         bitmap.setPixel(0, 0, Color.TRANSPARENT);
1712         assertTrue(bitmap.isPremultiplied() == expectedPremul);
1713 
1714         bitmap.setHasAlpha(false);
1715         assertFalse(bitmap.isPremultiplied());
1716     }
1717 
1718     @Test
testSetPremultipliedSimple()1719     public void testSetPremultipliedSimple() {
1720         verifyPremultipliedBitmapConfig(Bitmap.Config.ALPHA_8, true);
1721         verifyPremultipliedBitmapConfig(Bitmap.Config.RGB_565, false);
1722         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_4444, true);
1723         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_8888, true);
1724     }
1725 
1726     @Test
testSetPremultipliedData()1727     public void testSetPremultipliedData() {
1728         // with premul, will store 2,2,2,2, so it doesn't get value correct
1729         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1730         bitmap.setPixel(0, 0, PREMUL_COLOR);
1731         assertEquals(bitmap.getPixel(0, 0), PREMUL_ROUNDED_COLOR);
1732 
1733         // read premultiplied value directly
1734         bitmap.setPremultiplied(false);
1735         assertEquals(bitmap.getPixel(0, 0), PREMUL_STORED_COLOR);
1736 
1737         // value can now be stored/read correctly
1738         bitmap.setPixel(0, 0, PREMUL_COLOR);
1739         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1740 
1741         // verify with array methods
1742         int testArray[] = new int[] { PREMUL_COLOR };
1743         bitmap.setPixels(testArray, 0, 1, 0, 0, 1, 1);
1744         bitmap.getPixels(testArray, 0, 1, 0, 0, 1, 1);
1745         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1746     }
1747 
1748     @Test
testPremultipliedCanvas()1749     public void testPremultipliedCanvas() {
1750         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1751         bitmap.setHasAlpha(true);
1752         bitmap.setPremultiplied(false);
1753         assertFalse(bitmap.isPremultiplied());
1754 
1755         Canvas c = new Canvas();
1756         try {
1757             c.drawBitmap(bitmap, 0, 0, null);
1758             fail("canvas should fail with exception");
1759         } catch (RuntimeException e) {
1760         }
1761     }
1762 
getBitmapRawInt(Bitmap bitmap)1763     private int getBitmapRawInt(Bitmap bitmap) {
1764         IntBuffer buffer = IntBuffer.allocate(1);
1765         bitmap.copyPixelsToBuffer(buffer);
1766         return buffer.get(0);
1767     }
1768 
bitmapStoreRawInt(Bitmap bitmap, int value)1769     private void bitmapStoreRawInt(Bitmap bitmap, int value) {
1770         IntBuffer buffer = IntBuffer.allocate(1);
1771         buffer.put(0, value);
1772         bitmap.copyPixelsFromBuffer(buffer);
1773     }
1774 
1775     @Test
testSetPremultipliedToBuffer()1776     public void testSetPremultipliedToBuffer() {
1777         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1778         bitmap.setPixel(0, 0, PREMUL_COLOR);
1779         int storedPremul = getBitmapRawInt(bitmap);
1780 
1781         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1782         bitmap.setPremultiplied(false);
1783         bitmap.setPixel(0, 0, PREMUL_STORED_COLOR);
1784 
1785         assertEquals(getBitmapRawInt(bitmap), storedPremul);
1786     }
1787 
1788     @Test
testSetPremultipliedFromBuffer()1789     public void testSetPremultipliedFromBuffer() {
1790         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1791         bitmap.setPremultiplied(false);
1792         bitmap.setPixel(0, 0, PREMUL_COLOR);
1793         int rawTestColor = getBitmapRawInt(bitmap);
1794 
1795         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1796         bitmap.setPremultiplied(false);
1797         bitmapStoreRawInt(bitmap, rawTestColor);
1798         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1799     }
1800 
1801     @Test
testSetWidth()1802     public void testSetWidth() {
1803         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1804         int alloc = mBitmap.getAllocationByteCount();
1805 
1806         // test shrinking
1807         mBitmap.setWidth(50);
1808         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1809         assertEquals(mBitmap.getByteCount() * 2, alloc);
1810     }
1811 
1812     @Test(expected=IllegalArgumentException.class)
testSetWidthExpanding()1813     public void testSetWidthExpanding() {
1814         // test expanding
1815         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1816 
1817         mBitmap.setWidth(101);
1818     }
1819 
1820     @Test(expected=IllegalStateException.class)
testSetWidthMutable()1821     public void testSetWidthMutable() {
1822         // test mutable
1823         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1824 
1825         mBitmap.setWidth(1);
1826     }
1827 
1828     @Test(expected=IllegalStateException.class)
testWriteToParcelRecycled()1829     public void testWriteToParcelRecycled() {
1830         mBitmap.recycle();
1831 
1832         mBitmap.writeToParcel(null, 0);
1833     }
1834 
1835     @Test
testWriteToParcel()1836     public void testWriteToParcel() {
1837         // abnormal case: failed to unparcel Bitmap
1838         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1839         Parcel p = Parcel.obtain();
1840         mBitmap.writeToParcel(p, 0);
1841 
1842         try {
1843             Bitmap.CREATOR.createFromParcel(p);
1844             fail("shouldn't come to here");
1845         } catch(RuntimeException e){
1846         }
1847 
1848         p.recycle();
1849         // normal case
1850         p = Parcel.obtain();
1851         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1852         mBitmap.writeToParcel(p, 0);
1853         p.setDataPosition(0);
1854         assertTrue(mBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1855 
1856         p.recycle();
1857     }
1858 
1859     /**
1860      * Although not specified as required behavior, it's something that some apps appear
1861      * to rely upon when sending bitmaps between themselves
1862      */
1863     @Test
testWriteToParcelPreserveMutability()1864     public void testWriteToParcelPreserveMutability() {
1865         Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1866         assertTrue(source.isMutable());
1867         Parcel p = Parcel.obtain();
1868         source.writeToParcel(p, 0);
1869         p.setDataPosition(0);
1870         Bitmap result = Bitmap.CREATOR.createFromParcel(p);
1871         p.recycle();
1872         assertTrue(result.isMutable());
1873     }
1874 
1875     @Test
testWriteToParcelPreserveImmutability()1876     public void testWriteToParcelPreserveImmutability() {
1877         // Kinda silly way to create an immutable bitmap but it works
1878         Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888)
1879                 .copy(Config.ARGB_8888, false);
1880         assertFalse(source.isMutable());
1881         Parcel p = Parcel.obtain();
1882         source.writeToParcel(p, 0);
1883         p.setDataPosition(0);
1884         Bitmap result = Bitmap.CREATOR.createFromParcel(p);
1885         p.recycle();
1886         assertFalse(result.isMutable());
1887     }
1888 
1889     @Test
testWriteHwBitmapToParcel()1890     public void testWriteHwBitmapToParcel() {
1891         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1892         Parcel p = Parcel.obtain();
1893         mBitmap.writeToParcel(p, 0);
1894         p.setDataPosition(0);
1895         Bitmap expectedBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot);
1896         assertTrue(expectedBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1897 
1898         p.recycle();
1899     }
1900 
1901     @Test
testParcelF16ColorSpace()1902     public void testParcelF16ColorSpace() {
1903         for (ColorSpace.Named e : new ColorSpace.Named[] {
1904                 ColorSpace.Named.EXTENDED_SRGB,
1905                 ColorSpace.Named.LINEAR_EXTENDED_SRGB,
1906                 ColorSpace.Named.PRO_PHOTO_RGB,
1907                 ColorSpace.Named.DISPLAY_P3
1908         }) {
1909             final ColorSpace cs = ColorSpace.get(e);
1910             Bitmap b = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, cs);
1911             assertSame(cs, b.getColorSpace());
1912 
1913             Parcel p = Parcel.obtain();
1914             b.writeToParcel(p, 0);
1915             p.setDataPosition(0);
1916             Bitmap unparceled = Bitmap.CREATOR.createFromParcel(p);
1917             assertSame(cs, unparceled.getColorSpace());
1918         }
1919     }
1920 
1921     @Test
testGetScaledHeight1()1922     public void testGetScaledHeight1() {
1923         int dummyDensity = 5;
1924         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1925         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity);
1926         assertNotNull(ret);
1927         assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity));
1928     }
1929 
1930     @Test
testGetScaledHeight2()1931     public void testGetScaledHeight2() {
1932         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1933         DisplayMetrics metrics =
1934                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1935         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi);
1936         assertEquals(scaledHeight, ret.getScaledHeight(metrics));
1937     }
1938 
1939     @Test
testGetScaledHeight3()1940     public void testGetScaledHeight3() {
1941         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1942         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1943         Canvas mCanvas = new Canvas(mMutableBitmap);
1944         // set Density
1945         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1946         int scaledHeight = scaleFromDensity(
1947                 ret.getHeight(), ret.getDensity(), mCanvas.getDensity());
1948         assertEquals(scaledHeight, ret.getScaledHeight(mCanvas));
1949     }
1950 
1951     @Test
testGetScaledWidth1()1952     public void testGetScaledWidth1() {
1953         int dummyDensity = 5;
1954         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1955         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity);
1956         assertNotNull(ret);
1957         assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity));
1958     }
1959 
1960     @Test
testGetScaledWidth2()1961     public void testGetScaledWidth2() {
1962         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1963         DisplayMetrics metrics =
1964                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1965         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi);
1966         assertEquals(scaledWidth, ret.getScaledWidth(metrics));
1967     }
1968 
1969     @Test
testGetScaledWidth3()1970     public void testGetScaledWidth3() {
1971         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1972         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1973         Canvas mCanvas = new Canvas(mMutableBitmap);
1974         // set Density
1975         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1976         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(),  mCanvas.getDensity());
1977         assertEquals(scaledWidth, ret.getScaledWidth(mCanvas));
1978     }
1979 
1980     @Test
testSameAs_simpleSuccess()1981     public void testSameAs_simpleSuccess() {
1982         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1983         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1984         bitmap1.eraseColor(Color.BLACK);
1985         bitmap2.eraseColor(Color.BLACK);
1986         assertTrue(bitmap1.sameAs(bitmap2));
1987         assertTrue(bitmap2.sameAs(bitmap1));
1988     }
1989 
1990     @Test
testSameAs_simpleFail()1991     public void testSameAs_simpleFail() {
1992         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1993         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1994         bitmap1.eraseColor(Color.BLACK);
1995         bitmap2.eraseColor(Color.BLACK);
1996         bitmap2.setPixel(20, 10, Color.WHITE);
1997         assertFalse(bitmap1.sameAs(bitmap2));
1998         assertFalse(bitmap2.sameAs(bitmap1));
1999     }
2000 
2001     @Test
testSameAs_reconfigure()2002     public void testSameAs_reconfigure() {
2003         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2004         Bitmap bitmap2 = Bitmap.createBitmap(150, 150, Config.ARGB_8888);
2005         bitmap2.reconfigure(100, 100, Config.ARGB_8888); // now same size, so should be same
2006         bitmap1.eraseColor(Color.BLACK);
2007         bitmap2.eraseColor(Color.BLACK);
2008         assertTrue(bitmap1.sameAs(bitmap2));
2009         assertTrue(bitmap2.sameAs(bitmap1));
2010     }
2011 
2012     @Test
testSameAs_config()2013     public void testSameAs_config() {
2014         Bitmap bitmap1 = Bitmap.createBitmap(100, 200, Config.RGB_565);
2015         Bitmap bitmap2 = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
2016 
2017         // both bitmaps can represent black perfectly
2018         bitmap1.eraseColor(Color.BLACK);
2019         bitmap2.eraseColor(Color.BLACK);
2020 
2021         // but not same due to config
2022         assertFalse(bitmap1.sameAs(bitmap2));
2023         assertFalse(bitmap2.sameAs(bitmap1));
2024     }
2025 
2026     @Test
testSameAs_width()2027     public void testSameAs_width() {
2028         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2029         Bitmap bitmap2 = Bitmap.createBitmap(101, 100, Config.ARGB_8888);
2030         bitmap1.eraseColor(Color.BLACK);
2031         bitmap2.eraseColor(Color.BLACK);
2032         assertFalse(bitmap1.sameAs(bitmap2));
2033         assertFalse(bitmap2.sameAs(bitmap1));
2034     }
2035 
2036     @Test
testSameAs_height()2037     public void testSameAs_height() {
2038         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2039         Bitmap bitmap2 = Bitmap.createBitmap(102, 100, Config.ARGB_8888);
2040         bitmap1.eraseColor(Color.BLACK);
2041         bitmap2.eraseColor(Color.BLACK);
2042         assertFalse(bitmap1.sameAs(bitmap2));
2043         assertFalse(bitmap2.sameAs(bitmap1));
2044     }
2045 
2046     @Test
testSameAs_opaque()2047     public void testSameAs_opaque() {
2048         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2049         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2050         bitmap1.eraseColor(Color.BLACK);
2051         bitmap2.eraseColor(Color.BLACK);
2052         bitmap1.setHasAlpha(true);
2053         bitmap2.setHasAlpha(false);
2054         assertFalse(bitmap1.sameAs(bitmap2));
2055         assertFalse(bitmap2.sameAs(bitmap1));
2056     }
2057 
2058     @Test
testSameAs_hardware()2059     public void testSameAs_hardware() {
2060         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2061         Bitmap bitmap2 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2062         Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
2063         Bitmap bitmap4 = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2064         assertTrue(bitmap1.sameAs(bitmap2));
2065         assertTrue(bitmap2.sameAs(bitmap1));
2066         assertFalse(bitmap1.sameAs(bitmap3));
2067         assertFalse(bitmap1.sameAs(bitmap4));
2068     }
2069 
2070     @Test
testSameAs_wrappedHardwareBuffer()2071     public void testSameAs_wrappedHardwareBuffer() {
2072         try (HardwareBuffer hwBufferA = createTestBuffer(512, 512, true);
2073              HardwareBuffer hwBufferB = createTestBuffer(512, 512, true);
2074              HardwareBuffer hwBufferC = createTestBuffer(512, 512, true);) {
2075             // Fill buffer C with generated data
2076             nFillRgbaHwBuffer(hwBufferC);
2077 
2078             // Create the test bitmaps
2079             Bitmap bitmap1 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
2080             Bitmap bitmap2 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
2081             Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
2082             Bitmap bitmap4 = Bitmap.wrapHardwareBuffer(hwBufferB, ColorSpace.get(Named.SRGB));
2083             Bitmap bitmap5 = Bitmap.wrapHardwareBuffer(hwBufferC, ColorSpace.get(Named.SRGB));
2084 
2085             // Run the compare-a-thon
2086             assertTrue(bitmap1.sameAs(bitmap2));  // SAME UNDERLYING BUFFER
2087             assertTrue(bitmap2.sameAs(bitmap1));  // SAME UNDERLYING BUFFER
2088             assertFalse(bitmap1.sameAs(bitmap3)); // HW vs. NON-HW
2089             assertTrue(bitmap1.sameAs(bitmap4));  // DIFFERENT BUFFERS, SAME CONTENT
2090             assertFalse(bitmap1.sameAs(bitmap5)); // DIFFERENT BUFFERS, DIFFERENT CONTENT
2091         }
2092     }
2093 
2094     @Test(expected=IllegalStateException.class)
testHardwareGetPixel()2095     public void testHardwareGetPixel() {
2096         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2097         bitmap.getPixel(0, 0);
2098     }
2099 
2100     @Test(expected=IllegalStateException.class)
testHardwareGetPixels()2101     public void testHardwareGetPixels() {
2102         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2103         bitmap.getPixels(new int[5], 0, 5, 0, 0, 5, 1);
2104     }
2105 
2106     @Test
testGetConfigOnRecycled()2107     public void testGetConfigOnRecycled() {
2108         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2109         bitmap1.recycle();
2110         assertEquals(Config.HARDWARE, bitmap1.getConfig());
2111         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2112         bitmap2.recycle();
2113         assertEquals(Config.ARGB_8888, bitmap2.getConfig());
2114     }
2115 
2116     @Test(expected = IllegalStateException.class)
testHardwareSetWidth()2117     public void testHardwareSetWidth() {
2118         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2119         bitmap.setWidth(30);
2120     }
2121 
2122     @Test(expected = IllegalStateException.class)
testHardwareSetHeight()2123     public void testHardwareSetHeight() {
2124         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2125         bitmap.setHeight(30);
2126     }
2127 
2128     @Test(expected = IllegalStateException.class)
testHardwareSetConfig()2129     public void testHardwareSetConfig() {
2130         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2131         bitmap.setConfig(Config.ARGB_8888);
2132     }
2133 
2134     @Test(expected = IllegalStateException.class)
testHardwareReconfigure()2135     public void testHardwareReconfigure() {
2136         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2137         bitmap.reconfigure(30, 30, Config.ARGB_8888);
2138     }
2139 
2140     @Test(expected = IllegalStateException.class)
testHardwareSetPixels()2141     public void testHardwareSetPixels() {
2142         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2143         bitmap.setPixels(new int[10], 0, 1, 0, 0, 1, 1);
2144     }
2145 
2146     @Test(expected = IllegalStateException.class)
testHardwareSetPixel()2147     public void testHardwareSetPixel() {
2148         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2149         bitmap.setPixel(1, 1, 0);
2150     }
2151 
2152     @Test(expected = IllegalStateException.class)
testHardwareEraseColor()2153     public void testHardwareEraseColor() {
2154         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2155         bitmap.eraseColor(0);
2156     }
2157 
2158     @Test(expected = IllegalStateException.class)
testHardwareEraseColorLong()2159     public void testHardwareEraseColorLong() {
2160         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
2161         bitmap.eraseColor(Color.pack(0));
2162     }
2163 
2164     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsToBuffer()2165     public void testHardwareCopyPixelsToBuffer() {
2166         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2167         ByteBuffer byteBuf = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight());
2168         bitmap.copyPixelsToBuffer(byteBuf);
2169     }
2170 
2171     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsFromBuffer()2172     public void testHardwareCopyPixelsFromBuffer() {
2173         IntBuffer intBuf1 = IntBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight());
2174         assertEquals(0, intBuf1.position());
2175         mBitmap.copyPixelsToBuffer(intBuf1);
2176         Bitmap hwBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
2177         hwBitmap.copyPixelsFromBuffer(intBuf1);
2178     }
2179 
2180     @Test
testUseMetadataAfterRecycle()2181     public void testUseMetadataAfterRecycle() {
2182         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
2183         bitmap.recycle();
2184         assertEquals(10, bitmap.getWidth());
2185         assertEquals(20, bitmap.getHeight());
2186         assertEquals(Config.RGB_565, bitmap.getConfig());
2187     }
2188 
2189     @Test
testCopyHWBitmapInStrictMode()2190     public void testCopyHWBitmapInStrictMode() {
2191         strictModeTest(()->{
2192             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2193             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2194             hwBitmap.copy(Config.ARGB_8888, false);
2195         });
2196     }
2197 
2198     @Test
testCreateScaledFromHWInStrictMode()2199     public void testCreateScaledFromHWInStrictMode() {
2200         strictModeTest(()->{
2201             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2202             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2203             Bitmap.createScaledBitmap(hwBitmap, 200, 200, false);
2204         });
2205     }
2206 
2207     @Test
testExtractAlphaFromHWInStrictMode()2208     public void testExtractAlphaFromHWInStrictMode() {
2209         strictModeTest(()->{
2210             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2211             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2212             hwBitmap.extractAlpha();
2213         });
2214     }
2215 
2216     @Test
testCompressInStrictMode()2217     public void testCompressInStrictMode() {
2218         strictModeTest(()->{
2219             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2220             bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream());
2221         });
2222     }
2223 
2224     @Test
testParcelHWInStrictMode()2225     public void testParcelHWInStrictMode() {
2226         strictModeTest(()->{
2227             mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2228             Bitmap hwBitmap = mBitmap.copy(Config.HARDWARE, false);
2229             hwBitmap.writeToParcel(Parcel.obtain(), 0);
2230         });
2231     }
2232 
2233     @Test
testSameAsFirstHWInStrictMode()2234     public void testSameAsFirstHWInStrictMode() {
2235         strictModeTest(()->{
2236             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2237             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2238             hwBitmap.sameAs(bitmap);
2239         });
2240     }
2241 
2242     @Test
testSameAsSecondHWInStrictMode()2243     public void testSameAsSecondHWInStrictMode() {
2244         strictModeTest(()->{
2245             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
2246             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
2247             bitmap.sameAs(hwBitmap);
2248         });
2249     }
2250 
2251     @Test
testNdkAccessAfterRecycle()2252     public void testNdkAccessAfterRecycle() {
2253         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
2254         Bitmap hardware = bitmap.copy(Config.HARDWARE, false);
2255         nValidateBitmapInfo(bitmap, 10, 20, true);
2256         nValidateBitmapInfo(hardware, 10, 20, true);
2257 
2258         bitmap.recycle();
2259         hardware.recycle();
2260 
2261         nValidateBitmapInfo(bitmap, 10, 20, true);
2262         nValidateBitmapInfo(hardware, 10, 20, true);
2263         nValidateNdkAccessFails(bitmap);
2264     }
2265 
2266     @Test
bitmapIsMutable()2267     public void bitmapIsMutable() {
2268         Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2269         assertTrue("CreateBitmap w/ params should be mutable", b.isMutable());
2270         assertTrue("CreateBitmap from bitmap should be mutable",
2271                 Bitmap.createBitmap(b).isMutable());
2272     }
2273 
2274     @Test
2275     @LargeTest
testHardwareBitmapNotLeaking()2276     public void testHardwareBitmapNotLeaking() {
2277         BitmapFactory.Options opts = new BitmapFactory.Options();
2278         opts.inPreferredConfig = Config.HARDWARE;
2279         opts.inScaled = false;
2280 
2281         runNotLeakingTest(() -> {
2282             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2283             assertNotNull(bitmap);
2284             // Make sure nothing messed with the bitmap
2285             assertEquals(128, bitmap.getWidth());
2286             assertEquals(128, bitmap.getHeight());
2287             assertEquals(Config.HARDWARE, bitmap.getConfig());
2288             bitmap.recycle();
2289         });
2290     }
2291 
2292     @Test
2293     @LargeTest
testWrappedHardwareBufferBitmapNotLeaking()2294     public void testWrappedHardwareBufferBitmapNotLeaking() {
2295         final ColorSpace colorSpace = ColorSpace.get(Named.SRGB);
2296         try (HardwareBuffer hwBuffer = createTestBuffer(1024, 512, false)) {
2297             runNotLeakingTest(() -> {
2298                 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, colorSpace);
2299                 assertNotNull(bitmap);
2300                 // Make sure nothing messed with the bitmap
2301                 assertEquals(1024, bitmap.getWidth());
2302                 assertEquals(512, bitmap.getHeight());
2303                 assertEquals(Config.HARDWARE, bitmap.getConfig());
2304                 bitmap.recycle();
2305             });
2306         }
2307     }
2308 
2309     @Test
2310     @LargeTest
testDrawingHardwareBitmapNotLeaking()2311     public void testDrawingHardwareBitmapNotLeaking() {
2312         BitmapFactory.Options opts = new BitmapFactory.Options();
2313         opts.inPreferredConfig = Config.HARDWARE;
2314         opts.inScaled = false;
2315         RenderTarget renderTarget = RenderTarget.create();
2316         renderTarget.setDefaultSize(128, 128);
2317         final Surface surface = renderTarget.getSurface();
2318 
2319         runNotLeakingTest(() -> {
2320             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2321             assertNotNull(bitmap);
2322             // Make sure nothing messed with the bitmap
2323             assertEquals(128, bitmap.getWidth());
2324             assertEquals(128, bitmap.getHeight());
2325             assertEquals(Config.HARDWARE, bitmap.getConfig());
2326             Canvas canvas = surface.lockHardwareCanvas();
2327             canvas.drawBitmap(bitmap, 0, 0, null);
2328             surface.unlockCanvasAndPost(canvas);
2329             bitmap.recycle();
2330         });
2331 
2332         surface.release();
2333         renderTarget.destroy();
2334     }
2335 
2336     @Test
testWrapHardwareBufferHoldsReference()2337     public void testWrapHardwareBufferHoldsReference() {
2338         Bitmap bitmap;
2339         // Create hardware-buffer and wrap it in a Bitmap
2340         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
2341             // Fill buffer with colors (x, y, 42, 255)
2342             nFillRgbaHwBuffer(hwBuffer);
2343             bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2344         }
2345 
2346         // Buffer is closed at this point. Ensure bitmap still works by drawing it
2347         assertEquals(128, bitmap.getWidth());
2348         assertEquals(128, bitmap.getHeight());
2349         assertEquals(Config.HARDWARE, bitmap.getConfig());
2350 
2351         // Copy bitmap to target bitmap we can read from
2352         Bitmap dstBitmap = bitmap.copy(Config.ARGB_8888, false);
2353         bitmap.recycle();
2354 
2355         // Ensure that the bitmap has valid contents
2356         int pixel = dstBitmap.getPixel(0, 0);
2357         assertEquals(255 << 24 | 42, pixel);
2358         dstBitmap.recycle();
2359     }
2360 
2361     @Test
testWrapHardwareBufferPreservesColors()2362     public void testWrapHardwareBufferPreservesColors() {
2363         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
2364             // Fill buffer with colors (x, y, 42, 255)
2365             nFillRgbaHwBuffer(hwBuffer);
2366 
2367             // Create HW bitmap from this buffer
2368             Bitmap srcBitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2369             assertNotNull(srcBitmap);
2370 
2371             // Copy it to target non-HW bitmap
2372             Bitmap dstBitmap = srcBitmap.copy(Config.ARGB_8888, false);
2373             srcBitmap.recycle();
2374 
2375             // Ensure all colors are as expected (matches the nFillRgbaHwBuffer call used above).
2376             for (int y = 0; y < 128; ++y) {
2377                 for (int x = 0; x < 128; ++x) {
2378                     int pixel = dstBitmap.getPixel(x, y);
2379                     short a = 255;
2380                     short r = (short) (x % 255);
2381                     short g = (short) (y % 255);
2382                     short b = 42;
2383                     assertEquals(a << 24 | r << 16 | g << 8 | b, pixel);
2384                 }
2385             }
2386             dstBitmap.recycle();
2387         }
2388     }
2389 
compressToPng(Bitmap bitmap)2390     private static byte[] compressToPng(Bitmap bitmap) {
2391         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2392             assertTrue("Failed to encode a Bitmap with Config " + bitmap.getConfig()
2393                     + " and ColorSpace " + bitmap.getColorSpace() + "!",
2394                     bitmap.compress(CompressFormat.PNG, 100, stream));
2395             return stream.toByteArray();
2396         } catch (IOException e) {
2397             fail("Failed to compress with " + e);
2398             return null;
2399         }
2400     }
2401 
parametersForTestAsShared()2402     private static Object[] parametersForTestAsShared() {
2403         return Utils.crossProduct(Config.values(), getRgbColorSpaces().toArray(new Object[0]));
2404     }
2405 
2406     @Test
2407     @Parameters(method = "parametersForTestAsShared")
testAsShared(Config config, ColorSpace colorSpace)2408     public void testAsShared(Config config, ColorSpace colorSpace) {
2409         Bitmap original = Bitmap.createBitmap(10, 10,
2410                 config == Config.HARDWARE ? Config.ARGB_8888 : config, true /*hasAlpha*/,
2411                 colorSpace);
2412         drawGradient(original);
2413 
2414         if (config == Config.HARDWARE) {
2415             original = original.copy(Config.HARDWARE, false /*mutable*/);
2416         }
2417 
2418         // There's no visible way to test that the memory is allocated in shared memory, but we can
2419         // verify that the Bitmaps look the same.
2420         Bitmap shared = original.asShared();
2421         assertNotNull(shared);
2422 
2423         if (config == Config.HARDWARE) {
2424             int expectedFormat = nGetFormat(original);
2425             assertEquals(expectedFormat, configToFormat(shared.getConfig()));
2426 
2427             // There's no public way to look at the pixels in the HARDWARE Bitmap, but if we
2428             // compress each as a lossless PNG, they should look identical.
2429             byte[] origBytes = compressToPng(original);
2430             byte[] sharedBytes = compressToPng(shared);
2431             assertTrue(Arrays.equals(origBytes, sharedBytes));
2432         } else {
2433             assertSame(original.getConfig(), shared.getConfig());
2434             assertTrue(shared.sameAs(original));
2435         }
2436         assertSame(original.getColorSpace(), shared.getColorSpace());
2437 
2438         // The Bitmap is already in shared memory, so no work is done.
2439         Bitmap shared2 = shared.asShared();
2440         assertSame(shared, shared2);
2441 
2442         original.recycle();
2443         shared.recycle();
2444         shared2.recycle();
2445     }
2446 
2447     @Test
testAsSharedRecycled()2448     public void testAsSharedRecycled() {
2449         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2450         bitmap.recycle();
2451         assertThrows(IllegalStateException.class, bitmap::asShared);
2452     }
2453 
2454     @Test
testAsSharedDensity()2455     public void testAsSharedDensity() {
2456         DisplayMetrics metrics =
2457                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
2458         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
2459         for (int density : new int[] { Bitmap.DENSITY_NONE, metrics.densityDpi,
2460                 DisplayMetrics.DENSITY_HIGH, DisplayMetrics.DENSITY_DEVICE_STABLE,
2461                 DisplayMetrics.DENSITY_MEDIUM }) {
2462             bitmap.setDensity(density);
2463             Bitmap shared = bitmap.asShared();
2464             assertEquals(density, shared.getDensity());
2465             shared.recycle();
2466         }
2467     }
2468 
2469     @Test
2470     @Parameters({"true", "false"})
testAsSharedImageDecoder(boolean mutable)2471     public void testAsSharedImageDecoder(boolean mutable) {
2472         Resources res = InstrumentationRegistry.getTargetContext().getResources();
2473         ImageDecoder.Source source = ImageDecoder.createSource(res.getAssets(),
2474                 "grayscale-16bit-linearSrgb.png");
2475         try {
2476             Bitmap bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, s) -> {
2477                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SHARED_MEMORY);
2478                 if (mutable) decoder.setMutableRequired(true);
2479             });
2480 
2481             Bitmap shared = bitmap.asShared();
2482             if (mutable) {
2483                 // bitmap is mutable, so asShared must make a copy.
2484                 assertNotEquals(bitmap, shared);
2485                 assertTrue(bitmap.sameAs(shared));
2486             } else {
2487                 // bitmap is already immutable and in shared memory, so asShared will return
2488                 // itself.
2489                 assertSame(bitmap, shared);
2490             }
2491         } catch (IOException e) {
2492             fail("Failed to decode with " + e);
2493         }
2494     }
2495 
2496     @Test
testNdkFormats()2497     public void testNdkFormats() {
2498         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2499             Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
2500             assertNotNull(bm);
2501             int nativeFormat = nGetFormat(bm);
2502             assertEquals("Config: " + pair.config, pair.format, nativeFormat);
2503         }
2504     }
2505 
2506     @Test
testNdkFormatsHardware()2507     public void testNdkFormatsHardware() {
2508         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2509             Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
2510             bm = bm.copy(Bitmap.Config.HARDWARE, false);
2511 
2512             // ALPHA_8 may not be supported in HARDWARE.
2513             if (bm == null) {
2514                 assertEquals(Bitmap.Config.ALPHA_8, pair.config);
2515                 continue;
2516             }
2517 
2518             int nativeFormat = nGetFormat(bm);
2519             // We allow everything to fall back to 8888
2520             if (nativeFormat != ANDROID_BITMAP_FORMAT_RGBA_8888) {
2521                 assertEquals("Config: " + pair.config, pair.format, nativeFormat);
2522             }
2523         }
2524     }
2525 
2526     @Test
testNullBitmapNdk()2527     public void testNullBitmapNdk() {
2528         Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
2529         nTestNullBitmap(bitmap);
2530     }
2531 
parametersForTestNdkInfo()2532     private Object[] parametersForTestNdkInfo() {
2533         return new Object[] {
2534             new Object[] { Config.ALPHA_8,   ANDROID_BITMAP_FORMAT_A_8  },
2535             new Object[] { Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888 },
2536             new Object[] { Config.RGB_565,   ANDROID_BITMAP_FORMAT_RGB_565 },
2537             new Object[] { Config.RGBA_F16,  ANDROID_BITMAP_FORMAT_RGBA_F16 },
2538             new Object[] { Config.RGBA_1010102,  ANDROID_BITMAP_FORMAT_RGBA_1010102 },
2539         };
2540     }
2541 
2542     @Test
2543     @Parameters(method = "parametersForTestNdkInfo")
testNdkInfo(Config config, final int expectedFormat)2544     public void testNdkInfo(Config config, final int expectedFormat) {
2545         // Arbitrary width and height.
2546         final int width = 13;
2547         final int height = 7;
2548         boolean[] trueFalse = new boolean[] { true, false };
2549         for (boolean hasAlpha : trueFalse) {
2550             for (boolean premultiplied : trueFalse) {
2551                 Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha);
2552                 bm.setPremultiplied(premultiplied);
2553                 nTestInfo(bm, expectedFormat, width, height, bm.hasAlpha(),
2554                         bm.isPremultiplied(), false);
2555                 Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false);
2556                 assertNotNull(hwBitmap);
2557                 // Formats that are not supported by gralloc fall back to 8888.
2558                 // Check what the HWB format is and compare against that
2559                 int tempExpectedFormat = expectedFormat;
2560                 HardwareBuffer buffer = hwBitmap.getHardwareBuffer();
2561                 if (buffer.getFormat() == HardwareBuffer.RGBA_8888) {
2562                     tempExpectedFormat = ANDROID_BITMAP_FORMAT_RGBA_8888;
2563                 }
2564                 nTestInfo(hwBitmap, tempExpectedFormat, width, height, hwBitmap.hasAlpha(),
2565                         hwBitmap.isPremultiplied(), true);
2566                 hwBitmap.recycle();
2567                 bm.recycle();
2568             }
2569         }
2570     }
2571 
2572     @Test
testNdkDataSpaceF16Extended()2573     public void testNdkDataSpaceF16Extended() {
2574         // In RGBA_F16 we force EXTENDED in these cases.
2575         for (ColorSpace colorSpace : new ColorSpace[] {
2576                 ColorSpace.get(Named.SRGB),
2577                 ColorSpace.get(Named.EXTENDED_SRGB),
2578         }) {
2579             Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace);
2580             assertNotNull(bm);
2581 
2582             assertEquals(ColorSpace.get(Named.EXTENDED_SRGB), bm.getColorSpace());
2583             assertEquals(DataSpace.ADATASPACE_SCRGB, nGetDataSpace(bm));
2584         }
2585 
2586         for (ColorSpace colorSpace : new ColorSpace[] {
2587                 ColorSpace.get(Named.LINEAR_SRGB),
2588                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2589         }) {
2590             Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace);
2591             assertNotNull(bm);
2592 
2593             assertEquals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), bm.getColorSpace());
2594             assertEquals(DataSpace.ADATASPACE_SCRGB_LINEAR, nGetDataSpace(bm));
2595         }
2596     }
2597 
2598     @Test
testNdkDataSpaceNonExtended()2599     public void testNdkDataSpaceNonExtended() {
2600         // In 565 and 8888, these force non-extended.
2601         for (ColorSpace colorSpace : new ColorSpace[] {
2602                 ColorSpace.get(Named.SRGB),
2603                 ColorSpace.get(Named.EXTENDED_SRGB),
2604         }) {
2605             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) {
2606                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2607                 assertNotNull(bm);
2608 
2609                 assertEquals(ColorSpace.get(Named.SRGB), bm.getColorSpace());
2610                 assertEquals(DataSpace.ADATASPACE_SRGB, nGetDataSpace(bm));
2611             }
2612         }
2613 
2614         for (ColorSpace colorSpace : new ColorSpace[] {
2615                 ColorSpace.get(Named.LINEAR_SRGB),
2616                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2617         }) {
2618             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) {
2619                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2620                 assertNotNull(bm);
2621 
2622                 assertEquals(ColorSpace.get(Named.LINEAR_SRGB), bm.getColorSpace());
2623                 assertEquals(DataSpace.ADATASPACE_SRGB_LINEAR, nGetDataSpace(bm));
2624             }
2625         }
2626     }
2627 
2628     @Test
testNdkDataSpace()2629     public void testNdkDataSpace() {
2630         // DataSpace.ADATASPACEs that do not depend on the Config.
2631         for (ColorSpace colorSpace : new ColorSpace[] {
2632                 // These have corresponding DataSpace.ADATASPACEs that are independent of the Config
2633                 ColorSpace.get(Named.DISPLAY_P3),
2634                 ColorSpace.get(Named.BT2020),
2635                 ColorSpace.get(Named.ADOBE_RGB),
2636                 ColorSpace.get(Named.BT709),
2637                 ColorSpace.get(Named.DCI_P3),
2638 
2639                 // These have no public ADATASPACE.
2640                 ColorSpace.get(Named.ACES),
2641                 ColorSpace.get(Named.ACESCG),
2642                 ColorSpace.get(Named.NTSC_1953),
2643                 ColorSpace.get(Named.PRO_PHOTO_RGB),
2644                 ColorSpace.get(Named.SMPTE_C),
2645         }) {
2646             for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16 }) {
2647                 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace);
2648                 assertNotNull(bm);
2649 
2650                 int dataSpace = nGetDataSpace(bm);
2651                 assertEquals("Bitmap with " + c + " and " + bm.getColorSpace()
2652                         + " has unexpected data space", colorSpace.getDataSpace(),
2653                         dataSpace);
2654             }
2655         }
2656     }
2657 
2658     @Test
testNdkDataSpaceAlpha8()2659     public void testNdkDataSpaceAlpha8() {
2660         // ALPHA_8 doesn't support ColorSpaces
2661         Bitmap bm = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
2662         assertNotNull(bm);
2663         assertNull(bm.getColorSpace());
2664         int dataSpace = nGetDataSpace(bm);
2665         assertEquals(DataSpace.ADATASPACE_UNKNOWN, dataSpace);
2666     }
2667 
2668     @Test
testNdkDataSpaceNullBitmap()2669     public void testNdkDataSpaceNullBitmap() {
2670         assertEquals(DataSpace.ADATASPACE_UNKNOWN, nGetDataSpace(null));
2671     }
2672 
nGetDataSpace(Bitmap bm)2673     private static native int nGetDataSpace(Bitmap bm);
2674 
2675     // These match the NDK APIs.
2676     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_JPEG = 0;
2677     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_PNG = 1;
2678     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY = 3;
2679     private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS = 4;
2680 
nativeCompressFormat(CompressFormat format)2681     private int nativeCompressFormat(CompressFormat format) {
2682         switch (format) {
2683             case JPEG:
2684                 return ANDROID_BITMAP_COMPRESS_FORMAT_JPEG;
2685             case PNG:
2686                 return ANDROID_BITMAP_COMPRESS_FORMAT_PNG;
2687             case WEBP_LOSSY:
2688                 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY;
2689             case WEBP_LOSSLESS:
2690                 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS;
2691             default:
2692                 fail("format " + format + " has no corresponding native compress format!");
2693                 return -1;
2694         }
2695     }
2696 
parametersForNdkCompress()2697     private static Object[] parametersForNdkCompress() {
2698         // Skip WEBP, which has no corresponding native compress format.
2699         Object[] formats = new Object[] {
2700                 CompressFormat.JPEG,
2701                 CompressFormat.PNG,
2702                 CompressFormat.WEBP_LOSSY,
2703                 CompressFormat.WEBP_LOSSLESS,
2704         };
2705         // These are the ColorSpaces with corresponding ADataSpaces
2706         Object[] colorSpaces = new Object[] {
2707                 ColorSpace.get(Named.SRGB),
2708                 ColorSpace.get(Named.EXTENDED_SRGB),
2709                 ColorSpace.get(Named.LINEAR_SRGB),
2710                 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB),
2711 
2712                 ColorSpace.get(Named.DISPLAY_P3),
2713                 ColorSpace.get(Named.DCI_P3),
2714                 ColorSpace.get(Named.BT2020),
2715                 ColorSpace.get(Named.BT709),
2716                 ColorSpace.get(Named.ADOBE_RGB),
2717         };
2718 
2719         Object[] configs = new Object[] {
2720                 Config.ARGB_8888,
2721                 Config.RGB_565,
2722                 Config.RGBA_F16,
2723         };
2724 
2725         return crossProduct(formats, colorSpaces, configs);
2726     }
2727 
crossProduct(Object[] a, Object[] b, Object[] c)2728     private static Object[] crossProduct(Object[] a, Object[] b, Object[] c) {
2729         final int length = a.length * b.length * c.length;
2730         Object[] ret = new Object[length];
2731         for (int i = 0; i < a.length; i++) {
2732             for (int j = 0; j < b.length; j++) {
2733                 for (int k = 0; k < c.length; k++) {
2734                     int index = i * (b.length * c.length) + j * c.length + k;
2735                     assertNull(ret[index]);
2736                     ret[index] = new Object[] { a[i], b[j], c[k] };
2737                 }
2738             }
2739         }
2740         return ret;
2741     }
2742 
isSrgb(ColorSpace cs)2743     private static boolean isSrgb(ColorSpace cs) {
2744         return cs == ColorSpace.get(Named.SRGB)
2745                 || cs == ColorSpace.get(Named.EXTENDED_SRGB)
2746                 || cs == ColorSpace.get(Named.LINEAR_SRGB)
2747                 || cs == ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
2748     }
2749 
2750     // Helper method for populating a Bitmap with interesting pixels for comparison.
drawGradient(Bitmap bitmap)2751     private static void drawGradient(Bitmap bitmap) {
2752         // Use different colors and alphas.
2753         Canvas canvas = new Canvas(bitmap);
2754         ColorSpace cs = bitmap.getColorSpace();
2755         if (cs == null) {
2756             assertSame(Config.ALPHA_8, bitmap.getConfig());
2757             cs = ColorSpace.get(ColorSpace.Named.SRGB);
2758         }
2759         long color0 = Color.pack(0, 0, 1, 1, cs);
2760         long color1 = Color.pack(1, 0, 0, 0, cs);
2761         LinearGradient gradient = new LinearGradient(0, 0, 10, 10, color0, color1,
2762                 Shader.TileMode.CLAMP);
2763         Paint paint = new Paint();
2764         paint.setShader(gradient);
2765         canvas.drawPaint(paint);
2766     }
2767 
2768     @Test
2769     @Parameters(method = "parametersForNdkCompress")
testNdkCompress(CompressFormat format, ColorSpace cs, Config config)2770     public void testNdkCompress(CompressFormat format, ColorSpace cs, Config config)
2771             throws IOException {
2772         // Verify that ndk compress behaves the same as Bitmap#compress
2773         Bitmap bitmap = Bitmap.createBitmap(10, 10, config, true /* hasAlpha */, cs);
2774         assertNotNull(bitmap);
2775 
2776         {
2777             drawGradient(bitmap);
2778         }
2779 
2780         byte[] storage = new byte[16 * 1024];
2781         for (int quality : new int[] { 50, 80, 100 }) {
2782             byte[] expected = null;
2783             try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2784                 assertTrue("Failed to encode a Bitmap with " + cs + " to " + format + " at quality "
2785                         + quality + " from Java API", bitmap.compress(format, quality, stream));
2786                 expected = stream.toByteArray();
2787             }
2788 
2789             try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2790                 boolean success = nCompress(bitmap, nativeCompressFormat(format),
2791                         quality, stream, storage);
2792                 assertTrue("Failed to encode pixels with " + cs + " to " + format + " at quality "
2793                         + quality + " from NDK API", success);
2794                 byte[] actual = stream.toByteArray();
2795 
2796                 if (isSrgb(cs)) {
2797                     if (!Arrays.equals(expected, actual)) {
2798                         fail("NDK compression did not match for " + cs + " and format " + format
2799                                 + " at quality " + quality);
2800                     }
2801                 } else {
2802                     // The byte arrays will match exactly for SRGB and its variants, because those
2803                     // are treated specially. For the others, there are some small differences
2804                     // between Skia's and ColorSpace's values that result in the ICC profiles being
2805                     // written slightly differently. They should still look the same, though.
2806                     Bitmap expectedBitmap = decodeBytes(expected);
2807                     Bitmap actualBitmap = decodeBytes(actual);
2808                     boolean matched = BitmapUtils.compareBitmapsMse(expectedBitmap, actualBitmap,
2809                               5, true, false);
2810                     expectedBitmap.recycle();
2811                     actualBitmap.recycle();
2812                     assertTrue("NDK compression did not match for " + cs + " and format " + format
2813                                 + " at quality " + quality, matched);
2814                 }
2815             }
2816         }
2817     }
2818 
2819     @Test
testNdkCompressBadParameter()2820     public void testNdkCompressBadParameter() throws IOException {
2821         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
2822             nTestNdkCompressBadParameter(mBitmap, stream, new byte[16 * 1024]);
2823         }
2824     }
2825 
nCompress(Bitmap bitmap, int format, int quality, OutputStream stream, byte[] storage)2826     private static native boolean nCompress(Bitmap bitmap, int format, int quality,
2827             OutputStream stream, byte[] storage);
nTestNdkCompressBadParameter(Bitmap bitmap, OutputStream stream, byte[] storage)2828     private static native void nTestNdkCompressBadParameter(Bitmap bitmap,
2829             OutputStream stream, byte[] storage);
2830 
strictModeTest(Runnable runnable)2831     private void strictModeTest(Runnable runnable) {
2832         StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
2833         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
2834                 .detectCustomSlowCalls().penaltyDeath().build());
2835         try {
2836             runnable.run();
2837             fail("Shouldn't reach it");
2838         } catch (RuntimeException expected){
2839             // expect to receive StrictModeViolation
2840         } finally {
2841             StrictMode.setThreadPolicy(originalPolicy);
2842         }
2843     }
2844 
nValidateBitmapInfo(Bitmap bitmap, int width, int height, boolean is565)2845     private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
2846             boolean is565);
nValidateNdkAccessFails(Bitmap bitmap)2847     private static native void nValidateNdkAccessFails(Bitmap bitmap);
2848 
nFillRgbaHwBuffer(HardwareBuffer hwBuffer)2849     private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
nTestNullBitmap(Bitmap bitmap)2850     private static native void nTestNullBitmap(Bitmap bitmap);
2851 
2852     private static final int ANDROID_BITMAP_FORMAT_NONE = 0;
2853     static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
2854     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
2855     private static final int ANDROID_BITMAP_FORMAT_A_8 = 8;
2856     private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9;
2857     private static final int ANDROID_BITMAP_FORMAT_RGBA_1010102 = 10;
2858 
2859     private static class ConfigToFormat {
2860         public final Config config;
2861         public final int format;
2862 
ConfigToFormat(Config c, int f)2863         ConfigToFormat(Config c, int f) {
2864             this.config = c;
2865             this.format = f;
2866         }
2867     }
2868 
configToFormat(Config config)2869     private static int configToFormat(Config config) {
2870         for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
2871             if (config == pair.config) {
2872                 return pair.format;
2873             }
2874         }
2875         return ANDROID_BITMAP_FORMAT_NONE;
2876     }
2877 
2878     private static final ConfigToFormat[] CONFIG_TO_FORMAT = new ConfigToFormat[] {
2879         new ConfigToFormat(Bitmap.Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888),
2880         // ARGB_4444 is deprecated, and createBitmap converts to 8888.
2881         new ConfigToFormat(Bitmap.Config.ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_8888),
2882         new ConfigToFormat(Bitmap.Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565),
2883         new ConfigToFormat(Bitmap.Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8),
2884         new ConfigToFormat(Bitmap.Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16),
2885         new ConfigToFormat(Bitmap.Config.RGBA_1010102, ANDROID_BITMAP_FORMAT_RGBA_1010102),
2886     };
2887 
nGetFormat(Bitmap bitmap)2888     static native int nGetFormat(Bitmap bitmap);
2889 
nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height, boolean hasAlpha, boolean premultiplied, boolean hardware)2890     private static native void nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height,
2891             boolean hasAlpha, boolean premultiplied, boolean hardware);
2892 
createTestBuffer(int width, int height, boolean cpuAccess)2893     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
2894         long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
2895         if (cpuAccess) {
2896             usage |= HardwareBuffer.USAGE_CPU_WRITE_RARELY;
2897         }
2898         // We can assume that RGBA_8888 format is supported for every platform.
2899         HardwareBuffer hwBuffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
2900                 1, usage);
2901         return hwBuffer;
2902     }
2903 
scaleFromDensity(int size, int sdensity, int tdensity)2904     private static int scaleFromDensity(int size, int sdensity, int tdensity) {
2905         if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) {
2906             return size;
2907         }
2908 
2909         // Scale by tdensity / sdensity, rounding up.
2910         return ((size * tdensity) + (sdensity >> 1)) / sdensity;
2911     }
2912 
createColors(int size)2913     private static int[] createColors(int size) {
2914         int[] colors = new int[size];
2915 
2916         for (int i = 0; i < size; i++) {
2917             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
2918         }
2919 
2920         return colors;
2921     }
2922 
createHardwareBitmapOptions()2923     private static BitmapFactory.Options createHardwareBitmapOptions() {
2924         BitmapFactory.Options options = new BitmapFactory.Options();
2925         options.inPreferredConfig = Config.HARDWARE;
2926         return options;
2927     }
2928 
2929     @Test
testCopyAlpha8ToHardware()2930     public void testCopyAlpha8ToHardware() {
2931         Bitmap bm = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
2932         assertNotNull(bm);
2933         Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false /* mutable */);
2934         // Some devices may not support ALPHA_8 + HARDWARE
2935         if (hwBitmap != null) {
2936             assertNull(hwBitmap.getColorSpace());
2937         }
2938 
2939         bm.recycle();
2940     }
2941 }
2942