• 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 
27 import android.content.res.Resources;
28 import android.graphics.Bitmap;
29 import android.graphics.Bitmap.CompressFormat;
30 import android.graphics.Bitmap.Config;
31 import android.graphics.BitmapFactory;
32 import android.graphics.Canvas;
33 import android.graphics.Color;
34 import android.graphics.ColorSpace;
35 import android.graphics.ColorSpace.Named;
36 import android.graphics.Matrix;
37 import android.graphics.Paint;
38 import android.graphics.Picture;
39 import android.hardware.HardwareBuffer;
40 import android.os.Debug;
41 import android.os.Parcel;
42 import android.os.StrictMode;
43 import android.util.DisplayMetrics;
44 import android.view.Surface;
45 
46 import androidx.test.InstrumentationRegistry;
47 import androidx.test.filters.LargeTest;
48 import androidx.test.filters.SmallTest;
49 import androidx.test.runner.AndroidJUnit4;
50 
51 import com.android.compatibility.common.util.ColorUtils;
52 import com.android.compatibility.common.util.WidgetTestUtils;
53 
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 
58 import java.io.ByteArrayOutputStream;
59 import java.io.File;
60 import java.nio.ByteBuffer;
61 import java.nio.CharBuffer;
62 import java.nio.IntBuffer;
63 import java.nio.ShortBuffer;
64 import java.util.ArrayList;
65 import java.util.List;
66 import java.util.concurrent.CountDownLatch;
67 import java.util.concurrent.TimeUnit;
68 
69 @SmallTest
70 @RunWith(AndroidJUnit4.class)
71 public class BitmapTest {
72     // small alpha values cause color values to be pre-multiplied down, losing accuracy
73     private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253);
74     private static final int PREMUL_ROUNDED_COLOR = Color.argb(2, 255, 255, 255);
75     private static final int PREMUL_STORED_COLOR = Color.argb(2, 2, 2, 2);
76 
77     private static final BitmapFactory.Options HARDWARE_OPTIONS = createHardwareBitmapOptions();
78 
79     static {
80         System.loadLibrary("ctsgraphics_jni");
81     }
82 
83     private Resources mRes;
84     private Bitmap mBitmap;
85     private BitmapFactory.Options mOptions;
86 
getRgbColorSpaces()87     public static List<ColorSpace> getRgbColorSpaces() {
88         List<ColorSpace> rgbColorSpaces;
89         rgbColorSpaces = new ArrayList<ColorSpace>();
90         for (ColorSpace.Named e : ColorSpace.Named.values()) {
91             ColorSpace cs = ColorSpace.get(e);
92             if (cs.getModel() != ColorSpace.Model.RGB) {
93                 continue;
94             }
95             if (((ColorSpace.Rgb) cs).getTransferParameters() == null) {
96                 continue;
97             }
98             rgbColorSpaces.add(cs);
99         }
100         return rgbColorSpaces;
101     }
102 
103     @Before
setup()104     public void setup() {
105         mRes = InstrumentationRegistry.getTargetContext().getResources();
106         mOptions = new BitmapFactory.Options();
107         mOptions.inScaled = false;
108         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
109     }
110 
111     @Test(expected=IllegalStateException.class)
testCompressRecycled()112     public void testCompressRecycled() {
113         mBitmap.recycle();
114         mBitmap.compress(CompressFormat.JPEG, 0, null);
115     }
116 
117     @Test(expected=NullPointerException.class)
testCompressNullStream()118     public void testCompressNullStream() {
119         mBitmap.compress(CompressFormat.JPEG, 0, null);
120     }
121 
122     @Test(expected=IllegalArgumentException.class)
testCompressQualityTooLow()123     public void testCompressQualityTooLow() {
124         mBitmap.compress(CompressFormat.JPEG, -1, new ByteArrayOutputStream());
125     }
126 
127     @Test(expected=IllegalArgumentException.class)
testCompressQualityTooHigh()128     public void testCompressQualityTooHigh() {
129         mBitmap.compress(CompressFormat.JPEG, 101, new ByteArrayOutputStream());
130     }
131 
132     @Test
testCompress()133     public void testCompress() {
134         assertTrue(mBitmap.compress(CompressFormat.JPEG, 50, new ByteArrayOutputStream()));
135     }
136 
137     @Test(expected=IllegalStateException.class)
testCopyRecycled()138     public void testCopyRecycled() {
139         mBitmap.recycle();
140         mBitmap.copy(Config.RGB_565, false);
141     }
142 
143     @Test
testCopy()144     public void testCopy() {
145         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
146         Bitmap bitmap = mBitmap.copy(Config.ARGB_8888, false);
147         WidgetTestUtils.assertEquals(mBitmap, bitmap);
148     }
149 
150     @Test
testCopyConfigs()151     public void testCopyConfigs() {
152         Config[] supportedConfigs = new Config[] {
153                 Config.ALPHA_8, Config.RGB_565, Config.ARGB_8888, Config.RGBA_F16,
154         };
155         for (Config src : supportedConfigs) {
156             for (Config dst : supportedConfigs) {
157                 Bitmap srcBitmap = Bitmap.createBitmap(1, 1, src);
158                 srcBitmap.eraseColor(Color.WHITE);
159                 Bitmap dstBitmap = srcBitmap.copy(dst, false);
160                 assertNotNull("Should support copying from " + src + " to " + dst,
161                         dstBitmap);
162                 if (Config.ALPHA_8 == dst || Config.ALPHA_8 == src) {
163                     // Color will be opaque but color information will be lost.
164                     assertEquals("Color should be black when copying from " + src + " to "
165                             + dst, Color.BLACK, dstBitmap.getPixel(0, 0));
166                 } else {
167                     assertEquals("Color should be preserved when copying from " + src + " to "
168                             + dst, Color.WHITE, dstBitmap.getPixel(0, 0));
169                 }
170             }
171         }
172     }
173 
174     @Test(expected=IllegalArgumentException.class)
testCopyMutableHwBitmap()175     public void testCopyMutableHwBitmap() {
176         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
177         mBitmap.copy(Config.HARDWARE, true);
178     }
179 
180     @Test(expected=RuntimeException.class)
testCopyPixelsToBufferUnsupportedBufferClass()181     public void testCopyPixelsToBufferUnsupportedBufferClass() {
182         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
183 
184         mBitmap.copyPixelsToBuffer(CharBuffer.allocate(pixSize));
185     }
186 
187     @Test(expected=RuntimeException.class)
testCopyPixelsToBufferBufferTooSmall()188     public void testCopyPixelsToBufferBufferTooSmall() {
189         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
190         final int tooSmall = pixSize / 2;
191 
192         mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall));
193     }
194 
195     @Test
testCopyPixelsToBuffer()196     public void testCopyPixelsToBuffer() {
197         final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight();
198 
199         ByteBuffer byteBuf = ByteBuffer.allocate(pixSize);
200         assertEquals(0, byteBuf.position());
201         mBitmap.copyPixelsToBuffer(byteBuf);
202         assertEquals(pixSize, byteBuf.position());
203 
204         ShortBuffer shortBuf = ShortBuffer.allocate(pixSize);
205         assertEquals(0, shortBuf.position());
206         mBitmap.copyPixelsToBuffer(shortBuf);
207         assertEquals(pixSize >> 1, shortBuf.position());
208 
209         IntBuffer intBuf1 = IntBuffer.allocate(pixSize);
210         assertEquals(0, intBuf1.position());
211         mBitmap.copyPixelsToBuffer(intBuf1);
212         assertEquals(pixSize >> 2, intBuf1.position());
213 
214         Bitmap bitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
215                 mBitmap.getConfig());
216         intBuf1.position(0); // copyPixelsToBuffer adjusted the position, so rewind to start
217         bitmap.copyPixelsFromBuffer(intBuf1);
218         IntBuffer intBuf2 = IntBuffer.allocate(pixSize);
219         bitmap.copyPixelsToBuffer(intBuf2);
220 
221         assertEquals(pixSize >> 2, intBuf2.position());
222         assertEquals(intBuf1.position(), intBuf2.position());
223         int size = intBuf1.position();
224         intBuf1.position(0);
225         intBuf2.position(0);
226         for (int i = 0; i < size; i++) {
227             assertEquals("mismatching pixels at position " + i, intBuf1.get(), intBuf2.get());
228         }
229     }
230 
231     @Test
testCreateBitmap1()232     public void testCreateBitmap1() {
233         int[] colors = createColors(100);
234         Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565);
235         assertFalse(bitmap.isMutable());
236         Bitmap ret = Bitmap.createBitmap(bitmap);
237         assertNotNull(ret);
238         assertFalse(ret.isMutable());
239         assertEquals(10, ret.getWidth());
240         assertEquals(10, ret.getHeight());
241         assertEquals(Config.RGB_565, ret.getConfig());
242         assertEquals(ANDROID_BITMAP_FORMAT_RGB_565, nGetFormat(ret));
243     }
244 
245     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeX()246     public void testCreateBitmapNegativeX() {
247         Bitmap.createBitmap(mBitmap, -100, 50, 50, 200);
248     }
249 
250     @Test
testCreateBitmap2()251     public void testCreateBitmap2() {
252         // special case: output bitmap is equal to the input bitmap
253         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
254         assertFalse(mBitmap.isMutable()); // createBitmap w/ colors should be immutable
255         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100);
256         assertNotNull(ret);
257         assertFalse(ret.isMutable()); // createBitmap from subset should be immutable
258         assertTrue(mBitmap.equals(ret));
259 
260         //normal case
261         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
262         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50);
263         assertNotNull(ret);
264         assertFalse(mBitmap.equals(ret));
265         assertEquals(ANDROID_BITMAP_FORMAT_RGBA_8888, nGetFormat(mBitmap));
266     }
267 
268     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeXY()269     public void testCreateBitmapNegativeXY() {
270         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
271 
272         // abnormal case: x and/or y less than 0
273         Bitmap.createBitmap(mBitmap, -1, -1, 10, 10, null, false);
274     }
275 
276     @Test(expected=IllegalArgumentException.class)
testCreateBitmapNegativeWidthHeight()277     public void testCreateBitmapNegativeWidthHeight() {
278         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
279 
280         // abnormal case: width and/or height less than 0
281         Bitmap.createBitmap(mBitmap, 1, 1, -10, -10, null, false);
282     }
283 
284     @Test(expected=IllegalArgumentException.class)
testCreateBitmapXRegionTooWide()285     public void testCreateBitmapXRegionTooWide() {
286         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
287 
288         // abnormal case: (x + width) bigger than source bitmap's width
289         Bitmap.createBitmap(mBitmap, 10, 10, 95, 50, null, false);
290     }
291 
292     @Test(expected=IllegalArgumentException.class)
testCreateBitmapYRegionTooTall()293     public void testCreateBitmapYRegionTooTall() {
294         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
295 
296         // abnormal case: (y + height) bigger than source bitmap's height
297         Bitmap.createBitmap(mBitmap, 10, 10, 50, 95, null, false);
298     }
299 
300     @Test(expected=IllegalArgumentException.class)
testCreateMutableBitmapWithHardwareConfig()301     public void testCreateMutableBitmapWithHardwareConfig() {
302         Bitmap.createBitmap(100, 100, Config.HARDWARE);
303     }
304 
305     @Test
testCreateBitmap3()306     public void testCreateBitmap3() {
307         // special case: output bitmap is equal to the input bitmap
308         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
309         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100, null, false);
310         assertNotNull(ret);
311         assertFalse(ret.isMutable()); // subset should be immutable
312         assertTrue(mBitmap.equals(ret));
313 
314         // normal case
315         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
316         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50, new Matrix(), true);
317         assertTrue(ret.isMutable());
318         assertNotNull(ret);
319         assertFalse(mBitmap.equals(ret));
320     }
321 
322     @Test
testCreateBitmapFromHardwareBitmap()323     public void testCreateBitmapFromHardwareBitmap() {
324         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
325                 HARDWARE_OPTIONS);
326         assertEquals(Config.HARDWARE, hardwareBitmap.getConfig());
327 
328         Bitmap ret = Bitmap.createBitmap(hardwareBitmap, 0, 0, 100, 100, null, false);
329         assertEquals(Config.HARDWARE, ret.getConfig());
330         assertFalse(ret.isMutable());
331     }
332 
333     @Test
testCreateBitmap4()334     public void testCreateBitmap4() {
335         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
336         assertNotNull(ret);
337         assertTrue(ret.isMutable());
338         assertEquals(100, ret.getWidth());
339         assertEquals(200, ret.getHeight());
340         assertEquals(Config.RGB_565, ret.getConfig());
341     }
342 
verify2x2BitmapContents(int[] expected, Bitmap observed)343     private static void verify2x2BitmapContents(int[] expected, Bitmap observed) {
344         ColorUtils.verifyColor(expected[0], observed.getPixel(0, 0));
345         ColorUtils.verifyColor(expected[1], observed.getPixel(1, 0));
346         ColorUtils.verifyColor(expected[2], observed.getPixel(0, 1));
347         ColorUtils.verifyColor(expected[3], observed.getPixel(1, 1));
348     }
349 
350     @Test
testCreateBitmap_matrix()351     public void testCreateBitmap_matrix() {
352         int[] colorArray = new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK };
353         Bitmap src = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
354         assertTrue(src.isMutable());
355         src.setPixels(colorArray,0, 2, 0, 0, 2, 2);
356 
357         // baseline
358         verify2x2BitmapContents(colorArray, src);
359 
360         // null
361         Bitmap dst = Bitmap.createBitmap(src, 0, 0, 2, 2, null, false);
362         assertTrue(dst.isMutable());
363         verify2x2BitmapContents(colorArray, dst);
364 
365         // identity matrix
366         Matrix matrix = new Matrix();
367         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
368         assertTrue(dst.isMutable());
369         verify2x2BitmapContents(colorArray, dst);
370 
371         // big scale - only red visible
372         matrix.setScale(10, 10);
373         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
374         assertTrue(dst.isMutable());
375         verify2x2BitmapContents(new int[] { Color.RED, Color.RED, Color.RED, Color.RED }, dst);
376 
377         // rotation
378         matrix.setRotate(90);
379         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
380         assertTrue(dst.isMutable());
381         verify2x2BitmapContents(
382                 new int[] { Color.BLUE, Color.RED, Color.BLACK, Color.GREEN }, dst);
383     }
384 
385     @Test(expected=IllegalArgumentException.class)
testCreateBitmapFromColorsNegativeWidthHeight()386     public void testCreateBitmapFromColorsNegativeWidthHeight() {
387         int[] colors = createColors(100);
388 
389         // abnormal case: width and/or height less than 0
390         Bitmap.createBitmap(colors, 0, 100, -1, 100, Config.RGB_565);
391     }
392 
393     @Test(expected=IllegalArgumentException.class)
testCreateBitmapFromColorsIllegalStride()394     public void testCreateBitmapFromColorsIllegalStride() {
395         int[] colors = createColors(100);
396 
397         // abnormal case: stride less than width and bigger than -width
398         Bitmap.createBitmap(colors, 10, 10, 100, 100, Config.RGB_565);
399     }
400 
401     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsNegativeOffset()402     public void testCreateBitmapFromColorsNegativeOffset() {
403         int[] colors = createColors(100);
404 
405         // abnormal case: offset less than 0
406         Bitmap.createBitmap(colors, -10, 100, 100, 100, Config.RGB_565);
407     }
408 
409     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsOffsetTooLarge()410     public void testCreateBitmapFromColorsOffsetTooLarge() {
411         int[] colors = createColors(100);
412 
413         // abnormal case: (offset + width) bigger than colors' length
414         Bitmap.createBitmap(colors, 10, 100, 100, 100, Config.RGB_565);
415     }
416 
417     @Test(expected=ArrayIndexOutOfBoundsException.class)
testCreateBitmapFromColorsScalnlineTooLarge()418     public void testCreateBitmapFromColorsScalnlineTooLarge() {
419         int[] colors = createColors(100);
420 
421         // abnormal case: (lastScanline + width) bigger than colors' length
422         Bitmap.createBitmap(colors, 10, 100, 50, 100, Config.RGB_565);
423     }
424 
425     @Test
testCreateBitmap6()426     public void testCreateBitmap6() {
427         int[] colors = createColors(100);
428 
429         // normal case
430         Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565);
431         assertNotNull(ret);
432         assertFalse(ret.isMutable());
433         assertEquals(10, ret.getWidth());
434         assertEquals(5, ret.getHeight());
435         assertEquals(Config.RGB_565, ret.getConfig());
436     }
437 
438     @Test
testCreateBitmap_displayMetrics_mutable()439     public void testCreateBitmap_displayMetrics_mutable() {
440         DisplayMetrics metrics =
441                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
442 
443         Bitmap bitmap;
444         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
445         assertTrue(bitmap.isMutable());
446         assertEquals(metrics.densityDpi, bitmap.getDensity());
447 
448         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888);
449         assertTrue(bitmap.isMutable());
450         assertEquals(metrics.densityDpi, bitmap.getDensity());
451 
452         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true);
453         assertTrue(bitmap.isMutable());
454         assertEquals(metrics.densityDpi, bitmap.getDensity());
455 
456         bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true, ColorSpace.get(
457                 ColorSpace.Named.SRGB));
458 
459         assertTrue(bitmap.isMutable());
460         assertEquals(metrics.densityDpi, bitmap.getDensity());
461 
462         int[] colors = createColors(100);
463         bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
464         assertNotNull(bitmap);
465         assertFalse(bitmap.isMutable());
466 
467         bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
468         assertNotNull(bitmap);
469         assertFalse(bitmap.isMutable());
470     }
471 
472     @Test
testCreateBitmap_noDisplayMetrics_mutable()473     public void testCreateBitmap_noDisplayMetrics_mutable() {
474         Bitmap bitmap;
475         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
476         assertTrue(bitmap.isMutable());
477 
478         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true);
479         assertTrue(bitmap.isMutable());
480 
481         bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB));
482         assertTrue(bitmap.isMutable());
483     }
484 
485     @Test
testCreateBitmap_displayMetrics_immutable()486     public void testCreateBitmap_displayMetrics_immutable() {
487         DisplayMetrics metrics =
488                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
489         int[] colors = createColors(100);
490 
491         Bitmap bitmap;
492         bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
493         assertFalse(bitmap.isMutable());
494         assertEquals(metrics.densityDpi, bitmap.getDensity());
495 
496         bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
497         assertFalse(bitmap.isMutable());
498         assertEquals(metrics.densityDpi, bitmap.getDensity());
499     }
500 
501     @Test
testCreateBitmap_noDisplayMetrics_immutable()502     public void testCreateBitmap_noDisplayMetrics_immutable() {
503         int[] colors = createColors(100);
504         Bitmap bitmap;
505         bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888);
506         assertFalse(bitmap.isMutable());
507 
508         bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888);
509         assertFalse(bitmap.isMutable());
510     }
511 
512     @Test
testCreateBitmap_Picture_immutable()513     public void testCreateBitmap_Picture_immutable() {
514         Picture picture = new Picture();
515         Canvas canvas = picture.beginRecording(200, 100);
516 
517         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
518 
519         p.setColor(0x88FF0000);
520         canvas.drawCircle(50, 50, 40, p);
521 
522         p.setColor(Color.GREEN);
523         p.setTextSize(30);
524         canvas.drawText("Pictures", 60, 60, p);
525         picture.endRecording();
526 
527         Bitmap bitmap;
528         bitmap = Bitmap.createBitmap(picture);
529         assertFalse(bitmap.isMutable());
530 
531         bitmap = Bitmap.createBitmap(picture, 100, 100, Config.HARDWARE);
532         assertFalse(bitmap.isMutable());
533         assertNotNull(bitmap.getColorSpace());
534 
535         bitmap = Bitmap.createBitmap(picture, 100, 100, Config.ARGB_8888);
536         assertFalse(bitmap.isMutable());
537     }
538 
539     @Test
testCreateScaledBitmap()540     public void testCreateScaledBitmap() {
541         mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565);
542         assertTrue(mBitmap.isMutable());
543         Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false);
544         assertNotNull(ret);
545         assertEquals(50, ret.getWidth());
546         assertEquals(100, ret.getHeight());
547         assertTrue(ret.isMutable());
548     }
549 
550     @Test
testWrapHardwareBufferSucceeds()551     public void testWrapHardwareBufferSucceeds() {
552         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
553             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
554             assertNotNull(bitmap);
555             bitmap.recycle();
556         }
557     }
558 
559     @Test(expected = IllegalArgumentException.class)
testWrapHardwareBufferWithInvalidUsageFails()560     public void testWrapHardwareBufferWithInvalidUsageFails() {
561         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
562             HardwareBuffer.USAGE_CPU_WRITE_RARELY)) {
563             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
564         }
565     }
566 
567     @Test(expected = IllegalArgumentException.class)
testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails()568     public void testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails() {
569         try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1,
570             HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)) {
571             Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.CIE_LAB));
572         }
573     }
574 
575     @Test
testGenerationId()576     public void testGenerationId() {
577         Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
578         int genId = bitmap.getGenerationId();
579         assertEquals("not expected to change", genId, bitmap.getGenerationId());
580         bitmap.setDensity(bitmap.getDensity() + 4);
581         assertEquals("not expected to change", genId, bitmap.getGenerationId());
582         bitmap.getPixel(0, 0);
583         assertEquals("not expected to change", genId, bitmap.getGenerationId());
584 
585         int beforeGenId = bitmap.getGenerationId();
586         bitmap.eraseColor(Color.WHITE);
587         int afterGenId = bitmap.getGenerationId();
588         assertTrue("expected to increase", afterGenId > beforeGenId);
589 
590         beforeGenId = bitmap.getGenerationId();
591         bitmap.setPixel(4, 4, Color.BLUE);
592         afterGenId = bitmap.getGenerationId();
593         assertTrue("expected to increase again", afterGenId > beforeGenId);
594     }
595 
596     @Test
testDescribeContents()597     public void testDescribeContents() {
598         assertEquals(0, mBitmap.describeContents());
599     }
600 
601     @Test(expected=IllegalStateException.class)
testEraseColorOnRecycled()602     public void testEraseColorOnRecycled() {
603         mBitmap.recycle();
604 
605         mBitmap.eraseColor(0);
606     }
607 
608     @Test(expected = IllegalStateException.class)
testEraseColorLongOnRecycled()609     public void testEraseColorLongOnRecycled() {
610         mBitmap.recycle();
611 
612         mBitmap.eraseColor(Color.pack(0));
613     }
614 
615     @Test(expected=IllegalStateException.class)
testEraseColorOnImmutable()616     public void testEraseColorOnImmutable() {
617         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
618 
619         //abnormal case: bitmap is immutable
620         mBitmap.eraseColor(0);
621     }
622 
623     @Test(expected = IllegalStateException.class)
testEraseColorLongOnImmutable()624     public void testEraseColorLongOnImmutable() {
625         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
626 
627         //abnormal case: bitmap is immutable
628         mBitmap.eraseColor(Color.pack(0));
629     }
630 
631     @Test
testEraseColor()632     public void testEraseColor() {
633         // normal case
634         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
635         mBitmap.eraseColor(0xffff0000);
636         assertEquals(0xffff0000, mBitmap.getPixel(10, 10));
637         assertEquals(0xffff0000, mBitmap.getPixel(50, 50));
638     }
639 
640     @Test(expected = IllegalArgumentException.class)
testGetColorOOB()641     public void testGetColorOOB() {
642         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
643         mBitmap.getColor(-1, 0);
644     }
645 
646     @Test(expected = IllegalArgumentException.class)
testGetColorOOB2()647     public void testGetColorOOB2() {
648         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
649         mBitmap.getColor(5, -10);
650     }
651 
652     @Test(expected = IllegalArgumentException.class)
testGetColorOOB3()653     public void testGetColorOOB3() {
654         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
655         mBitmap.getColor(100, 10);
656     }
657 
658     @Test(expected = IllegalArgumentException.class)
testGetColorOOB4()659     public void testGetColorOOB4() {
660         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
661         mBitmap.getColor(99, 1000);
662     }
663 
664     @Test(expected = IllegalStateException.class)
testGetColorRecycled()665     public void testGetColorRecycled() {
666         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
667         mBitmap.recycle();
668         mBitmap.getColor(0, 0);
669     }
670 
671     @Test(expected = IllegalStateException.class)
testGetColorHardware()672     public void testGetColorHardware() {
673         BitmapFactory.Options options = new BitmapFactory.Options();
674         options.inPreferredConfig = Bitmap.Config.HARDWARE;
675         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
676         mBitmap.getColor(50, 50);
677 
678     }
679 
clamp(float f)680     private static float clamp(float f) {
681         return clamp(f, 0.0f, 1.0f);
682     }
683 
clamp(float f, float min, float max)684     private static float clamp(float f, float min, float max) {
685         return Math.min(Math.max(f, min), max);
686     }
687 
688     @Test
testGetColor()689     public void testGetColor() {
690         final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
691         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
692         for (Config config : new Config[] { Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565 }) {
693             for (ColorSpace bitmapColorSpace : rgbColorSpaces) {
694                 mBitmap = Bitmap.createBitmap(1, 1, config, /*hasAlpha*/ false,
695                         bitmapColorSpace);
696                 bitmapColorSpace = mBitmap.getColorSpace();
697                 for (ColorSpace eraseColorSpace : rgbColorSpaces) {
698                     for (long wideGamutLong : new long[] {
699                             Color.pack(1.0f, 0.0f, 0.0f, 1.0f, eraseColorSpace),
700                             Color.pack(0.0f, 1.0f, 0.0f, 1.0f, eraseColorSpace),
701                             Color.pack(0.0f, 0.0f, 1.0f, 1.0f, eraseColorSpace)}) {
702                         mBitmap.eraseColor(wideGamutLong);
703 
704                         Color result = mBitmap.getColor(0, 0);
705                         if (mBitmap.getColorSpace().equals(sRGB)) {
706                             assertEquals(mBitmap.getPixel(0, 0), result.toArgb());
707                         }
708                         if (eraseColorSpace.equals(bitmapColorSpace)) {
709                             final Color wideGamutColor = Color.valueOf(wideGamutLong);
710                             ColorUtils.verifyColor("Erasing to Bitmap's ColorSpace "
711                                     + bitmapColorSpace, wideGamutColor, result, .001f);
712 
713                         } else {
714                             Color convertedColor = Color.valueOf(
715                                     Color.convert(wideGamutLong, bitmapColorSpace));
716                             if (mBitmap.getConfig() != Config.RGBA_F16) {
717                                 // It's possible that we have to clip to fit into the Config.
718                                 convertedColor = Color.valueOf(
719                                         clamp(convertedColor.red()),
720                                         clamp(convertedColor.green()),
721                                         clamp(convertedColor.blue()),
722                                         convertedColor.alpha(),
723                                         bitmapColorSpace);
724                             }
725                             ColorUtils.verifyColor("Bitmap(Config: " + mBitmap.getConfig()
726                                     + ", ColorSpace: " + bitmapColorSpace
727                                     + ") erasing to " + Color.valueOf(wideGamutLong),
728                                     convertedColor, result, .03f);
729                         }
730                     }
731                 }
732             }
733         }
734     }
735 
736     private static class ARGB {
737         public float alpha;
738         public float red;
739         public float green;
740         public float blue;
ARGB(float alpha, float red, float green, float blue)741         ARGB(float alpha, float red, float green, float blue) {
742             this.alpha = alpha;
743             this.red = red;
744             this.green = green;
745             this.blue = blue;
746         }
747     };
748 
749     @Test
testEraseColorLong()750     public void testEraseColorLong() {
751         List<ColorSpace> rgbColorSpaces = getRgbColorSpaces();
752         for (Config config : new Config[]{Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16}) {
753             mBitmap = Bitmap.createBitmap(100, 100, config);
754             // pack SRGB colors into ColorLongs.
755             for (int color : new int[]{ Color.RED, Color.BLUE, Color.GREEN, Color.BLACK,
756                     Color.WHITE, Color.TRANSPARENT }) {
757                 if (config.equals(Config.RGB_565) && Float.compare(Color.alpha(color), 1.0f) != 0) {
758                     // 565 doesn't support alpha.
759                     continue;
760                 }
761                 mBitmap.eraseColor(Color.pack(color));
762                 // The Bitmap is either SRGB or SRGBLinear (F16). getPixel(), which retrieves the
763                 // color in SRGB, should match exactly.
764                 ColorUtils.verifyColor("Config " + config + " mismatch at 10, 10 ",
765                         color, mBitmap.getPixel(10, 10), 0);
766                 ColorUtils.verifyColor("Config " + config + " mismatch at 50, 50 ",
767                         color, mBitmap.getPixel(50, 50), 0);
768             }
769 
770             // Use arbitrary colors in various ColorSpaces. getPixel() should approximately match
771             // the SRGB version of the color.
772             for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
773                                           new ARGB(1.0f, .3f, .6f, .9f),
774                                           new ARGB(0.5f, .2f, .8f, .7f) }) {
775                 if (config.equals(Config.RGB_565) && Float.compare(color.alpha, 1.0f) != 0) {
776                     continue;
777                 }
778                 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
779                 for (ColorSpace cs : rgbColorSpaces) {
780                     long longColor = Color.convert(srgbColor, cs);
781                     mBitmap.eraseColor(longColor);
782                     // These tolerances were chosen by trial and error. It is expected that
783                     // some conversions do not round-trip perfectly.
784                     int tolerance = 1;
785                     if (config.equals(Config.RGB_565)) {
786                         tolerance = 4;
787                     } else if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) {
788                         tolerance = 3;
789                     }
790 
791                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
792                             + ", mismatch at 10, 10 ", srgbColor, mBitmap.getPixel(10, 10),
793                             tolerance);
794                     ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs
795                             + ", mismatch at 50, 50 ", srgbColor, mBitmap.getPixel(50, 50),
796                             tolerance);
797                 }
798             }
799         }
800     }
801 
802     @Test
testEraseColorOnP3()803     public void testEraseColorOnP3() {
804         // Use a ColorLong with a different ColorSpace than the Bitmap. getPixel() should
805         // approximately match the SRGB version of the color.
806         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888, true,
807                 ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
808         int srgbColor = Color.argb(.5f, .3f, .6f, .7f);
809         long acesColor = Color.convert(srgbColor, ColorSpace.get(ColorSpace.Named.ACES));
810         mBitmap.eraseColor(acesColor);
811         ColorUtils.verifyColor("Mismatch at 15, 15", srgbColor, mBitmap.getPixel(15, 15), 1);
812     }
813 
814     @Test(expected = IllegalArgumentException.class)
testEraseColorXYZ()815     public void testEraseColorXYZ() {
816         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
817         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ)));
818     }
819 
820     @Test(expected = IllegalArgumentException.class)
testEraseColorLAB()821     public void testEraseColorLAB() {
822         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
823         mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB)));
824     }
825 
826     @Test(expected = IllegalArgumentException.class)
testEraseColorUnknown()827     public void testEraseColorUnknown() {
828         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
829         mBitmap.eraseColor(-1L);
830     }
831 
832     @Test(expected=IllegalStateException.class)
testExtractAlphaFromRecycled()833     public void testExtractAlphaFromRecycled() {
834         mBitmap.recycle();
835 
836         mBitmap.extractAlpha();
837     }
838 
839     @Test
testExtractAlpha()840     public void testExtractAlpha() {
841         // normal case
842         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
843         Bitmap ret = mBitmap.extractAlpha();
844         assertNotNull(ret);
845         int source = mBitmap.getPixel(10, 20);
846         int result = ret.getPixel(10, 20);
847         assertEquals(Color.alpha(source), Color.alpha(result));
848         assertEquals(0xFF, Color.alpha(result));
849     }
850 
851     @Test(expected=IllegalStateException.class)
testExtractAlphaWithPaintAndOffsetFromRecycled()852     public void testExtractAlphaWithPaintAndOffsetFromRecycled() {
853         mBitmap.recycle();
854 
855         mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
856     }
857 
858     @Test
testExtractAlphaWithPaintAndOffset()859     public void testExtractAlphaWithPaintAndOffset() {
860         // normal case
861         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
862         Bitmap ret = mBitmap.extractAlpha(new Paint(), new int[]{0, 1});
863         assertNotNull(ret);
864         int source = mBitmap.getPixel(10, 20);
865         int result = ret.getPixel(10, 20);
866         assertEquals(Color.alpha(source), Color.alpha(result));
867         assertEquals(0xFF, Color.alpha(result));
868     }
869 
870     @Test
testGetAllocationByteCount()871     public void testGetAllocationByteCount() {
872         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
873         int alloc = mBitmap.getAllocationByteCount();
874         assertEquals(mBitmap.getByteCount(), alloc);
875 
876         // reconfigure same size
877         mBitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888);
878         assertEquals(mBitmap.getByteCount(), alloc);
879         assertEquals(mBitmap.getAllocationByteCount(), alloc);
880 
881         // reconfigure different size
882         mBitmap.reconfigure(10, 10, Bitmap.Config.ALPHA_8);
883         assertEquals(mBitmap.getByteCount(), 100);
884         assertEquals(mBitmap.getAllocationByteCount(), alloc);
885     }
886 
887     @Test
testGetConfig()888     public void testGetConfig() {
889         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
890         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
891         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
892         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
893 
894         assertEquals(Bitmap.Config.ALPHA_8, bm0.getConfig());
895         assertEquals(Bitmap.Config.ARGB_8888, bm1.getConfig());
896         assertEquals(Bitmap.Config.RGB_565, bm2.getConfig());
897         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
898         assertEquals(Bitmap.Config.ARGB_8888, bm3.getConfig());
899 
900         // Can't call Bitmap.createBitmap with Bitmap.Config.HARDWARE,
901         // because createBitmap creates mutable bitmap and hardware bitmaps are always immutable,
902         // so such call will throw an exception.
903         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
904                 HARDWARE_OPTIONS);
905         assertEquals(Bitmap.Config.HARDWARE, hardwareBitmap.getConfig());
906     }
907 
908     @Test
testGetHeight()909     public void testGetHeight() {
910         assertEquals(31, mBitmap.getHeight());
911         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
912         assertEquals(200, mBitmap.getHeight());
913     }
914 
915     @Test
testGetNinePatchChunk()916     public void testGetNinePatchChunk() {
917         assertNull(mBitmap.getNinePatchChunk());
918     }
919 
920     @Test(expected=IllegalStateException.class)
testGetPixelFromRecycled()921     public void testGetPixelFromRecycled() {
922         mBitmap.recycle();
923 
924         mBitmap.getPixel(10, 16);
925     }
926 
927     @Test(expected=IllegalArgumentException.class)
testGetPixelXTooLarge()928     public void testGetPixelXTooLarge() {
929         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
930 
931         // abnormal case: x bigger than the source bitmap's width
932         mBitmap.getPixel(200, 16);
933     }
934 
935     @Test(expected=IllegalArgumentException.class)
testGetPixelYTooLarge()936     public void testGetPixelYTooLarge() {
937         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
938 
939         // abnormal case: y bigger than the source bitmap's height
940         mBitmap.getPixel(10, 300);
941     }
942 
943     @Test
testGetPixel()944     public void testGetPixel() {
945         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
946 
947         // normal case 565
948         mBitmap.setPixel(10, 16, 0xFF << 24);
949         assertEquals(0xFF << 24, mBitmap.getPixel(10, 16));
950 
951         // normal case A_8
952         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
953         mBitmap.setPixel(5, 5, 0xFFFFFFFF);
954         assertEquals(0xFF000000, mBitmap.getPixel(5, 5));
955         mBitmap.setPixel(5, 5, 0xA8A8A8A8);
956         assertEquals(0xA8000000, mBitmap.getPixel(5, 5));
957         mBitmap.setPixel(5, 5, 0x00000000);
958         assertEquals(0x00000000, mBitmap.getPixel(5, 5));
959         mBitmap.setPixel(5, 5, 0x1F000000);
960         assertEquals(0x1F000000, mBitmap.getPixel(5, 5));
961     }
962 
963     @Test
testGetRowBytes()964     public void testGetRowBytes() {
965         Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8);
966         Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
967         Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
968         Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444);
969 
970         assertEquals(100, bm0.getRowBytes());
971         assertEquals(400, bm1.getRowBytes());
972         assertEquals(200, bm2.getRowBytes());
973         // Attempting to create a 4444 bitmap actually creates an 8888 bitmap.
974         assertEquals(400, bm3.getRowBytes());
975     }
976 
977     @Test
testGetWidth()978     public void testGetWidth() {
979         assertEquals(31, mBitmap.getWidth());
980         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
981         assertEquals(100, mBitmap.getWidth());
982     }
983 
984     @Test
testHasAlpha()985     public void testHasAlpha() {
986         assertFalse(mBitmap.hasAlpha());
987         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
988         assertTrue(mBitmap.hasAlpha());
989     }
990 
991     @Test
testIsMutable()992     public void testIsMutable() {
993         assertFalse(mBitmap.isMutable());
994         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
995         assertTrue(mBitmap.isMutable());
996     }
997 
998     @Test
testIsRecycled()999     public void testIsRecycled() {
1000         assertFalse(mBitmap.isRecycled());
1001         mBitmap.recycle();
1002         assertTrue(mBitmap.isRecycled());
1003     }
1004 
1005     @Test
testReconfigure()1006     public void testReconfigure() {
1007         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1008         int alloc = mBitmap.getAllocationByteCount();
1009 
1010         // test shrinking
1011         mBitmap.reconfigure(50, 100, Bitmap.Config.ALPHA_8);
1012         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1013         assertEquals(mBitmap.getByteCount() * 8, alloc);
1014     }
1015 
1016     @Test(expected=IllegalArgumentException.class)
testReconfigureExpanding()1017     public void testReconfigureExpanding() {
1018         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1019         mBitmap.reconfigure(101, 201, Bitmap.Config.ARGB_8888);
1020     }
1021 
1022     @Test(expected=IllegalStateException.class)
testReconfigureMutable()1023     public void testReconfigureMutable() {
1024         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1025         mBitmap.reconfigure(1, 1, Bitmap.Config.ALPHA_8);
1026     }
1027 
1028     // Used by testAlphaAndPremul.
1029     private static Config[] CONFIGS = new Config[] { Config.ALPHA_8, Config.ARGB_4444,
1030             Config.ARGB_8888, Config.RGB_565 };
1031 
1032     // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with
1033     // respect to alpha and premultiplied.
1034     @Test
testAlphaAndPremul()1035     public void testAlphaAndPremul() {
1036         boolean falseTrue[] = new boolean[] { false, true };
1037         for (Config fromConfig : CONFIGS) {
1038             for (Config toConfig : CONFIGS) {
1039                 for (boolean hasAlpha : falseTrue) {
1040                     for (boolean isPremul : falseTrue) {
1041                         Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig);
1042 
1043                         // 4444 is deprecated, and will convert to 8888. No need to
1044                         // attempt a reconfigure, which will be tested when fromConfig
1045                         // is 8888.
1046                         if (fromConfig == Config.ARGB_4444) {
1047                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1048                             break;
1049                         }
1050 
1051                         bitmap.setHasAlpha(hasAlpha);
1052                         bitmap.setPremultiplied(isPremul);
1053 
1054                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, false);
1055 
1056                         // reconfigure to a smaller size so the function will still succeed when
1057                         // going to a Config that requires more bits.
1058                         bitmap.reconfigure(1, 1, toConfig);
1059                         if (toConfig == Config.ARGB_4444) {
1060                             assertEquals(bitmap.getConfig(), Config.ARGB_8888);
1061                         } else {
1062                             assertEquals(bitmap.getConfig(), toConfig);
1063                         }
1064 
1065                         // Check that the alpha and premultiplied state has not changed (unless
1066                         // we expected it to).
1067                         verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565);
1068                     }
1069                 }
1070             }
1071         }
1072     }
1073 
1074     /**
1075      *  Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied().
1076      *  @param bitmap Bitmap to check.
1077      *  @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based
1078      *          on what was set, but may be different from the actual return value depending on the
1079      *          Config and convertedFrom565.
1080      *  @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to
1081      *          expectedAlpha, this is based on what was set, but may be different from the actual
1082      *          return value depending on the Config.
1083      *  @param convertedFrom565 Whether bitmap was converted to its current Config by being
1084      *          reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha,
1085      *          hasAlpha() is expected to be true even if expectedAlpha is false.
1086      */
verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565)1087     private void verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul,
1088             boolean convertedFrom565) {
1089         switch (bitmap.getConfig()) {
1090             case ARGB_4444:
1091                 // This shouldn't happen, since we don't allow creating or converting
1092                 // to 4444.
1093                 assertFalse(true);
1094                 break;
1095             case RGB_565:
1096                 assertFalse(bitmap.hasAlpha());
1097                 assertFalse(bitmap.isPremultiplied());
1098                 break;
1099             case ALPHA_8:
1100                 // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through.
1101             case ARGB_8888:
1102                 // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type
1103                 // that can have alpha.
1104                 if (convertedFrom565) {
1105                     assertTrue(bitmap.hasAlpha());
1106                 } else {
1107                     assertEquals(bitmap.hasAlpha(), expectedAlpha);
1108                 }
1109 
1110                 if (bitmap.hasAlpha()) {
1111                     // ALPHA_8's premultiplied status is undefined.
1112                     if (bitmap.getConfig() != Config.ALPHA_8) {
1113                         assertEquals(bitmap.isPremultiplied(), expectedPremul);
1114                     }
1115                 } else {
1116                     // Opaque bitmap is never considered premultiplied.
1117                     assertFalse(bitmap.isPremultiplied());
1118                 }
1119                 break;
1120         }
1121     }
1122 
1123     @Test
testSetColorSpace()1124     public void testSetColorSpace() {
1125         // Use arbitrary colors and assign to various ColorSpaces.
1126         for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f),
1127                 new ARGB(1.0f, .3f, .6f, .9f),
1128                 new ARGB(0.5f, .2f, .8f, .7f) }) {
1129 
1130             int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue);
1131             for (ColorSpace cs : getRgbColorSpaces()) {
1132                 for (Config config : new Config[] {
1133                         // F16 is tested elsewhere, since it defaults to EXTENDED_SRGB, and
1134                         // many of these calls to setColorSpace would reduce the range, resulting
1135                         // in an Exception.
1136                         Config.ARGB_8888,
1137                         Config.RGB_565,
1138                 }) {
1139                     mBitmap = Bitmap.createBitmap(10, 10, config);
1140                     mBitmap.eraseColor(srgbColor);
1141                     mBitmap.setColorSpace(cs);
1142                     ColorSpace actual = mBitmap.getColorSpace();
1143                     if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
1144                         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
1145                     } else if (cs == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
1146                         assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual);
1147                     } else {
1148                         assertSame(cs, actual);
1149                     }
1150 
1151                     // This tolerance was chosen by trial and error. It is expected that
1152                     // some conversions do not round-trip perfectly.
1153                     int tolerance = 2;
1154                     Color c = Color.valueOf(color.red, color.green, color.blue, color.alpha, cs);
1155                     ColorUtils.verifyColor("Mismatch after setting the colorSpace to "
1156                             + cs.getName(), c.convert(mBitmap.getColorSpace()),
1157                             mBitmap.getColor(5, 5), tolerance);
1158                 }
1159             }
1160         }
1161     }
1162 
1163     @Test(expected = IllegalStateException.class)
testSetColorSpaceRecycled()1164     public void testSetColorSpaceRecycled() {
1165         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1166         mBitmap.recycle();
1167         mBitmap.setColorSpace(ColorSpace.get(Named.DISPLAY_P3));
1168     }
1169 
1170     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNull()1171     public void testSetColorSpaceNull() {
1172         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1173         mBitmap.setColorSpace(null);
1174     }
1175 
1176     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceXYZ()1177     public void testSetColorSpaceXYZ() {
1178         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1179         mBitmap.setColorSpace(ColorSpace.get(Named.CIE_XYZ));
1180     }
1181 
1182     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceNoTransferParameters()1183     public void testSetColorSpaceNoTransferParameters() {
1184         mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1185         ColorSpace cs = new ColorSpace.Rgb("NoTransferParams",
1186                 new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
1187                 ColorSpace.ILLUMINANT_D50,
1188                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f),
1189                 0, 1);
1190         mBitmap.setColorSpace(cs);
1191     }
1192 
1193     @Test(expected = IllegalArgumentException.class)
testSetColorSpaceAlpha8()1194     public void testSetColorSpaceAlpha8() {
1195         mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8);
1196         assertNull(mBitmap.getColorSpace());
1197         mBitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
1198     }
1199 
1200     @Test
testSetColorSpaceReducedRange()1201     public void testSetColorSpaceReducedRange() {
1202         ColorSpace aces = ColorSpace.get(Named.ACES);
1203         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, aces);
1204         try {
1205             mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1206             fail("Expected IllegalArgumentException!");
1207         } catch (IllegalArgumentException e) {
1208             assertSame(aces, mBitmap.getColorSpace());
1209         }
1210     }
1211 
1212     @Test
testSetColorSpaceNotReducedRange()1213     public void testSetColorSpaceNotReducedRange() {
1214         ColorSpace extended = ColorSpace.get(Named.EXTENDED_SRGB);
1215         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1216                 extended);
1217         mBitmap.setColorSpace(ColorSpace.get(Named.SRGB));
1218         assertSame(mBitmap.getColorSpace(), extended);
1219     }
1220 
1221     @Test
testSetColorSpaceNotReducedRangeLinear()1222     public void testSetColorSpaceNotReducedRangeLinear() {
1223         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1224         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1225                 linearExtended);
1226         mBitmap.setColorSpace(ColorSpace.get(Named.LINEAR_SRGB));
1227         assertSame(mBitmap.getColorSpace(), linearExtended);
1228     }
1229 
1230     @Test
testSetColorSpaceIncreasedRange()1231     public void testSetColorSpaceIncreasedRange() {
1232         mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true,
1233                 ColorSpace.get(Named.DISPLAY_P3));
1234         ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB);
1235         mBitmap.setColorSpace(linearExtended);
1236         assertSame(mBitmap.getColorSpace(), linearExtended);
1237     }
1238 
1239     @Test
testSetConfig()1240     public void testSetConfig() {
1241         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1242         int alloc = mBitmap.getAllocationByteCount();
1243 
1244         // test shrinking
1245         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1246         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1247         assertEquals(mBitmap.getByteCount() * 2, alloc);
1248     }
1249 
1250     @Test(expected=IllegalArgumentException.class)
testSetConfigExpanding()1251     public void testSetConfigExpanding() {
1252         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1253         // test expanding
1254         mBitmap.setConfig(Bitmap.Config.ARGB_8888);
1255     }
1256 
1257     @Test(expected=IllegalStateException.class)
testSetConfigMutable()1258     public void testSetConfigMutable() {
1259         // test mutable
1260         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1261         mBitmap.setConfig(Bitmap.Config.ALPHA_8);
1262     }
1263 
1264     @Test
testSetHeight()1265     public void testSetHeight() {
1266         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1267         int alloc = mBitmap.getAllocationByteCount();
1268 
1269         // test shrinking
1270         mBitmap.setHeight(100);
1271         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1272         assertEquals(mBitmap.getByteCount() * 2, alloc);
1273     }
1274 
1275     @Test(expected=IllegalArgumentException.class)
testSetHeightExpanding()1276     public void testSetHeightExpanding() {
1277         // test expanding
1278         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1279         mBitmap.setHeight(201);
1280     }
1281 
1282     @Test(expected=IllegalStateException.class)
testSetHeightMutable()1283     public void testSetHeightMutable() {
1284         // test mutable
1285         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1286         mBitmap.setHeight(1);
1287     }
1288 
1289     @Test(expected=IllegalStateException.class)
testSetPixelOnRecycled()1290     public void testSetPixelOnRecycled() {
1291         int color = 0xff << 24;
1292 
1293         mBitmap.recycle();
1294         mBitmap.setPixel(10, 16, color);
1295     }
1296 
1297     @Test(expected=IllegalStateException.class)
testSetPixelOnImmutable()1298     public void testSetPixelOnImmutable() {
1299         int color = 0xff << 24;
1300         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1301 
1302         mBitmap.setPixel(10, 16, color);
1303     }
1304 
1305     @Test(expected=IllegalArgumentException.class)
testSetPixelXIsTooLarge()1306     public void testSetPixelXIsTooLarge() {
1307         int color = 0xff << 24;
1308         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1309 
1310         // abnormal case: x bigger than the source bitmap's width
1311         mBitmap.setPixel(200, 16, color);
1312     }
1313 
1314     @Test(expected=IllegalArgumentException.class)
testSetPixelYIsTooLarge()1315     public void testSetPixelYIsTooLarge() {
1316         int color = 0xff << 24;
1317         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1318 
1319         // abnormal case: y bigger than the source bitmap's height
1320         mBitmap.setPixel(10, 300, color);
1321     }
1322 
1323     @Test
testSetPixel()1324     public void testSetPixel() {
1325         int color = 0xff << 24;
1326         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
1327 
1328         // normal case
1329         mBitmap.setPixel(10, 16, color);
1330         assertEquals(color, mBitmap.getPixel(10, 16));
1331     }
1332 
1333     @Test(expected=IllegalStateException.class)
testSetPixelsOnRecycled()1334     public void testSetPixelsOnRecycled() {
1335         int[] colors = createColors(100);
1336 
1337         mBitmap.recycle();
1338         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1339     }
1340 
1341     @Test(expected=IllegalStateException.class)
testSetPixelsOnImmutable()1342     public void testSetPixelsOnImmutable() {
1343         int[] colors = createColors(100);
1344         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1345 
1346         mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0);
1347     }
1348 
1349     @Test(expected=IllegalArgumentException.class)
testSetPixelsXYNegative()1350     public void testSetPixelsXYNegative() {
1351         int[] colors = createColors(100);
1352         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1353 
1354         // abnormal case: x and/or y less than 0
1355         mBitmap.setPixels(colors, 0, 0, -1, -1, 200, 16);
1356     }
1357 
1358     @Test(expected=IllegalArgumentException.class)
testSetPixelsWidthHeightNegative()1359     public void testSetPixelsWidthHeightNegative() {
1360         int[] colors = createColors(100);
1361         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1362 
1363         // abnormal case: width and/or height less than 0
1364         mBitmap.setPixels(colors, 0, 0, 0, 0, -1, -1);
1365     }
1366 
1367     @Test(expected=IllegalArgumentException.class)
testSetPixelsXTooHigh()1368     public void testSetPixelsXTooHigh() {
1369         int[] colors = createColors(100);
1370         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1371 
1372         // abnormal case: (x + width) bigger than the source bitmap's width
1373         mBitmap.setPixels(colors, 0, 0, 10, 10, 95, 50);
1374     }
1375 
1376     @Test(expected=IllegalArgumentException.class)
testSetPixelsYTooHigh()1377     public void testSetPixelsYTooHigh() {
1378         int[] colors = createColors(100);
1379         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1380 
1381         // abnormal case: (y + height) bigger than the source bitmap's height
1382         mBitmap.setPixels(colors, 0, 0, 10, 10, 50, 95);
1383     }
1384 
1385     @Test(expected=IllegalArgumentException.class)
testSetPixelsStrideIllegal()1386     public void testSetPixelsStrideIllegal() {
1387         int[] colors = createColors(100);
1388         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1389 
1390         // abnormal case: stride less than width and bigger than -width
1391         mBitmap.setPixels(colors, 0, 10, 10, 10, 50, 50);
1392     }
1393 
1394     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetNegative()1395     public void testSetPixelsOffsetNegative() {
1396         int[] colors = createColors(100);
1397         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1398 
1399         // abnormal case: offset less than 0
1400         mBitmap.setPixels(colors, -1, 50, 10, 10, 50, 50);
1401     }
1402 
1403     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsOffsetTooBig()1404     public void testSetPixelsOffsetTooBig() {
1405         int[] colors = createColors(100);
1406         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1407 
1408         // abnormal case: (offset + width) bigger than the length of colors
1409         mBitmap.setPixels(colors, 60, 50, 10, 10, 50, 50);
1410     }
1411 
1412     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineNegative()1413     public void testSetPixelsLastScanlineNegative() {
1414         int[] colors = createColors(100);
1415         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1416 
1417         // abnormal case: lastScanline less than 0
1418         mBitmap.setPixels(colors, 10, -50, 10, 10, 50, 50);
1419     }
1420 
1421     @Test(expected=ArrayIndexOutOfBoundsException.class)
testSetPixelsLastScanlineTooBig()1422     public void testSetPixelsLastScanlineTooBig() {
1423         int[] colors = createColors(100);
1424         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1425 
1426         // abnormal case: (lastScanline + width) bigger than the length of colors
1427         mBitmap.setPixels(colors, 10, 50, 10, 10, 50, 50);
1428     }
1429 
1430     @Test
testSetPixels()1431     public void testSetPixels() {
1432         int[] colors = createColors(100 * 100);
1433         mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
1434         mBitmap.setPixels(colors, 0, 100, 0, 0, 100, 100);
1435         int[] ret = new int[100 * 100];
1436         mBitmap.getPixels(ret, 0, 100, 0, 0, 100, 100);
1437 
1438         for(int i = 0; i < 10000; i++){
1439             assertEquals(ret[i], colors[i]);
1440         }
1441     }
1442 
verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul)1443     private void verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul) {
1444         Bitmap bitmap = Bitmap.createBitmap(1, 1, config);
1445         bitmap.setPremultiplied(true);
1446         bitmap.setPixel(0, 0, Color.TRANSPARENT);
1447         assertTrue(bitmap.isPremultiplied() == expectedPremul);
1448 
1449         bitmap.setHasAlpha(false);
1450         assertFalse(bitmap.isPremultiplied());
1451     }
1452 
1453     @Test
testSetPremultipliedSimple()1454     public void testSetPremultipliedSimple() {
1455         verifyPremultipliedBitmapConfig(Bitmap.Config.ALPHA_8, true);
1456         verifyPremultipliedBitmapConfig(Bitmap.Config.RGB_565, false);
1457         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_4444, true);
1458         verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_8888, true);
1459     }
1460 
1461     @Test
testSetPremultipliedData()1462     public void testSetPremultipliedData() {
1463         // with premul, will store 2,2,2,2, so it doesn't get value correct
1464         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1465         bitmap.setPixel(0, 0, PREMUL_COLOR);
1466         assertEquals(bitmap.getPixel(0, 0), PREMUL_ROUNDED_COLOR);
1467 
1468         // read premultiplied value directly
1469         bitmap.setPremultiplied(false);
1470         assertEquals(bitmap.getPixel(0, 0), PREMUL_STORED_COLOR);
1471 
1472         // value can now be stored/read correctly
1473         bitmap.setPixel(0, 0, PREMUL_COLOR);
1474         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1475 
1476         // verify with array methods
1477         int testArray[] = new int[] { PREMUL_COLOR };
1478         bitmap.setPixels(testArray, 0, 1, 0, 0, 1, 1);
1479         bitmap.getPixels(testArray, 0, 1, 0, 0, 1, 1);
1480         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1481     }
1482 
1483     @Test
testPremultipliedCanvas()1484     public void testPremultipliedCanvas() {
1485         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1486         bitmap.setHasAlpha(true);
1487         bitmap.setPremultiplied(false);
1488         assertFalse(bitmap.isPremultiplied());
1489 
1490         Canvas c = new Canvas();
1491         try {
1492             c.drawBitmap(bitmap, 0, 0, null);
1493             fail("canvas should fail with exception");
1494         } catch (RuntimeException e) {
1495         }
1496     }
1497 
getBitmapRawInt(Bitmap bitmap)1498     private int getBitmapRawInt(Bitmap bitmap) {
1499         IntBuffer buffer = IntBuffer.allocate(1);
1500         bitmap.copyPixelsToBuffer(buffer);
1501         return buffer.get(0);
1502     }
1503 
bitmapStoreRawInt(Bitmap bitmap, int value)1504     private void bitmapStoreRawInt(Bitmap bitmap, int value) {
1505         IntBuffer buffer = IntBuffer.allocate(1);
1506         buffer.put(0, value);
1507         bitmap.copyPixelsFromBuffer(buffer);
1508     }
1509 
1510     @Test
testSetPremultipliedToBuffer()1511     public void testSetPremultipliedToBuffer() {
1512         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1513         bitmap.setPixel(0, 0, PREMUL_COLOR);
1514         int storedPremul = getBitmapRawInt(bitmap);
1515 
1516         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1517         bitmap.setPremultiplied(false);
1518         bitmap.setPixel(0, 0, PREMUL_STORED_COLOR);
1519 
1520         assertEquals(getBitmapRawInt(bitmap), storedPremul);
1521     }
1522 
1523     @Test
testSetPremultipliedFromBuffer()1524     public void testSetPremultipliedFromBuffer() {
1525         Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1526         bitmap.setPremultiplied(false);
1527         bitmap.setPixel(0, 0, PREMUL_COLOR);
1528         int rawTestColor = getBitmapRawInt(bitmap);
1529 
1530         bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
1531         bitmap.setPremultiplied(false);
1532         bitmapStoreRawInt(bitmap, rawTestColor);
1533         assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR);
1534     }
1535 
1536     @Test
testSetWidth()1537     public void testSetWidth() {
1538         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1539         int alloc = mBitmap.getAllocationByteCount();
1540 
1541         // test shrinking
1542         mBitmap.setWidth(50);
1543         assertEquals(mBitmap.getAllocationByteCount(), alloc);
1544         assertEquals(mBitmap.getByteCount() * 2, alloc);
1545     }
1546 
1547     @Test(expected=IllegalArgumentException.class)
testSetWidthExpanding()1548     public void testSetWidthExpanding() {
1549         // test expanding
1550         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888);
1551 
1552         mBitmap.setWidth(101);
1553     }
1554 
1555     @Test(expected=IllegalStateException.class)
testSetWidthMutable()1556     public void testSetWidthMutable() {
1557         // test mutable
1558         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1559 
1560         mBitmap.setWidth(1);
1561     }
1562 
1563     @Test(expected=IllegalStateException.class)
testWriteToParcelRecycled()1564     public void testWriteToParcelRecycled() {
1565         mBitmap.recycle();
1566 
1567         mBitmap.writeToParcel(null, 0);
1568     }
1569 
1570     @Test
testWriteToParcel()1571     public void testWriteToParcel() {
1572         // abnormal case: failed to unparcel Bitmap
1573         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions);
1574         Parcel p = Parcel.obtain();
1575         mBitmap.writeToParcel(p, 0);
1576 
1577         try {
1578             Bitmap.CREATOR.createFromParcel(p);
1579             fail("shouldn't come to here");
1580         } catch(RuntimeException e){
1581         }
1582 
1583         p.recycle();
1584         // normal case
1585         p = Parcel.obtain();
1586         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1587         mBitmap.writeToParcel(p, 0);
1588         p.setDataPosition(0);
1589         assertTrue(mBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1590 
1591         p.recycle();
1592     }
1593 
1594     @Test
testWriteHwBitmapToParcel()1595     public void testWriteHwBitmapToParcel() {
1596         mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1597         Parcel p = Parcel.obtain();
1598         mBitmap.writeToParcel(p, 0);
1599         p.setDataPosition(0);
1600         Bitmap expectedBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot);
1601         assertTrue(expectedBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p)));
1602 
1603         p.recycle();
1604     }
1605 
1606     @Test
testParcelF16ColorSpace()1607     public void testParcelF16ColorSpace() {
1608         for (ColorSpace.Named e : new ColorSpace.Named[] {
1609                 ColorSpace.Named.EXTENDED_SRGB,
1610                 ColorSpace.Named.LINEAR_EXTENDED_SRGB,
1611                 ColorSpace.Named.PRO_PHOTO_RGB,
1612                 ColorSpace.Named.DISPLAY_P3
1613         }) {
1614             final ColorSpace cs = ColorSpace.get(e);
1615             Bitmap b = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, cs);
1616             assertSame(cs, b.getColorSpace());
1617 
1618             Parcel p = Parcel.obtain();
1619             b.writeToParcel(p, 0);
1620             p.setDataPosition(0);
1621             Bitmap unparceled = Bitmap.CREATOR.createFromParcel(p);
1622             assertSame(cs, unparceled.getColorSpace());
1623         }
1624     }
1625 
1626     @Test
testGetScaledHeight1()1627     public void testGetScaledHeight1() {
1628         int dummyDensity = 5;
1629         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1630         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity);
1631         assertNotNull(ret);
1632         assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity));
1633     }
1634 
1635     @Test
testGetScaledHeight2()1636     public void testGetScaledHeight2() {
1637         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1638         DisplayMetrics metrics =
1639                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1640         int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi);
1641         assertEquals(scaledHeight, ret.getScaledHeight(metrics));
1642     }
1643 
1644     @Test
testGetScaledHeight3()1645     public void testGetScaledHeight3() {
1646         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1647         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1648         Canvas mCanvas = new Canvas(mMutableBitmap);
1649         // set Density
1650         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1651         int scaledHeight = scaleFromDensity(
1652                 ret.getHeight(), ret.getDensity(), mCanvas.getDensity());
1653         assertEquals(scaledHeight, ret.getScaledHeight(mCanvas));
1654     }
1655 
1656     @Test
testGetScaledWidth1()1657     public void testGetScaledWidth1() {
1658         int dummyDensity = 5;
1659         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1660         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity);
1661         assertNotNull(ret);
1662         assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity));
1663     }
1664 
1665     @Test
testGetScaledWidth2()1666     public void testGetScaledWidth2() {
1667         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1668         DisplayMetrics metrics =
1669                 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics();
1670         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi);
1671         assertEquals(scaledWidth, ret.getScaledWidth(metrics));
1672     }
1673 
1674     @Test
testGetScaledWidth3()1675     public void testGetScaledWidth3() {
1676         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
1677         Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1678         Canvas mCanvas = new Canvas(mMutableBitmap);
1679         // set Density
1680         mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH);
1681         int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(),  mCanvas.getDensity());
1682         assertEquals(scaledWidth, ret.getScaledWidth(mCanvas));
1683     }
1684 
1685     @Test
testSameAs_simpleSuccess()1686     public void testSameAs_simpleSuccess() {
1687         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1688         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1689         bitmap1.eraseColor(Color.BLACK);
1690         bitmap2.eraseColor(Color.BLACK);
1691         assertTrue(bitmap1.sameAs(bitmap2));
1692         assertTrue(bitmap2.sameAs(bitmap1));
1693     }
1694 
1695     @Test
testSameAs_simpleFail()1696     public void testSameAs_simpleFail() {
1697         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1698         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1699         bitmap1.eraseColor(Color.BLACK);
1700         bitmap2.eraseColor(Color.BLACK);
1701         bitmap2.setPixel(20, 10, Color.WHITE);
1702         assertFalse(bitmap1.sameAs(bitmap2));
1703         assertFalse(bitmap2.sameAs(bitmap1));
1704     }
1705 
1706     @Test
testSameAs_reconfigure()1707     public void testSameAs_reconfigure() {
1708         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1709         Bitmap bitmap2 = Bitmap.createBitmap(150, 150, Config.ARGB_8888);
1710         bitmap2.reconfigure(100, 100, Config.ARGB_8888); // now same size, so should be same
1711         bitmap1.eraseColor(Color.BLACK);
1712         bitmap2.eraseColor(Color.BLACK);
1713         assertTrue(bitmap1.sameAs(bitmap2));
1714         assertTrue(bitmap2.sameAs(bitmap1));
1715     }
1716 
1717     @Test
testSameAs_config()1718     public void testSameAs_config() {
1719         Bitmap bitmap1 = Bitmap.createBitmap(100, 200, Config.RGB_565);
1720         Bitmap bitmap2 = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
1721 
1722         // both bitmaps can represent black perfectly
1723         bitmap1.eraseColor(Color.BLACK);
1724         bitmap2.eraseColor(Color.BLACK);
1725 
1726         // but not same due to config
1727         assertFalse(bitmap1.sameAs(bitmap2));
1728         assertFalse(bitmap2.sameAs(bitmap1));
1729     }
1730 
1731     @Test
testSameAs_width()1732     public void testSameAs_width() {
1733         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1734         Bitmap bitmap2 = Bitmap.createBitmap(101, 100, Config.ARGB_8888);
1735         bitmap1.eraseColor(Color.BLACK);
1736         bitmap2.eraseColor(Color.BLACK);
1737         assertFalse(bitmap1.sameAs(bitmap2));
1738         assertFalse(bitmap2.sameAs(bitmap1));
1739     }
1740 
1741     @Test
testSameAs_height()1742     public void testSameAs_height() {
1743         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1744         Bitmap bitmap2 = Bitmap.createBitmap(102, 100, Config.ARGB_8888);
1745         bitmap1.eraseColor(Color.BLACK);
1746         bitmap2.eraseColor(Color.BLACK);
1747         assertFalse(bitmap1.sameAs(bitmap2));
1748         assertFalse(bitmap2.sameAs(bitmap1));
1749     }
1750 
1751     @Test
testSameAs_opaque()1752     public void testSameAs_opaque() {
1753         Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1754         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1755         bitmap1.eraseColor(Color.BLACK);
1756         bitmap2.eraseColor(Color.BLACK);
1757         bitmap1.setHasAlpha(true);
1758         bitmap2.setHasAlpha(false);
1759         assertFalse(bitmap1.sameAs(bitmap2));
1760         assertFalse(bitmap2.sameAs(bitmap1));
1761     }
1762 
1763     @Test
testSameAs_hardware()1764     public void testSameAs_hardware() {
1765         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1766         Bitmap bitmap2 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1767         Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
1768         Bitmap bitmap4 = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
1769         assertTrue(bitmap1.sameAs(bitmap2));
1770         assertTrue(bitmap2.sameAs(bitmap1));
1771         assertFalse(bitmap1.sameAs(bitmap3));
1772         assertFalse(bitmap1.sameAs(bitmap4));
1773     }
1774 
1775     @Test
testSameAs_wrappedHardwareBuffer()1776     public void testSameAs_wrappedHardwareBuffer() {
1777         try (HardwareBuffer hwBufferA = createTestBuffer(512, 512, true);
1778              HardwareBuffer hwBufferB = createTestBuffer(512, 512, true);
1779              HardwareBuffer hwBufferC = createTestBuffer(512, 512, true);) {
1780             // Fill buffer C with generated data
1781             nFillRgbaHwBuffer(hwBufferC);
1782 
1783             // Create the test bitmaps
1784             Bitmap bitmap1 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
1785             Bitmap bitmap2 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB));
1786             Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot);
1787             Bitmap bitmap4 = Bitmap.wrapHardwareBuffer(hwBufferB, ColorSpace.get(Named.SRGB));
1788             Bitmap bitmap5 = Bitmap.wrapHardwareBuffer(hwBufferC, ColorSpace.get(Named.SRGB));
1789 
1790             // Run the compare-a-thon
1791             assertTrue(bitmap1.sameAs(bitmap2));  // SAME UNDERLYING BUFFER
1792             assertTrue(bitmap2.sameAs(bitmap1));  // SAME UNDERLYING BUFFER
1793             assertFalse(bitmap1.sameAs(bitmap3)); // HW vs. NON-HW
1794             assertTrue(bitmap1.sameAs(bitmap4));  // DIFFERENT BUFFERS, SAME CONTENT
1795             assertFalse(bitmap1.sameAs(bitmap5)); // DIFFERENT BUFFERS, DIFFERENT CONTENT
1796         }
1797     }
1798 
1799     @Test(expected=IllegalStateException.class)
testHardwareGetPixel()1800     public void testHardwareGetPixel() {
1801         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1802         bitmap.getPixel(0, 0);
1803     }
1804 
1805     @Test(expected=IllegalStateException.class)
testHardwareGetPixels()1806     public void testHardwareGetPixels() {
1807         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1808         bitmap.getPixels(new int[5], 0, 5, 0, 0, 5, 1);
1809     }
1810 
1811     @Test
testGetConfigOnRecycled()1812     public void testGetConfigOnRecycled() {
1813         Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1814         bitmap1.recycle();
1815         assertEquals(Config.HARDWARE, bitmap1.getConfig());
1816         Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1817         bitmap2.recycle();
1818         assertEquals(Config.ARGB_8888, bitmap2.getConfig());
1819     }
1820 
1821     @Test(expected = IllegalStateException.class)
testHardwareSetWidth()1822     public void testHardwareSetWidth() {
1823         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1824         bitmap.setWidth(30);
1825     }
1826 
1827     @Test(expected = IllegalStateException.class)
testHardwareSetHeight()1828     public void testHardwareSetHeight() {
1829         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1830         bitmap.setHeight(30);
1831     }
1832 
1833     @Test(expected = IllegalStateException.class)
testHardwareSetConfig()1834     public void testHardwareSetConfig() {
1835         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1836         bitmap.setConfig(Config.ARGB_8888);
1837     }
1838 
1839     @Test(expected = IllegalStateException.class)
testHardwareReconfigure()1840     public void testHardwareReconfigure() {
1841         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1842         bitmap.reconfigure(30, 30, Config.ARGB_8888);
1843     }
1844 
1845     @Test(expected = IllegalStateException.class)
testHardwareSetPixels()1846     public void testHardwareSetPixels() {
1847         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1848         bitmap.setPixels(new int[10], 0, 1, 0, 0, 1, 1);
1849     }
1850 
1851     @Test(expected = IllegalStateException.class)
testHardwareSetPixel()1852     public void testHardwareSetPixel() {
1853         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1854         bitmap.setPixel(1, 1, 0);
1855     }
1856 
1857     @Test(expected = IllegalStateException.class)
testHardwareEraseColor()1858     public void testHardwareEraseColor() {
1859         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1860         bitmap.eraseColor(0);
1861     }
1862 
1863     @Test(expected = IllegalStateException.class)
testHardwareEraseColorLong()1864     public void testHardwareEraseColorLong() {
1865         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS);
1866         bitmap.eraseColor(Color.pack(0));
1867     }
1868 
1869     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsToBuffer()1870     public void testHardwareCopyPixelsToBuffer() {
1871         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
1872         ByteBuffer byteBuf = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight());
1873         bitmap.copyPixelsToBuffer(byteBuf);
1874     }
1875 
1876     @Test(expected = IllegalStateException.class)
testHardwareCopyPixelsFromBuffer()1877     public void testHardwareCopyPixelsFromBuffer() {
1878         IntBuffer intBuf1 = IntBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight());
1879         assertEquals(0, intBuf1.position());
1880         mBitmap.copyPixelsToBuffer(intBuf1);
1881         Bitmap hwBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS);
1882         hwBitmap.copyPixelsFromBuffer(intBuf1);
1883     }
1884 
1885     @Test
testUseMetadataAfterRecycle()1886     public void testUseMetadataAfterRecycle() {
1887         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
1888         bitmap.recycle();
1889         assertEquals(10, bitmap.getWidth());
1890         assertEquals(20, bitmap.getHeight());
1891         assertEquals(Config.RGB_565, bitmap.getConfig());
1892     }
1893 
1894     @Test
testCopyHWBitmapInStrictMode()1895     public void testCopyHWBitmapInStrictMode() {
1896         strictModeTest(()->{
1897             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1898             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
1899             hwBitmap.copy(Config.ARGB_8888, false);
1900         });
1901     }
1902 
1903     @Test
testCreateScaledFromHWInStrictMode()1904     public void testCreateScaledFromHWInStrictMode() {
1905         strictModeTest(()->{
1906             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1907             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
1908             Bitmap.createScaledBitmap(hwBitmap, 200, 200, false);
1909         });
1910     }
1911 
1912     @Test
testExtractAlphaFromHWInStrictMode()1913     public void testExtractAlphaFromHWInStrictMode() {
1914         strictModeTest(()->{
1915             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1916             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
1917             hwBitmap.extractAlpha();
1918         });
1919     }
1920 
1921     @Test
testCompressInStrictMode()1922     public void testCompressInStrictMode() {
1923         strictModeTest(()->{
1924             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1925             bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream());
1926         });
1927     }
1928 
1929     @Test
testParcelHWInStrictMode()1930     public void testParcelHWInStrictMode() {
1931         strictModeTest(()->{
1932             mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1933             Bitmap hwBitmap = mBitmap.copy(Config.HARDWARE, false);
1934             hwBitmap.writeToParcel(Parcel.obtain(), 0);
1935         });
1936     }
1937 
1938     @Test
testSameAsFirstHWInStrictMode()1939     public void testSameAsFirstHWInStrictMode() {
1940         strictModeTest(()->{
1941             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1942             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
1943             hwBitmap.sameAs(bitmap);
1944         });
1945     }
1946 
1947     @Test
testSameAsSecondHWInStrictMode()1948     public void testSameAsSecondHWInStrictMode() {
1949         strictModeTest(()->{
1950             Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
1951             Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false);
1952             bitmap.sameAs(hwBitmap);
1953         });
1954     }
1955 
1956     @Test
testNdkAccessAfterRecycle()1957     public void testNdkAccessAfterRecycle() {
1958         Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565);
1959         nValidateBitmapInfo(bitmap, 10, 20, true);
1960         bitmap.recycle();
1961         nValidateBitmapInfo(bitmap, 10, 20, true);
1962         nValidateNdkAccessAfterRecycle(bitmap);
1963     }
1964 
1965     @Test
bitmapIsMutable()1966     public void bitmapIsMutable() {
1967         Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
1968         assertTrue("CreateBitmap w/ params should be mutable", b.isMutable());
1969         assertTrue("CreateBitmap from bitmap should be mutable",
1970                 Bitmap.createBitmap(b).isMutable());
1971     }
1972 
runGcAndFinalizersSync()1973     private static void runGcAndFinalizersSync() {
1974         Runtime.getRuntime().gc();
1975         Runtime.getRuntime().runFinalization();
1976 
1977         final CountDownLatch fence = new CountDownLatch(1);
1978         new Object() {
1979             @Override
1980             protected void finalize() throws Throwable {
1981                 try {
1982                     fence.countDown();
1983                 } finally {
1984                     super.finalize();
1985                 }
1986             }
1987         };
1988         try {
1989             do {
1990                 Runtime.getRuntime().gc();
1991                 Runtime.getRuntime().runFinalization();
1992             } while (!fence.await(100, TimeUnit.MILLISECONDS));
1993         } catch (InterruptedException ex) {
1994             throw new RuntimeException(ex);
1995         }
1996     }
1997 
1998     private static File sProcSelfFd = new File("/proc/self/fd");
getFdCount()1999     private static int getFdCount() {
2000         return sProcSelfFd.listFiles().length;
2001     }
2002 
assertNotLeaking(int iteration, Debug.MemoryInfo start, Debug.MemoryInfo end)2003     private static void assertNotLeaking(int iteration,
2004             Debug.MemoryInfo start, Debug.MemoryInfo end) {
2005         Debug.getMemoryInfo(end);
2006         assertNotEquals(0, start.getTotalPss());
2007         assertNotEquals(0, end.getTotalPss());
2008         if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) {
2009             runGcAndFinalizersSync();
2010             Debug.getMemoryInfo(end);
2011             if (end.getTotalPss() - start.getTotalPss() > 4000 /* kB */) {
2012                 // Guarded by if so we don't continually generate garbage for the
2013                 // assertion string.
2014                 assertEquals("Memory leaked, iteration=" + iteration,
2015                         start.getTotalPss(), end.getTotalPss(),
2016                         4000 /* kb */);
2017             }
2018         }
2019     }
2020 
runNotLeakingTest(Runnable test)2021     private static void runNotLeakingTest(Runnable test) {
2022         Debug.MemoryInfo meminfoStart = new Debug.MemoryInfo();
2023         Debug.MemoryInfo meminfoEnd = new Debug.MemoryInfo();
2024         int fdCount = -1;
2025         for (int i = 0; i < 2000; i++) {
2026             if (i == 4) {
2027                 // Not really the "start" but by having done a couple
2028                 // we've fully initialized any state that may be required,
2029                 // so memory usage should be stable now
2030                 runGcAndFinalizersSync();
2031                 Debug.getMemoryInfo(meminfoStart);
2032                 fdCount = getFdCount();
2033             }
2034             if (i % 100 == 5) {
2035                 assertNotLeaking(i, meminfoStart, meminfoEnd);
2036                 final int curFdCount = getFdCount();
2037                 if (curFdCount - fdCount > 10) {
2038                     fail(String.format("FDs leaked. Expected=%d, current=%d, iteration=%d",
2039                             fdCount, curFdCount, i));
2040                 }
2041             }
2042             test.run();
2043         }
2044         assertNotLeaking(2000, meminfoStart, meminfoEnd);
2045         final int curFdCount = getFdCount();
2046         if (curFdCount - fdCount > 10) {
2047             fail(String.format("FDs leaked. Expected=%d, current=%d", fdCount, curFdCount));
2048         }
2049     }
2050 
2051     @Test
2052     @LargeTest
testHardwareBitmapNotLeaking()2053     public void testHardwareBitmapNotLeaking() {
2054         BitmapFactory.Options opts = new BitmapFactory.Options();
2055         opts.inPreferredConfig = Config.HARDWARE;
2056         opts.inScaled = false;
2057 
2058         runNotLeakingTest(() -> {
2059             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2060             assertNotNull(bitmap);
2061             // Make sure nothing messed with the bitmap
2062             assertEquals(128, bitmap.getWidth());
2063             assertEquals(128, bitmap.getHeight());
2064             assertEquals(Config.HARDWARE, bitmap.getConfig());
2065             bitmap.recycle();
2066         });
2067     }
2068 
2069     @Test
2070     @LargeTest
testWrappedHardwareBufferBitmapNotLeaking()2071     public void testWrappedHardwareBufferBitmapNotLeaking() {
2072         final ColorSpace colorSpace = ColorSpace.get(Named.SRGB);
2073         try (HardwareBuffer hwBuffer = createTestBuffer(1024, 512, false)) {
2074             runNotLeakingTest(() -> {
2075                 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, colorSpace);
2076                 assertNotNull(bitmap);
2077                 // Make sure nothing messed with the bitmap
2078                 assertEquals(1024, bitmap.getWidth());
2079                 assertEquals(512, bitmap.getHeight());
2080                 assertEquals(Config.HARDWARE, bitmap.getConfig());
2081                 bitmap.recycle();
2082             });
2083         }
2084     }
2085 
2086     @Test
2087     @LargeTest
testDrawingHardwareBitmapNotLeaking()2088     public void testDrawingHardwareBitmapNotLeaking() {
2089         BitmapFactory.Options opts = new BitmapFactory.Options();
2090         opts.inPreferredConfig = Config.HARDWARE;
2091         opts.inScaled = false;
2092         RenderTarget renderTarget = RenderTarget.create();
2093         renderTarget.setDefaultSize(128, 128);
2094         final Surface surface = renderTarget.getSurface();
2095 
2096         runNotLeakingTest(() -> {
2097             Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts);
2098             assertNotNull(bitmap);
2099             // Make sure nothing messed with the bitmap
2100             assertEquals(128, bitmap.getWidth());
2101             assertEquals(128, bitmap.getHeight());
2102             assertEquals(Config.HARDWARE, bitmap.getConfig());
2103             Canvas canvas = surface.lockHardwareCanvas();
2104             canvas.drawBitmap(bitmap, 0, 0, null);
2105             surface.unlockCanvasAndPost(canvas);
2106             bitmap.recycle();
2107         });
2108     }
2109 
2110     @Test
testWrapHardwareBufferHoldsReference()2111     public void testWrapHardwareBufferHoldsReference() {
2112         Bitmap bitmap;
2113         // Create hardware-buffer and wrap it in a Bitmap
2114         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) {
2115             // Fill buffer with colors (x, y, 42, 255)
2116             nFillRgbaHwBuffer(hwBuffer);
2117             bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2118         }
2119 
2120         // Buffer is closed at this point. Ensure bitmap still works by drawing it
2121         assertEquals(128, bitmap.getWidth());
2122         assertEquals(128, bitmap.getHeight());
2123         assertEquals(Config.HARDWARE, bitmap.getConfig());
2124 
2125         // Copy bitmap to target bitmap we can read from
2126         Bitmap dstBitmap = bitmap.copy(Config.ARGB_8888, false);
2127         bitmap.recycle();
2128 
2129         // Ensure that the bitmap has valid contents
2130         int pixel = dstBitmap.getPixel(0, 0);
2131         assertEquals(255 << 24 | 42, pixel);
2132         dstBitmap.recycle();
2133     }
2134 
2135     @Test
testWrapHardwareBufferPreservesColors()2136     public void testWrapHardwareBufferPreservesColors() {
2137         try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) {
2138             // Fill buffer with colors (x, y, 42, 255)
2139             nFillRgbaHwBuffer(hwBuffer);
2140 
2141             // Create HW bitmap from this buffer
2142             Bitmap srcBitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB));
2143             assertNotNull(srcBitmap);
2144 
2145             // Copy it to target non-HW bitmap
2146             Bitmap dstBitmap = srcBitmap.copy(Config.ARGB_8888, false);
2147             srcBitmap.recycle();
2148 
2149             // Ensure all colors are as expected (matches the nFillRgbaHwBuffer call used above).
2150             for (int y = 0; y < 128; ++y) {
2151                 for (int x = 0; x < 128; ++x) {
2152                     int pixel = dstBitmap.getPixel(x, y);
2153                     short a = 255;
2154                     short r = (short) (x % 255);
2155                     short g = (short) (y % 255);
2156                     short b = 42;
2157                     assertEquals(a << 24 | r << 16 | g << 8 | b, pixel);
2158                 }
2159             }
2160             dstBitmap.recycle();
2161         }
2162     }
2163 
strictModeTest(Runnable runnable)2164     private void strictModeTest(Runnable runnable) {
2165         StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
2166         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
2167                 .detectCustomSlowCalls().penaltyDeath().build());
2168         try {
2169             runnable.run();
2170             fail("Shouldn't reach it");
2171         } catch (RuntimeException expected){
2172             // expect to receive StrictModeViolation
2173         } finally {
2174             StrictMode.setThreadPolicy(originalPolicy);
2175         }
2176     }
2177 
nValidateBitmapInfo(Bitmap bitmap, int width, int height, boolean is565)2178     private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
2179             boolean is565);
nValidateNdkAccessAfterRecycle(Bitmap bitmap)2180     private static native void nValidateNdkAccessAfterRecycle(Bitmap bitmap);
2181 
nFillRgbaHwBuffer(HardwareBuffer hwBuffer)2182     private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
2183 
2184     private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
2185     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
nGetFormat(Bitmap bitmap)2186     private static native int nGetFormat(Bitmap bitmap);
2187 
createTestBuffer(int width, int height, boolean cpuAccess)2188     private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
2189         long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
2190         if (cpuAccess) {
2191             usage |= HardwareBuffer.USAGE_CPU_WRITE_RARELY;
2192         }
2193         // We can assume that RGBA_8888 format is supported for every platform.
2194         HardwareBuffer hwBuffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
2195                 1, usage);
2196         return hwBuffer;
2197     }
2198 
scaleFromDensity(int size, int sdensity, int tdensity)2199     private static int scaleFromDensity(int size, int sdensity, int tdensity) {
2200         if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) {
2201             return size;
2202         }
2203 
2204         // Scale by tdensity / sdensity, rounding up.
2205         return ((size * tdensity) + (sdensity >> 1)) / sdensity;
2206     }
2207 
createColors(int size)2208     private static int[] createColors(int size) {
2209         int[] colors = new int[size];
2210 
2211         for (int i = 0; i < size; i++) {
2212             colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i;
2213         }
2214 
2215         return colors;
2216     }
2217 
createHardwareBitmapOptions()2218     private static BitmapFactory.Options createHardwareBitmapOptions() {
2219         BitmapFactory.Options options = new BitmapFactory.Options();
2220         options.inPreferredConfig = Config.HARDWARE;
2221         return options;
2222     }
2223 }
2224