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