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