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