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