• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.system.OsConstants.SEEK_SET;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.fail;
25 import static org.junit.Assume.assumeTrue;
26 
27 import android.content.ContentResolver;
28 import android.content.res.AssetManager;
29 import android.content.res.Resources;
30 import android.graphics.Bitmap;
31 import android.graphics.BitmapFactory;
32 import android.graphics.Color;
33 import android.graphics.ColorSpace;
34 import android.graphics.ColorSpace.Named;
35 import android.graphics.ImageDecoder;
36 import android.graphics.Rect;
37 import android.graphics.drawable.cts.AnimatedImageDrawableTest;
38 import android.net.Uri;
39 import android.os.ParcelFileDescriptor;
40 import android.media.MediaFormat;
41 import android.system.ErrnoException;
42 import android.system.Os;
43 import android.util.DisplayMetrics;
44 
45 import androidx.test.InstrumentationRegistry;
46 import androidx.test.filters.RequiresDevice;
47 
48 import com.android.compatibility.common.util.CddTest;
49 import com.android.compatibility.common.util.MediaUtils;
50 
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.io.File;
55 import java.io.FileDescriptor;
56 import java.io.FileNotFoundException;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.io.InputStream;
60 
61 import junitparams.JUnitParamsRunner;
62 import junitparams.Parameters;
63 
64 @RunWith(JUnitParamsRunner.class)
65 public class AImageDecoderTest {
66     static {
67         System.loadLibrary("ctsgraphics_jni");
68     }
69 
getAssetManager()70     private static AssetManager getAssetManager() {
71         return InstrumentationRegistry.getTargetContext().getAssets();
72     }
73 
getResources()74     private static Resources getResources() {
75         return InstrumentationRegistry.getTargetContext().getResources();
76     }
77 
getContentResolver()78     private static ContentResolver getContentResolver() {
79         return InstrumentationRegistry.getTargetContext().getContentResolver();
80     }
81 
82     // These match the formats in the NDK.
83     // ANDROID_BITMAP_FORMAT_NONE is used by nTestDecode to signal using the default.
84     private static final int ANDROID_BITMAP_FORMAT_NONE = 0;
85     private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
86     private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
87     private static final int ANDROID_BITMAP_FORMAT_A_8 = 8;
88     private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9;
89     private static final int ANDROID_BITMAP_FORMAT_RGBA_1010102 = 10;
90 
91     @Test
testEmptyCreate()92     public void testEmptyCreate() {
93         nTestEmptyCreate();
94     }
95 
getAssetRecords()96     private static Object[] getAssetRecords() {
97         return ImageDecoderTest.getAssetRecords();
98     }
99 
getRecords()100     private static Object[] getRecords() {
101         return ImageDecoderTest.getRecords();
102     }
103 
104     // For testing all of the assets as premul and unpremul.
getAssetRecordsUnpremul()105     private static Object[] getAssetRecordsUnpremul() {
106         return Utils.crossProduct(getAssetRecords(), new Object[] { true, false });
107     }
108 
getRecordsUnpremul()109     private static Object[] getRecordsUnpremul() {
110         return Utils.crossProduct(getRecords(), new Object[] { true, false });
111     }
112 
113     // For testing all of the assets at different sample sizes.
getAssetRecordsSample()114     private static Object[] getAssetRecordsSample() {
115         return Utils.crossProduct(getAssetRecords(), new Object[] { 2, 3, 4, 8, 16 });
116     }
117 
getRecordsSample()118     private static Object[] getRecordsSample() {
119         return Utils.crossProduct(getRecords(), new Object[] { 2, 3, 4, 8, 16 });
120     }
121 
getBitMapFormatsUnpremul()122     private static Object[] getBitMapFormatsUnpremul() {
123         return Utils.crossProduct(
124             new Object[] {
125                 ANDROID_BITMAP_FORMAT_NONE,
126                 ANDROID_BITMAP_FORMAT_RGBA_1010102,
127                 ANDROID_BITMAP_FORMAT_RGB_565,
128                 ANDROID_BITMAP_FORMAT_RGBA_F16
129             },
130             new Object[] {
131                 true,
132                 false
133             });
134     }
135 
136     @Test
137     @Parameters(method = "getAssetRecords")
testNullDecoder(ImageDecoderTest.AssetRecord record)138     public void testNullDecoder(ImageDecoderTest.AssetRecord record) {
139         nTestNullDecoder(getAssetManager(), record.name);
140     }
141 
nativeDataSpace(ColorSpace cs)142     private static int nativeDataSpace(ColorSpace cs) {
143         if (cs == null) {
144             return DataSpace.ADATASPACE_UNKNOWN;
145         }
146         return DataSpace.fromColorSpace(cs);
147     }
148 
149     @Test
150     @Parameters(method = "getAssetRecords")
testCreateBuffer(ImageDecoderTest.AssetRecord record)151     public void testCreateBuffer(ImageDecoderTest.AssetRecord record) {
152         // Note: This uses an asset for simplicity, but in native it gets a
153         // buffer.
154         long asset = nOpenAsset(getAssetManager(), record.name);
155         long aimagedecoder = nCreateFromAssetBuffer(asset);
156 
157         nTestInfo(aimagedecoder, record.width, record.height, "image/png",
158                 record.isF16, nativeDataSpace(record.getColorSpace()));
159         nCloseAsset(asset);
160     }
161 
162     @Test
163     @Parameters(method = "getAssetRecords")
testCreateFd(ImageDecoderTest.AssetRecord record)164     public void testCreateFd(ImageDecoderTest.AssetRecord record) {
165         // Note: This uses an asset for simplicity, but in native it gets a
166         // file descriptor.
167         long asset = nOpenAsset(getAssetManager(), record.name);
168         long aimagedecoder = nCreateFromAssetFd(asset);
169 
170         nTestInfo(aimagedecoder, record.width, record.height, "image/png",
171                 record.isF16, nativeDataSpace(record.getColorSpace()));
172         nCloseAsset(asset);
173     }
174 
175     @Test
176     @Parameters(method = "getAssetRecords")
testCreateAsset(ImageDecoderTest.AssetRecord record)177     public void testCreateAsset(ImageDecoderTest.AssetRecord record) {
178         long asset = nOpenAsset(getAssetManager(), record.name);
179         long aimagedecoder = nCreateFromAsset(asset);
180 
181         nTestInfo(aimagedecoder, record.width, record.height, "image/png",
182                 record.isF16, nativeDataSpace(record.getColorSpace()));
183         nCloseAsset(asset);
184     }
185 
open(int resId, int offset)186     private static ParcelFileDescriptor open(int resId, int offset) throws FileNotFoundException {
187         File file = Utils.obtainFile(resId, offset);
188         assertNotNull(file);
189 
190         ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
191                 ParcelFileDescriptor.MODE_READ_ONLY);
192         assertNotNull(pfd);
193         return pfd;
194     }
195 
open(int resId)196     private static ParcelFileDescriptor open(int resId) throws FileNotFoundException {
197         return open(resId, 0);
198     }
199 
200     @Test
201     @Parameters(method = "getRecords")
testCreateFdResources(ImageDecoderTest.Record record)202     public void testCreateFdResources(ImageDecoderTest.Record record) throws IOException {
203         try (ParcelFileDescriptor pfd = open(record.resId)) {
204             long aimagedecoder = nCreateFromFd(pfd.getFd());
205 
206             nTestInfo(aimagedecoder, record.width, record.height, record.mimeType,
207                     false /*isF16*/, nativeDataSpace(record.colorSpace));
208         } catch (FileNotFoundException e) {
209             fail("Could not open " + Utils.getAsResourceUri(record.resId));
210         }
211     }
212 
213     @Test
214     @Parameters(method = "getRecords")
testCreateFdOffset(ImageDecoderTest.Record record)215     public void testCreateFdOffset(ImageDecoderTest.Record record) throws IOException {
216         // Use an arbitrary offset. This ensures that we rewind to the correct offset.
217         final int offset = 15;
218         try (ParcelFileDescriptor pfd = open(record.resId, offset)) {
219             FileDescriptor fd = pfd.getFileDescriptor();
220             Os.lseek(fd, offset, SEEK_SET);
221             long aimagedecoder = nCreateFromFd(pfd.getFd());
222 
223             nTestInfo(aimagedecoder, record.width, record.height, record.mimeType,
224                     false /*isF16*/, nativeDataSpace(record.colorSpace));
225         } catch (FileNotFoundException e) {
226             fail("Could not open " + Utils.getAsResourceUri(record.resId));
227         } catch (ErrnoException err) {
228             fail("Failed to seek " + Utils.getAsResourceUri(record.resId));
229         }
230     }
231 
232     @Test
testCreateIncomplete()233     public void testCreateIncomplete() {
234         String file = "green-srgb.png";
235         // This truncates the file before the IDAT.
236         nTestCreateIncomplete(getAssetManager(), file, 823);
237     }
238 
239     @Test
240     @Parameters({"shaders/tri.frag", "test_video.mp4"})
testUnsupportedFormat(String file)241     public void testUnsupportedFormat(String file) {
242         nTestCreateUnsupported(getAssetManager(), file);
243     }
244 
245     @Test
246     @Parameters(method = "getAssetRecords")
testSetFormat(ImageDecoderTest.AssetRecord record)247     public void testSetFormat(ImageDecoderTest.AssetRecord record) {
248         long asset = nOpenAsset(getAssetManager(), record.name);
249         long aimagedecoder = nCreateFromAsset(asset);
250 
251         nTestSetFormat(aimagedecoder, record.isF16, record.isGray);
252         nCloseAsset(asset);
253     }
254 
255     @Test
256     @Parameters(method = "getRecords")
testSetFormatResources(ImageDecoderTest.Record record)257     public void testSetFormatResources(ImageDecoderTest.Record record) throws IOException {
258         try (ParcelFileDescriptor pfd = open(record.resId)) {
259             long aimagedecoder = nCreateFromFd(pfd.getFd());
260 
261             nTestSetFormat(aimagedecoder, false /* isF16 */, record.isGray);
262         } catch (FileNotFoundException e) {
263             fail("Could not open " + Utils.getAsResourceUri(record.resId));
264         }
265     }
266 
267     @Test
268     @Parameters(method = "getAssetRecords")
testSetUnpremul(ImageDecoderTest.AssetRecord record)269     public void testSetUnpremul(ImageDecoderTest.AssetRecord record) {
270         long asset = nOpenAsset(getAssetManager(), record.name);
271         long aimagedecoder = nCreateFromAsset(asset);
272 
273         nTestSetUnpremul(aimagedecoder, record.hasAlpha);
274         nCloseAsset(asset);
275     }
276 
277     @Test
278     @Parameters(method = "getRecords")
testSetUnpremulResources(ImageDecoderTest.Record record)279     public void testSetUnpremulResources(ImageDecoderTest.Record record) throws IOException {
280         try (ParcelFileDescriptor pfd = open(record.resId)) {
281             long aimagedecoder = nCreateFromFd(pfd.getFd());
282 
283             nTestSetUnpremul(aimagedecoder, record.hasAlpha);
284         } catch (FileNotFoundException e) {
285             fail("Could not open " + Utils.getAsResourceUri(record.resId));
286         }
287     }
288 
289     @Test
290     @Parameters(method = "getAssetRecords")
testGetMinimumStride(ImageDecoderTest.AssetRecord record)291     public void testGetMinimumStride(ImageDecoderTest.AssetRecord record) {
292         long asset = nOpenAsset(getAssetManager(), record.name);
293         long aimagedecoder = nCreateFromAsset(asset);
294 
295         nTestGetMinimumStride(aimagedecoder, record.isF16, record.isGray);
296     }
297 
298     @Test
299     @Parameters(method = "getRecords")
testGetMinimumStrideResources(ImageDecoderTest.Record record)300     public void testGetMinimumStrideResources(ImageDecoderTest.Record record) throws IOException {
301         try (ParcelFileDescriptor pfd = open(record.resId)) {
302             long aimagedecoder = nCreateFromFd(pfd.getFd());
303 
304             nTestGetMinimumStride(aimagedecoder, false /* isF16 */, record.isGray);
305         } catch (FileNotFoundException e) {
306             fail("Could not open " + Utils.getAsResourceUri(record.resId));
307         }
308     }
309 
decode(ImageDecoder.Source src, boolean unpremul)310     private static Bitmap decode(ImageDecoder.Source src, boolean unpremul) {
311         try {
312             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
313                 // So we can compare pixels to the native decode.
314                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
315                 decoder.setUnpremultipliedRequired(unpremul);
316             });
317         } catch (IOException e) {
318             fail("Failed to decode in Java with " + e);
319             return null;
320         }
321     }
322 
decode(int resId, boolean unpremul)323     private static Bitmap decode(int resId, boolean unpremul) {
324         // This test relies on ImageDecoder *not* scaling to account for density.
325         // Temporarily change the DisplayMetrics to prevent that scaling.
326         Resources res = getResources();
327         final int originalDensity = res.getDisplayMetrics().densityDpi;
328         try {
329             res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_DEFAULT;
330             ImageDecoder.Source src = ImageDecoder.createSource(res, resId);
331             return decode(src, unpremul);
332         } finally {
333             res.getDisplayMetrics().densityDpi = originalDensity;
334         }
335     }
336 
337     @Test
338     @RequiresDevice
339     @Parameters(method = "getBitMapFormatsUnpremul")
testDecode10BitHeif(int bitmapFormat, boolean unpremul)340     public void testDecode10BitHeif(int bitmapFormat, boolean unpremul) throws IOException {
341         if (!MediaUtils.hasHardwareCodec(MediaFormat.MIMETYPE_VIDEO_HEVC, false)) {
342             return;
343         }
344         final int resId = R.raw.heifimage_10bit;
345         Bitmap bm = null;
346         switch (bitmapFormat) {
347             case ANDROID_BITMAP_FORMAT_NONE:
348             case ANDROID_BITMAP_FORMAT_RGBA_1010102:
349                 bm = decode(resId, unpremul);
350                 break;
351             case ANDROID_BITMAP_FORMAT_RGB_565:
352                 bm = decode(resId, Bitmap.Config.RGB_565);
353                 break;
354             case ANDROID_BITMAP_FORMAT_RGBA_F16:
355                 BitmapFactory.Options opt = new BitmapFactory.Options();
356                 opt.inPreferredConfig = Bitmap.Config.RGBA_F16;
357                 opt.inPremultiplied = !unpremul;
358                 bm = BitmapFactory.decodeStream(getResources().openRawResource(resId), null, opt);
359                 break;
360             default:
361                 fail("Unsupported Bitmap format: " + bitmapFormat);
362         }
363         assertNotNull(bm);
364 
365         try (ParcelFileDescriptor pfd = open(resId)) {
366             long aimagedecoder = nCreateFromFd(pfd.getFd());
367             nTestDecode(aimagedecoder, bitmapFormat, unpremul, bm);
368         } catch (FileNotFoundException e) {
369             fail("Could not open " + Utils.getAsResourceUri(resId));
370         }
371     }
372 
373     @Test
374     @RequiresDevice
375     @CddTest(requirements = {"5.1.5/C-0-7"})
376     @Parameters(method = "getBitMapFormatsUnpremul")
testDecode10BitAvif(int bitmapFormat, boolean unpremul)377     public void testDecode10BitAvif(int bitmapFormat, boolean unpremul) throws IOException {
378         assumeTrue("AVIF is not supported on this device, skip this test.",
379                 ImageDecoder.isMimeTypeSupported("image/avif"));
380 
381         final int resId = R.raw.avif_yuv_420_10bit;
382         Bitmap bm = null;
383         switch (bitmapFormat) {
384             case ANDROID_BITMAP_FORMAT_NONE:
385             case ANDROID_BITMAP_FORMAT_RGBA_1010102:
386                 bm = decode(resId, unpremul);
387                 break;
388             case ANDROID_BITMAP_FORMAT_RGB_565:
389                 bm = decode(resId, Bitmap.Config.RGB_565);
390                 break;
391             case ANDROID_BITMAP_FORMAT_RGBA_F16:
392                 BitmapFactory.Options opt = new BitmapFactory.Options();
393                 opt.inPreferredConfig = Bitmap.Config.RGBA_F16;
394                 opt.inPremultiplied = !unpremul;
395                 bm = BitmapFactory.decodeStream(getResources().openRawResource(resId), null, opt);
396                 break;
397             default:
398                 fail("Unsupported Bitmap format: " + bitmapFormat);
399         }
400         assertNotNull(bm);
401 
402         try (ParcelFileDescriptor pfd = open(resId)) {
403             long aimagedecoder = nCreateFromFd(pfd.getFd());
404             nTestDecode(aimagedecoder, bitmapFormat, unpremul, bm);
405         } catch (FileNotFoundException e) {
406             fail("Could not open " + Utils.getAsResourceUri(resId));
407         }
408     }
409 
410     @Test
411     @Parameters(method = "getAssetRecordsUnpremul")
testDecode(ImageDecoderTest.AssetRecord record, boolean unpremul)412     public void testDecode(ImageDecoderTest.AssetRecord record, boolean unpremul) {
413         AssetManager assets = getAssetManager();
414         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
415         Bitmap bm = decode(src, unpremul);
416 
417         long asset = nOpenAsset(assets, record.name);
418         long aimagedecoder = nCreateFromAsset(asset);
419 
420         nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_NONE, unpremul, bm);
421         nCloseAsset(asset);
422     }
423 
424     @Test
425     @Parameters(method = "getRecordsUnpremul")
testDecodeResources(ImageDecoderTest.Record record, boolean unpremul)426     public void testDecodeResources(ImageDecoderTest.Record record, boolean unpremul)
427             throws IOException {
428         Bitmap bm = decode(record.resId, unpremul);
429         try (ParcelFileDescriptor pfd = open(record.resId)) {
430             long aimagedecoder = nCreateFromFd(pfd.getFd());
431 
432             nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_NONE, unpremul, bm);
433         } catch (FileNotFoundException e) {
434             fail("Could not open " + Utils.getAsResourceUri(record.resId));
435         }
436     }
437 
decode(ImageDecoder.Source src, Bitmap.Config config)438     private static Bitmap decode(ImageDecoder.Source src, Bitmap.Config config) {
439         try {
440             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
441                 // So we can compare pixels to the native decode.
442                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
443                 switch (config) {
444                     case RGB_565:
445                         decoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
446                         break;
447                     case ALPHA_8:
448                         decoder.setDecodeAsAlphaMaskEnabled(true);
449                         break;
450                     default:
451                         fail("Unexpected Config " + config);
452                         break;
453                 }
454             });
455         } catch (IOException e) {
456             fail("Failed to decode in Java with " + e);
457             return null;
458         }
459     }
460 
461     private static Bitmap decode(int resId, Bitmap.Config config) {
462         // This test relies on ImageDecoder *not* scaling to account for density.
463         // Temporarily change the DisplayMetrics to prevent that scaling.
464         Resources res = getResources();
465         final int originalDensity = res.getDisplayMetrics().densityDpi;
466         try {
467             res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_DEFAULT;
468             ImageDecoder.Source src = ImageDecoder.createSource(res, resId);
469             return decode(src, config);
470         } finally {
471             res.getDisplayMetrics().densityDpi = originalDensity;
472         }
473     }
474 
475     @Test
476     @Parameters(method = "getAssetRecords")
477     public void testDecode565(ImageDecoderTest.AssetRecord record) {
478         AssetManager assets = getAssetManager();
479         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
480         Bitmap bm = decode(src, Bitmap.Config.RGB_565);
481 
482         if (bm.getConfig() != Bitmap.Config.RGB_565) {
483             bm = null;
484         }
485 
486         long asset = nOpenAsset(assets, record.name);
487         long aimagedecoder = nCreateFromAsset(asset);
488 
489         nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_RGB_565, false, bm);
490         nCloseAsset(asset);
491     }
492 
493     @Test
494     @Parameters(method = "getRecords")
495     public void testDecode565Resources(ImageDecoderTest.Record record)
496             throws IOException {
497         Bitmap bm = decode(record.resId, Bitmap.Config.RGB_565);
498 
499         if (bm.getConfig() != Bitmap.Config.RGB_565) {
500             bm = null;
501         }
502 
503         try (ParcelFileDescriptor pfd = open(record.resId)) {
504             long aimagedecoder = nCreateFromFd(pfd.getFd());
505 
506             nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_RGB_565, false, bm);
507         } catch (FileNotFoundException e) {
508             fail("Could not open " + Utils.getAsResourceUri(record.resId));
509         }
510     }
511 
512     @Test
513     @Parameters("grayscale-linearSrgb.png")
514     public void testDecodeA8(String name) {
515         AssetManager assets = getAssetManager();
516         ImageDecoder.Source src = ImageDecoder.createSource(assets, name);
517         Bitmap bm = decode(src, Bitmap.Config.ALPHA_8);
518 
519         assertNotNull(bm);
520         assertNull(bm.getColorSpace());
521         assertEquals(Bitmap.Config.ALPHA_8, bm.getConfig());
522 
523         long asset = nOpenAsset(assets, name);
524         long aimagedecoder = nCreateFromAsset(asset);
525 
526         nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_A_8, false, bm);
527         nCloseAsset(asset);
528     }
529 
530     @Test
531     public void testDecodeA8Resources()
532             throws IOException {
533         final int resId = R.drawable.grayscale_jpg;
534         Bitmap bm = decode(resId, Bitmap.Config.ALPHA_8);
535 
536         assertNotNull(bm);
537         assertNull(bm.getColorSpace());
538         assertEquals(Bitmap.Config.ALPHA_8, bm.getConfig());
539 
540         try (ParcelFileDescriptor pfd = open(resId)) {
541             long aimagedecoder = nCreateFromFd(pfd.getFd());
542 
543             nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_A_8, false, bm);
544         } catch (FileNotFoundException e) {
545             fail("Could not open " + Utils.getAsResourceUri(resId));
546         }
547     }
548 
549     @Test
550     @Parameters(method = "getAssetRecordsUnpremul")
551     public void testDecodeF16(ImageDecoderTest.AssetRecord record, boolean unpremul) {
552         AssetManager assets = getAssetManager();
553 
554         // ImageDecoder doesn't allow forcing a decode to F16, so use BitmapFactory.
555         BitmapFactory.Options options = new BitmapFactory.Options();
556         options.inPreferredConfig = Bitmap.Config.RGBA_F16;
557         options.inPremultiplied = !unpremul;
558 
559         InputStream is = null;
560         try {
561             is = assets.open(record.name);
562         } catch (IOException e) {
563             fail("Failed to open " + record.name + " with " + e);
564         }
565         assertNotNull(is);
566 
567         Bitmap bm = BitmapFactory.decodeStream(is, null, options);
568         assertNotNull(bm);
569 
570         long asset = nOpenAsset(assets, record.name);
571         long aimagedecoder = nCreateFromAsset(asset);
572 
573         nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_RGBA_F16, unpremul, bm);
574         nCloseAsset(asset);
575     }
576 
577     @Test
578     @Parameters(method = "getRecordsUnpremul")
579     public void testDecodeF16Resources(ImageDecoderTest.Record record, boolean unpremul)
580             throws IOException {
581         // ImageDecoder doesn't allow forcing a decode to F16, so use BitmapFactory.
582         BitmapFactory.Options options = new BitmapFactory.Options();
583         options.inPreferredConfig = Bitmap.Config.RGBA_F16;
584         options.inPremultiplied = !unpremul;
585         options.inScaled = false;
586 
587         Bitmap bm = BitmapFactory.decodeResource(getResources(),
588                 record.resId, options);
589         assertNotNull(bm);
590 
591         try (ParcelFileDescriptor pfd = open(record.resId)) {
592             long aimagedecoder = nCreateFromFd(pfd.getFd());
593 
594             nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_RGBA_F16, unpremul, bm);
595         } catch (FileNotFoundException e) {
596             fail("Could not open " + Utils.getAsResourceUri(record.resId));
597         }
598     }
599 
600     @Test
601     @Parameters(method = "getAssetRecords")
602     public void testDecodeStride(ImageDecoderTest.AssetRecord record) {
603         long asset = nOpenAsset(getAssetManager(), record.name);
604         long aimagedecoder = nCreateFromAsset(asset);
605         nTestDecodeStride(aimagedecoder);
606         nCloseAsset(asset);
607     }
608 
609     @Test
610     @Parameters(method = "getRecords")
611     public void testDecodeStrideResources(ImageDecoderTest.Record record)
612             throws IOException {
613         try (ParcelFileDescriptor pfd = open(record.resId)) {
614             long aimagedecoder = nCreateFromFd(pfd.getFd());
615 
616             nTestDecodeStride(aimagedecoder);
617         } catch (FileNotFoundException e) {
618             fail("Could not open " + Utils.getAsResourceUri(record.resId));
619         }
620     }
621 
622     @Test
623     @Parameters(method = "getAssetRecords")
624     public void testSetTargetSize(ImageDecoderTest.AssetRecord record) {
625         long asset = nOpenAsset(getAssetManager(), record.name);
626         long aimagedecoder = nCreateFromAsset(asset);
627         nTestSetTargetSize(aimagedecoder);
628         nCloseAsset(asset);
629     }
630 
631     @Test
632     @Parameters(method = "getRecords")
633     public void testSetTargetSizeResources(ImageDecoderTest.Record record)
634             throws IOException {
635         try (ParcelFileDescriptor pfd = open(record.resId)) {
636             long aimagedecoder = nCreateFromFd(pfd.getFd());
637 
638             nTestSetTargetSize(aimagedecoder);
639         } catch (FileNotFoundException e) {
640             fail("Could not open " + Utils.getAsResourceUri(record.resId));
641         }
642     }
643 
644     private Bitmap decodeSampled(String name, ImageDecoder.Source src, int sampleSize) {
645         try {
646             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
647                 // So we can compare pixels to the native decode.
648                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
649                 decoder.setTargetSampleSize(sampleSize);
650             });
651         } catch (IOException e) {
652             fail("Failed to decode " + name + " in Java (sampleSize: "
653                     + sampleSize + ") with " + e);
654             return null;
655         }
656     }
657 
658     @Test
659     @Parameters(method = "getAssetRecordsSample")
660     public void testDecodeSampled(ImageDecoderTest.AssetRecord record, int sampleSize) {
661         AssetManager assets = getAssetManager();
662         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
663         Bitmap bm = decodeSampled(record.name, src, sampleSize);
664 
665         long asset = nOpenAsset(assets, record.name);
666         long aimagedecoder = nCreateFromAsset(asset);
667 
668         nTestDecodeScaled(aimagedecoder, bm);
669         nCloseAsset(asset);
670     }
671 
672     @Test
673     @Parameters(method = "getRecordsSample")
674     public void testDecodeResourceSampled(ImageDecoderTest.Record record, int sampleSize)
675             throws IOException {
676         ImageDecoder.Source src = ImageDecoder.createSource(getResources(),
677                 record.resId);
678         String name = Utils.getAsResourceUri(record.resId).toString();
679         Bitmap bm = decodeSampled(name, src, sampleSize);
680         assertNotNull(bm);
681 
682         try (ParcelFileDescriptor pfd = open(record.resId)) {
683             long aimagedecoder = nCreateFromFd(pfd.getFd());
684 
685             nTestDecodeScaled(aimagedecoder, bm);
686         } catch (FileNotFoundException e) {
687             fail("Could not open " + name + ": " + e);
688         }
689     }
690 
691     @Test
692     @Parameters(method = "getRecordsSample")
693     public void testComputeSampledSize(ImageDecoderTest.Record record, int sampleSize)
694             throws IOException {
695         if (record.mimeType.equals("image/x-adobe-dng")) {
696             // SkRawCodec does not support sampling.
697             return;
698         }
699         testComputeSampledSizeInternal(record.resId, sampleSize);
700     }
701 
702     private void testComputeSampledSizeInternal(int resId, int sampleSize)
703             throws IOException {
704         ImageDecoder.Source src = ImageDecoder.createSource(getResources(), resId);
705         String name = Utils.getAsResourceUri(resId).toString();
706         Bitmap bm = decodeSampled(name, src, sampleSize);
707         assertNotNull(bm);
708 
709         try (ParcelFileDescriptor pfd = open(resId)) {
710             long aimagedecoder = nCreateFromFd(pfd.getFd());
711 
712             nTestComputeSampledSize(aimagedecoder, bm, sampleSize);
713         } catch (FileNotFoundException e) {
714             fail("Could not open " + name + ": " + e);
715         }
716     }
717 
718     private static Object[] getExifsSample() {
719         return Utils.crossProduct(getExifImages(), new Object[] { 2, 3, 4, 8, 16 });
720     }
721 
722     @Test
723     @Parameters(method = "getExifsSample")
724     public void testComputeSampledSizeExif(int resId, int sampleSize)
725             throws IOException {
726         testComputeSampledSizeInternal(resId, sampleSize);
727     }
728 
729     private Bitmap decodeScaled(String name, ImageDecoder.Source src) {
730         try {
731             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
732                 // So we can compare pixels to the native decode.
733                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
734 
735                 // Scale to an arbitrary width and height.
736                 decoder.setTargetSize(300, 300);
737             });
738         } catch (IOException e) {
739             fail("Failed to decode " + name + " in Java (size: "
740                     + "300 x 300) with " + e);
741             return null;
742         }
743     }
744 
745     @Test
746     @Parameters(method = "getAssetRecords")
747     public void testDecodeScaled(ImageDecoderTest.AssetRecord record) {
748         AssetManager assets = getAssetManager();
749         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
750         Bitmap bm = decodeScaled(record.name, src);
751 
752         long asset = nOpenAsset(assets, record.name);
753         long aimagedecoder = nCreateFromAsset(asset);
754 
755         nTestDecodeScaled(aimagedecoder, bm);
756         nCloseAsset(asset);
757     }
758 
759     @Test
760     @Parameters(method = "getRecords")
761     public void testDecodeResourceScaled(ImageDecoderTest.Record record)
762             throws IOException {
763         ImageDecoder.Source src = ImageDecoder.createSource(getResources(),
764                 record.resId);
765         String name = Utils.getAsResourceUri(record.resId).toString();
766         Bitmap bm = decodeScaled(name, src);
767         assertNotNull(bm);
768 
769         try (ParcelFileDescriptor pfd = open(record.resId)) {
770             long aimagedecoder = nCreateFromFd(pfd.getFd());
771 
772             nTestDecodeScaled(aimagedecoder, bm);
773         } catch (FileNotFoundException e) {
774             fail("Could not open " + name + ": " + e);
775         }
776     }
777 
778     private Bitmap decodeScaleUp(String name, ImageDecoder.Source src) {
779         try {
780             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
781                 // So we can compare pixels to the native decode.
782                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
783 
784                 decoder.setTargetSize(info.getSize().getWidth() * 2,
785                         info.getSize().getHeight() * 2);
786             });
787         } catch (IOException e) {
788             fail("Failed to decode " + name + " in Java (scaled up) with " + e);
789             return null;
790         }
791     }
792 
793     @Test
794     @Parameters(method = "getAssetRecords")
795     public void testDecodeScaleUp(ImageDecoderTest.AssetRecord record) {
796         AssetManager assets = getAssetManager();
797         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
798         Bitmap bm = decodeScaleUp(record.name, src);
799 
800         long asset = nOpenAsset(assets, record.name);
801         long aimagedecoder = nCreateFromAsset(asset);
802 
803         nTestDecodeScaled(aimagedecoder, bm);
804         nCloseAsset(asset);
805     }
806 
807     @Test
808     @Parameters(method = "getRecords")
809     public void testDecodeResourceScaleUp(ImageDecoderTest.Record record)
810             throws IOException {
811         ImageDecoder.Source src = ImageDecoder.createSource(getResources(),
812                 record.resId);
813         String name = Utils.getAsResourceUri(record.resId).toString();
814         Bitmap bm = decodeScaleUp(name, src);
815         assertNotNull(bm);
816 
817         try (ParcelFileDescriptor pfd = open(record.resId)) {
818             long aimagedecoder = nCreateFromFd(pfd.getFd());
819 
820             nTestDecodeScaled(aimagedecoder, bm);
821         } catch (FileNotFoundException e) {
822             fail("Could not open " + name + ": " + e);
823         }
824     }
825 
826     @Test
827     @Parameters(method = "getAssetRecords")
828     public void testSetCrop(ImageDecoderTest.AssetRecord record) {
829         nTestSetCrop(getAssetManager(), record.name);
830     }
831 
832     private static class Cropper implements ImageDecoder.OnHeaderDecodedListener {
833         Cropper(boolean scale) {
834             mScale = scale;
835         }
836 
837         public boolean withScale() {
838             return mScale;
839         }
840 
841         public int getWidth() {
842             return mWidth;
843         }
844 
845         public int getHeight() {
846             return mHeight;
847         }
848 
849         public Rect getCropRect() {
850             return mCropRect;
851         }
852 
853         @Override
854         public void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
855                 ImageDecoder.Source source) {
856             mWidth = info.getSize().getWidth();
857             mHeight = info.getSize().getHeight();
858             if (mScale) {
859                 mWidth = 40;
860                 mHeight = 40;
861                 decoder.setTargetSize(mWidth, mHeight);
862             }
863 
864             mCropRect = new Rect(mWidth / 2, mHeight / 2, mWidth, mHeight);
865             decoder.setCrop(mCropRect);
866 
867             // So we can compare pixels to the native decode.
868             decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
869         }
870 
871         private final boolean mScale;
872         private Rect mCropRect;
873         private int mWidth;
874         private int mHeight;
875     }
876 
877     private static Bitmap decodeCropped(String name, Cropper cropper, ImageDecoder.Source src) {
878         try {
879             return ImageDecoder.decodeBitmap(src, cropper);
880         } catch (IOException e) {
881             fail("Failed to decode " + name + " in Java with "
882                     + (cropper.withScale() ? "scale and " : "a ") + "crop ("
883                     + cropper.getCropRect() + "): " + e);
884             return null;
885         }
886     }
887 
888     private static Bitmap decodeCropped(String name, Cropper cropper, int resId) {
889         // This test relies on ImageDecoder *not* scaling to account for density.
890         // Temporarily change the DisplayMetrics to prevent that scaling.
891         Resources res = getResources();
892         final int originalDensity = res.getDisplayMetrics().densityDpi;
893         try {
894             res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_DEFAULT;
895             ImageDecoder.Source src = ImageDecoder.createSource(res, resId);
896             return decodeCropped(name, cropper, src);
897         } finally {
898             res.getDisplayMetrics().densityDpi = originalDensity;
899         }
900     }
901 
902     @Test
903     @Parameters(method = "getAssetRecords")
904     public void testCrop(ImageDecoderTest.AssetRecord record) {
905         AssetManager assets = getAssetManager();
906         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
907         Cropper cropper = new Cropper(false /* scale */);
908         Bitmap bm = decodeCropped(record.name, cropper, src);
909 
910         long asset = nOpenAsset(assets, record.name);
911         long aimagedecoder = nCreateFromAsset(asset);
912 
913         Rect crop = cropper.getCropRect();
914         nTestDecodeCrop(aimagedecoder, bm, 0, 0, crop.left, crop.top, crop.right, crop.bottom);
915         nCloseAsset(asset);
916     }
917 
918     @Test
919     @Parameters(method = "getRecords")
920     public void testCropResource(ImageDecoderTest.Record record)
921             throws IOException {
922         String name = Utils.getAsResourceUri(record.resId).toString();
923         Cropper cropper = new Cropper(false /* scale */);
924         Bitmap bm = decodeCropped(name, cropper, record.resId);
925         assertNotNull(bm);
926 
927         try (ParcelFileDescriptor pfd = open(record.resId)) {
928             long aimagedecoder = nCreateFromFd(pfd.getFd());
929 
930             Rect crop = cropper.getCropRect();
931             nTestDecodeCrop(aimagedecoder, bm, 0, 0, crop.left, crop.top, crop.right, crop.bottom);
932         } catch (FileNotFoundException e) {
933             fail("Could not open " + name + ": " + e);
934         }
935     }
936 
937     @Test
938     @Parameters(method = "getAssetRecords")
939     public void testCropAndScale(ImageDecoderTest.AssetRecord record) {
940         AssetManager assets = getAssetManager();
941         ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
942         Cropper cropper = new Cropper(true /* scale */);
943         Bitmap bm = decodeCropped(record.name, cropper, src);
944 
945         long asset = nOpenAsset(assets, record.name);
946         long aimagedecoder = nCreateFromAsset(asset);
947 
948         Rect crop = cropper.getCropRect();
949         nTestDecodeCrop(aimagedecoder, bm, cropper.getWidth(), cropper.getHeight(),
950                 crop.left, crop.top, crop.right, crop.bottom);
951         nCloseAsset(asset);
952     }
953 
954     @Test
955     @Parameters(method = "getRecords")
956     public void testCropAndScaleResource(ImageDecoderTest.Record record)
957             throws IOException {
958         ImageDecoder.Source src = ImageDecoder.createSource(getResources(),
959                 record.resId);
960         String name = Utils.getAsResourceUri(record.resId).toString();
961         Cropper cropper = new Cropper(true /* scale */);
962         Bitmap bm = decodeCropped(name, cropper, src);
963         assertNotNull(bm);
964 
965         try (ParcelFileDescriptor pfd = open(record.resId)) {
966             long aimagedecoder = nCreateFromFd(pfd.getFd());
967 
968             Rect crop = cropper.getCropRect();
969             nTestDecodeCrop(aimagedecoder, bm, cropper.getWidth(), cropper.getHeight(),
970                     crop.left, crop.top, crop.right, crop.bottom);
971         } catch (FileNotFoundException e) {
972             fail("Could not open " + name + ": " + e);
973         }
974     }
975 
976     private static Object[] getExifImages() {
977         return new Object[] {
978             R.drawable.orientation_1,
979             R.drawable.orientation_2,
980             R.drawable.orientation_3,
981             R.drawable.orientation_4,
982             R.drawable.orientation_5,
983             R.drawable.orientation_6,
984             R.drawable.orientation_7,
985             R.drawable.orientation_8,
986             R.drawable.webp_orientation1,
987             R.drawable.webp_orientation2,
988             R.drawable.webp_orientation3,
989             R.drawable.webp_orientation4,
990             R.drawable.webp_orientation5,
991             R.drawable.webp_orientation6,
992             R.drawable.webp_orientation7,
993             R.drawable.webp_orientation8,
994         };
995     }
996 
997     @Test
998     @Parameters(method = "getExifImages")
999     public void testRespectOrientation(int resId) throws IOException {
1000         Uri uri = Utils.getAsResourceUri(resId);
1001         ImageDecoder.Source src = ImageDecoder.createSource(getContentResolver(),
1002                 uri);
1003         Bitmap bm = decode(src, false /* unpremul */);
1004         assertNotNull(bm);
1005         assertEquals(100, bm.getWidth());
1006         assertEquals(80,  bm.getHeight());
1007 
1008         // First verify that the info (and in particular, the width and height)
1009         // are correct. This uses a separate ParcelFileDescriptor/aimagedecoder
1010         // because the native methods delete the aimagedecoder.
1011         try (ParcelFileDescriptor pfd = open(resId)) {
1012             long aimagedecoder = nCreateFromFd(pfd.getFd());
1013 
1014             String mimeType = uri.toString().contains("webp") ? "image/webp" : "image/jpeg";
1015             nTestInfo(aimagedecoder, 100, 80, mimeType, false,
1016                     DataSpace.fromColorSpace(bm.getColorSpace()));
1017         } catch (FileNotFoundException e) {
1018             e.printStackTrace();
1019             fail("Could not open " + uri + " to check info");
1020         }
1021 
1022         try (ParcelFileDescriptor pfd = open(resId)) {
1023             long aimagedecoder = nCreateFromFd(pfd.getFd());
1024 
1025             nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_NONE, false /* unpremul */, bm);
1026         } catch (FileNotFoundException e) {
1027             e.printStackTrace();
1028             fail("Could not open " + uri);
1029         }
1030         bm.recycle();
1031     }
1032 
1033     @Test
1034     @Parameters(method = "getAssetRecords")
1035     public void testScalePlusUnpremul(ImageDecoderTest.AssetRecord record) {
1036         long asset = nOpenAsset(getAssetManager(), record.name);
1037         long aimagedecoder = nCreateFromAsset(asset);
1038 
1039         nTestScalePlusUnpremul(aimagedecoder);
1040         nCloseAsset(asset);
1041     }
1042 
1043     private static File createCompressedBitmap(int width, int height, ColorSpace colorSpace,
1044             Bitmap.CompressFormat format) {
1045         File dir = InstrumentationRegistry.getTargetContext().getFilesDir();
1046         dir.mkdirs();
1047 
1048         File file = new File(dir, colorSpace.getName());
1049         try {
1050             file.createNewFile();
1051         } catch (IOException e) {
1052             e.printStackTrace();
1053             // If the file does not exist it will be handled below.
1054         }
1055         if (!file.exists()) {
1056             fail("Failed to create new File for " + file + "!");
1057         }
1058 
1059         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888, true,
1060                 colorSpace);
1061         bitmap.eraseColor(Color.BLUE);
1062 
1063         try (FileOutputStream fOutput = new FileOutputStream(file)) {
1064             bitmap.compress(format, 80, fOutput);
1065             return file;
1066         } catch (IOException e) {
1067             e.printStackTrace();
1068             fail("Failed to create file \"" + file + "\" with exception " + e);
1069             return null;
1070         }
1071     }
1072 
1073     private static Object[] rgbColorSpaces() {
1074         return BitmapTest.getRgbColorSpaces().toArray();
1075     }
1076 
1077     private static Object[] rgbColorSpacesAndCompressFormats() {
1078         return Utils.crossProduct(rgbColorSpaces(), Bitmap.CompressFormat.values());
1079     }
1080 
1081     String toMimeType(Bitmap.CompressFormat format) {
1082         switch (format) {
1083             case JPEG:
1084                 return "image/jpeg";
1085             case PNG:
1086                 return "image/png";
1087             case WEBP:
1088             case WEBP_LOSSY:
1089             case WEBP_LOSSLESS:
1090                 return "image/webp";
1091             default:
1092                 return "";
1093         }
1094     }
1095 
1096     @Test
1097     @Parameters(method = "rgbColorSpacesAndCompressFormats")
1098     public void testGetDataSpace(ColorSpace colorSpace, Bitmap.CompressFormat format) {
1099         if (colorSpace == ColorSpace.get(Named.EXTENDED_SRGB)
1100                 || colorSpace == ColorSpace.get(Named.LINEAR_EXTENDED_SRGB)) {
1101             // These will only be reported when the default AndroidBitmapFormat is F16.
1102             // Bitmap.compress will not compress to an image that will be decoded as F16 by default,
1103             // so these are covered by the AssetRecord tests.
1104             return;
1105         }
1106 
1107         final int width = 10;
1108         final int height = 10;
1109         File file = createCompressedBitmap(width, height, colorSpace, format);
1110         assertNotNull(file);
1111 
1112         int dataSpace = DataSpace.fromColorSpace(colorSpace);
1113 
1114         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
1115                 ParcelFileDescriptor.MODE_READ_ONLY)) {
1116             long aimagedecoder = nCreateFromFd(pfd.getFd());
1117             nTestInfo(aimagedecoder, width, height, toMimeType(format), false, dataSpace);
1118         } catch (IOException e) {
1119             e.printStackTrace();
1120             fail("Could not read " + file);
1121         }
1122     }
1123 
1124     private static Bitmap decode(ImageDecoder.Source src, ColorSpace colorSpace) {
1125         try {
1126             return ImageDecoder.decodeBitmap(src, (decoder, info, source) -> {
1127                 // So we can compare pixels to the native decode.
1128                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
1129 
1130                 decoder.setTargetColorSpace(colorSpace);
1131             });
1132         } catch (IOException e) {
1133             fail("Failed to decode in Java with " + e);
1134             return null;
1135         }
1136     }
1137 
1138     @Test
1139     @Parameters(method = "rgbColorSpaces")
1140     public void testSetDataSpace(ColorSpace colorSpace) {
1141         int dataSpace = DataSpace.fromColorSpace(colorSpace);
1142         if (dataSpace == DataSpace.ADATASPACE_UNKNOWN) {
1143             // AImageDecoder cannot decode to these ADATASPACEs
1144             return;
1145         }
1146 
1147         String name = "translucent-green-p3.png";
1148         AssetManager assets = getAssetManager();
1149         ImageDecoder.Source src = ImageDecoder.createSource(assets, name);
1150         Bitmap bm = decode(src, colorSpace);
1151         assertEquals(colorSpace, bm.getColorSpace());
1152 
1153         long asset = nOpenAsset(assets, name);
1154         long aimagedecoder = nCreateFromAsset(asset);
1155 
1156         nTestDecode(aimagedecoder, bm, dataSpace);
1157         nCloseAsset(asset);
1158     }
1159 
1160     @Test
1161     @Parameters({ "cmyk_yellow_224_224_32.jpg", "wide_gamut_yellow_224_224_64.jpeg" })
1162     public void testNonStandardDataSpaces(String name) {
1163         AssetManager assets = getAssetManager();
1164         long asset = nOpenAsset(assets, name);
1165         long aimagedecoder = nCreateFromAsset(asset);
1166 
1167         // These images have profiles that do not map to ADataSpaces (or even SkColorSpaces).
1168         // Verify that by default, AImageDecoder will treat them as ADATASPACE_UNKNOWN.
1169         nTestInfo(aimagedecoder, 32, 32, "image/jpeg", false, DataSpace.ADATASPACE_UNKNOWN);
1170         nCloseAsset(asset);
1171     }
1172 
1173     @Test
1174     @Parameters({ "cmyk_yellow_224_224_32.jpg,#FFD8FC04",
1175                   "wide_gamut_yellow_224_224_64.jpeg,#FFE0E040" })
1176     public void testNonStandardDataSpacesDecode(String name, String color) {
1177         AssetManager assets = getAssetManager();
1178         long asset = nOpenAsset(assets, name);
1179         long aimagedecoder = nCreateFromAsset(asset);
1180 
1181         // These images are each a solid color. If we correctly do no color correction, they should
1182         // match |color|.
1183         int colorInt = Color.parseColor(color);
1184         Bitmap bm = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
1185         bm.eraseColor(colorInt);
1186 
1187         nTestDecode(aimagedecoder, ANDROID_BITMAP_FORMAT_NONE, false /* unpremul */, bm);
1188         nCloseAsset(asset);
1189     }
1190 
1191     @Test
1192     @Parameters(method = "getAssetRecords")
1193     public void testNotAnimatedAssets(ImageDecoderTest.AssetRecord record) {
1194         long asset = nOpenAsset(getAssetManager(), record.name);
1195         long aimagedecoder = nCreateFromAsset(asset);
1196 
1197         nTestIsAnimated(aimagedecoder, false);
1198         nCloseAsset(asset);
1199     }
1200 
1201     @Test
1202     @Parameters(method = "getRecords")
1203     public void testNotAnimated(ImageDecoderTest.Record record) throws IOException {
1204         try (ParcelFileDescriptor pfd = open(record.resId)) {
1205             long aimagedecoder = nCreateFromFd(pfd.getFd());
1206 
1207             nTestIsAnimated(aimagedecoder, false);
1208         } catch (FileNotFoundException e) {
1209             fail("Could not open " + Utils.getAsResourceUri(record.resId));
1210         }
1211     }
1212 
1213     private static Object[] getAnimatedImagesPlusRepeatCounts() {
1214         return AnimatedImageDrawableTest.parametersForTestEncodedRepeats();
1215     }
1216 
1217     // Although these images have an encoded repeat count, they have only one frame,
1218     // so they are not considered animated.
1219     @Test
1220     @Parameters({"still_with_loop_count.gif", "webp_still_with_loop_count.webp"})
1221     public void testStill(String name) {
1222         long asset = nOpenAsset(getAssetManager(), name);
1223         long aimagedecoder = nCreateFromAsset(asset);
1224 
1225         nTestIsAnimated(aimagedecoder, false);
1226         nCloseAsset(asset);
1227     }
1228 
1229     @Test
1230     @Parameters(method = "getAnimatedImagesPlusRepeatCounts")
1231     public void testAnimated(int resId, int unused) throws IOException {
1232         try (ParcelFileDescriptor pfd = open(resId)) {
1233             long aimagedecoder = nCreateFromFd(pfd.getFd());
1234 
1235             nTestIsAnimated(aimagedecoder, true);
1236         } catch (FileNotFoundException e) {
1237             fail("Could not open " + Utils.getAsResourceUri(resId));
1238         }
1239     }
1240 
1241     @Test
1242     @Parameters(method = "getAnimatedImagesPlusRepeatCounts")
1243     public void testRepeatCount(int resId, int repeatCount) throws IOException {
1244         try (ParcelFileDescriptor pfd = open(resId)) {
1245             long aimagedecoder = nCreateFromFd(pfd.getFd());
1246 
1247             nTestRepeatCount(aimagedecoder, repeatCount);
1248         } catch (FileNotFoundException e) {
1249             fail("Could not open " + Utils.getAsResourceUri(resId));
1250         }
1251     }
1252 
1253     @Test
1254     @Parameters({"still_with_loop_count.gif, 1", "webp_still_with_loop_count.webp,31999"})
1255     public void testRepeatCountStill(String name, int repeatCount) {
1256         long asset = nOpenAsset(getAssetManager(), name);
1257         long aimagedecoder = nCreateFromAsset(asset);
1258 
1259         nTestRepeatCount(aimagedecoder, repeatCount);
1260         nCloseAsset(asset);
1261     }
1262 
1263     // Return a pointer to the native AAsset named |file|. Must be closed with nCloseAsset.
1264     // Throws an Exception on failure.
1265     private static native long nOpenAsset(AssetManager assets, String file);
1266     private static native void nCloseAsset(long asset);
1267 
1268     // Methods for creating and returning a pointer to an AImageDecoder. All
1269     // throw an Exception on failure.
1270     private static native long nCreateFromFd(int fd);
1271     private static native long nCreateFromAsset(long asset);
1272     private static native long nCreateFromAssetFd(long asset);
1273     private static native long nCreateFromAssetBuffer(long asset);
1274 
1275     private static native void nTestEmptyCreate();
1276     private static native void nTestNullDecoder(AssetManager assets, String file);
1277     private static native void nTestCreateIncomplete(AssetManager assets,
1278             String file, int truncatedLength);
1279     private static native void nTestCreateUnsupported(AssetManager assets, String file);
1280 
1281     // For convenience, all methods that take aimagedecoder as a parameter delete
1282     // it.
1283     private static native void nTestInfo(long aimagedecoder, int width, int height,
1284             String mimeType, boolean isF16, int dataspace);
1285     private static native void nTestSetFormat(long aimagedecoder, boolean isF16, boolean isGray);
1286     private static native void nTestSetUnpremul(long aimagedecoder, boolean hasAlpha);
1287     private static native void nTestGetMinimumStride(long aimagedecoder,
1288             boolean isF16, boolean isGray);
1289     private static native void nTestDecode(long aimagedecoder,
1290             int requestedAndroidBitmapFormat, boolean unpremul, Bitmap bitmap);
1291     private static native void nTestDecodeStride(long aimagedecoder);
1292     private static native void nTestSetTargetSize(long aimagedecoder);
1293     // Decode with the target width and height to match |bitmap|.
1294     private static native void nTestDecodeScaled(long aimagedecoder, Bitmap bitmap);
1295     private static native void nTestComputeSampledSize(long aimagedecoder, Bitmap bm,
1296             int sampleSize);
1297     private static native void nTestSetCrop(AssetManager assets, String file);
1298     // Decode and compare to |bitmap|, where they both use the specified target
1299     // size and crop rect. target size of 0 means to skip scaling.
1300     private static native void nTestDecodeCrop(long aimagedecoder,
1301             Bitmap bitmap, int targetWidth, int targetHeight,
1302             int cropLeft, int cropTop, int cropRight, int cropBottom);
1303     private static native void nTestScalePlusUnpremul(long aimagedecoder);
1304     private static native void nTestDecode(long aimagedecoder, Bitmap bm, int dataSpace);
1305     private static native void nTestIsAnimated(long aimagedecoder, boolean animated);
1306     private static native void nTestRepeatCount(long aimagedecoder, int repeatCount);
1307 }
1308