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