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