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