• 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 
17 package android.graphics.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertSame;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 import static org.junit.Assume.assumeTrue;
28 import static org.testng.Assert.assertThrows;
29 
30 import android.content.res.Resources;
31 import android.graphics.Bitmap;
32 import android.graphics.Bitmap.CompressFormat;
33 import android.graphics.Bitmap.Config;
34 import android.graphics.BitmapFactory;
35 import android.graphics.BitmapFactory.Options;
36 import android.graphics.Color;
37 import android.graphics.ImageDecoder;
38 import android.graphics.Rect;
39 import android.hardware.HardwareBuffer;
40 import android.media.MediaCodecInfo;
41 import android.media.MediaCodecList;
42 import android.media.MediaFormat;
43 import android.os.Parcel;
44 import android.os.ParcelFileDescriptor;
45 import android.platform.test.annotations.DisabledOnRavenwood;
46 import android.platform.test.annotations.RequiresDevice;
47 import android.system.ErrnoException;
48 import android.system.Os;
49 import android.util.DisplayMetrics;
50 import android.util.TypedValue;
51 
52 import androidx.test.InstrumentationRegistry;
53 import androidx.test.filters.LargeTest;
54 import androidx.test.filters.SmallTest;
55 
56 import com.android.compatibility.common.util.BitmapUtils;
57 import com.android.compatibility.common.util.CddTest;
58 
59 import junitparams.JUnitParamsRunner;
60 import junitparams.Parameters;
61 
62 import org.junit.Before;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 
66 import java.io.ByteArrayInputStream;
67 import java.io.ByteArrayOutputStream;
68 import java.io.File;
69 import java.io.FileDescriptor;
70 import java.io.FileOutputStream;
71 import java.io.IOException;
72 import java.io.InputStream;
73 import java.io.RandomAccessFile;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.concurrent.CountDownLatch;
77 
78 @SmallTest
79 @RunWith(JUnitParamsRunner.class)
80 public class BitmapFactoryTest {
81     // height and width of start.jpg
82     private static final int START_HEIGHT = 31;
83     private static final int START_WIDTH = 31;
84 
85     static class TestImage {
TestImage(int id, int width, int height)86         TestImage(int id, int width, int height) {
87             this.id = id;
88             this.width = width;
89             this.height = height;
90         }
91         public final int id;
92         public final int width;
93         public final int height;
94     }
95 
testImages()96     private Object[] testImages() {
97         ArrayList<Object> testImages = new ArrayList<>(Arrays.asList(new Object[] {
98                 new TestImage(R.drawable.baseline_jpeg, 1280, 960),
99                 new TestImage(R.drawable.png_test, 640, 480),
100                 new TestImage(R.drawable.gif_test, 320, 240),
101                 new TestImage(R.drawable.bmp_test, 320, 240),
102                 new TestImage(R.drawable.webp_test, 640, 480)
103         }));
104         if (ImageDecoder.isMimeTypeSupported("image/heif")) {
105             // HEIF support is optional when HEVC decoder is not supported.
106             testImages.add(new TestImage(R.raw.heifwriter_input, 1920, 1080));
107         }
108         if (ImageDecoder.isMimeTypeSupported("image/avif")) {
109             testImages.add(new TestImage(R.raw.avif_yuv_420_8bit, 120, 160));
110         }
111         return testImages.toArray(new Object[] {});
112     }
113 
114     private static final int[] RAW_COLORS = new int[] {
115         // raw data from R.drawable.premul_data
116         Color.argb(255, 0, 0, 0),
117         Color.argb(128, 255, 0, 0),
118         Color.argb(128, 25, 26, 27),
119         Color.argb(2, 255, 254, 253),
120     };
121 
122     private static final int[] DEPREMUL_COLORS = new int[] {
123         // data from R.drawable.premul_data, after premultiplied store + un-premultiplied load
124         Color.argb(255, 0, 0, 0),
125         Color.argb(128, 255, 0, 0),
126         Color.argb(128, 26, 26, 28),
127         Color.argb(2, 255, 255, 255),
128     };
129 
130     private Resources mRes;
131     // opt for non-null
132     private BitmapFactory.Options mOpt1;
133     // opt for null
134     private BitmapFactory.Options mOpt2;
135     private int mDefaultDensity;
136     private int mTargetDensity;
137 
138     @Before
setup()139     public void setup() {
140         mRes = InstrumentationRegistry.getTargetContext().getResources();
141         mDefaultDensity = DisplayMetrics.DENSITY_DEFAULT;
142         mTargetDensity = mRes.getDisplayMetrics().densityDpi;
143 
144         mOpt1 = new BitmapFactory.Options();
145         mOpt1.inScaled = false;
146         mOpt2 = new BitmapFactory.Options();
147         mOpt2.inScaled = false;
148         mOpt2.inJustDecodeBounds = true;
149     }
150 
151     @Test
testConstructor()152     public void testConstructor() {
153         new BitmapFactory();
154     }
155 
156     @Test
testDecodeResource1()157     public void testDecodeResource1() {
158         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.start,
159                 mOpt1);
160         assertNotNull(b);
161         // Test the bitmap size
162         assertEquals(START_HEIGHT, b.getHeight());
163         assertEquals(START_WIDTH, b.getWidth());
164         // Test if no bitmap
165         assertNull(BitmapFactory.decodeResource(mRes, R.drawable.start, mOpt2));
166     }
167 
168     @Test
testDecodeResource2()169     public void testDecodeResource2() {
170         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.start);
171         assertNotNull(b);
172         // Test the bitmap size
173         assertEquals(START_HEIGHT * mTargetDensity / mDefaultDensity, b.getHeight(), 1.1);
174         assertEquals(START_WIDTH * mTargetDensity / mDefaultDensity, b.getWidth(), 1.1);
175     }
176 
177     @Test
testDecodeResourceStream()178     public void testDecodeResourceStream() {
179         InputStream is = obtainInputStream();
180         Rect r = new Rect(1, 1, 1, 1);
181         TypedValue value = new TypedValue();
182         Bitmap b = BitmapFactory.decodeResourceStream(mRes, value, is, r, mOpt1);
183         assertNotNull(b);
184         // Test the bitmap size
185         assertEquals(START_HEIGHT, b.getHeight());
186         assertEquals(START_WIDTH, b.getWidth());
187     }
188 
189     @Test
testDecodeByteArray1()190     public void testDecodeByteArray1() {
191         byte[] array = obtainArray();
192         Bitmap b = BitmapFactory.decodeByteArray(array, 0, array.length, mOpt1);
193         assertNotNull(b);
194         // Test the bitmap size
195         assertEquals(START_HEIGHT, b.getHeight());
196         assertEquals(START_WIDTH, b.getWidth());
197         // Test if no bitmap
198         assertNull(BitmapFactory.decodeByteArray(array, 0, array.length, mOpt2));
199     }
200 
201     @Test
testDecodeByteArray2()202     public void testDecodeByteArray2() {
203         byte[] array = obtainArray();
204         Bitmap b = BitmapFactory.decodeByteArray(array, 0, array.length);
205         assertNotNull(b);
206         // Test the bitmap size
207         assertEquals(START_HEIGHT, b.getHeight());
208         assertEquals(START_WIDTH, b.getWidth());
209     }
210 
211     @Test
testDecodeStream1()212     public void testDecodeStream1() {
213         InputStream is = obtainInputStream();
214         Rect r = new Rect(1, 1, 1, 1);
215         Bitmap b = BitmapFactory.decodeStream(is, r, mOpt1);
216         assertNotNull(b);
217         // Test the bitmap size
218         assertEquals(START_HEIGHT, b.getHeight());
219         assertEquals(START_WIDTH, b.getWidth());
220         // Test if no bitmap
221         assertNull(BitmapFactory.decodeStream(is, r, mOpt2));
222     }
223 
224     @Test
testDecodeStream2()225     public void testDecodeStream2() {
226         InputStream is = obtainInputStream();
227         Bitmap b = BitmapFactory.decodeStream(is);
228         assertNotNull(b);
229         // Test the bitmap size
230         assertEquals(START_HEIGHT, b.getHeight());
231         assertEquals(START_WIDTH, b.getWidth());
232     }
233 
234     @Test
235     @Parameters(method = "testImages")
testDecodeStream3(TestImage testImage)236     public void testDecodeStream3(TestImage testImage) {
237         InputStream is = obtainInputStream(testImage.id);
238         Bitmap b = BitmapFactory.decodeStream(is);
239         assertNotNull(b);
240         // Test the bitmap size
241         assertEquals(testImage.width, b.getWidth());
242         assertEquals(testImage.height, b.getHeight());
243     }
244 
paramsForWebpDecodeEncode()245     private Object[] paramsForWebpDecodeEncode() {
246         return new Object[] {
247                 new Object[] {Config.ARGB_8888, 16},
248                 new Object[] {Config.RGB_565, 49}
249         };
250     }
251 
decodeOpaqueImage(int resId, BitmapFactory.Options options)252     private Bitmap decodeOpaqueImage(int resId, BitmapFactory.Options options) {
253         return decodeOpaqueImage(obtainInputStream(resId), options);
254     }
255 
decodeOpaqueImage(InputStream stream, BitmapFactory.Options options)256     private Bitmap decodeOpaqueImage(InputStream stream, BitmapFactory.Options options) {
257         Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
258         assertNotNull(bitmap);
259         assertFalse(bitmap.isPremultiplied());
260         assertFalse(bitmap.hasAlpha());
261         return bitmap;
262     }
263 
264     @Test
265     @Parameters(method = "paramsForWebpDecodeEncode")
testWebpStreamDecode(Config config, int tolerance)266     public void testWebpStreamDecode(Config config, int tolerance) {
267         BitmapFactory.Options options = new BitmapFactory.Options();
268         options.inPreferredConfig = config;
269 
270         // Decode the PNG & WebP test images. The WebP test image has been encoded from PNG test
271         // image and should have same similar (within some error-tolerance) Bitmap data.
272         Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
273         assertEquals(bPng.getConfig(), config);
274         Bitmap bWebp = decodeOpaqueImage(R.drawable.webp_test, options);
275         BitmapUtils.assertBitmapsMse(bPng, bWebp, tolerance, true, bPng.isPremultiplied());
276     }
277 
278     @Test
279     @Parameters(method = "paramsForWebpDecodeEncode")
testWebpStreamEncode(Config config, int tolerance)280     public void testWebpStreamEncode(Config config, int tolerance) {
281         BitmapFactory.Options options = new BitmapFactory.Options();
282         options.inPreferredConfig = config;
283 
284         Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
285         assertEquals(bPng.getConfig(), config);
286 
287         // Compress the PNG image to WebP format (Quality=90) and decode it back.
288         // This will test end-to-end WebP encoding and decoding.
289         ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
290         assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
291         InputStream iStreamWebp = new ByteArrayInputStream(oStreamWebp.toByteArray());
292         Bitmap bWebp2 = decodeOpaqueImage(iStreamWebp, options);
293         BitmapUtils.assertBitmapsMse(bPng, bWebp2, tolerance, true, bPng.isPremultiplied());
294     }
295 
296     @Test
testDecodeStream5()297     public void testDecodeStream5() {
298         final int tolerance = 72;
299         BitmapFactory.Options options = new BitmapFactory.Options();
300         options.inPreferredConfig = Config.ARGB_8888;
301 
302         // Decode the PNG & WebP (google_logo) images. The WebP image has
303         // been encoded from PNG image.
304         InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
305         Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
306         assertNotNull(bPng);
307         assertEquals(bPng.getConfig(), Config.ARGB_8888);
308         assertTrue(bPng.isPremultiplied());
309         assertTrue(bPng.hasAlpha());
310 
311         // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
312         InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
313         Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
314         assertNotNull(bWebP1);
315         assertEquals(bWebP1.getConfig(), Config.ARGB_8888);
316         assertTrue(bWebP1.isPremultiplied());
317         assertTrue(bWebP1.hasAlpha());
318         BitmapUtils.assertBitmapsMse(bPng, bWebP1, tolerance, true, bPng.isPremultiplied());
319 
320         // Compress the PNG image to WebP format (Quality=90) and decode it back.
321         // This will test end-to-end WebP encoding and decoding.
322         ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
323         assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
324         InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
325         Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
326         assertNotNull(bWebP2);
327         assertEquals(bWebP2.getConfig(), Config.ARGB_8888);
328         assertTrue(bWebP2.isPremultiplied());
329         assertTrue(bWebP2.hasAlpha());
330         BitmapUtils.assertBitmapsMse(bPng, bWebP2, tolerance, true, bPng.isPremultiplied());
331     }
332 
333     @Test
testDecodeFileDescriptor1()334     public void testDecodeFileDescriptor1() throws IOException {
335         ParcelFileDescriptor pfd = obtainParcelDescriptor(obtainPath());
336         FileDescriptor input = pfd.getFileDescriptor();
337         Rect r = new Rect(1, 1, 1, 1);
338         Bitmap b = BitmapFactory.decodeFileDescriptor(input, r, mOpt1);
339         assertNotNull(b);
340         // Test the bitmap size
341         assertEquals(START_HEIGHT, b.getHeight());
342         assertEquals(START_WIDTH, b.getWidth());
343         // Test if no bitmap
344         assertNull(BitmapFactory.decodeFileDescriptor(input, r, mOpt2));
345     }
346 
347     @Test
testDecodeFileDescriptor2()348     public void testDecodeFileDescriptor2() throws IOException {
349         ParcelFileDescriptor pfd = obtainParcelDescriptor(obtainPath());
350         FileDescriptor input = pfd.getFileDescriptor();
351         Bitmap b = BitmapFactory.decodeFileDescriptor(input);
352         assertNotNull(b);
353         // Test the bitmap size
354         assertEquals(START_HEIGHT, b.getHeight());
355         assertEquals(START_WIDTH, b.getWidth());
356     }
357 
358 
359     // TODO: Better parameterize this and split it up.
360     @Test
361     @Parameters(method = "testImages")
testDecodeFileDescriptor3(TestImage testImage)362     public void testDecodeFileDescriptor3(TestImage testImage) throws IOException {
363         // Arbitrary offsets to use. If the offset of the FD matches the offset of the image,
364         // decoding should succeed, but if they do not match, decoding should fail.
365         final long[] actual_offsets = new long[] { 0, 17 };
366         for (int j = 0; j < actual_offsets.length; ++j) {
367             // FIXME: The purgeable test should attempt to purge the memory
368             // to force a re-decode.
369             for (boolean purgeable : new boolean[] { false, true }) {
370                 BitmapFactory.Options opts = new BitmapFactory.Options();
371                 opts.inPurgeable = purgeable;
372                 opts.inInputShareable = purgeable;
373 
374                 long actualOffset = actual_offsets[j];
375                 String path = Utils.obtainPath(testImage.id, actualOffset);
376                 RandomAccessFile file = new RandomAccessFile(path, "r");
377                 FileDescriptor fd = file.getFD();
378                 assertTrue(fd.valid());
379 
380                 // Set the offset to ACTUAL_OFFSET
381                 file.seek(actualOffset);
382                 assertEquals(file.getFilePointer(), actualOffset);
383 
384                 // Now decode. This should be successful and leave the offset
385                 // unchanged.
386                 Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
387                 assertNotNull(b);
388                 assertEquals(file.getFilePointer(), actualOffset);
389 
390                 // Now use the other offset. It should fail to decode, and
391                 // the offset should remain unchanged.
392                 long otherOffset = actual_offsets[(j + 1) % actual_offsets.length];
393                 assertFalse(otherOffset == actualOffset);
394                 file.seek(otherOffset);
395                 assertEquals(file.getFilePointer(), otherOffset);
396 
397                 b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
398                 assertNull(b);
399                 assertEquals(file.getFilePointer(), otherOffset);
400             }
401         }
402     }
403 
404     @Test
testDecodeFile1()405     public void testDecodeFile1() throws IOException {
406         Bitmap b = BitmapFactory.decodeFile(obtainPath(), mOpt1);
407         assertNotNull(b);
408         // Test the bitmap size
409         assertEquals(START_HEIGHT, b.getHeight());
410         assertEquals(START_WIDTH, b.getWidth());
411         // Test if no bitmap
412         assertNull(BitmapFactory.decodeFile(obtainPath(), mOpt2));
413     }
414 
415     @Test
testDecodeFile2()416     public void testDecodeFile2() throws IOException {
417         Bitmap b = BitmapFactory.decodeFile(obtainPath());
418         assertNotNull(b);
419         // Test the bitmap size
420         assertEquals(START_HEIGHT, b.getHeight());
421         assertEquals(START_WIDTH, b.getWidth());
422     }
423 
424     @Test
testDecodeReuseBasic()425     public void testDecodeReuseBasic() {
426         BitmapFactory.Options options = new BitmapFactory.Options();
427         options.inMutable = true;
428         options.inSampleSize = 0; // treated as 1
429         options.inScaled = false;
430         Bitmap start = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
431         int originalSize = start.getByteCount();
432         assertEquals(originalSize, start.getAllocationByteCount());
433 
434         options.inBitmap = start;
435         options.inMutable = false; // will be overridden by non-null inBitmap
436         options.inSampleSize = -42; // treated as 1
437         Bitmap pass = BitmapFactory.decodeResource(mRes, R.drawable.pass, options);
438 
439         assertEquals(originalSize, pass.getByteCount());
440         assertEquals(originalSize, pass.getAllocationByteCount());
441         assertSame(start, pass);
442         assertTrue(pass.isMutable());
443     }
444 
445     @Test
testDecodeReuseAttempt()446     public void testDecodeReuseAttempt() {
447         // BitmapFactory "silently" ignores an immutable inBitmap. (It does print a log message.)
448         BitmapFactory.Options options = new BitmapFactory.Options();
449         options.inMutable = false;
450 
451         Bitmap start = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
452         assertFalse(start.isMutable());
453 
454         options.inBitmap = start;
455         Bitmap pass = BitmapFactory.decodeResource(mRes, R.drawable.pass, options);
456         assertNotNull(pass);
457         assertNotEquals(start, pass);
458     }
459 
460     @Test
testDecodeReuseRecycled()461     public void testDecodeReuseRecycled() {
462         BitmapFactory.Options options = new BitmapFactory.Options();
463         options.inMutable = true;
464 
465         Bitmap start = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
466         assertNotNull(start);
467         start.recycle();
468 
469         options.inBitmap = start;
470 
471         assertThrows(IllegalArgumentException.class, () -> {
472             BitmapFactory.decodeResource(mRes, R.drawable.pass, options);
473         });
474     }
475 
476     @Test
477     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testDecodeReuseHardware()478     public void testDecodeReuseHardware() {
479         BitmapFactory.Options options = new BitmapFactory.Options();
480         options.inPreferredConfig = Bitmap.Config.HARDWARE;
481 
482         Bitmap start = BitmapFactory.decodeResource(mRes, R.drawable.start, options);
483         assertNotNull(start);
484 
485         options.inBitmap = start;
486 
487         assertThrows(IllegalArgumentException.class, () -> {
488             BitmapFactory.decodeResource(mRes, R.drawable.pass, options);
489         });
490     }
491 
492     /**
493      * Create bitmap sized to load unscaled resources: start, pass, and alpha
494      */
createBitmapForReuse(int pixelCount)495     private Bitmap createBitmapForReuse(int pixelCount) {
496         Bitmap bitmap = Bitmap.createBitmap(pixelCount, 1, Config.ARGB_8888);
497         bitmap.eraseColor(Color.BLACK);
498         bitmap.setHasAlpha(false);
499         return bitmap;
500     }
501 
502     /**
503      * Decode resource with ResId into reuse bitmap without scaling, verifying expected hasAlpha
504      */
decodeResourceWithReuse(Bitmap reuse, int resId, boolean hasAlpha)505     private void decodeResourceWithReuse(Bitmap reuse, int resId, boolean hasAlpha) {
506         BitmapFactory.Options options = new BitmapFactory.Options();
507         options.inMutable = true;
508         options.inSampleSize = 1;
509         options.inScaled = false;
510         options.inBitmap = reuse;
511         Bitmap output = BitmapFactory.decodeResource(mRes, resId, options);
512         assertSame(reuse, output);
513         assertEquals(output.hasAlpha(), hasAlpha);
514     }
515 
516     @Test
testDecodeReuseHasAlpha()517     public void testDecodeReuseHasAlpha() {
518         final int bitmapSize = 31; // size in pixels of start, pass, and alpha resources
519         final int pixelCount = bitmapSize * bitmapSize;
520 
521         // Test reuse, hasAlpha false and true
522         Bitmap bitmap = createBitmapForReuse(pixelCount);
523         decodeResourceWithReuse(bitmap, R.drawable.start, false);
524         decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
525 
526         // Test pre-reconfigure, hasAlpha false and true
527         bitmap = createBitmapForReuse(pixelCount);
528         bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
529         bitmap.setHasAlpha(true);
530         decodeResourceWithReuse(bitmap, R.drawable.start, false);
531 
532         bitmap = createBitmapForReuse(pixelCount);
533         bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
534         decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
535     }
536 
537     @Test
538     @Parameters(method = "testImages")
testDecodeReuseFormats(TestImage testImage)539     public void testDecodeReuseFormats(TestImage testImage) {
540         // reuse should support all image formats
541         Bitmap reuseBuffer = Bitmap.createBitmap(1000000, 1, Bitmap.Config.ALPHA_8);
542 
543         BitmapFactory.Options options = new BitmapFactory.Options();
544         options.inBitmap = reuseBuffer;
545         options.inSampleSize = 4;
546         options.inScaled = false;
547         Bitmap decoded = BitmapFactory.decodeResource(mRes, testImage.id, options);
548         assertSame(reuseBuffer, decoded);
549     }
550 
551     @Test
testDecodeReuseFailure()552     public void testDecodeReuseFailure() {
553         BitmapFactory.Options options = new BitmapFactory.Options();
554         options.inMutable = true;
555         options.inScaled = false;
556         options.inSampleSize = 4;
557         Bitmap reduced = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
558 
559         options.inBitmap = reduced;
560         options.inSampleSize = 1;
561         try {
562             BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
563             fail("should throw exception due to lack of space");
564         } catch (IllegalArgumentException e) {
565         }
566     }
567 
568     @Test
testDecodeReuseScaling()569     public void testDecodeReuseScaling() {
570         BitmapFactory.Options options = new BitmapFactory.Options();
571         options.inMutable = true;
572         options.inScaled = false;
573         Bitmap original = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
574         int originalSize = original.getByteCount();
575         assertEquals(originalSize, original.getAllocationByteCount());
576 
577         options.inBitmap = original;
578         options.inSampleSize = 4;
579         Bitmap reduced = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
580 
581         assertSame(original, reduced);
582         assertEquals(originalSize, reduced.getAllocationByteCount());
583         assertEquals(originalSize, reduced.getByteCount() * 16);
584     }
585 
586     @Test
testDecodeReuseDoubleScaling()587     public void testDecodeReuseDoubleScaling() {
588         BitmapFactory.Options options = new BitmapFactory.Options();
589         options.inMutable = true;
590         options.inScaled = false;
591         options.inSampleSize = 1;
592         Bitmap original = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
593         int originalSize = original.getByteCount();
594 
595         // Verify that inSampleSize and density scaling both work with reuse concurrently
596         options.inBitmap = original;
597         options.inScaled = true;
598         options.inSampleSize = 2;
599         options.inDensity = 2;
600         options.inTargetDensity = 4;
601         Bitmap doubleScaled = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
602 
603         assertSame(original, doubleScaled);
604         assertEquals(4, doubleScaled.getDensity());
605         assertEquals(originalSize, doubleScaled.getByteCount());
606     }
607 
608     @Test
testDecodeReuseEquivalentScaling()609     public void testDecodeReuseEquivalentScaling() {
610         BitmapFactory.Options options = new BitmapFactory.Options();
611         options.inMutable = true;
612         options.inScaled = true;
613         options.inDensity = 4;
614         options.inTargetDensity = 2;
615         Bitmap densityReduced = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
616         assertEquals(2, densityReduced.getDensity());
617         options.inBitmap = densityReduced;
618         options.inDensity = 0;
619         options.inScaled = false;
620         options.inSampleSize = 2;
621         Bitmap scaleReduced = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
622         // verify that density isn't incorrectly carried over during bitmap reuse
623         assertFalse(densityReduced.getDensity() == 2);
624         assertFalse(densityReduced.getDensity() == 0);
625         assertSame(densityReduced, scaleReduced);
626     }
627 
628     @Test
testDecodePremultipliedDefault()629     public void testDecodePremultipliedDefault() {
630         Bitmap simplePremul = BitmapFactory.decodeResource(mRes, R.drawable.premul_data);
631         assertTrue(simplePremul.isPremultiplied());
632     }
633 
634     @Test
testDecodePremultipliedData()635     public void testDecodePremultipliedData() {
636         BitmapFactory.Options options = new BitmapFactory.Options();
637         options.inScaled = false;
638         Bitmap premul = BitmapFactory.decodeResource(mRes, R.drawable.premul_data, options);
639         options.inPremultiplied = false;
640         Bitmap unpremul = BitmapFactory.decodeResource(mRes, R.drawable.premul_data, options);
641         assertEquals(premul.getConfig(), Bitmap.Config.ARGB_8888);
642         assertEquals(unpremul.getConfig(), Bitmap.Config.ARGB_8888);
643         assertTrue(premul.getHeight() == 1 && unpremul.getHeight() == 1);
644         assertTrue(premul.getWidth() == unpremul.getWidth() &&
645                    DEPREMUL_COLORS.length == RAW_COLORS.length &&
646                    premul.getWidth() == DEPREMUL_COLORS.length);
647 
648         // verify pixel data - unpremul should have raw values, premul will have rounding errors
649         for (int i = 0; i < premul.getWidth(); i++) {
650             assertEquals(premul.getPixel(i, 0), DEPREMUL_COLORS[i]);
651             assertEquals(unpremul.getPixel(i, 0), RAW_COLORS[i]);
652         }
653     }
654 
655     @Test
testDecodeInPurgeableAllocationCount()656     public void testDecodeInPurgeableAllocationCount() {
657         BitmapFactory.Options options = new BitmapFactory.Options();
658         options.inSampleSize = 1;
659         options.inJustDecodeBounds = false;
660         options.inPurgeable = true;
661         options.inInputShareable = false;
662         byte[] array = obtainArray();
663         Bitmap purgeableBitmap = BitmapFactory.decodeByteArray(array, 0, array.length, options);
664         assertFalse(purgeableBitmap.getAllocationByteCount() == 0);
665     }
666 
667     private int mDefaultCreationDensity;
verifyScaled(Bitmap b)668     private void verifyScaled(Bitmap b) {
669         assertEquals(b.getWidth(), START_WIDTH * 2);
670         assertEquals(b.getDensity(), 2);
671     }
672 
verifyUnscaled(Bitmap b)673     private void verifyUnscaled(Bitmap b) {
674         assertEquals(b.getWidth(), START_WIDTH);
675         assertEquals(b.getDensity(), mDefaultCreationDensity);
676     }
677 
678     @Test
testDecodeScaling()679     public void testDecodeScaling() {
680         BitmapFactory.Options defaultOpt = new BitmapFactory.Options();
681 
682         BitmapFactory.Options unscaledOpt = new BitmapFactory.Options();
683         unscaledOpt.inScaled = false;
684 
685         BitmapFactory.Options scaledOpt = new BitmapFactory.Options();
686         scaledOpt.inScaled = true;
687         scaledOpt.inDensity = 1;
688         scaledOpt.inTargetDensity = 2;
689 
690         mDefaultCreationDensity = Bitmap.createBitmap(1, 1, Config.ARGB_8888).getDensity();
691 
692         byte[] bytes = obtainArray();
693 
694         verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length));
695         verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null));
696         verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, unscaledOpt));
697         verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, defaultOpt));
698 
699         verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream()));
700         verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, null));
701         verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, unscaledOpt));
702         verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, defaultOpt));
703 
704         // scaling should only occur if Options are passed with inScaled=true
705         verifyScaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, scaledOpt));
706         verifyScaled(BitmapFactory.decodeStream(obtainInputStream(), null, scaledOpt));
707     }
708 
709     @Test
testParcel()710     public void testParcel() {
711         BitmapFactory.Options opts = new BitmapFactory.Options();
712         opts.inScaled = false;
713         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.gif_test, opts);
714         assertNotNull(b);
715 
716         Parcel p = Parcel.obtain();
717         b.writeToParcel(p, 0);
718 
719         p.setDataPosition(0);
720         Bitmap b2 = Bitmap.CREATOR.createFromParcel(p);
721         assertTrue(BitmapUtils.compareBitmaps(b, b2));
722 
723         ByteArrayOutputStream baos = new ByteArrayOutputStream();
724         assertTrue(b2.compress(Bitmap.CompressFormat.JPEG, 50, baos));
725     }
726 
727     @Test
testConfigs()728     public void testConfigs() {
729         // The output Config of a BitmapFactory decode depends on the request from the
730         // client and the properties of the image to be decoded.
731         //
732         // Options.inPreferredConfig = Config.ARGB_8888
733         //     This is the default value of inPreferredConfig.  In this case, the image
734         //     will always be decoded to Config.ARGB_8888.
735         // Options.inPreferredConfig = Config.RGB_565
736         //     If the encoded image is opaque, we will decode to Config.RGB_565,
737         //     otherwise we will decode to whichever color type is the most natural match
738         //     for the encoded data.
739         // Options.inPreferredConfig = Config.ARGB_4444
740         //     This is deprecated and will always decode to Config.ARGB_8888.
741         // Options.inPreferredConfig = Config.ALPHA_8
742         //     If the encoded image is gray, we will decode to 8-bit grayscale values
743         //     and indicate that the output bitmap is Config.ALPHA_8.  This is somewhat
744         //     misleading because the image is really opaque and grayscale, but we are
745         //     labeling each pixel as if it is a translucency (alpha) value.  If the
746         //     encoded image is not gray, we will decode to whichever color type is the
747         //     most natural match for the encoded data.
748         // Options.inPreferredConfig = null
749         //     We will decode to whichever Config is the most natural match with the
750         //     encoded data.  This could be ALPHA_8 (gray) or ARGB_8888.
751         //
752         // This test ensures that images are decoded to the intended Config and that the
753         // decodes match regardless of the Config.
754         decodeConfigs(R.drawable.alpha, 31, 31, true, false);
755         decodeConfigs(R.drawable.baseline_jpeg, 1280, 960, false, false);
756         decodeConfigs(R.drawable.bmp_test, 320, 240, false, false);
757         decodeConfigs(R.drawable.scaled2, 6, 8, false, false);
758         decodeConfigs(R.drawable.grayscale_jpg, 128, 128, false, true);
759         decodeConfigs(R.drawable.grayscale_png, 128, 128, false, true);
760     }
761 
762     @Test(expected = IllegalArgumentException.class)
763     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testMutableHardwareInDecodeResource()764     public void testMutableHardwareInDecodeResource() {
765         Options options = new Options();
766         options.inMutable = true;
767         options.inPreferredConfig = Config.HARDWARE;
768         BitmapFactory.decodeResource(mRes, R.drawable.alpha, options);
769     }
770 
771     @Test(expected = IllegalArgumentException.class)
772     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testMutableHardwareInDecodeByteArray()773     public void testMutableHardwareInDecodeByteArray() {
774         Options options = new Options();
775         options.inMutable = true;
776         options.inPreferredConfig = Config.HARDWARE;
777         BitmapFactory.decodeByteArray(new byte[100], 1, 20, options);
778     }
779 
780     @Test(expected = IllegalArgumentException.class)
781     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testMutableHardwareInDecodeFile()782     public void testMutableHardwareInDecodeFile() {
783         Options options = new Options();
784         options.inMutable = true;
785         options.inPreferredConfig = Config.HARDWARE;
786         BitmapFactory.decodeFile("barely/care.jpg", options);
787     }
788 
789     @Test(expected = IllegalArgumentException.class)
790     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testMutableHardwareInDecodeFileDescriptor()791     public void testMutableHardwareInDecodeFileDescriptor() {
792         Options options = new Options();
793         options.inMutable = true;
794         options.inPreferredConfig = Config.HARDWARE;
795         BitmapFactory.decodeFileDescriptor(null, new Rect(), options);
796     }
797 
798     @Test(expected = IllegalArgumentException.class)
799     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testMutableHardwareInDecodeResourceStream()800     public void testMutableHardwareInDecodeResourceStream() {
801         Options options = new Options();
802         options.inMutable = true;
803         options.inPreferredConfig = Config.HARDWARE;
804         TypedValue value = new TypedValue();
805         BitmapFactory.decodeResourceStream(mRes, value,
806                 new ByteArrayInputStream(new byte[20]), new Rect(), options);
807     }
808 
809     @Test
810     @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
testDecodeHardwareBitmap()811     public void testDecodeHardwareBitmap() {
812         BitmapFactory.Options options = new BitmapFactory.Options();
813         options.inPreferredConfig = Bitmap.Config.HARDWARE;
814         Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, options);
815         assertNotNull(hardwareBitmap);
816         // Test that checks that correct bitmap was obtained is in uirendering/HardwareBitmapTests
817         assertEquals(Config.HARDWARE, hardwareBitmap.getConfig());
818     }
819 
820     @Test
testJpegInfiniteLoop()821     public void testJpegInfiniteLoop() {
822         BitmapFactory.Options options = new BitmapFactory.Options();
823         options.inSampleSize = 19;
824         Bitmap bm = BitmapFactory.decodeResource(mRes, R.raw.b78329453, options);
825         assertNotNull(bm);
826     }
827 
828     private static final class DNG {
829         public final int resId;
830         public final int width;
831         public final int height;
832 
DNG(int resId, int width, int height)833         DNG(int resId, int width, int height) {
834             this.resId    = resId;
835             this.width    = width;
836             this.height   = height;
837         }
838     }
839 
840     @Test
841     @CddTest(requirements = {"5.1.5/C-0-6"})
842     @Parameters(method = "parametersForTestDng")
843     @LargeTest
testDng(DNG dng)844     public void testDng(DNG dng) {
845         byte[] bytes = ImageDecoderTest.getAsByteArray(dng.resId);
846         // No scaling
847         Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, mOpt1);
848         assertNotNull(bm);
849         assertEquals(dng.width, bm.getWidth());
850         assertEquals(dng.height, bm.getHeight());
851     }
852 
parametersForTestDng()853     private Object[] parametersForTestDng() {
854         return new Object[]{
855                 new DNG(R.raw.sample_1mp, 600, 338),
856                 new DNG(R.raw.sample_arw, 1616, 1080),
857                 new DNG(R.raw.sample_cr2, 2304, 1536),
858                 new DNG(R.raw.sample_nef, 4608, 3072),
859                 new DNG(R.raw.sample_nrw, 4000, 3000),
860                 new DNG(R.raw.sample_orf, 3200, 2400),
861                 new DNG(R.raw.sample_pef, 4928, 3264),
862                 new DNG(R.raw.sample_raf, 2048, 1536),
863                 new DNG(R.raw.sample_rw2, 1920, 1440),
864                 new DNG(R.raw.sample_srw, 5472, 3648),
865         };
866     }
867 
868     @Test
869     @DisabledOnRavenwood(bug = 397498134)
testDecodePngFromPipe()870     public void testDecodePngFromPipe() {
871         // This test verifies that we can send a PNG over a pipe and
872         // successfully decode it. This behavior worked in N, so this
873         // verifies that do not break it for backwards compatibility.
874         // This was already not supported for the other Bitmap.CompressFormats
875         // (JPEG and WEBP), so we do not test those.
876         Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
877         source.eraseColor(Color.RED);
878         try {
879             Bitmap result = sendOverPipe(source, CompressFormat.PNG);
880             assertTrue(source.sameAs(result));
881         } catch (Exception e) {
882             fail(e.toString());
883         }
884     }
885 
sendOverPipe(Bitmap source, CompressFormat format)886     private Bitmap sendOverPipe(Bitmap source, CompressFormat format)
887             throws IOException, ErrnoException, InterruptedException {
888         FileDescriptor[] pipeFds = Os.pipe();
889         final FileDescriptor readFd = pipeFds[0];
890         final FileDescriptor writeFd = pipeFds[1];
891         final Throwable[] compressErrors = new Throwable[1];
892         final CountDownLatch writeFinished = new CountDownLatch(1);
893         final CountDownLatch readFinished = new CountDownLatch(1);
894         final Bitmap[] decodedResult = new Bitmap[1];
895         Thread writeThread = new Thread() {
896             @Override
897             public void run() {
898                 try {
899                     FileOutputStream output = new FileOutputStream(writeFd);
900                     source.compress(format, 100, output);
901                     output.close();
902                 } catch (Throwable t) {
903                     compressErrors[0] = t;
904                     // Try closing the FD to unblock the test thread
905                     try {
906                         Os.close(writeFd);
907                     } catch (Throwable ignore) {}
908                 } finally {
909                     writeFinished.countDown();
910                 }
911             }
912         };
913         Thread readThread = new Thread() {
914             @Override
915             public void run() {
916                 decodedResult[0] = BitmapFactory.decodeFileDescriptor(readFd);
917             }
918         };
919         writeThread.start();
920         readThread.start();
921         writeThread.join(1000);
922         readThread.join(1000);
923         assertFalse(writeThread.isAlive());
924         if (compressErrors[0] != null) {
925             fail(compressErrors[0].toString());
926         }
927         if (readThread.isAlive()) {
928             // Test failure, try to clean up
929             Os.close(writeFd);
930             readThread.join(500);
931             fail("Read timed out");
932         }
933         assertValidFd("readFd", readFd);
934         assertValidFd("writeFd", writeFd);
935         Os.close(readFd);
936         Os.close(writeFd);
937         return decodedResult[0];
938     }
939 
assertValidFd(String name, FileDescriptor fd)940     private static void assertValidFd(String name, FileDescriptor fd) {
941         try {
942             assertTrue(fd.valid());
943             // Hacky check to test that the underlying FD is still valid without using
944             // the private fcntlVoid to do F_GETFD
945             Os.close(Os.dup(fd));
946         } catch (ErrnoException ex) {
947             fail(name + " is invalid: " + ex.getMessage());
948         }
949     }
950 
decodeConfigs(int id, int width, int height, boolean hasAlpha, boolean isGray)951     private void decodeConfigs(int id, int width, int height, boolean hasAlpha, boolean isGray) {
952         Options opts = new BitmapFactory.Options();
953         opts.inScaled = false;
954         assertEquals(Config.ARGB_8888, opts.inPreferredConfig);
955         Bitmap reference = BitmapFactory.decodeResource(mRes, id, opts);
956         assertNotNull(reference);
957         assertEquals(width, reference.getWidth());
958         assertEquals(height, reference.getHeight());
959         assertEquals(Config.ARGB_8888, reference.getConfig());
960 
961         opts.inPreferredConfig = Config.ARGB_4444;
962         Bitmap argb4444 = BitmapFactory.decodeResource(mRes, id, opts);
963         assertNotNull(argb4444);
964         assertEquals(width, argb4444.getWidth());
965         assertEquals(height, argb4444.getHeight());
966         // ARGB_4444 is deprecated and we should decode to ARGB_8888.
967         assertEquals(Config.ARGB_8888, argb4444.getConfig());
968         assertTrue(BitmapUtils.compareBitmaps(reference, argb4444));
969 
970         opts.inPreferredConfig = Config.RGB_565;
971         Bitmap rgb565 = BitmapFactory.decodeResource(mRes, id, opts);
972         assertNotNull(rgb565);
973         assertEquals(width, rgb565.getWidth());
974         assertEquals(height, rgb565.getHeight());
975         if (!hasAlpha) {
976             assertEquals(Config.RGB_565, rgb565.getConfig());
977             // Convert the RGB_565 bitmap to ARGB_8888 and test that it is similar to
978             // the reference.  We lose information when decoding to 565, so there must
979             // be some tolerance.  The tolerance is intentionally loose to allow us some
980             // flexibility regarding if we dither and how we color convert.
981             BitmapUtils.assertBitmapsMse(reference, rgb565.copy(Config.ARGB_8888, false), 30, true,
982                     true);
983         }
984 
985         opts.inPreferredConfig = Config.ALPHA_8;
986         Bitmap alpha8 = BitmapFactory.decodeResource(mRes, id, opts);
987         assertNotNull(alpha8);
988         assertEquals(width, reference.getWidth());
989         assertEquals(height, reference.getHeight());
990         if (isGray) {
991             assertEquals(Config.ALPHA_8, alpha8.getConfig());
992             // Convert the ALPHA_8 bitmap to ARGB_8888 and test that it is identical to
993             // the reference.  We must do this manually because we are abusing ALPHA_8
994             // in order to represent grayscale.
995             assertTrue(BitmapUtils.compareBitmaps(reference, grayToARGB(alpha8)));
996             assertNull(alpha8.getColorSpace());
997         }
998 
999         // Setting inPreferredConfig to nullptr will cause the default Config to be
1000         // selected, which in this case is ARGB_8888.
1001         opts.inPreferredConfig = null;
1002         Bitmap defaultBitmap = BitmapFactory.decodeResource(mRes, id, opts);
1003         assertNotNull(defaultBitmap);
1004         assertEquals(width, defaultBitmap.getWidth());
1005         assertEquals(height, defaultBitmap.getHeight());
1006         assertEquals(Config.ARGB_8888, defaultBitmap.getConfig());
1007         assertTrue(BitmapUtils.compareBitmaps(reference, defaultBitmap));
1008     }
1009 
grayToARGB(Bitmap gray)1010     private static Bitmap grayToARGB(Bitmap gray) {
1011         Bitmap argb = Bitmap.createBitmap(gray.getWidth(), gray.getHeight(), Config.ARGB_8888);
1012         for (int y = 0; y < argb.getHeight(); y++) {
1013             for (int x = 0; x < argb.getWidth(); x++) {
1014                 int grayByte = Color.alpha(gray.getPixel(x, y));
1015                 argb.setPixel(x, y, Color.rgb(grayByte, grayByte, grayByte));
1016             }
1017         }
1018         return argb;
1019     }
1020 
1021     @Test
1022     @RequiresDevice
testDecode10BitHEIF10BitBitmap()1023     public void testDecode10BitHEIF10BitBitmap() {
1024         assumeTrue("HEIF is not supported on this device, skip this test.",
1025                 ImageDecoder.isMimeTypeSupported("image/heif"));
1026         assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
1027 
1028         Config expectedConfig = Config.RGBA_1010102;
1029 
1030         // Even if the device advertises that 10 bits profile is supported, the output
1031         // format might not be CPU readable, but the video can still be displayed. When the
1032         // hevc decoder doesn't support YUVP010 format, and inPreferredConfig is RGBA_1010102,
1033         // then the color type of output falls back to RGBA_8888 automatically.
1034         if (!hasHEVCDecoderSupportsYUVP010()) {
1035             expectedConfig = Config.ARGB_8888;
1036         }
1037 
1038         BitmapFactory.Options opt = new BitmapFactory.Options();
1039         opt.inPreferredConfig = Config.RGBA_1010102;
1040         Bitmap bm = BitmapFactory.decodeStream(obtainInputStream(R.raw.heifimage_10bit), null, opt);
1041         assertNotNull(bm);
1042         assertEquals(4096, bm.getWidth());
1043         assertEquals(3072, bm.getHeight());
1044         assertEquals(expectedConfig, bm.getConfig());
1045     }
1046 
1047     @Test
1048     @CddTest(requirements = {"5.1.5/C-0-7"})
1049     @RequiresDevice
testDecode10BitAVIFTo10BitBitmap()1050     public void testDecode10BitAVIFTo10BitBitmap() {
1051         assumeTrue("AVIF is not supported on this device, skip this test.",
1052                 ImageDecoder.isMimeTypeSupported("image/avif"));
1053 
1054         BitmapFactory.Options opt = new BitmapFactory.Options();
1055         opt.inPreferredConfig = Config.RGBA_1010102;
1056         Bitmap bm = BitmapFactory.decodeStream(
1057                 obtainInputStream(R.raw.avif_yuv_420_10bit), null, opt);
1058         assertNotNull(bm);
1059         assertEquals(120, bm.getWidth());
1060         assertEquals(160, bm.getHeight());
1061         assertEquals(Config.RGBA_1010102, bm.getConfig());
1062     }
1063 
1064     @Test
1065     @RequiresDevice
testDecode10BitHEIFTo8BitBitmap()1066     public void testDecode10BitHEIFTo8BitBitmap() {
1067         assumeTrue("HEIF is not supported on this device, skip this test.",
1068                 ImageDecoder.isMimeTypeSupported("image/heif"));
1069         assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
1070 
1071         // When device does not support P010 color type of output is RGBA_8888 when decoding 10-bit
1072         // heif, and this behavior is tested in testDecode10BitHEIF10BitBitmap. So skipping this
1073         // test when P010 is not supported.
1074         assumeTrue("No HEVC decoder that supports YUVP010, skip the test.",
1075                 hasHEVCDecoderSupportsYUVP010());
1076 
1077         BitmapFactory.Options opt = new BitmapFactory.Options();
1078         opt.inPreferredConfig = Config.ARGB_8888;
1079         Bitmap bm1 =
1080             BitmapFactory.decodeStream(obtainInputStream(R.raw.heifimage_10bit), null, opt);
1081         Bitmap bm2 = BitmapFactory.decodeStream(obtainInputStream(R.raw.heifimage_10bit));
1082         assertNotNull(bm1);
1083         assertEquals(4096, bm1.getWidth());
1084         assertEquals(3072, bm1.getHeight());
1085         assertEquals(Config.RGBA_1010102, bm1.getConfig());
1086         assertNotNull(bm2);
1087         assertEquals(4096, bm2.getWidth());
1088         assertEquals(3072, bm2.getHeight());
1089         assertEquals(Config.RGBA_1010102, bm2.getConfig());
1090     }
1091 
1092     @Test
1093     @CddTest(requirements = {"5.1.5/C-0-7"})
1094     @RequiresDevice
testDecode10BitAVIFTo8BitBitmap()1095     public void testDecode10BitAVIFTo8BitBitmap() {
1096         assumeTrue("AVIF is not supported on this device, skip this test.",
1097                 ImageDecoder.isMimeTypeSupported("image/avif"));
1098 
1099         BitmapFactory.Options opt = new BitmapFactory.Options();
1100         opt.inPreferredConfig = Config.ARGB_8888;
1101         Bitmap bm1 =
1102             BitmapFactory.decodeStream(obtainInputStream(R.raw.avif_yuv_420_10bit), null, opt);
1103         Bitmap bm2 = BitmapFactory.decodeStream(obtainInputStream(R.raw.avif_yuv_420_10bit));
1104         assertNotNull(bm1);
1105         assertEquals(120, bm1.getWidth());
1106         assertEquals(160, bm1.getHeight());
1107         assertEquals(Config.RGBA_1010102, bm1.getConfig());
1108         assertNotNull(bm2);
1109         assertEquals(120, bm2.getWidth());
1110         assertEquals(160, bm2.getHeight());
1111         assertEquals(Config.RGBA_1010102, bm2.getConfig());
1112     }
1113 
1114     @Test
1115     @RequiresDevice
testDecode8BitHEIFTo10BitBitmap()1116     public void testDecode8BitHEIFTo10BitBitmap() {
1117         if (!ImageDecoder.isMimeTypeSupported("image/heif")) {
1118             return;
1119         }
1120         BitmapFactory.Options opt = new BitmapFactory.Options();
1121         opt.inPreferredConfig = Config.RGBA_1010102;
1122         Bitmap bm1 =
1123             BitmapFactory.decodeStream(obtainInputStream(R.raw.heifwriter_input), null, opt);
1124         Bitmap bm2 = BitmapFactory.decodeStream(obtainInputStream(R.raw.heifwriter_input));
1125         assertNotNull(bm1);
1126         assertEquals(1920, bm1.getWidth());
1127         assertEquals(1080, bm1.getHeight());
1128         assertEquals(Config.ARGB_8888, bm1.getConfig());
1129         assertNotNull(bm2);
1130         assertEquals(1920, bm2.getWidth());
1131         assertEquals(1080, bm2.getHeight());
1132         assertEquals(Config.ARGB_8888, bm2.getConfig());
1133     }
1134 
1135     @Test
1136     @CddTest(requirements = {"5.1.5/C-0-7"})
1137     @RequiresDevice
testDecode8BitAVIFTo10BitBitmap()1138     public void testDecode8BitAVIFTo10BitBitmap() {
1139         assumeTrue("AVIF is not supported on this device, skip this test.",
1140                 ImageDecoder.isMimeTypeSupported("image/avif"));
1141 
1142         BitmapFactory.Options opt = new BitmapFactory.Options();
1143         opt.inPreferredConfig = Config.RGBA_1010102;
1144         Bitmap bm1 =
1145             BitmapFactory.decodeStream(obtainInputStream(R.raw.avif_yuv_420_8bit), null, opt);
1146         Bitmap bm2 = BitmapFactory.decodeStream(obtainInputStream(R.raw.avif_yuv_420_8bit));
1147         assertNotNull(bm1);
1148         assertEquals(120, bm1.getWidth());
1149         assertEquals(160, bm1.getHeight());
1150         assertEquals(Config.ARGB_8888, bm1.getConfig());
1151         assertNotNull(bm2);
1152         assertEquals(120, bm2.getWidth());
1153         assertEquals(160, bm2.getHeight());
1154         assertEquals(Config.ARGB_8888, bm2.getConfig());
1155     }
1156 
1157     @Test
testAssertionFromColorSpace()1158     public void testAssertionFromColorSpace() {
1159         BitmapFactory.Options opt = new BitmapFactory.Options();
1160         Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.b198155681, opt);
1161         assertNotNull(b);
1162         assertNull(opt.outColorSpace);
1163     }
1164 
obtainArray()1165     private byte[] obtainArray() {
1166         ByteArrayOutputStream stm = new ByteArrayOutputStream();
1167         Options opt = new BitmapFactory.Options();
1168         opt.inScaled = false;
1169         Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, opt);
1170         bitmap.compress(Bitmap.CompressFormat.JPEG, 0, stm);
1171         return(stm.toByteArray());
1172     }
1173 
obtainInputStream()1174     private InputStream obtainInputStream() {
1175         return mRes.openRawResource(R.drawable.start);
1176     }
1177 
obtainInputStream(int resId)1178     private InputStream obtainInputStream(int resId) {
1179         return mRes.openRawResource(resId);
1180     }
1181 
obtainParcelDescriptor(String path)1182     private static ParcelFileDescriptor obtainParcelDescriptor(String path) throws IOException {
1183         File file = new File(path);
1184         return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
1185     }
1186 
obtainPath()1187     private String obtainPath() throws IOException {
1188         return Utils.obtainPath(R.drawable.start, 0);
1189     }
1190 
has10BitHEVCDecoder()1191     private static boolean has10BitHEVCDecoder() {
1192         MediaFormat format = new MediaFormat();
1193         format.setString(MediaFormat.KEY_MIME, "video/hevc");
1194         format.setInteger(
1195             MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10);
1196         format.setInteger(
1197             MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel5);
1198 
1199         MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
1200         if (mcl.findDecoderForFormat(format) == null) {
1201             return false;
1202         }
1203         return true;
1204     }
1205 
hasHEVCDecoderSupportsYUVP010()1206     private static boolean hasHEVCDecoderSupportsYUVP010() {
1207         MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
1208         for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) {
1209             if (mediaCodecInfo.isEncoder()) {
1210                 continue;
1211             }
1212             for (String mediaType : mediaCodecInfo.getSupportedTypes()) {
1213                 if (mediaType.equalsIgnoreCase("video/hevc")) {
1214                     MediaCodecInfo.CodecCapabilities codecCapabilities =
1215                             mediaCodecInfo.getCapabilitiesForType(mediaType);
1216                     for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) {
1217                         if (codecCapabilities.colorFormats[i]
1218                                 == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) {
1219                             return true;
1220                         }
1221                     }
1222                 }
1223             }
1224         }
1225         return false;
1226     }
1227 }
1228