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