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 package android.graphics.cts; 17 18 import static android.graphics.cts.utils.LeakTest.runNotLeakingTest; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotEquals; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertThrows; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 import static org.junit.Assume.assumeNoException; 30 import static org.junit.Assume.assumeNotNull; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.content.res.Resources; 34 import android.graphics.Bitmap; 35 import android.graphics.Bitmap.CompressFormat; 36 import android.graphics.Bitmap.Config; 37 import android.graphics.BitmapFactory; 38 import android.graphics.Canvas; 39 import android.graphics.Color; 40 import android.graphics.ColorSpace; 41 import android.graphics.ColorSpace.Named; 42 import android.graphics.ImageDecoder; 43 import android.graphics.ImageFormat; 44 import android.graphics.LinearGradient; 45 import android.graphics.Matrix; 46 import android.graphics.Paint; 47 import android.graphics.Picture; 48 import android.graphics.Shader; 49 import android.hardware.HardwareBuffer; 50 import android.os.Parcel; 51 import android.os.StrictMode; 52 import android.util.DisplayMetrics; 53 import android.view.Surface; 54 55 import androidx.test.InstrumentationRegistry; 56 import androidx.test.filters.LargeTest; 57 import androidx.test.filters.SmallTest; 58 59 import com.android.compatibility.common.util.BitmapUtils; 60 import com.android.compatibility.common.util.ColorUtils; 61 import com.android.compatibility.common.util.WidgetTestUtils; 62 63 import junitparams.JUnitParamsRunner; 64 import junitparams.Parameters; 65 66 import org.junit.Before; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 70 import java.io.ByteArrayOutputStream; 71 import java.io.IOException; 72 import java.io.OutputStream; 73 import java.nio.ByteBuffer; 74 import java.nio.CharBuffer; 75 import java.nio.IntBuffer; 76 import java.nio.ShortBuffer; 77 import java.util.ArrayList; 78 import java.util.Arrays; 79 import java.util.HashSet; 80 import java.util.List; 81 82 @SmallTest 83 @RunWith(JUnitParamsRunner.class) 84 public class BitmapTest { 85 // small alpha values cause color values to be pre-multiplied down, losing accuracy 86 private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253); 87 private static final int PREMUL_ROUNDED_COLOR = Color.argb(2, 255, 255, 255); 88 private static final int PREMUL_STORED_COLOR = Color.argb(2, 2, 2, 2); 89 90 private static final BitmapFactory.Options HARDWARE_OPTIONS = createHardwareBitmapOptions(); 91 92 static { 93 System.loadLibrary("ctsgraphics_jni"); 94 } 95 96 private Resources mRes; 97 private Bitmap mBitmap; 98 private BitmapFactory.Options mOptions; 99 getRgbColorSpaces()100 public static List<ColorSpace> getRgbColorSpaces() { 101 List<ColorSpace> rgbColorSpaces; 102 rgbColorSpaces = new ArrayList<ColorSpace>(); 103 for (ColorSpace.Named e : ColorSpace.Named.values()) { 104 ColorSpace cs = ColorSpace.get(e); 105 if (cs.getModel() != ColorSpace.Model.RGB) { 106 continue; 107 } 108 if (((ColorSpace.Rgb) cs).getTransferParameters() == null) { 109 continue; 110 } 111 rgbColorSpaces.add(cs); 112 } 113 return rgbColorSpaces; 114 } 115 116 @Before setup()117 public void setup() { 118 mRes = InstrumentationRegistry.getTargetContext().getResources(); 119 mOptions = new BitmapFactory.Options(); 120 mOptions.inScaled = false; 121 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 122 } 123 124 @Test(expected=IllegalStateException.class) testCompressRecycled()125 public void testCompressRecycled() { 126 mBitmap.recycle(); 127 mBitmap.compress(CompressFormat.JPEG, 0, null); 128 } 129 130 @Test(expected=NullPointerException.class) testCompressNullStream()131 public void testCompressNullStream() { 132 mBitmap.compress(CompressFormat.JPEG, 0, null); 133 } 134 135 @Test(expected=IllegalArgumentException.class) testCompressQualityTooLow()136 public void testCompressQualityTooLow() { 137 mBitmap.compress(CompressFormat.JPEG, -1, new ByteArrayOutputStream()); 138 } 139 140 @Test(expected=IllegalArgumentException.class) testCompressQualityTooHigh()141 public void testCompressQualityTooHigh() { 142 mBitmap.compress(CompressFormat.JPEG, 101, new ByteArrayOutputStream()); 143 } 144 compressFormats()145 private static Object[] compressFormats() { 146 return CompressFormat.values(); 147 } 148 149 @Test 150 @Parameters(method = "compressFormats") testCompress(CompressFormat format)151 public void testCompress(CompressFormat format) { 152 assertTrue(mBitmap.compress(format, 50, new ByteArrayOutputStream())); 153 } 154 decodeBytes(byte[] bytes)155 private Bitmap decodeBytes(byte[] bytes) { 156 ByteBuffer buffer = ByteBuffer.wrap(bytes); 157 ImageDecoder.Source src = ImageDecoder.createSource(buffer); 158 try { 159 return ImageDecoder.decodeBitmap(src, (decoder, info, s) -> { 160 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); 161 }); 162 } catch (IOException e) { 163 fail("Failed to decode with " + e); 164 return null; 165 } 166 } 167 168 // There are three color components and 169 // each should be within a square difference of 15 * 15. 170 private static final int MSE_MARGIN = 3 * (15 * 15); 171 172 @Test testCompressWebpLossy()173 public void testCompressWebpLossy() { 174 // For qualities < 100, WEBP performs a lossy decode. 175 byte[] last = null; 176 Bitmap lastBitmap = null; 177 for (int quality : new int[] { 25, 50, 80, 99 }) { 178 ByteArrayOutputStream webp = new ByteArrayOutputStream(); 179 assertTrue(mBitmap.compress(CompressFormat.WEBP, quality, webp)); 180 byte[] webpCompressed = webp.toByteArray(); 181 182 183 ByteArrayOutputStream webpLossy = new ByteArrayOutputStream(); 184 assertTrue(mBitmap.compress(CompressFormat.WEBP_LOSSY, quality, webpLossy)); 185 byte[] webpLossyCompressed = webpLossy.toByteArray(); 186 187 assertTrue("Compression did not match at quality " + quality, 188 Arrays.equals(webpCompressed, webpLossyCompressed)); 189 190 Bitmap result = decodeBytes(webpCompressed); 191 if (last != null) { 192 // Higher quality will generally result in a larger file. 193 assertTrue(webpCompressed.length > last.length); 194 if (!BitmapUtils.compareBitmapsMse(lastBitmap, result, MSE_MARGIN, true, false)) { 195 fail("Bad comparison for quality " + quality); 196 } 197 } 198 last = webpCompressed; 199 lastBitmap = result; 200 } 201 } 202 203 @Test 204 @Parameters({ "0", "50", "80", "99", "100" }) testCompressWebpLossless(int quality)205 public void testCompressWebpLossless(int quality) { 206 ByteArrayOutputStream webp = new ByteArrayOutputStream(); 207 assertTrue(mBitmap.compress(CompressFormat.WEBP_LOSSLESS, quality, webp)); 208 byte[] webpCompressed = webp.toByteArray(); 209 Bitmap result = decodeBytes(webpCompressed); 210 211 assertTrue("WEBP_LOSSLESS did not losslessly compress at quality " + quality, 212 BitmapUtils.compareBitmaps(mBitmap, result)); 213 } 214 215 @Test testCompressWebp100MeansLossless()216 public void testCompressWebp100MeansLossless() { 217 ByteArrayOutputStream webp = new ByteArrayOutputStream(); 218 assertTrue(mBitmap.compress(CompressFormat.WEBP, 100, webp)); 219 byte[] webpCompressed = webp.toByteArray(); 220 Bitmap result = decodeBytes(webpCompressed); 221 assertTrue("WEBP_LOSSLESS did not losslessly compress at quality 100", 222 BitmapUtils.compareBitmaps(mBitmap, result)); 223 } 224 225 @Test 226 @Parameters(method = "compressFormats") testCompressAlpha8Fails(CompressFormat format)227 public void testCompressAlpha8Fails(CompressFormat format) { 228 Bitmap bitmap = Bitmap.createBitmap(1, 1, Config.ALPHA_8); 229 assertFalse("Incorrectly compressed ALPHA_8 to " + format, 230 bitmap.compress(format, 50, new ByteArrayOutputStream())); 231 232 if (format == CompressFormat.WEBP) { 233 // Skip the native test, since the NDK just has equivalents for 234 // WEBP_LOSSY and WEBP_LOSSLESS. 235 return; 236 } 237 238 byte[] storage = new byte[16 * 1024]; 239 OutputStream stream = new ByteArrayOutputStream(); 240 assertFalse("Incorrectly compressed ALPHA_8 with the NDK to " + format, 241 nCompress(bitmap, nativeCompressFormat(format), 50, stream, storage)); 242 } 243 244 @Test(expected=IllegalStateException.class) testCopyRecycled()245 public void testCopyRecycled() { 246 mBitmap.recycle(); 247 mBitmap.copy(Config.RGB_565, false); 248 } 249 250 @Test testCopy()251 public void testCopy() { 252 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 253 Bitmap bitmap = mBitmap.copy(Config.ARGB_8888, false); 254 WidgetTestUtils.assertEquals(mBitmap, bitmap); 255 } 256 257 @Test testCopyConfigs()258 public void testCopyConfigs() { 259 Config[] supportedConfigs = new Config[] { 260 Config.ALPHA_8, Config.RGB_565, Config.ARGB_8888, Config.RGBA_F16, 261 }; 262 for (Config src : supportedConfigs) { 263 for (Config dst : supportedConfigs) { 264 Bitmap srcBitmap = Bitmap.createBitmap(1, 1, src); 265 srcBitmap.eraseColor(Color.WHITE); 266 Bitmap dstBitmap = srcBitmap.copy(dst, false); 267 assertNotNull("Should support copying from " + src + " to " + dst, 268 dstBitmap); 269 if (Config.ALPHA_8 == dst || Config.ALPHA_8 == src) { 270 // Color will be opaque but color information will be lost. 271 assertEquals("Color should be black when copying from " + src + " to " 272 + dst, Color.BLACK, dstBitmap.getPixel(0, 0)); 273 } else { 274 assertEquals("Color should be preserved when copying from " + src + " to " 275 + dst, Color.WHITE, dstBitmap.getPixel(0, 0)); 276 } 277 } 278 } 279 } 280 281 @Test(expected=IllegalArgumentException.class) testCopyMutableHwBitmap()282 public void testCopyMutableHwBitmap() { 283 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 284 mBitmap.copy(Config.HARDWARE, true); 285 } 286 287 @Test(expected=RuntimeException.class) testCopyPixelsToBufferUnsupportedBufferClass()288 public void testCopyPixelsToBufferUnsupportedBufferClass() { 289 final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight(); 290 291 mBitmap.copyPixelsToBuffer(CharBuffer.allocate(pixSize)); 292 } 293 294 @Test(expected=RuntimeException.class) testCopyPixelsToBufferBufferTooSmall()295 public void testCopyPixelsToBufferBufferTooSmall() { 296 final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight(); 297 final int tooSmall = pixSize / 2; 298 299 mBitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall)); 300 } 301 302 @Test testCopyPixelsToBuffer()303 public void testCopyPixelsToBuffer() { 304 final int pixSize = mBitmap.getRowBytes() * mBitmap.getHeight(); 305 306 ByteBuffer byteBuf = ByteBuffer.allocate(pixSize); 307 assertEquals(0, byteBuf.position()); 308 mBitmap.copyPixelsToBuffer(byteBuf); 309 assertEquals(pixSize, byteBuf.position()); 310 311 ShortBuffer shortBuf = ShortBuffer.allocate(pixSize); 312 assertEquals(0, shortBuf.position()); 313 mBitmap.copyPixelsToBuffer(shortBuf); 314 assertEquals(pixSize >> 1, shortBuf.position()); 315 316 IntBuffer intBuf1 = IntBuffer.allocate(pixSize); 317 assertEquals(0, intBuf1.position()); 318 mBitmap.copyPixelsToBuffer(intBuf1); 319 assertEquals(pixSize >> 2, intBuf1.position()); 320 321 Bitmap bitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), 322 mBitmap.getConfig()); 323 intBuf1.position(0); // copyPixelsToBuffer adjusted the position, so rewind to start 324 bitmap.copyPixelsFromBuffer(intBuf1); 325 IntBuffer intBuf2 = IntBuffer.allocate(pixSize); 326 bitmap.copyPixelsToBuffer(intBuf2); 327 328 assertEquals(pixSize >> 2, intBuf2.position()); 329 assertEquals(intBuf1.position(), intBuf2.position()); 330 int size = intBuf1.position(); 331 intBuf1.position(0); 332 intBuf2.position(0); 333 for (int i = 0; i < size; i++) { 334 assertEquals("mismatching pixels at position " + i, intBuf1.get(), intBuf2.get()); 335 } 336 } 337 338 @Test testCreateBitmap1()339 public void testCreateBitmap1() { 340 int[] colors = createColors(100); 341 Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565); 342 assertFalse(bitmap.isMutable()); 343 Bitmap ret = Bitmap.createBitmap(bitmap); 344 assertNotNull(ret); 345 assertFalse(ret.isMutable()); 346 assertEquals(10, ret.getWidth()); 347 assertEquals(10, ret.getHeight()); 348 assertEquals(Config.RGB_565, ret.getConfig()); 349 assertEquals(ANDROID_BITMAP_FORMAT_RGB_565, nGetFormat(ret)); 350 } 351 352 @Test(expected=IllegalArgumentException.class) testCreateBitmapNegativeX()353 public void testCreateBitmapNegativeX() { 354 Bitmap.createBitmap(mBitmap, -100, 50, 50, 200); 355 } 356 357 @Test testCreateBitmap2()358 public void testCreateBitmap2() { 359 // special case: output bitmap is equal to the input bitmap 360 mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888); 361 assertFalse(mBitmap.isMutable()); // createBitmap w/ colors should be immutable 362 Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100); 363 assertNotNull(ret); 364 assertFalse(ret.isMutable()); // createBitmap from subset should be immutable 365 assertTrue(mBitmap.equals(ret)); 366 367 //normal case 368 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 369 ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50); 370 assertNotNull(ret); 371 assertFalse(mBitmap.equals(ret)); 372 assertEquals(ANDROID_BITMAP_FORMAT_RGBA_8888, nGetFormat(mBitmap)); 373 } 374 375 @Test(expected=IllegalArgumentException.class) testCreateBitmapNegativeXY()376 public void testCreateBitmapNegativeXY() { 377 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 378 379 // abnormal case: x and/or y less than 0 380 Bitmap.createBitmap(mBitmap, -1, -1, 10, 10, null, false); 381 } 382 383 @Test(expected=IllegalArgumentException.class) testCreateBitmapNegativeWidthHeight()384 public void testCreateBitmapNegativeWidthHeight() { 385 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 386 387 // abnormal case: width and/or height less than 0 388 Bitmap.createBitmap(mBitmap, 1, 1, -10, -10, null, false); 389 } 390 391 @Test(expected=IllegalArgumentException.class) testCreateBitmapXRegionTooWide()392 public void testCreateBitmapXRegionTooWide() { 393 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 394 395 // abnormal case: (x + width) bigger than source bitmap's width 396 Bitmap.createBitmap(mBitmap, 10, 10, 95, 50, null, false); 397 } 398 399 @Test(expected=IllegalArgumentException.class) testCreateBitmapYRegionTooTall()400 public void testCreateBitmapYRegionTooTall() { 401 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 402 403 // abnormal case: (y + height) bigger than source bitmap's height 404 Bitmap.createBitmap(mBitmap, 10, 10, 50, 95, null, false); 405 } 406 407 @Test(expected=IllegalArgumentException.class) testCreateMutableBitmapWithHardwareConfig()408 public void testCreateMutableBitmapWithHardwareConfig() { 409 Bitmap.createBitmap(100, 100, Config.HARDWARE); 410 } 411 412 @Test testCreateBitmap3()413 public void testCreateBitmap3() { 414 // special case: output bitmap is equal to the input bitmap 415 mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888); 416 Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100, null, false); 417 assertNotNull(ret); 418 assertFalse(ret.isMutable()); // subset should be immutable 419 assertTrue(mBitmap.equals(ret)); 420 421 // normal case 422 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 423 ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50, new Matrix(), true); 424 assertTrue(ret.isMutable()); 425 assertNotNull(ret); 426 assertFalse(mBitmap.equals(ret)); 427 } 428 429 @Test testCreateBitmapFromHardwareBitmap()430 public void testCreateBitmapFromHardwareBitmap() { 431 Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, 432 HARDWARE_OPTIONS); 433 assertEquals(Config.HARDWARE, hardwareBitmap.getConfig()); 434 435 Bitmap ret = Bitmap.createBitmap(hardwareBitmap, 0, 0, 96, 96, null, false); 436 assertEquals(Config.HARDWARE, ret.getConfig()); 437 assertFalse(ret.isMutable()); 438 } 439 440 @Test testCreateBitmap4()441 public void testCreateBitmap4() { 442 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 443 assertNotNull(ret); 444 assertTrue(ret.isMutable()); 445 assertEquals(100, ret.getWidth()); 446 assertEquals(200, ret.getHeight()); 447 assertEquals(Config.RGB_565, ret.getConfig()); 448 } 449 verify2x2BitmapContents(int[] expected, Bitmap observed)450 private static void verify2x2BitmapContents(int[] expected, Bitmap observed) { 451 ColorUtils.verifyColor(expected[0], observed.getPixel(0, 0)); 452 ColorUtils.verifyColor(expected[1], observed.getPixel(1, 0)); 453 ColorUtils.verifyColor(expected[2], observed.getPixel(0, 1)); 454 ColorUtils.verifyColor(expected[3], observed.getPixel(1, 1)); 455 } 456 457 @Test testCreateBitmap_matrix()458 public void testCreateBitmap_matrix() { 459 int[] colorArray = new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK }; 460 Bitmap src = Bitmap.createBitmap(2, 2, Config.ARGB_8888); 461 assertTrue(src.isMutable()); 462 src.setPixels(colorArray,0, 2, 0, 0, 2, 2); 463 464 // baseline 465 verify2x2BitmapContents(colorArray, src); 466 467 // null 468 Bitmap dst = Bitmap.createBitmap(src, 0, 0, 2, 2, null, false); 469 assertTrue(dst.isMutable()); 470 verify2x2BitmapContents(colorArray, dst); 471 472 // identity matrix 473 Matrix matrix = new Matrix(); 474 dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false); 475 assertTrue(dst.isMutable()); 476 verify2x2BitmapContents(colorArray, dst); 477 478 // big scale - only red visible 479 matrix.setScale(10, 10); 480 dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false); 481 assertTrue(dst.isMutable()); 482 verify2x2BitmapContents(new int[] { Color.RED, Color.RED, Color.RED, Color.RED }, dst); 483 484 // rotation 485 matrix.setRotate(90); 486 dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false); 487 assertTrue(dst.isMutable()); 488 verify2x2BitmapContents( 489 new int[] { Color.BLUE, Color.RED, Color.BLACK, Color.GREEN }, dst); 490 } 491 492 @Test(expected=IllegalArgumentException.class) testCreateBitmapFromColorsNegativeWidthHeight()493 public void testCreateBitmapFromColorsNegativeWidthHeight() { 494 int[] colors = createColors(100); 495 496 // abnormal case: width and/or height less than 0 497 Bitmap.createBitmap(colors, 0, 100, -1, 100, Config.RGB_565); 498 } 499 500 @Test(expected=IllegalArgumentException.class) testCreateBitmapFromColorsIllegalStride()501 public void testCreateBitmapFromColorsIllegalStride() { 502 int[] colors = createColors(100); 503 504 // abnormal case: stride less than width and bigger than -width 505 Bitmap.createBitmap(colors, 10, 10, 100, 100, Config.RGB_565); 506 } 507 508 @Test(expected=ArrayIndexOutOfBoundsException.class) testCreateBitmapFromColorsNegativeOffset()509 public void testCreateBitmapFromColorsNegativeOffset() { 510 int[] colors = createColors(100); 511 512 // abnormal case: offset less than 0 513 Bitmap.createBitmap(colors, -10, 100, 100, 100, Config.RGB_565); 514 } 515 516 @Test(expected=ArrayIndexOutOfBoundsException.class) testCreateBitmapFromColorsOffsetTooLarge()517 public void testCreateBitmapFromColorsOffsetTooLarge() { 518 int[] colors = createColors(100); 519 520 // abnormal case: (offset + width) bigger than colors' length 521 Bitmap.createBitmap(colors, 10, 100, 100, 100, Config.RGB_565); 522 } 523 524 @Test(expected=ArrayIndexOutOfBoundsException.class) testCreateBitmapFromColorsScalnlineTooLarge()525 public void testCreateBitmapFromColorsScalnlineTooLarge() { 526 int[] colors = createColors(100); 527 528 // abnormal case: (lastScanline + width) bigger than colors' length 529 Bitmap.createBitmap(colors, 10, 100, 50, 100, Config.RGB_565); 530 } 531 532 @Test testCreateBitmap6()533 public void testCreateBitmap6() { 534 int[] colors = createColors(100); 535 536 // normal case 537 Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565); 538 assertNotNull(ret); 539 assertFalse(ret.isMutable()); 540 assertEquals(10, ret.getWidth()); 541 assertEquals(5, ret.getHeight()); 542 assertEquals(Config.RGB_565, ret.getConfig()); 543 } 544 545 @Test testCreateBitmap_displayMetrics_mutable()546 public void testCreateBitmap_displayMetrics_mutable() { 547 DisplayMetrics metrics = 548 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics(); 549 550 Bitmap bitmap; 551 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888); 552 assertTrue(bitmap.isMutable()); 553 assertEquals(metrics.densityDpi, bitmap.getDensity()); 554 555 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888); 556 assertTrue(bitmap.isMutable()); 557 assertEquals(metrics.densityDpi, bitmap.getDensity()); 558 559 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true); 560 assertTrue(bitmap.isMutable()); 561 assertEquals(metrics.densityDpi, bitmap.getDensity()); 562 563 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true, ColorSpace.get( 564 ColorSpace.Named.SRGB)); 565 566 assertTrue(bitmap.isMutable()); 567 assertEquals(metrics.densityDpi, bitmap.getDensity()); 568 569 int[] colors = createColors(100); 570 bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888); 571 assertNotNull(bitmap); 572 assertFalse(bitmap.isMutable()); 573 574 bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888); 575 assertNotNull(bitmap); 576 assertFalse(bitmap.isMutable()); 577 } 578 579 @Test testCreateBitmap_noDisplayMetrics_mutable()580 public void testCreateBitmap_noDisplayMetrics_mutable() { 581 Bitmap bitmap; 582 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 583 assertTrue(bitmap.isMutable()); 584 585 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true); 586 assertTrue(bitmap.isMutable()); 587 588 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB)); 589 assertTrue(bitmap.isMutable()); 590 } 591 592 @Test testCreateBitmap_displayMetrics_immutable()593 public void testCreateBitmap_displayMetrics_immutable() { 594 DisplayMetrics metrics = 595 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics(); 596 int[] colors = createColors(100); 597 598 Bitmap bitmap; 599 bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888); 600 assertFalse(bitmap.isMutable()); 601 assertEquals(metrics.densityDpi, bitmap.getDensity()); 602 603 bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888); 604 assertFalse(bitmap.isMutable()); 605 assertEquals(metrics.densityDpi, bitmap.getDensity()); 606 } 607 608 @Test testCreateBitmap_noDisplayMetrics_immutable()609 public void testCreateBitmap_noDisplayMetrics_immutable() { 610 int[] colors = createColors(100); 611 Bitmap bitmap; 612 bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888); 613 assertFalse(bitmap.isMutable()); 614 615 bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888); 616 assertFalse(bitmap.isMutable()); 617 } 618 619 @Test testCreateBitmap_Picture_immutable()620 public void testCreateBitmap_Picture_immutable() { 621 Picture picture = new Picture(); 622 Canvas canvas = picture.beginRecording(200, 100); 623 624 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); 625 626 p.setColor(0x88FF0000); 627 canvas.drawCircle(50, 50, 40, p); 628 629 p.setColor(Color.GREEN); 630 p.setTextSize(30); 631 canvas.drawText("Pictures", 60, 60, p); 632 picture.endRecording(); 633 634 Bitmap bitmap; 635 bitmap = Bitmap.createBitmap(picture); 636 assertFalse(bitmap.isMutable()); 637 638 bitmap = Bitmap.createBitmap(picture, 100, 100, Config.HARDWARE); 639 assertFalse(bitmap.isMutable()); 640 assertNotNull(bitmap.getColorSpace()); 641 642 bitmap = Bitmap.createBitmap(picture, 100, 100, Config.ARGB_8888); 643 assertFalse(bitmap.isMutable()); 644 } 645 646 @Test testCreateScaledBitmap()647 public void testCreateScaledBitmap() { 648 mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565); 649 assertTrue(mBitmap.isMutable()); 650 Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false); 651 assertNotNull(ret); 652 assertEquals(50, ret.getWidth()); 653 assertEquals(100, ret.getHeight()); 654 assertTrue(ret.isMutable()); 655 } 656 657 @Test testWrapHardwareBufferSucceeds()658 public void testWrapHardwareBufferSucceeds() { 659 try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) { 660 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 661 assertNotNull(bitmap); 662 bitmap.recycle(); 663 } 664 } 665 666 @Test testWrapHardwareBufferMissingGpuUsageFails()667 public void testWrapHardwareBufferMissingGpuUsageFails() { 668 try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1, 669 HardwareBuffer.USAGE_CPU_WRITE_RARELY)) { 670 assertThrows(IllegalArgumentException.class, () -> { 671 Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 672 }); 673 } 674 } 675 676 @Test testWrapHardwareBufferWithProtectedUsageFails()677 public void testWrapHardwareBufferWithProtectedUsageFails() { 678 try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1, 679 HardwareBuffer.USAGE_GPU_COLOR_OUTPUT 680 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE 681 | HardwareBuffer.USAGE_COMPOSER_OVERLAY 682 | HardwareBuffer.USAGE_PROTECTED_CONTENT)) { 683 assertThrows(IllegalArgumentException.class, () -> { 684 Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 685 }); 686 } 687 } 688 689 @Test(expected = IllegalArgumentException.class) testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails()690 public void testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails() { 691 try (HardwareBuffer hwBuffer = HardwareBuffer.create(512, 512, HardwareBuffer.RGBA_8888, 1, 692 HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)) { 693 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.CIE_LAB)); 694 } 695 } 696 697 @Test testWrapHardwareBufferFor1010102BufferSucceeds()698 public void testWrapHardwareBufferFor1010102BufferSucceeds() { 699 HardwareBuffer hwBufferMaybe = null; 700 701 try { 702 hwBufferMaybe = HardwareBuffer.create(128, 128, HardwareBuffer.RGBA_1010102, 1, 703 HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 704 } catch (IllegalArgumentException e) { 705 assumeNoException("Creating a 1010102 HW buffer was not supported", e); 706 } 707 708 assumeNotNull(hwBufferMaybe); 709 710 try (HardwareBuffer buffer = hwBufferMaybe) { 711 Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(Named.SRGB)); 712 assertNotNull(bitmap); 713 bitmap.recycle(); 714 } 715 } 716 assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2)717 private void assertMatches(HardwareBuffer hwBuffer, HardwareBuffer hwBuffer2) { 718 assertEquals(hwBuffer, hwBuffer2); 719 assertEquals(hwBuffer.hashCode(), hwBuffer2.hashCode()); 720 assertEquals(hwBuffer.getWidth(), hwBuffer2.getWidth()); 721 assertEquals(hwBuffer.getHeight(), hwBuffer2.getHeight()); 722 assertEquals(hwBuffer.getFormat(), hwBuffer2.getFormat()); 723 assertEquals(hwBuffer.getLayers(), hwBuffer2.getLayers()); 724 assertEquals(hwBuffer.getUsage(), hwBuffer2.getUsage()); 725 } 726 727 @Test testGetHardwareBufferMatchesWrapped()728 public void testGetHardwareBufferMatchesWrapped() { 729 try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, false)) { 730 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 731 assertNotNull(bitmap); 732 733 try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) { 734 assertNotNull(hwBuffer2); 735 assertMatches(hwBuffer, hwBuffer2); 736 } 737 bitmap.recycle(); 738 } 739 } 740 parametersFor_testGetAllocationSizeWrappedBuffer()741 private static Object[] parametersFor_testGetAllocationSizeWrappedBuffer() { 742 return new Object[] { 743 HardwareBuffer.YCBCR_420_888, 744 HardwareBuffer.YCBCR_P010, 745 ImageFormat.YV12, 746 }; 747 } 748 749 @Test 750 @Parameters(method = "parametersFor_testGetAllocationSizeWrappedBuffer") testGetAllocationSizeWrappedBuffer(int format)751 public void testGetAllocationSizeWrappedBuffer(int format) { 752 final long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE; 753 assumeTrue(HardwareBuffer.isSupported(1, 1, format, 1, usage)); 754 HardwareBuffer buffer = HardwareBuffer.create(100, 100, format, 1, usage); 755 assertNotNull("isSupported = true but allocation failed", buffer); 756 Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, null); 757 buffer.close(); 758 try { 759 // We can probably assert closer to at least 100 * 100 but maybe someone has super 760 // duper good compression rates, so assume a lower bound of 2kb 761 assertTrue(bitmap.getAllocationByteCount() > 2000); 762 } finally { 763 bitmap.recycle(); 764 } 765 } 766 parametersFor_testGetHardwareBufferConfig()767 private static Object[] parametersFor_testGetHardwareBufferConfig() { 768 return new Object[] {Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565}; 769 } 770 771 @Test 772 @Parameters(method = "parametersFor_testGetHardwareBufferConfig") testGetHardwareBufferConfig(Config config)773 public void testGetHardwareBufferConfig(Config config) { 774 Bitmap bitmap = Bitmap.createBitmap(10, 10, config); 775 bitmap = bitmap.copy(Config.HARDWARE, false); 776 if (bitmap == null) { 777 fail("Failed to copy to HARDWARE with Config " + config); 778 } 779 try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) { 780 assertNotNull(hwBuffer); 781 assertEquals(hwBuffer.getWidth(), 10); 782 assertEquals(hwBuffer.getHeight(), 10); 783 } 784 } 785 786 @Test testGetHardwareBufferTwice()787 public void testGetHardwareBufferTwice() { 788 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 789 bitmap = bitmap.copy(Config.HARDWARE, false); 790 try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) { 791 assertNotNull(hwBuffer); 792 try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) { 793 assertNotNull(hwBuffer2); 794 assertMatches(hwBuffer, hwBuffer2); 795 } 796 } 797 } 798 799 @Test testGetHardwareBufferMatches()800 public void testGetHardwareBufferMatches() { 801 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 802 bitmap = bitmap.copy(Config.HARDWARE, false); 803 try (HardwareBuffer hwBuffer = bitmap.getHardwareBuffer()) { 804 HashSet<HardwareBuffer> set = new HashSet<HardwareBuffer>(); 805 set.add(hwBuffer); 806 assertTrue(set.contains(bitmap.getHardwareBuffer())); 807 } 808 } 809 810 @Test(expected = IllegalStateException.class) testGetHardwareBufferNonHardware()811 public void testGetHardwareBufferNonHardware() { 812 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 813 bitmap.getHardwareBuffer(); 814 } 815 816 @Test(expected = IllegalStateException.class) testGetHardwareBufferRecycled()817 public void testGetHardwareBufferRecycled() { 818 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 819 bitmap = bitmap.copy(Config.HARDWARE, false); 820 bitmap.recycle(); 821 bitmap.getHardwareBuffer(); 822 } 823 824 @Test testGetHardwareBufferClosed()825 public void testGetHardwareBufferClosed() { 826 HardwareBuffer hwBuffer = createTestBuffer(128, 128, false); 827 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 828 assertNotNull(bitmap); 829 830 hwBuffer.close(); 831 832 try (HardwareBuffer hwBuffer2 = bitmap.getHardwareBuffer()) { 833 assertNotNull(hwBuffer2); 834 assertFalse(hwBuffer2.isClosed()); 835 assertNotEquals(hwBuffer, hwBuffer2); 836 } 837 bitmap.recycle(); 838 } 839 840 @Test testGenerationId()841 public void testGenerationId() { 842 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 843 int genId = bitmap.getGenerationId(); 844 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 845 bitmap.setDensity(bitmap.getDensity() + 4); 846 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 847 bitmap.getPixel(0, 0); 848 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 849 850 int beforeGenId = bitmap.getGenerationId(); 851 bitmap.eraseColor(Color.WHITE); 852 int afterGenId = bitmap.getGenerationId(); 853 assertTrue("expected to increase", afterGenId > beforeGenId); 854 855 beforeGenId = bitmap.getGenerationId(); 856 bitmap.setPixel(4, 4, Color.BLUE); 857 afterGenId = bitmap.getGenerationId(); 858 assertTrue("expected to increase again", afterGenId > beforeGenId); 859 } 860 861 @Test testDescribeContents()862 public void testDescribeContents() { 863 assertEquals(0, mBitmap.describeContents()); 864 } 865 866 @Test(expected=IllegalStateException.class) testEraseColorOnRecycled()867 public void testEraseColorOnRecycled() { 868 mBitmap.recycle(); 869 870 mBitmap.eraseColor(0); 871 } 872 873 @Test(expected = IllegalStateException.class) testEraseColorLongOnRecycled()874 public void testEraseColorLongOnRecycled() { 875 mBitmap.recycle(); 876 877 mBitmap.eraseColor(Color.pack(0)); 878 } 879 880 @Test(expected=IllegalStateException.class) testEraseColorOnImmutable()881 public void testEraseColorOnImmutable() { 882 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 883 884 //abnormal case: bitmap is immutable 885 mBitmap.eraseColor(0); 886 } 887 888 @Test(expected = IllegalStateException.class) testEraseColorLongOnImmutable()889 public void testEraseColorLongOnImmutable() { 890 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 891 892 //abnormal case: bitmap is immutable 893 mBitmap.eraseColor(Color.pack(0)); 894 } 895 896 @Test testEraseColor()897 public void testEraseColor() { 898 // normal case 899 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 900 mBitmap.eraseColor(0xffff0000); 901 assertEquals(0xffff0000, mBitmap.getPixel(10, 10)); 902 assertEquals(0xffff0000, mBitmap.getPixel(50, 50)); 903 } 904 905 @Test(expected = IllegalArgumentException.class) testGetColorOOB()906 public void testGetColorOOB() { 907 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 908 mBitmap.getColor(-1, 0); 909 } 910 911 @Test(expected = IllegalArgumentException.class) testGetColorOOB2()912 public void testGetColorOOB2() { 913 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 914 mBitmap.getColor(5, -10); 915 } 916 917 @Test(expected = IllegalArgumentException.class) testGetColorOOB3()918 public void testGetColorOOB3() { 919 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 920 mBitmap.getColor(100, 10); 921 } 922 923 @Test(expected = IllegalArgumentException.class) testGetColorOOB4()924 public void testGetColorOOB4() { 925 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 926 mBitmap.getColor(99, 1000); 927 } 928 929 @Test(expected = IllegalStateException.class) testGetColorRecycled()930 public void testGetColorRecycled() { 931 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 932 mBitmap.recycle(); 933 mBitmap.getColor(0, 0); 934 } 935 936 @Test(expected = IllegalStateException.class) testGetColorHardware()937 public void testGetColorHardware() { 938 BitmapFactory.Options options = new BitmapFactory.Options(); 939 options.inPreferredConfig = Bitmap.Config.HARDWARE; 940 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, options); 941 mBitmap.getColor(50, 50); 942 943 } 944 clamp(float f)945 private static float clamp(float f) { 946 return clamp(f, 0.0f, 1.0f); 947 } 948 clamp(float f, float min, float max)949 private static float clamp(float f, float min, float max) { 950 return Math.min(Math.max(f, min), max); 951 } 952 953 @Test testGetColor()954 public void testGetColor() { 955 final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 956 List<ColorSpace> rgbColorSpaces = getRgbColorSpaces(); 957 for (Config config : new Config[] { Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565 }) { 958 for (ColorSpace bitmapColorSpace : rgbColorSpaces) { 959 mBitmap = Bitmap.createBitmap(1, 1, config, /*hasAlpha*/ false, 960 bitmapColorSpace); 961 bitmapColorSpace = mBitmap.getColorSpace(); 962 for (ColorSpace eraseColorSpace : rgbColorSpaces) { 963 for (long wideGamutLong : new long[] { 964 Color.pack(1.0f, 0.0f, 0.0f, 1.0f, eraseColorSpace), 965 Color.pack(0.0f, 1.0f, 0.0f, 1.0f, eraseColorSpace), 966 Color.pack(0.0f, 0.0f, 1.0f, 1.0f, eraseColorSpace)}) { 967 mBitmap.eraseColor(wideGamutLong); 968 969 Color result = mBitmap.getColor(0, 0); 970 if (mBitmap.getColorSpace().equals(sRGB)) { 971 assertEquals(mBitmap.getPixel(0, 0), result.toArgb()); 972 } 973 if (eraseColorSpace.equals(bitmapColorSpace)) { 974 final Color wideGamutColor = Color.valueOf(wideGamutLong); 975 ColorUtils.verifyColor("Erasing to Bitmap's ColorSpace " 976 + bitmapColorSpace, wideGamutColor, result, .001f); 977 978 } else { 979 Color convertedColor = Color.valueOf( 980 Color.convert(wideGamutLong, bitmapColorSpace)); 981 if (mBitmap.getConfig() != Config.RGBA_F16) { 982 // It's possible that we have to clip to fit into the Config. 983 convertedColor = Color.valueOf( 984 clamp(convertedColor.red()), 985 clamp(convertedColor.green()), 986 clamp(convertedColor.blue()), 987 convertedColor.alpha(), 988 bitmapColorSpace); 989 } 990 ColorUtils.verifyColor("Bitmap(Config: " + mBitmap.getConfig() 991 + ", ColorSpace: " + bitmapColorSpace 992 + ") erasing to " + Color.valueOf(wideGamutLong), 993 convertedColor, result, .03f); 994 } 995 } 996 } 997 } 998 } 999 } 1000 1001 private static class ARGB { 1002 public float alpha; 1003 public float red; 1004 public float green; 1005 public float blue; ARGB(float alpha, float red, float green, float blue)1006 ARGB(float alpha, float red, float green, float blue) { 1007 this.alpha = alpha; 1008 this.red = red; 1009 this.green = green; 1010 this.blue = blue; 1011 } 1012 }; 1013 1014 @Test testEraseColorLong()1015 public void testEraseColorLong() { 1016 List<ColorSpace> rgbColorSpaces = getRgbColorSpaces(); 1017 for (Config config : new Config[]{Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16}) { 1018 mBitmap = Bitmap.createBitmap(100, 100, config); 1019 // pack SRGB colors into ColorLongs. 1020 for (int color : new int[]{ Color.RED, Color.BLUE, Color.GREEN, Color.BLACK, 1021 Color.WHITE, Color.TRANSPARENT }) { 1022 if (config.equals(Config.RGB_565) && Float.compare(Color.alpha(color), 1.0f) != 0) { 1023 // 565 doesn't support alpha. 1024 continue; 1025 } 1026 mBitmap.eraseColor(Color.pack(color)); 1027 // The Bitmap is either SRGB or SRGBLinear (F16). getPixel(), which retrieves the 1028 // color in SRGB, should match exactly. 1029 ColorUtils.verifyColor("Config " + config + " mismatch at 10, 10 ", 1030 color, mBitmap.getPixel(10, 10), 0); 1031 ColorUtils.verifyColor("Config " + config + " mismatch at 50, 50 ", 1032 color, mBitmap.getPixel(50, 50), 0); 1033 } 1034 1035 // Use arbitrary colors in various ColorSpaces. getPixel() should approximately match 1036 // the SRGB version of the color. 1037 for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f), 1038 new ARGB(1.0f, .3f, .6f, .9f), 1039 new ARGB(0.5f, .2f, .8f, .7f) }) { 1040 if (config.equals(Config.RGB_565) && Float.compare(color.alpha, 1.0f) != 0) { 1041 continue; 1042 } 1043 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue); 1044 for (ColorSpace cs : rgbColorSpaces) { 1045 long longColor = Color.convert(srgbColor, cs); 1046 mBitmap.eraseColor(longColor); 1047 // These tolerances were chosen by trial and error. It is expected that 1048 // some conversions do not round-trip perfectly. 1049 int tolerance = 1; 1050 if (config.equals(Config.RGB_565)) { 1051 tolerance = 4; 1052 } else if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) { 1053 tolerance = 3; 1054 } 1055 1056 ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs 1057 + ", mismatch at 10, 10 ", srgbColor, mBitmap.getPixel(10, 10), 1058 tolerance); 1059 ColorUtils.verifyColor("Config " + config + ", ColorSpace " + cs 1060 + ", mismatch at 50, 50 ", srgbColor, mBitmap.getPixel(50, 50), 1061 tolerance); 1062 } 1063 } 1064 } 1065 } 1066 1067 @Test testEraseColorOnP3()1068 public void testEraseColorOnP3() { 1069 // Use a ColorLong with a different ColorSpace than the Bitmap. getPixel() should 1070 // approximately match the SRGB version of the color. 1071 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888, true, 1072 ColorSpace.get(ColorSpace.Named.DISPLAY_P3)); 1073 int srgbColor = Color.argb(.5f, .3f, .6f, .7f); 1074 long acesColor = Color.convert(srgbColor, ColorSpace.get(ColorSpace.Named.ACES)); 1075 mBitmap.eraseColor(acesColor); 1076 ColorUtils.verifyColor("Mismatch at 15, 15", srgbColor, mBitmap.getPixel(15, 15), 1); 1077 } 1078 1079 @Test(expected = IllegalArgumentException.class) testEraseColorXYZ()1080 public void testEraseColorXYZ() { 1081 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1082 mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ))); 1083 } 1084 1085 @Test(expected = IllegalArgumentException.class) testEraseColorLAB()1086 public void testEraseColorLAB() { 1087 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1088 mBitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB))); 1089 } 1090 1091 @Test(expected = IllegalArgumentException.class) testEraseColorUnknown()1092 public void testEraseColorUnknown() { 1093 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1094 mBitmap.eraseColor(-1L); 1095 } 1096 1097 @Test(expected=IllegalStateException.class) testExtractAlphaFromRecycled()1098 public void testExtractAlphaFromRecycled() { 1099 mBitmap.recycle(); 1100 1101 mBitmap.extractAlpha(); 1102 } 1103 1104 @Test testExtractAlpha()1105 public void testExtractAlpha() { 1106 // normal case 1107 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1108 Bitmap ret = mBitmap.extractAlpha(); 1109 assertNotNull(ret); 1110 int source = mBitmap.getPixel(10, 20); 1111 int result = ret.getPixel(10, 20); 1112 assertEquals(Color.alpha(source), Color.alpha(result)); 1113 assertEquals(0xFF, Color.alpha(result)); 1114 } 1115 1116 @Test(expected=IllegalStateException.class) testExtractAlphaWithPaintAndOffsetFromRecycled()1117 public void testExtractAlphaWithPaintAndOffsetFromRecycled() { 1118 mBitmap.recycle(); 1119 1120 mBitmap.extractAlpha(new Paint(), new int[]{0, 1}); 1121 } 1122 1123 @Test testExtractAlphaWithPaintAndOffset()1124 public void testExtractAlphaWithPaintAndOffset() { 1125 // normal case 1126 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1127 Bitmap ret = mBitmap.extractAlpha(new Paint(), new int[]{0, 1}); 1128 assertNotNull(ret); 1129 int source = mBitmap.getPixel(10, 20); 1130 int result = ret.getPixel(10, 20); 1131 assertEquals(Color.alpha(source), Color.alpha(result)); 1132 assertEquals(0xFF, Color.alpha(result)); 1133 } 1134 1135 @Test testGetAllocationByteCount()1136 public void testGetAllocationByteCount() { 1137 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); 1138 int alloc = mBitmap.getAllocationByteCount(); 1139 assertEquals(mBitmap.getByteCount(), alloc); 1140 1141 // reconfigure same size 1142 mBitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888); 1143 assertEquals(mBitmap.getByteCount(), alloc); 1144 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1145 1146 // reconfigure different size 1147 mBitmap.reconfigure(10, 10, Bitmap.Config.ALPHA_8); 1148 assertEquals(mBitmap.getByteCount(), 100); 1149 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1150 } 1151 1152 @Test testGetConfig()1153 public void testGetConfig() { 1154 Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); 1155 Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1156 Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1157 Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444); 1158 1159 assertEquals(Bitmap.Config.ALPHA_8, bm0.getConfig()); 1160 assertEquals(Bitmap.Config.ARGB_8888, bm1.getConfig()); 1161 assertEquals(Bitmap.Config.RGB_565, bm2.getConfig()); 1162 // Attempting to create a 4444 bitmap actually creates an 8888 bitmap. 1163 assertEquals(Bitmap.Config.ARGB_8888, bm3.getConfig()); 1164 1165 // Can't call Bitmap.createBitmap with Bitmap.Config.HARDWARE, 1166 // because createBitmap creates mutable bitmap and hardware bitmaps are always immutable, 1167 // so such call will throw an exception. 1168 Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, 1169 HARDWARE_OPTIONS); 1170 assertEquals(Bitmap.Config.HARDWARE, hardwareBitmap.getConfig()); 1171 } 1172 1173 @Test testGetHeight()1174 public void testGetHeight() { 1175 assertEquals(31, mBitmap.getHeight()); 1176 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1177 assertEquals(200, mBitmap.getHeight()); 1178 } 1179 1180 @Test testGetNinePatchChunk()1181 public void testGetNinePatchChunk() { 1182 assertNull(mBitmap.getNinePatchChunk()); 1183 } 1184 1185 @Test(expected=IllegalStateException.class) testGetPixelFromRecycled()1186 public void testGetPixelFromRecycled() { 1187 mBitmap.recycle(); 1188 1189 mBitmap.getPixel(10, 16); 1190 } 1191 1192 @Test(expected=IllegalArgumentException.class) testGetPixelXTooLarge()1193 public void testGetPixelXTooLarge() { 1194 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1195 1196 // abnormal case: x bigger than the source bitmap's width 1197 mBitmap.getPixel(200, 16); 1198 } 1199 1200 @Test(expected=IllegalArgumentException.class) testGetPixelYTooLarge()1201 public void testGetPixelYTooLarge() { 1202 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1203 1204 // abnormal case: y bigger than the source bitmap's height 1205 mBitmap.getPixel(10, 300); 1206 } 1207 1208 @Test testGetPixel()1209 public void testGetPixel() { 1210 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1211 1212 // normal case 565 1213 mBitmap.setPixel(10, 16, 0xFF << 24); 1214 assertEquals(0xFF << 24, mBitmap.getPixel(10, 16)); 1215 1216 // normal case A_8 1217 mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8); 1218 mBitmap.setPixel(5, 5, 0xFFFFFFFF); 1219 assertEquals(0xFF000000, mBitmap.getPixel(5, 5)); 1220 mBitmap.setPixel(5, 5, 0xA8A8A8A8); 1221 assertEquals(0xA8000000, mBitmap.getPixel(5, 5)); 1222 mBitmap.setPixel(5, 5, 0x00000000); 1223 assertEquals(0x00000000, mBitmap.getPixel(5, 5)); 1224 mBitmap.setPixel(5, 5, 0x1F000000); 1225 assertEquals(0x1F000000, mBitmap.getPixel(5, 5)); 1226 } 1227 1228 @Test testGetRowBytes()1229 public void testGetRowBytes() { 1230 Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); 1231 Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1232 Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1233 Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444); 1234 1235 assertEquals(100, bm0.getRowBytes()); 1236 assertEquals(400, bm1.getRowBytes()); 1237 assertEquals(200, bm2.getRowBytes()); 1238 // Attempting to create a 4444 bitmap actually creates an 8888 bitmap. 1239 assertEquals(400, bm3.getRowBytes()); 1240 } 1241 1242 @Test testGetWidth()1243 public void testGetWidth() { 1244 assertEquals(31, mBitmap.getWidth()); 1245 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1246 assertEquals(100, mBitmap.getWidth()); 1247 } 1248 1249 @Test testHasAlpha()1250 public void testHasAlpha() { 1251 assertFalse(mBitmap.hasAlpha()); 1252 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1253 assertTrue(mBitmap.hasAlpha()); 1254 } 1255 1256 @Test testIsMutable()1257 public void testIsMutable() { 1258 assertFalse(mBitmap.isMutable()); 1259 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1260 assertTrue(mBitmap.isMutable()); 1261 } 1262 1263 @Test testIsRecycled()1264 public void testIsRecycled() { 1265 assertFalse(mBitmap.isRecycled()); 1266 mBitmap.recycle(); 1267 assertTrue(mBitmap.isRecycled()); 1268 } 1269 1270 @Test testReconfigure()1271 public void testReconfigure() { 1272 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1273 int alloc = mBitmap.getAllocationByteCount(); 1274 1275 // test shrinking 1276 mBitmap.reconfigure(50, 100, Bitmap.Config.ALPHA_8); 1277 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1278 assertEquals(mBitmap.getByteCount() * 8, alloc); 1279 } 1280 1281 @Test(expected=IllegalArgumentException.class) testReconfigureExpanding()1282 public void testReconfigureExpanding() { 1283 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1284 mBitmap.reconfigure(101, 201, Bitmap.Config.ARGB_8888); 1285 } 1286 1287 @Test(expected=IllegalStateException.class) testReconfigureMutable()1288 public void testReconfigureMutable() { 1289 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1290 mBitmap.reconfigure(1, 1, Bitmap.Config.ALPHA_8); 1291 } 1292 1293 // Used by testAlphaAndPremul. 1294 private static Config[] CONFIGS = new Config[] { Config.ALPHA_8, Config.ARGB_4444, 1295 Config.ARGB_8888, Config.RGB_565 }; 1296 1297 // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with 1298 // respect to alpha and premultiplied. 1299 @Test testAlphaAndPremul()1300 public void testAlphaAndPremul() { 1301 boolean falseTrue[] = new boolean[] { false, true }; 1302 for (Config fromConfig : CONFIGS) { 1303 for (Config toConfig : CONFIGS) { 1304 for (boolean hasAlpha : falseTrue) { 1305 for (boolean isPremul : falseTrue) { 1306 Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig); 1307 1308 // 4444 is deprecated, and will convert to 8888. No need to 1309 // attempt a reconfigure, which will be tested when fromConfig 1310 // is 8888. 1311 if (fromConfig == Config.ARGB_4444) { 1312 assertEquals(bitmap.getConfig(), Config.ARGB_8888); 1313 break; 1314 } 1315 1316 bitmap.setHasAlpha(hasAlpha); 1317 bitmap.setPremultiplied(isPremul); 1318 1319 verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, false); 1320 1321 // reconfigure to a smaller size so the function will still succeed when 1322 // going to a Config that requires more bits. 1323 bitmap.reconfigure(1, 1, toConfig); 1324 if (toConfig == Config.ARGB_4444) { 1325 assertEquals(bitmap.getConfig(), Config.ARGB_8888); 1326 } else { 1327 assertEquals(bitmap.getConfig(), toConfig); 1328 } 1329 1330 // Check that the alpha and premultiplied state has not changed (unless 1331 // we expected it to). 1332 verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565); 1333 } 1334 } 1335 } 1336 } 1337 } 1338 1339 /** 1340 * Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied(). 1341 * @param bitmap Bitmap to check. 1342 * @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based 1343 * on what was set, but may be different from the actual return value depending on the 1344 * Config and convertedFrom565. 1345 * @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to 1346 * expectedAlpha, this is based on what was set, but may be different from the actual 1347 * return value depending on the Config. 1348 * @param convertedFrom565 Whether bitmap was converted to its current Config by being 1349 * reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha, 1350 * hasAlpha() is expected to be true even if expectedAlpha is false. 1351 */ verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565)1352 private void verifyAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, 1353 boolean convertedFrom565) { 1354 switch (bitmap.getConfig()) { 1355 case ARGB_4444: 1356 // This shouldn't happen, since we don't allow creating or converting 1357 // to 4444. 1358 assertFalse(true); 1359 break; 1360 case RGB_565: 1361 assertFalse(bitmap.hasAlpha()); 1362 assertFalse(bitmap.isPremultiplied()); 1363 break; 1364 case ALPHA_8: 1365 // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through. 1366 case ARGB_8888: 1367 // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type 1368 // that can have alpha. 1369 if (convertedFrom565) { 1370 assertTrue(bitmap.hasAlpha()); 1371 } else { 1372 assertEquals(bitmap.hasAlpha(), expectedAlpha); 1373 } 1374 1375 if (bitmap.hasAlpha()) { 1376 // ALPHA_8's premultiplied status is undefined. 1377 if (bitmap.getConfig() != Config.ALPHA_8) { 1378 assertEquals(bitmap.isPremultiplied(), expectedPremul); 1379 } 1380 } else { 1381 // Opaque bitmap is never considered premultiplied. 1382 assertFalse(bitmap.isPremultiplied()); 1383 } 1384 break; 1385 } 1386 } 1387 1388 @Test testSetColorSpace()1389 public void testSetColorSpace() { 1390 // Use arbitrary colors and assign to various ColorSpaces. 1391 for (ARGB color : new ARGB[]{ new ARGB(1.0f, .5f, .5f, .5f), 1392 new ARGB(1.0f, .3f, .6f, .9f), 1393 new ARGB(0.5f, .2f, .8f, .7f) }) { 1394 1395 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue); 1396 for (ColorSpace cs : getRgbColorSpaces()) { 1397 for (Config config : new Config[] { 1398 // F16 is tested elsewhere, since it defaults to EXTENDED_SRGB, and 1399 // many of these calls to setColorSpace would reduce the range, resulting 1400 // in an Exception. 1401 Config.ARGB_8888, 1402 Config.RGB_565, 1403 }) { 1404 mBitmap = Bitmap.createBitmap(10, 10, config); 1405 mBitmap.eraseColor(srgbColor); 1406 mBitmap.setColorSpace(cs); 1407 ColorSpace actual = mBitmap.getColorSpace(); 1408 if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) { 1409 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual); 1410 } else if (cs == ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) { 1411 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual); 1412 } else { 1413 assertSame(cs, actual); 1414 } 1415 1416 // This tolerance was chosen by trial and error. It is expected that 1417 // some conversions do not round-trip perfectly. 1418 int tolerance = 2; 1419 Color c = Color.valueOf(color.red, color.green, color.blue, color.alpha, cs); 1420 ColorUtils.verifyColor("Mismatch after setting the colorSpace to " 1421 + cs.getName(), c.convert(mBitmap.getColorSpace()), 1422 mBitmap.getColor(5, 5), tolerance); 1423 } 1424 } 1425 } 1426 } 1427 1428 @Test(expected = IllegalStateException.class) testSetColorSpaceRecycled()1429 public void testSetColorSpaceRecycled() { 1430 mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1431 mBitmap.recycle(); 1432 mBitmap.setColorSpace(ColorSpace.get(Named.DISPLAY_P3)); 1433 } 1434 1435 @Test(expected = IllegalArgumentException.class) testSetColorSpaceNull()1436 public void testSetColorSpaceNull() { 1437 mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1438 mBitmap.setColorSpace(null); 1439 } 1440 1441 @Test(expected = IllegalArgumentException.class) testSetColorSpaceXYZ()1442 public void testSetColorSpaceXYZ() { 1443 mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1444 mBitmap.setColorSpace(ColorSpace.get(Named.CIE_XYZ)); 1445 } 1446 1447 @Test(expected = IllegalArgumentException.class) testSetColorSpaceNoTransferParameters()1448 public void testSetColorSpaceNoTransferParameters() { 1449 mBitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1450 ColorSpace cs = new ColorSpace.Rgb("NoTransferParams", 1451 new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f }, 1452 ColorSpace.ILLUMINANT_D50, 1453 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f), 1454 0, 1); 1455 mBitmap.setColorSpace(cs); 1456 } 1457 1458 @Test(expected = IllegalArgumentException.class) testSetColorSpaceAlpha8()1459 public void testSetColorSpaceAlpha8() { 1460 mBitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8); 1461 assertNull(mBitmap.getColorSpace()); 1462 mBitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); 1463 } 1464 1465 @Test testSetColorSpaceReducedRange()1466 public void testSetColorSpaceReducedRange() { 1467 ColorSpace aces = ColorSpace.get(Named.ACES); 1468 mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, aces); 1469 try { 1470 mBitmap.setColorSpace(ColorSpace.get(Named.SRGB)); 1471 fail("Expected IllegalArgumentException!"); 1472 } catch (IllegalArgumentException e) { 1473 assertSame(aces, mBitmap.getColorSpace()); 1474 } 1475 } 1476 1477 @Test testSetColorSpaceNotReducedRange()1478 public void testSetColorSpaceNotReducedRange() { 1479 ColorSpace extended = ColorSpace.get(Named.EXTENDED_SRGB); 1480 mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, 1481 extended); 1482 mBitmap.setColorSpace(ColorSpace.get(Named.SRGB)); 1483 assertSame(mBitmap.getColorSpace(), extended); 1484 } 1485 1486 @Test testSetColorSpaceNotReducedRangeLinear()1487 public void testSetColorSpaceNotReducedRangeLinear() { 1488 ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB); 1489 mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, 1490 linearExtended); 1491 mBitmap.setColorSpace(ColorSpace.get(Named.LINEAR_SRGB)); 1492 assertSame(mBitmap.getColorSpace(), linearExtended); 1493 } 1494 1495 @Test testSetColorSpaceIncreasedRange()1496 public void testSetColorSpaceIncreasedRange() { 1497 mBitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, 1498 ColorSpace.get(Named.DISPLAY_P3)); 1499 ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB); 1500 mBitmap.setColorSpace(linearExtended); 1501 assertSame(mBitmap.getColorSpace(), linearExtended); 1502 } 1503 1504 @Test testSetConfig()1505 public void testSetConfig() { 1506 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1507 int alloc = mBitmap.getAllocationByteCount(); 1508 1509 // test shrinking 1510 mBitmap.setConfig(Bitmap.Config.ALPHA_8); 1511 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1512 assertEquals(mBitmap.getByteCount() * 2, alloc); 1513 } 1514 1515 @Test(expected=IllegalArgumentException.class) testSetConfigExpanding()1516 public void testSetConfigExpanding() { 1517 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1518 // test expanding 1519 mBitmap.setConfig(Bitmap.Config.ARGB_8888); 1520 } 1521 1522 @Test(expected=IllegalStateException.class) testSetConfigMutable()1523 public void testSetConfigMutable() { 1524 // test mutable 1525 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1526 mBitmap.setConfig(Bitmap.Config.ALPHA_8); 1527 } 1528 1529 @Test testSetHeight()1530 public void testSetHeight() { 1531 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1532 int alloc = mBitmap.getAllocationByteCount(); 1533 1534 // test shrinking 1535 mBitmap.setHeight(100); 1536 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1537 assertEquals(mBitmap.getByteCount() * 2, alloc); 1538 } 1539 1540 @Test(expected=IllegalArgumentException.class) testSetHeightExpanding()1541 public void testSetHeightExpanding() { 1542 // test expanding 1543 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1544 mBitmap.setHeight(201); 1545 } 1546 1547 @Test(expected=IllegalStateException.class) testSetHeightMutable()1548 public void testSetHeightMutable() { 1549 // test mutable 1550 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1551 mBitmap.setHeight(1); 1552 } 1553 1554 @Test(expected=IllegalStateException.class) testSetPixelOnRecycled()1555 public void testSetPixelOnRecycled() { 1556 int color = 0xff << 24; 1557 1558 mBitmap.recycle(); 1559 mBitmap.setPixel(10, 16, color); 1560 } 1561 1562 @Test(expected=IllegalStateException.class) testSetPixelOnImmutable()1563 public void testSetPixelOnImmutable() { 1564 int color = 0xff << 24; 1565 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1566 1567 mBitmap.setPixel(10, 16, color); 1568 } 1569 1570 @Test(expected=IllegalArgumentException.class) testSetPixelXIsTooLarge()1571 public void testSetPixelXIsTooLarge() { 1572 int color = 0xff << 24; 1573 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1574 1575 // abnormal case: x bigger than the source bitmap's width 1576 mBitmap.setPixel(200, 16, color); 1577 } 1578 1579 @Test(expected=IllegalArgumentException.class) testSetPixelYIsTooLarge()1580 public void testSetPixelYIsTooLarge() { 1581 int color = 0xff << 24; 1582 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1583 1584 // abnormal case: y bigger than the source bitmap's height 1585 mBitmap.setPixel(10, 300, color); 1586 } 1587 1588 @Test testSetPixel()1589 public void testSetPixel() { 1590 int color = 0xff << 24; 1591 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1592 1593 // normal case 1594 mBitmap.setPixel(10, 16, color); 1595 assertEquals(color, mBitmap.getPixel(10, 16)); 1596 } 1597 1598 @Test(expected=IllegalStateException.class) testSetPixelsOnRecycled()1599 public void testSetPixelsOnRecycled() { 1600 int[] colors = createColors(100); 1601 1602 mBitmap.recycle(); 1603 mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0); 1604 } 1605 1606 @Test(expected=IllegalStateException.class) testSetPixelsOnImmutable()1607 public void testSetPixelsOnImmutable() { 1608 int[] colors = createColors(100); 1609 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1610 1611 mBitmap.setPixels(colors, 0, 0, 0, 0, 0, 0); 1612 } 1613 1614 @Test(expected=IllegalArgumentException.class) testSetPixelsXYNegative()1615 public void testSetPixelsXYNegative() { 1616 int[] colors = createColors(100); 1617 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1618 1619 // abnormal case: x and/or y less than 0 1620 mBitmap.setPixels(colors, 0, 0, -1, -1, 200, 16); 1621 } 1622 1623 @Test(expected=IllegalArgumentException.class) testSetPixelsWidthHeightNegative()1624 public void testSetPixelsWidthHeightNegative() { 1625 int[] colors = createColors(100); 1626 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1627 1628 // abnormal case: width and/or height less than 0 1629 mBitmap.setPixels(colors, 0, 0, 0, 0, -1, -1); 1630 } 1631 1632 @Test(expected=IllegalArgumentException.class) testSetPixelsXTooHigh()1633 public void testSetPixelsXTooHigh() { 1634 int[] colors = createColors(100); 1635 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1636 1637 // abnormal case: (x + width) bigger than the source bitmap's width 1638 mBitmap.setPixels(colors, 0, 0, 10, 10, 95, 50); 1639 } 1640 1641 @Test(expected=IllegalArgumentException.class) testSetPixelsYTooHigh()1642 public void testSetPixelsYTooHigh() { 1643 int[] colors = createColors(100); 1644 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1645 1646 // abnormal case: (y + height) bigger than the source bitmap's height 1647 mBitmap.setPixels(colors, 0, 0, 10, 10, 50, 95); 1648 } 1649 1650 @Test(expected=IllegalArgumentException.class) testSetPixelsStrideIllegal()1651 public void testSetPixelsStrideIllegal() { 1652 int[] colors = createColors(100); 1653 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1654 1655 // abnormal case: stride less than width and bigger than -width 1656 mBitmap.setPixels(colors, 0, 10, 10, 10, 50, 50); 1657 } 1658 1659 @Test(expected=ArrayIndexOutOfBoundsException.class) testSetPixelsOffsetNegative()1660 public void testSetPixelsOffsetNegative() { 1661 int[] colors = createColors(100); 1662 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1663 1664 // abnormal case: offset less than 0 1665 mBitmap.setPixels(colors, -1, 50, 10, 10, 50, 50); 1666 } 1667 1668 @Test(expected=ArrayIndexOutOfBoundsException.class) testSetPixelsOffsetTooBig()1669 public void testSetPixelsOffsetTooBig() { 1670 int[] colors = createColors(100); 1671 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1672 1673 // abnormal case: (offset + width) bigger than the length of colors 1674 mBitmap.setPixels(colors, 60, 50, 10, 10, 50, 50); 1675 } 1676 1677 @Test(expected=ArrayIndexOutOfBoundsException.class) testSetPixelsLastScanlineNegative()1678 public void testSetPixelsLastScanlineNegative() { 1679 int[] colors = createColors(100); 1680 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1681 1682 // abnormal case: lastScanline less than 0 1683 mBitmap.setPixels(colors, 10, -50, 10, 10, 50, 50); 1684 } 1685 1686 @Test(expected=ArrayIndexOutOfBoundsException.class) testSetPixelsLastScanlineTooBig()1687 public void testSetPixelsLastScanlineTooBig() { 1688 int[] colors = createColors(100); 1689 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1690 1691 // abnormal case: (lastScanline + width) bigger than the length of colors 1692 mBitmap.setPixels(colors, 10, 50, 10, 10, 50, 50); 1693 } 1694 1695 @Test testSetPixels()1696 public void testSetPixels() { 1697 int[] colors = createColors(100 * 100); 1698 mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1699 mBitmap.setPixels(colors, 0, 100, 0, 0, 100, 100); 1700 int[] ret = new int[100 * 100]; 1701 mBitmap.getPixels(ret, 0, 100, 0, 0, 100, 100); 1702 1703 for(int i = 0; i < 10000; i++){ 1704 assertEquals(ret[i], colors[i]); 1705 } 1706 } 1707 verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul)1708 private void verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul) { 1709 Bitmap bitmap = Bitmap.createBitmap(1, 1, config); 1710 bitmap.setPremultiplied(true); 1711 bitmap.setPixel(0, 0, Color.TRANSPARENT); 1712 assertTrue(bitmap.isPremultiplied() == expectedPremul); 1713 1714 bitmap.setHasAlpha(false); 1715 assertFalse(bitmap.isPremultiplied()); 1716 } 1717 1718 @Test testSetPremultipliedSimple()1719 public void testSetPremultipliedSimple() { 1720 verifyPremultipliedBitmapConfig(Bitmap.Config.ALPHA_8, true); 1721 verifyPremultipliedBitmapConfig(Bitmap.Config.RGB_565, false); 1722 verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_4444, true); 1723 verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_8888, true); 1724 } 1725 1726 @Test testSetPremultipliedData()1727 public void testSetPremultipliedData() { 1728 // with premul, will store 2,2,2,2, so it doesn't get value correct 1729 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1730 bitmap.setPixel(0, 0, PREMUL_COLOR); 1731 assertEquals(bitmap.getPixel(0, 0), PREMUL_ROUNDED_COLOR); 1732 1733 // read premultiplied value directly 1734 bitmap.setPremultiplied(false); 1735 assertEquals(bitmap.getPixel(0, 0), PREMUL_STORED_COLOR); 1736 1737 // value can now be stored/read correctly 1738 bitmap.setPixel(0, 0, PREMUL_COLOR); 1739 assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR); 1740 1741 // verify with array methods 1742 int testArray[] = new int[] { PREMUL_COLOR }; 1743 bitmap.setPixels(testArray, 0, 1, 0, 0, 1, 1); 1744 bitmap.getPixels(testArray, 0, 1, 0, 0, 1, 1); 1745 assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR); 1746 } 1747 1748 @Test testPremultipliedCanvas()1749 public void testPremultipliedCanvas() { 1750 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1751 bitmap.setHasAlpha(true); 1752 bitmap.setPremultiplied(false); 1753 assertFalse(bitmap.isPremultiplied()); 1754 1755 Canvas c = new Canvas(); 1756 try { 1757 c.drawBitmap(bitmap, 0, 0, null); 1758 fail("canvas should fail with exception"); 1759 } catch (RuntimeException e) { 1760 } 1761 } 1762 getBitmapRawInt(Bitmap bitmap)1763 private int getBitmapRawInt(Bitmap bitmap) { 1764 IntBuffer buffer = IntBuffer.allocate(1); 1765 bitmap.copyPixelsToBuffer(buffer); 1766 return buffer.get(0); 1767 } 1768 bitmapStoreRawInt(Bitmap bitmap, int value)1769 private void bitmapStoreRawInt(Bitmap bitmap, int value) { 1770 IntBuffer buffer = IntBuffer.allocate(1); 1771 buffer.put(0, value); 1772 bitmap.copyPixelsFromBuffer(buffer); 1773 } 1774 1775 @Test testSetPremultipliedToBuffer()1776 public void testSetPremultipliedToBuffer() { 1777 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1778 bitmap.setPixel(0, 0, PREMUL_COLOR); 1779 int storedPremul = getBitmapRawInt(bitmap); 1780 1781 bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1782 bitmap.setPremultiplied(false); 1783 bitmap.setPixel(0, 0, PREMUL_STORED_COLOR); 1784 1785 assertEquals(getBitmapRawInt(bitmap), storedPremul); 1786 } 1787 1788 @Test testSetPremultipliedFromBuffer()1789 public void testSetPremultipliedFromBuffer() { 1790 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1791 bitmap.setPremultiplied(false); 1792 bitmap.setPixel(0, 0, PREMUL_COLOR); 1793 int rawTestColor = getBitmapRawInt(bitmap); 1794 1795 bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1796 bitmap.setPremultiplied(false); 1797 bitmapStoreRawInt(bitmap, rawTestColor); 1798 assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR); 1799 } 1800 1801 @Test testSetWidth()1802 public void testSetWidth() { 1803 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1804 int alloc = mBitmap.getAllocationByteCount(); 1805 1806 // test shrinking 1807 mBitmap.setWidth(50); 1808 assertEquals(mBitmap.getAllocationByteCount(), alloc); 1809 assertEquals(mBitmap.getByteCount() * 2, alloc); 1810 } 1811 1812 @Test(expected=IllegalArgumentException.class) testSetWidthExpanding()1813 public void testSetWidthExpanding() { 1814 // test expanding 1815 mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1816 1817 mBitmap.setWidth(101); 1818 } 1819 1820 @Test(expected=IllegalStateException.class) testSetWidthMutable()1821 public void testSetWidthMutable() { 1822 // test mutable 1823 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1824 1825 mBitmap.setWidth(1); 1826 } 1827 1828 @Test(expected=IllegalStateException.class) testWriteToParcelRecycled()1829 public void testWriteToParcelRecycled() { 1830 mBitmap.recycle(); 1831 1832 mBitmap.writeToParcel(null, 0); 1833 } 1834 1835 @Test testWriteToParcel()1836 public void testWriteToParcel() { 1837 // abnormal case: failed to unparcel Bitmap 1838 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, mOptions); 1839 Parcel p = Parcel.obtain(); 1840 mBitmap.writeToParcel(p, 0); 1841 1842 try { 1843 Bitmap.CREATOR.createFromParcel(p); 1844 fail("shouldn't come to here"); 1845 } catch(RuntimeException e){ 1846 } 1847 1848 p.recycle(); 1849 // normal case 1850 p = Parcel.obtain(); 1851 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1852 mBitmap.writeToParcel(p, 0); 1853 p.setDataPosition(0); 1854 assertTrue(mBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p))); 1855 1856 p.recycle(); 1857 } 1858 1859 /** 1860 * Although not specified as required behavior, it's something that some apps appear 1861 * to rely upon when sending bitmaps between themselves 1862 */ 1863 @Test testWriteToParcelPreserveMutability()1864 public void testWriteToParcelPreserveMutability() { 1865 Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1866 assertTrue(source.isMutable()); 1867 Parcel p = Parcel.obtain(); 1868 source.writeToParcel(p, 0); 1869 p.setDataPosition(0); 1870 Bitmap result = Bitmap.CREATOR.createFromParcel(p); 1871 p.recycle(); 1872 assertTrue(result.isMutable()); 1873 } 1874 1875 @Test testWriteToParcelPreserveImmutability()1876 public void testWriteToParcelPreserveImmutability() { 1877 // Kinda silly way to create an immutable bitmap but it works 1878 Bitmap source = Bitmap.createBitmap(100, 100, Config.ARGB_8888) 1879 .copy(Config.ARGB_8888, false); 1880 assertFalse(source.isMutable()); 1881 Parcel p = Parcel.obtain(); 1882 source.writeToParcel(p, 0); 1883 p.setDataPosition(0); 1884 Bitmap result = Bitmap.CREATOR.createFromParcel(p); 1885 p.recycle(); 1886 assertFalse(result.isMutable()); 1887 } 1888 1889 @Test testWriteHwBitmapToParcel()1890 public void testWriteHwBitmapToParcel() { 1891 mBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 1892 Parcel p = Parcel.obtain(); 1893 mBitmap.writeToParcel(p, 0); 1894 p.setDataPosition(0); 1895 Bitmap expectedBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot); 1896 assertTrue(expectedBitmap.sameAs(Bitmap.CREATOR.createFromParcel(p))); 1897 1898 p.recycle(); 1899 } 1900 1901 @Test testParcelF16ColorSpace()1902 public void testParcelF16ColorSpace() { 1903 for (ColorSpace.Named e : new ColorSpace.Named[] { 1904 ColorSpace.Named.EXTENDED_SRGB, 1905 ColorSpace.Named.LINEAR_EXTENDED_SRGB, 1906 ColorSpace.Named.PRO_PHOTO_RGB, 1907 ColorSpace.Named.DISPLAY_P3 1908 }) { 1909 final ColorSpace cs = ColorSpace.get(e); 1910 Bitmap b = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, cs); 1911 assertSame(cs, b.getColorSpace()); 1912 1913 Parcel p = Parcel.obtain(); 1914 b.writeToParcel(p, 0); 1915 p.setDataPosition(0); 1916 Bitmap unparceled = Bitmap.CREATOR.createFromParcel(p); 1917 assertSame(cs, unparceled.getColorSpace()); 1918 } 1919 } 1920 1921 @Test testGetScaledHeight1()1922 public void testGetScaledHeight1() { 1923 int dummyDensity = 5; 1924 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1925 int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity); 1926 assertNotNull(ret); 1927 assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity)); 1928 } 1929 1930 @Test testGetScaledHeight2()1931 public void testGetScaledHeight2() { 1932 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1933 DisplayMetrics metrics = 1934 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics(); 1935 int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi); 1936 assertEquals(scaledHeight, ret.getScaledHeight(metrics)); 1937 } 1938 1939 @Test testGetScaledHeight3()1940 public void testGetScaledHeight3() { 1941 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1942 Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 1943 Canvas mCanvas = new Canvas(mMutableBitmap); 1944 // set Density 1945 mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH); 1946 int scaledHeight = scaleFromDensity( 1947 ret.getHeight(), ret.getDensity(), mCanvas.getDensity()); 1948 assertEquals(scaledHeight, ret.getScaledHeight(mCanvas)); 1949 } 1950 1951 @Test testGetScaledWidth1()1952 public void testGetScaledWidth1() { 1953 int dummyDensity = 5; 1954 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1955 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity); 1956 assertNotNull(ret); 1957 assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity)); 1958 } 1959 1960 @Test testGetScaledWidth2()1961 public void testGetScaledWidth2() { 1962 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1963 DisplayMetrics metrics = 1964 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics(); 1965 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi); 1966 assertEquals(scaledWidth, ret.getScaledWidth(metrics)); 1967 } 1968 1969 @Test testGetScaledWidth3()1970 public void testGetScaledWidth3() { 1971 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1972 Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 1973 Canvas mCanvas = new Canvas(mMutableBitmap); 1974 // set Density 1975 mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH); 1976 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), mCanvas.getDensity()); 1977 assertEquals(scaledWidth, ret.getScaledWidth(mCanvas)); 1978 } 1979 1980 @Test testSameAs_simpleSuccess()1981 public void testSameAs_simpleSuccess() { 1982 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1983 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1984 bitmap1.eraseColor(Color.BLACK); 1985 bitmap2.eraseColor(Color.BLACK); 1986 assertTrue(bitmap1.sameAs(bitmap2)); 1987 assertTrue(bitmap2.sameAs(bitmap1)); 1988 } 1989 1990 @Test testSameAs_simpleFail()1991 public void testSameAs_simpleFail() { 1992 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1993 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1994 bitmap1.eraseColor(Color.BLACK); 1995 bitmap2.eraseColor(Color.BLACK); 1996 bitmap2.setPixel(20, 10, Color.WHITE); 1997 assertFalse(bitmap1.sameAs(bitmap2)); 1998 assertFalse(bitmap2.sameAs(bitmap1)); 1999 } 2000 2001 @Test testSameAs_reconfigure()2002 public void testSameAs_reconfigure() { 2003 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2004 Bitmap bitmap2 = Bitmap.createBitmap(150, 150, Config.ARGB_8888); 2005 bitmap2.reconfigure(100, 100, Config.ARGB_8888); // now same size, so should be same 2006 bitmap1.eraseColor(Color.BLACK); 2007 bitmap2.eraseColor(Color.BLACK); 2008 assertTrue(bitmap1.sameAs(bitmap2)); 2009 assertTrue(bitmap2.sameAs(bitmap1)); 2010 } 2011 2012 @Test testSameAs_config()2013 public void testSameAs_config() { 2014 Bitmap bitmap1 = Bitmap.createBitmap(100, 200, Config.RGB_565); 2015 Bitmap bitmap2 = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 2016 2017 // both bitmaps can represent black perfectly 2018 bitmap1.eraseColor(Color.BLACK); 2019 bitmap2.eraseColor(Color.BLACK); 2020 2021 // but not same due to config 2022 assertFalse(bitmap1.sameAs(bitmap2)); 2023 assertFalse(bitmap2.sameAs(bitmap1)); 2024 } 2025 2026 @Test testSameAs_width()2027 public void testSameAs_width() { 2028 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2029 Bitmap bitmap2 = Bitmap.createBitmap(101, 100, Config.ARGB_8888); 2030 bitmap1.eraseColor(Color.BLACK); 2031 bitmap2.eraseColor(Color.BLACK); 2032 assertFalse(bitmap1.sameAs(bitmap2)); 2033 assertFalse(bitmap2.sameAs(bitmap1)); 2034 } 2035 2036 @Test testSameAs_height()2037 public void testSameAs_height() { 2038 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2039 Bitmap bitmap2 = Bitmap.createBitmap(102, 100, Config.ARGB_8888); 2040 bitmap1.eraseColor(Color.BLACK); 2041 bitmap2.eraseColor(Color.BLACK); 2042 assertFalse(bitmap1.sameAs(bitmap2)); 2043 assertFalse(bitmap2.sameAs(bitmap1)); 2044 } 2045 2046 @Test testSameAs_opaque()2047 public void testSameAs_opaque() { 2048 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2049 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2050 bitmap1.eraseColor(Color.BLACK); 2051 bitmap2.eraseColor(Color.BLACK); 2052 bitmap1.setHasAlpha(true); 2053 bitmap2.setHasAlpha(false); 2054 assertFalse(bitmap1.sameAs(bitmap2)); 2055 assertFalse(bitmap2.sameAs(bitmap1)); 2056 } 2057 2058 @Test testSameAs_hardware()2059 public void testSameAs_hardware() { 2060 Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2061 Bitmap bitmap2 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2062 Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot); 2063 Bitmap bitmap4 = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS); 2064 assertTrue(bitmap1.sameAs(bitmap2)); 2065 assertTrue(bitmap2.sameAs(bitmap1)); 2066 assertFalse(bitmap1.sameAs(bitmap3)); 2067 assertFalse(bitmap1.sameAs(bitmap4)); 2068 } 2069 2070 @Test testSameAs_wrappedHardwareBuffer()2071 public void testSameAs_wrappedHardwareBuffer() { 2072 try (HardwareBuffer hwBufferA = createTestBuffer(512, 512, true); 2073 HardwareBuffer hwBufferB = createTestBuffer(512, 512, true); 2074 HardwareBuffer hwBufferC = createTestBuffer(512, 512, true);) { 2075 // Fill buffer C with generated data 2076 nFillRgbaHwBuffer(hwBufferC); 2077 2078 // Create the test bitmaps 2079 Bitmap bitmap1 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB)); 2080 Bitmap bitmap2 = Bitmap.wrapHardwareBuffer(hwBufferA, ColorSpace.get(Named.SRGB)); 2081 Bitmap bitmap3 = BitmapFactory.decodeResource(mRes, R.drawable.robot); 2082 Bitmap bitmap4 = Bitmap.wrapHardwareBuffer(hwBufferB, ColorSpace.get(Named.SRGB)); 2083 Bitmap bitmap5 = Bitmap.wrapHardwareBuffer(hwBufferC, ColorSpace.get(Named.SRGB)); 2084 2085 // Run the compare-a-thon 2086 assertTrue(bitmap1.sameAs(bitmap2)); // SAME UNDERLYING BUFFER 2087 assertTrue(bitmap2.sameAs(bitmap1)); // SAME UNDERLYING BUFFER 2088 assertFalse(bitmap1.sameAs(bitmap3)); // HW vs. NON-HW 2089 assertTrue(bitmap1.sameAs(bitmap4)); // DIFFERENT BUFFERS, SAME CONTENT 2090 assertFalse(bitmap1.sameAs(bitmap5)); // DIFFERENT BUFFERS, DIFFERENT CONTENT 2091 } 2092 } 2093 2094 @Test(expected=IllegalStateException.class) testHardwareGetPixel()2095 public void testHardwareGetPixel() { 2096 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2097 bitmap.getPixel(0, 0); 2098 } 2099 2100 @Test(expected=IllegalStateException.class) testHardwareGetPixels()2101 public void testHardwareGetPixels() { 2102 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2103 bitmap.getPixels(new int[5], 0, 5, 0, 0, 5, 1); 2104 } 2105 2106 @Test testGetConfigOnRecycled()2107 public void testGetConfigOnRecycled() { 2108 Bitmap bitmap1 = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2109 bitmap1.recycle(); 2110 assertEquals(Config.HARDWARE, bitmap1.getConfig()); 2111 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2112 bitmap2.recycle(); 2113 assertEquals(Config.ARGB_8888, bitmap2.getConfig()); 2114 } 2115 2116 @Test(expected = IllegalStateException.class) testHardwareSetWidth()2117 public void testHardwareSetWidth() { 2118 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2119 bitmap.setWidth(30); 2120 } 2121 2122 @Test(expected = IllegalStateException.class) testHardwareSetHeight()2123 public void testHardwareSetHeight() { 2124 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2125 bitmap.setHeight(30); 2126 } 2127 2128 @Test(expected = IllegalStateException.class) testHardwareSetConfig()2129 public void testHardwareSetConfig() { 2130 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2131 bitmap.setConfig(Config.ARGB_8888); 2132 } 2133 2134 @Test(expected = IllegalStateException.class) testHardwareReconfigure()2135 public void testHardwareReconfigure() { 2136 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2137 bitmap.reconfigure(30, 30, Config.ARGB_8888); 2138 } 2139 2140 @Test(expected = IllegalStateException.class) testHardwareSetPixels()2141 public void testHardwareSetPixels() { 2142 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2143 bitmap.setPixels(new int[10], 0, 1, 0, 0, 1, 1); 2144 } 2145 2146 @Test(expected = IllegalStateException.class) testHardwareSetPixel()2147 public void testHardwareSetPixel() { 2148 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2149 bitmap.setPixel(1, 1, 0); 2150 } 2151 2152 @Test(expected = IllegalStateException.class) testHardwareEraseColor()2153 public void testHardwareEraseColor() { 2154 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2155 bitmap.eraseColor(0); 2156 } 2157 2158 @Test(expected = IllegalStateException.class) testHardwareEraseColorLong()2159 public void testHardwareEraseColorLong() { 2160 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, HARDWARE_OPTIONS); 2161 bitmap.eraseColor(Color.pack(0)); 2162 } 2163 2164 @Test(expected = IllegalStateException.class) testHardwareCopyPixelsToBuffer()2165 public void testHardwareCopyPixelsToBuffer() { 2166 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS); 2167 ByteBuffer byteBuf = ByteBuffer.allocate(bitmap.getRowBytes() * bitmap.getHeight()); 2168 bitmap.copyPixelsToBuffer(byteBuf); 2169 } 2170 2171 @Test(expected = IllegalStateException.class) testHardwareCopyPixelsFromBuffer()2172 public void testHardwareCopyPixelsFromBuffer() { 2173 IntBuffer intBuf1 = IntBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight()); 2174 assertEquals(0, intBuf1.position()); 2175 mBitmap.copyPixelsToBuffer(intBuf1); 2176 Bitmap hwBitmap = BitmapFactory.decodeResource(mRes, R.drawable.start, HARDWARE_OPTIONS); 2177 hwBitmap.copyPixelsFromBuffer(intBuf1); 2178 } 2179 2180 @Test testUseMetadataAfterRecycle()2181 public void testUseMetadataAfterRecycle() { 2182 Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565); 2183 bitmap.recycle(); 2184 assertEquals(10, bitmap.getWidth()); 2185 assertEquals(20, bitmap.getHeight()); 2186 assertEquals(Config.RGB_565, bitmap.getConfig()); 2187 } 2188 2189 @Test testCopyHWBitmapInStrictMode()2190 public void testCopyHWBitmapInStrictMode() { 2191 strictModeTest(()->{ 2192 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2193 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 2194 hwBitmap.copy(Config.ARGB_8888, false); 2195 }); 2196 } 2197 2198 @Test testCreateScaledFromHWInStrictMode()2199 public void testCreateScaledFromHWInStrictMode() { 2200 strictModeTest(()->{ 2201 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2202 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 2203 Bitmap.createScaledBitmap(hwBitmap, 200, 200, false); 2204 }); 2205 } 2206 2207 @Test testExtractAlphaFromHWInStrictMode()2208 public void testExtractAlphaFromHWInStrictMode() { 2209 strictModeTest(()->{ 2210 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2211 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 2212 hwBitmap.extractAlpha(); 2213 }); 2214 } 2215 2216 @Test testCompressInStrictMode()2217 public void testCompressInStrictMode() { 2218 strictModeTest(()->{ 2219 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2220 bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream()); 2221 }); 2222 } 2223 2224 @Test testParcelHWInStrictMode()2225 public void testParcelHWInStrictMode() { 2226 strictModeTest(()->{ 2227 mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2228 Bitmap hwBitmap = mBitmap.copy(Config.HARDWARE, false); 2229 hwBitmap.writeToParcel(Parcel.obtain(), 0); 2230 }); 2231 } 2232 2233 @Test testSameAsFirstHWInStrictMode()2234 public void testSameAsFirstHWInStrictMode() { 2235 strictModeTest(()->{ 2236 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2237 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 2238 hwBitmap.sameAs(bitmap); 2239 }); 2240 } 2241 2242 @Test testSameAsSecondHWInStrictMode()2243 public void testSameAsSecondHWInStrictMode() { 2244 strictModeTest(()->{ 2245 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 2246 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 2247 bitmap.sameAs(hwBitmap); 2248 }); 2249 } 2250 2251 @Test testNdkAccessAfterRecycle()2252 public void testNdkAccessAfterRecycle() { 2253 Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565); 2254 Bitmap hardware = bitmap.copy(Config.HARDWARE, false); 2255 nValidateBitmapInfo(bitmap, 10, 20, true); 2256 nValidateBitmapInfo(hardware, 10, 20, true); 2257 2258 bitmap.recycle(); 2259 hardware.recycle(); 2260 2261 nValidateBitmapInfo(bitmap, 10, 20, true); 2262 nValidateBitmapInfo(hardware, 10, 20, true); 2263 nValidateNdkAccessFails(bitmap); 2264 } 2265 2266 @Test bitmapIsMutable()2267 public void bitmapIsMutable() { 2268 Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 2269 assertTrue("CreateBitmap w/ params should be mutable", b.isMutable()); 2270 assertTrue("CreateBitmap from bitmap should be mutable", 2271 Bitmap.createBitmap(b).isMutable()); 2272 } 2273 2274 @Test 2275 @LargeTest testHardwareBitmapNotLeaking()2276 public void testHardwareBitmapNotLeaking() { 2277 BitmapFactory.Options opts = new BitmapFactory.Options(); 2278 opts.inPreferredConfig = Config.HARDWARE; 2279 opts.inScaled = false; 2280 2281 runNotLeakingTest(() -> { 2282 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts); 2283 assertNotNull(bitmap); 2284 // Make sure nothing messed with the bitmap 2285 assertEquals(128, bitmap.getWidth()); 2286 assertEquals(128, bitmap.getHeight()); 2287 assertEquals(Config.HARDWARE, bitmap.getConfig()); 2288 bitmap.recycle(); 2289 }); 2290 } 2291 2292 @Test 2293 @LargeTest testWrappedHardwareBufferBitmapNotLeaking()2294 public void testWrappedHardwareBufferBitmapNotLeaking() { 2295 final ColorSpace colorSpace = ColorSpace.get(Named.SRGB); 2296 try (HardwareBuffer hwBuffer = createTestBuffer(1024, 512, false)) { 2297 runNotLeakingTest(() -> { 2298 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, colorSpace); 2299 assertNotNull(bitmap); 2300 // Make sure nothing messed with the bitmap 2301 assertEquals(1024, bitmap.getWidth()); 2302 assertEquals(512, bitmap.getHeight()); 2303 assertEquals(Config.HARDWARE, bitmap.getConfig()); 2304 bitmap.recycle(); 2305 }); 2306 } 2307 } 2308 2309 @Test 2310 @LargeTest testDrawingHardwareBitmapNotLeaking()2311 public void testDrawingHardwareBitmapNotLeaking() { 2312 BitmapFactory.Options opts = new BitmapFactory.Options(); 2313 opts.inPreferredConfig = Config.HARDWARE; 2314 opts.inScaled = false; 2315 RenderTarget renderTarget = RenderTarget.create(); 2316 renderTarget.setDefaultSize(128, 128); 2317 final Surface surface = renderTarget.getSurface(); 2318 2319 runNotLeakingTest(() -> { 2320 Bitmap bitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot, opts); 2321 assertNotNull(bitmap); 2322 // Make sure nothing messed with the bitmap 2323 assertEquals(128, bitmap.getWidth()); 2324 assertEquals(128, bitmap.getHeight()); 2325 assertEquals(Config.HARDWARE, bitmap.getConfig()); 2326 Canvas canvas = surface.lockHardwareCanvas(); 2327 canvas.drawBitmap(bitmap, 0, 0, null); 2328 surface.unlockCanvasAndPost(canvas); 2329 bitmap.recycle(); 2330 }); 2331 2332 surface.release(); 2333 renderTarget.destroy(); 2334 } 2335 2336 @Test testWrapHardwareBufferHoldsReference()2337 public void testWrapHardwareBufferHoldsReference() { 2338 Bitmap bitmap; 2339 // Create hardware-buffer and wrap it in a Bitmap 2340 try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) { 2341 // Fill buffer with colors (x, y, 42, 255) 2342 nFillRgbaHwBuffer(hwBuffer); 2343 bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 2344 } 2345 2346 // Buffer is closed at this point. Ensure bitmap still works by drawing it 2347 assertEquals(128, bitmap.getWidth()); 2348 assertEquals(128, bitmap.getHeight()); 2349 assertEquals(Config.HARDWARE, bitmap.getConfig()); 2350 2351 // Copy bitmap to target bitmap we can read from 2352 Bitmap dstBitmap = bitmap.copy(Config.ARGB_8888, false); 2353 bitmap.recycle(); 2354 2355 // Ensure that the bitmap has valid contents 2356 int pixel = dstBitmap.getPixel(0, 0); 2357 assertEquals(255 << 24 | 42, pixel); 2358 dstBitmap.recycle(); 2359 } 2360 2361 @Test testWrapHardwareBufferPreservesColors()2362 public void testWrapHardwareBufferPreservesColors() { 2363 try (HardwareBuffer hwBuffer = createTestBuffer(128, 128, true)) { 2364 // Fill buffer with colors (x, y, 42, 255) 2365 nFillRgbaHwBuffer(hwBuffer); 2366 2367 // Create HW bitmap from this buffer 2368 Bitmap srcBitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 2369 assertNotNull(srcBitmap); 2370 2371 // Copy it to target non-HW bitmap 2372 Bitmap dstBitmap = srcBitmap.copy(Config.ARGB_8888, false); 2373 srcBitmap.recycle(); 2374 2375 // Ensure all colors are as expected (matches the nFillRgbaHwBuffer call used above). 2376 for (int y = 0; y < 128; ++y) { 2377 for (int x = 0; x < 128; ++x) { 2378 int pixel = dstBitmap.getPixel(x, y); 2379 short a = 255; 2380 short r = (short) (x % 255); 2381 short g = (short) (y % 255); 2382 short b = 42; 2383 assertEquals(a << 24 | r << 16 | g << 8 | b, pixel); 2384 } 2385 } 2386 dstBitmap.recycle(); 2387 } 2388 } 2389 compressToPng(Bitmap bitmap)2390 private static byte[] compressToPng(Bitmap bitmap) { 2391 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { 2392 assertTrue("Failed to encode a Bitmap with Config " + bitmap.getConfig() 2393 + " and ColorSpace " + bitmap.getColorSpace() + "!", 2394 bitmap.compress(CompressFormat.PNG, 100, stream)); 2395 return stream.toByteArray(); 2396 } catch (IOException e) { 2397 fail("Failed to compress with " + e); 2398 return null; 2399 } 2400 } 2401 parametersForTestAsShared()2402 private static Object[] parametersForTestAsShared() { 2403 return Utils.crossProduct(Config.values(), getRgbColorSpaces().toArray(new Object[0])); 2404 } 2405 2406 @Test 2407 @Parameters(method = "parametersForTestAsShared") testAsShared(Config config, ColorSpace colorSpace)2408 public void testAsShared(Config config, ColorSpace colorSpace) { 2409 Bitmap original = Bitmap.createBitmap(10, 10, 2410 config == Config.HARDWARE ? Config.ARGB_8888 : config, true /*hasAlpha*/, 2411 colorSpace); 2412 drawGradient(original); 2413 2414 if (config == Config.HARDWARE) { 2415 original = original.copy(Config.HARDWARE, false /*mutable*/); 2416 } 2417 2418 // There's no visible way to test that the memory is allocated in shared memory, but we can 2419 // verify that the Bitmaps look the same. 2420 Bitmap shared = original.asShared(); 2421 assertNotNull(shared); 2422 2423 if (config == Config.HARDWARE) { 2424 int expectedFormat = nGetFormat(original); 2425 assertEquals(expectedFormat, configToFormat(shared.getConfig())); 2426 2427 // There's no public way to look at the pixels in the HARDWARE Bitmap, but if we 2428 // compress each as a lossless PNG, they should look identical. 2429 byte[] origBytes = compressToPng(original); 2430 byte[] sharedBytes = compressToPng(shared); 2431 assertTrue(Arrays.equals(origBytes, sharedBytes)); 2432 } else { 2433 assertSame(original.getConfig(), shared.getConfig()); 2434 assertTrue(shared.sameAs(original)); 2435 } 2436 assertSame(original.getColorSpace(), shared.getColorSpace()); 2437 2438 // The Bitmap is already in shared memory, so no work is done. 2439 Bitmap shared2 = shared.asShared(); 2440 assertSame(shared, shared2); 2441 2442 original.recycle(); 2443 shared.recycle(); 2444 shared2.recycle(); 2445 } 2446 2447 @Test testAsSharedRecycled()2448 public void testAsSharedRecycled() { 2449 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 2450 bitmap.recycle(); 2451 assertThrows(IllegalStateException.class, bitmap::asShared); 2452 } 2453 2454 @Test testAsSharedDensity()2455 public void testAsSharedDensity() { 2456 DisplayMetrics metrics = 2457 InstrumentationRegistry.getTargetContext().getResources().getDisplayMetrics(); 2458 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 2459 for (int density : new int[] { Bitmap.DENSITY_NONE, metrics.densityDpi, 2460 DisplayMetrics.DENSITY_HIGH, DisplayMetrics.DENSITY_DEVICE_STABLE, 2461 DisplayMetrics.DENSITY_MEDIUM }) { 2462 bitmap.setDensity(density); 2463 Bitmap shared = bitmap.asShared(); 2464 assertEquals(density, shared.getDensity()); 2465 shared.recycle(); 2466 } 2467 } 2468 2469 @Test 2470 @Parameters({"true", "false"}) testAsSharedImageDecoder(boolean mutable)2471 public void testAsSharedImageDecoder(boolean mutable) { 2472 Resources res = InstrumentationRegistry.getTargetContext().getResources(); 2473 ImageDecoder.Source source = ImageDecoder.createSource(res.getAssets(), 2474 "grayscale-16bit-linearSrgb.png"); 2475 try { 2476 Bitmap bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, s) -> { 2477 decoder.setAllocator(ImageDecoder.ALLOCATOR_SHARED_MEMORY); 2478 if (mutable) decoder.setMutableRequired(true); 2479 }); 2480 2481 Bitmap shared = bitmap.asShared(); 2482 if (mutable) { 2483 // bitmap is mutable, so asShared must make a copy. 2484 assertNotEquals(bitmap, shared); 2485 assertTrue(bitmap.sameAs(shared)); 2486 } else { 2487 // bitmap is already immutable and in shared memory, so asShared will return 2488 // itself. 2489 assertSame(bitmap, shared); 2490 } 2491 } catch (IOException e) { 2492 fail("Failed to decode with " + e); 2493 } 2494 } 2495 2496 @Test testNdkFormats()2497 public void testNdkFormats() { 2498 for (ConfigToFormat pair : CONFIG_TO_FORMAT) { 2499 Bitmap bm = Bitmap.createBitmap(10, 10, pair.config); 2500 assertNotNull(bm); 2501 int nativeFormat = nGetFormat(bm); 2502 assertEquals("Config: " + pair.config, pair.format, nativeFormat); 2503 } 2504 } 2505 2506 @Test testNdkFormatsHardware()2507 public void testNdkFormatsHardware() { 2508 for (ConfigToFormat pair : CONFIG_TO_FORMAT) { 2509 Bitmap bm = Bitmap.createBitmap(10, 10, pair.config); 2510 bm = bm.copy(Bitmap.Config.HARDWARE, false); 2511 2512 // ALPHA_8 may not be supported in HARDWARE. 2513 if (bm == null) { 2514 assertEquals(Bitmap.Config.ALPHA_8, pair.config); 2515 continue; 2516 } 2517 2518 int nativeFormat = nGetFormat(bm); 2519 // We allow everything to fall back to 8888 2520 if (nativeFormat != ANDROID_BITMAP_FORMAT_RGBA_8888) { 2521 assertEquals("Config: " + pair.config, pair.format, nativeFormat); 2522 } 2523 } 2524 } 2525 2526 @Test testNullBitmapNdk()2527 public void testNullBitmapNdk() { 2528 Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); 2529 nTestNullBitmap(bitmap); 2530 } 2531 parametersForTestNdkInfo()2532 private Object[] parametersForTestNdkInfo() { 2533 return new Object[] { 2534 new Object[] { Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8 }, 2535 new Object[] { Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888 }, 2536 new Object[] { Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565 }, 2537 new Object[] { Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16 }, 2538 new Object[] { Config.RGBA_1010102, ANDROID_BITMAP_FORMAT_RGBA_1010102 }, 2539 }; 2540 } 2541 2542 @Test 2543 @Parameters(method = "parametersForTestNdkInfo") testNdkInfo(Config config, final int expectedFormat)2544 public void testNdkInfo(Config config, final int expectedFormat) { 2545 // Arbitrary width and height. 2546 final int width = 13; 2547 final int height = 7; 2548 boolean[] trueFalse = new boolean[] { true, false }; 2549 for (boolean hasAlpha : trueFalse) { 2550 for (boolean premultiplied : trueFalse) { 2551 Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha); 2552 bm.setPremultiplied(premultiplied); 2553 nTestInfo(bm, expectedFormat, width, height, bm.hasAlpha(), 2554 bm.isPremultiplied(), false); 2555 Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false); 2556 assertNotNull(hwBitmap); 2557 // Formats that are not supported by gralloc fall back to 8888. 2558 // Check what the HWB format is and compare against that 2559 int tempExpectedFormat = expectedFormat; 2560 HardwareBuffer buffer = hwBitmap.getHardwareBuffer(); 2561 if (buffer.getFormat() == HardwareBuffer.RGBA_8888) { 2562 tempExpectedFormat = ANDROID_BITMAP_FORMAT_RGBA_8888; 2563 } 2564 nTestInfo(hwBitmap, tempExpectedFormat, width, height, hwBitmap.hasAlpha(), 2565 hwBitmap.isPremultiplied(), true); 2566 hwBitmap.recycle(); 2567 bm.recycle(); 2568 } 2569 } 2570 } 2571 2572 @Test testNdkDataSpaceF16Extended()2573 public void testNdkDataSpaceF16Extended() { 2574 // In RGBA_F16 we force EXTENDED in these cases. 2575 for (ColorSpace colorSpace : new ColorSpace[] { 2576 ColorSpace.get(Named.SRGB), 2577 ColorSpace.get(Named.EXTENDED_SRGB), 2578 }) { 2579 Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace); 2580 assertNotNull(bm); 2581 2582 assertEquals(ColorSpace.get(Named.EXTENDED_SRGB), bm.getColorSpace()); 2583 assertEquals(DataSpace.ADATASPACE_SCRGB, nGetDataSpace(bm)); 2584 } 2585 2586 for (ColorSpace colorSpace : new ColorSpace[] { 2587 ColorSpace.get(Named.LINEAR_SRGB), 2588 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), 2589 }) { 2590 Bitmap bm = Bitmap.createBitmap(10, 10, Config.RGBA_F16, false, colorSpace); 2591 assertNotNull(bm); 2592 2593 assertEquals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), bm.getColorSpace()); 2594 assertEquals(DataSpace.ADATASPACE_SCRGB_LINEAR, nGetDataSpace(bm)); 2595 } 2596 } 2597 2598 @Test testNdkDataSpaceNonExtended()2599 public void testNdkDataSpaceNonExtended() { 2600 // In 565 and 8888, these force non-extended. 2601 for (ColorSpace colorSpace : new ColorSpace[] { 2602 ColorSpace.get(Named.SRGB), 2603 ColorSpace.get(Named.EXTENDED_SRGB), 2604 }) { 2605 for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) { 2606 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace); 2607 assertNotNull(bm); 2608 2609 assertEquals(ColorSpace.get(Named.SRGB), bm.getColorSpace()); 2610 assertEquals(DataSpace.ADATASPACE_SRGB, nGetDataSpace(bm)); 2611 } 2612 } 2613 2614 for (ColorSpace colorSpace : new ColorSpace[] { 2615 ColorSpace.get(Named.LINEAR_SRGB), 2616 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), 2617 }) { 2618 for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565 }) { 2619 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace); 2620 assertNotNull(bm); 2621 2622 assertEquals(ColorSpace.get(Named.LINEAR_SRGB), bm.getColorSpace()); 2623 assertEquals(DataSpace.ADATASPACE_SRGB_LINEAR, nGetDataSpace(bm)); 2624 } 2625 } 2626 } 2627 2628 @Test testNdkDataSpace()2629 public void testNdkDataSpace() { 2630 // DataSpace.ADATASPACEs that do not depend on the Config. 2631 for (ColorSpace colorSpace : new ColorSpace[] { 2632 // These have corresponding DataSpace.ADATASPACEs that are independent of the Config 2633 ColorSpace.get(Named.DISPLAY_P3), 2634 ColorSpace.get(Named.BT2020), 2635 ColorSpace.get(Named.ADOBE_RGB), 2636 ColorSpace.get(Named.BT709), 2637 ColorSpace.get(Named.DCI_P3), 2638 2639 // These have no public ADATASPACE. 2640 ColorSpace.get(Named.ACES), 2641 ColorSpace.get(Named.ACESCG), 2642 ColorSpace.get(Named.NTSC_1953), 2643 ColorSpace.get(Named.PRO_PHOTO_RGB), 2644 ColorSpace.get(Named.SMPTE_C), 2645 }) { 2646 for (Config c: new Config[] { Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16 }) { 2647 Bitmap bm = Bitmap.createBitmap(10, 10, c, false, colorSpace); 2648 assertNotNull(bm); 2649 2650 int dataSpace = nGetDataSpace(bm); 2651 assertEquals("Bitmap with " + c + " and " + bm.getColorSpace() 2652 + " has unexpected data space", colorSpace.getDataSpace(), 2653 dataSpace); 2654 } 2655 } 2656 } 2657 2658 @Test testNdkDataSpaceAlpha8()2659 public void testNdkDataSpaceAlpha8() { 2660 // ALPHA_8 doesn't support ColorSpaces 2661 Bitmap bm = Bitmap.createBitmap(10, 10, Config.ALPHA_8); 2662 assertNotNull(bm); 2663 assertNull(bm.getColorSpace()); 2664 int dataSpace = nGetDataSpace(bm); 2665 assertEquals(DataSpace.ADATASPACE_UNKNOWN, dataSpace); 2666 } 2667 2668 @Test testNdkDataSpaceNullBitmap()2669 public void testNdkDataSpaceNullBitmap() { 2670 assertEquals(DataSpace.ADATASPACE_UNKNOWN, nGetDataSpace(null)); 2671 } 2672 nGetDataSpace(Bitmap bm)2673 private static native int nGetDataSpace(Bitmap bm); 2674 2675 // These match the NDK APIs. 2676 private static final int ANDROID_BITMAP_COMPRESS_FORMAT_JPEG = 0; 2677 private static final int ANDROID_BITMAP_COMPRESS_FORMAT_PNG = 1; 2678 private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY = 3; 2679 private static final int ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS = 4; 2680 nativeCompressFormat(CompressFormat format)2681 private int nativeCompressFormat(CompressFormat format) { 2682 switch (format) { 2683 case JPEG: 2684 return ANDROID_BITMAP_COMPRESS_FORMAT_JPEG; 2685 case PNG: 2686 return ANDROID_BITMAP_COMPRESS_FORMAT_PNG; 2687 case WEBP_LOSSY: 2688 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY; 2689 case WEBP_LOSSLESS: 2690 return ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS; 2691 default: 2692 fail("format " + format + " has no corresponding native compress format!"); 2693 return -1; 2694 } 2695 } 2696 parametersForNdkCompress()2697 private static Object[] parametersForNdkCompress() { 2698 // Skip WEBP, which has no corresponding native compress format. 2699 Object[] formats = new Object[] { 2700 CompressFormat.JPEG, 2701 CompressFormat.PNG, 2702 CompressFormat.WEBP_LOSSY, 2703 CompressFormat.WEBP_LOSSLESS, 2704 }; 2705 // These are the ColorSpaces with corresponding ADataSpaces 2706 Object[] colorSpaces = new Object[] { 2707 ColorSpace.get(Named.SRGB), 2708 ColorSpace.get(Named.EXTENDED_SRGB), 2709 ColorSpace.get(Named.LINEAR_SRGB), 2710 ColorSpace.get(Named.LINEAR_EXTENDED_SRGB), 2711 2712 ColorSpace.get(Named.DISPLAY_P3), 2713 ColorSpace.get(Named.DCI_P3), 2714 ColorSpace.get(Named.BT2020), 2715 ColorSpace.get(Named.BT709), 2716 ColorSpace.get(Named.ADOBE_RGB), 2717 }; 2718 2719 Object[] configs = new Object[] { 2720 Config.ARGB_8888, 2721 Config.RGB_565, 2722 Config.RGBA_F16, 2723 }; 2724 2725 return crossProduct(formats, colorSpaces, configs); 2726 } 2727 crossProduct(Object[] a, Object[] b, Object[] c)2728 private static Object[] crossProduct(Object[] a, Object[] b, Object[] c) { 2729 final int length = a.length * b.length * c.length; 2730 Object[] ret = new Object[length]; 2731 for (int i = 0; i < a.length; i++) { 2732 for (int j = 0; j < b.length; j++) { 2733 for (int k = 0; k < c.length; k++) { 2734 int index = i * (b.length * c.length) + j * c.length + k; 2735 assertNull(ret[index]); 2736 ret[index] = new Object[] { a[i], b[j], c[k] }; 2737 } 2738 } 2739 } 2740 return ret; 2741 } 2742 isSrgb(ColorSpace cs)2743 private static boolean isSrgb(ColorSpace cs) { 2744 return cs == ColorSpace.get(Named.SRGB) 2745 || cs == ColorSpace.get(Named.EXTENDED_SRGB) 2746 || cs == ColorSpace.get(Named.LINEAR_SRGB) 2747 || cs == ColorSpace.get(Named.LINEAR_EXTENDED_SRGB); 2748 } 2749 2750 // Helper method for populating a Bitmap with interesting pixels for comparison. drawGradient(Bitmap bitmap)2751 private static void drawGradient(Bitmap bitmap) { 2752 // Use different colors and alphas. 2753 Canvas canvas = new Canvas(bitmap); 2754 ColorSpace cs = bitmap.getColorSpace(); 2755 if (cs == null) { 2756 assertSame(Config.ALPHA_8, bitmap.getConfig()); 2757 cs = ColorSpace.get(ColorSpace.Named.SRGB); 2758 } 2759 long color0 = Color.pack(0, 0, 1, 1, cs); 2760 long color1 = Color.pack(1, 0, 0, 0, cs); 2761 LinearGradient gradient = new LinearGradient(0, 0, 10, 10, color0, color1, 2762 Shader.TileMode.CLAMP); 2763 Paint paint = new Paint(); 2764 paint.setShader(gradient); 2765 canvas.drawPaint(paint); 2766 } 2767 2768 @Test 2769 @Parameters(method = "parametersForNdkCompress") testNdkCompress(CompressFormat format, ColorSpace cs, Config config)2770 public void testNdkCompress(CompressFormat format, ColorSpace cs, Config config) 2771 throws IOException { 2772 // Verify that ndk compress behaves the same as Bitmap#compress 2773 Bitmap bitmap = Bitmap.createBitmap(10, 10, config, true /* hasAlpha */, cs); 2774 assertNotNull(bitmap); 2775 2776 { 2777 drawGradient(bitmap); 2778 } 2779 2780 byte[] storage = new byte[16 * 1024]; 2781 for (int quality : new int[] { 50, 80, 100 }) { 2782 byte[] expected = null; 2783 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { 2784 assertTrue("Failed to encode a Bitmap with " + cs + " to " + format + " at quality " 2785 + quality + " from Java API", bitmap.compress(format, quality, stream)); 2786 expected = stream.toByteArray(); 2787 } 2788 2789 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { 2790 boolean success = nCompress(bitmap, nativeCompressFormat(format), 2791 quality, stream, storage); 2792 assertTrue("Failed to encode pixels with " + cs + " to " + format + " at quality " 2793 + quality + " from NDK API", success); 2794 byte[] actual = stream.toByteArray(); 2795 2796 if (isSrgb(cs)) { 2797 if (!Arrays.equals(expected, actual)) { 2798 fail("NDK compression did not match for " + cs + " and format " + format 2799 + " at quality " + quality); 2800 } 2801 } else { 2802 // The byte arrays will match exactly for SRGB and its variants, because those 2803 // are treated specially. For the others, there are some small differences 2804 // between Skia's and ColorSpace's values that result in the ICC profiles being 2805 // written slightly differently. They should still look the same, though. 2806 Bitmap expectedBitmap = decodeBytes(expected); 2807 Bitmap actualBitmap = decodeBytes(actual); 2808 boolean matched = BitmapUtils.compareBitmapsMse(expectedBitmap, actualBitmap, 2809 5, true, false); 2810 expectedBitmap.recycle(); 2811 actualBitmap.recycle(); 2812 assertTrue("NDK compression did not match for " + cs + " and format " + format 2813 + " at quality " + quality, matched); 2814 } 2815 } 2816 } 2817 } 2818 2819 @Test testNdkCompressBadParameter()2820 public void testNdkCompressBadParameter() throws IOException { 2821 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { 2822 nTestNdkCompressBadParameter(mBitmap, stream, new byte[16 * 1024]); 2823 } 2824 } 2825 nCompress(Bitmap bitmap, int format, int quality, OutputStream stream, byte[] storage)2826 private static native boolean nCompress(Bitmap bitmap, int format, int quality, 2827 OutputStream stream, byte[] storage); nTestNdkCompressBadParameter(Bitmap bitmap, OutputStream stream, byte[] storage)2828 private static native void nTestNdkCompressBadParameter(Bitmap bitmap, 2829 OutputStream stream, byte[] storage); 2830 strictModeTest(Runnable runnable)2831 private void strictModeTest(Runnable runnable) { 2832 StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy(); 2833 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 2834 .detectCustomSlowCalls().penaltyDeath().build()); 2835 try { 2836 runnable.run(); 2837 fail("Shouldn't reach it"); 2838 } catch (RuntimeException expected){ 2839 // expect to receive StrictModeViolation 2840 } finally { 2841 StrictMode.setThreadPolicy(originalPolicy); 2842 } 2843 } 2844 nValidateBitmapInfo(Bitmap bitmap, int width, int height, boolean is565)2845 private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height, 2846 boolean is565); nValidateNdkAccessFails(Bitmap bitmap)2847 private static native void nValidateNdkAccessFails(Bitmap bitmap); 2848 nFillRgbaHwBuffer(HardwareBuffer hwBuffer)2849 private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer); nTestNullBitmap(Bitmap bitmap)2850 private static native void nTestNullBitmap(Bitmap bitmap); 2851 2852 private static final int ANDROID_BITMAP_FORMAT_NONE = 0; 2853 static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1; 2854 private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4; 2855 private static final int ANDROID_BITMAP_FORMAT_A_8 = 8; 2856 private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9; 2857 private static final int ANDROID_BITMAP_FORMAT_RGBA_1010102 = 10; 2858 2859 private static class ConfigToFormat { 2860 public final Config config; 2861 public final int format; 2862 ConfigToFormat(Config c, int f)2863 ConfigToFormat(Config c, int f) { 2864 this.config = c; 2865 this.format = f; 2866 } 2867 } 2868 configToFormat(Config config)2869 private static int configToFormat(Config config) { 2870 for (ConfigToFormat pair : CONFIG_TO_FORMAT) { 2871 if (config == pair.config) { 2872 return pair.format; 2873 } 2874 } 2875 return ANDROID_BITMAP_FORMAT_NONE; 2876 } 2877 2878 private static final ConfigToFormat[] CONFIG_TO_FORMAT = new ConfigToFormat[] { 2879 new ConfigToFormat(Bitmap.Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888), 2880 // ARGB_4444 is deprecated, and createBitmap converts to 8888. 2881 new ConfigToFormat(Bitmap.Config.ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_8888), 2882 new ConfigToFormat(Bitmap.Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565), 2883 new ConfigToFormat(Bitmap.Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8), 2884 new ConfigToFormat(Bitmap.Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16), 2885 new ConfigToFormat(Bitmap.Config.RGBA_1010102, ANDROID_BITMAP_FORMAT_RGBA_1010102), 2886 }; 2887 nGetFormat(Bitmap bitmap)2888 static native int nGetFormat(Bitmap bitmap); 2889 nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height, boolean hasAlpha, boolean premultiplied, boolean hardware)2890 private static native void nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height, 2891 boolean hasAlpha, boolean premultiplied, boolean hardware); 2892 createTestBuffer(int width, int height, boolean cpuAccess)2893 private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) { 2894 long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE; 2895 if (cpuAccess) { 2896 usage |= HardwareBuffer.USAGE_CPU_WRITE_RARELY; 2897 } 2898 // We can assume that RGBA_8888 format is supported for every platform. 2899 HardwareBuffer hwBuffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 2900 1, usage); 2901 return hwBuffer; 2902 } 2903 scaleFromDensity(int size, int sdensity, int tdensity)2904 private static int scaleFromDensity(int size, int sdensity, int tdensity) { 2905 if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) { 2906 return size; 2907 } 2908 2909 // Scale by tdensity / sdensity, rounding up. 2910 return ((size * tdensity) + (sdensity >> 1)) / sdensity; 2911 } 2912 createColors(int size)2913 private static int[] createColors(int size) { 2914 int[] colors = new int[size]; 2915 2916 for (int i = 0; i < size; i++) { 2917 colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i; 2918 } 2919 2920 return colors; 2921 } 2922 createHardwareBitmapOptions()2923 private static BitmapFactory.Options createHardwareBitmapOptions() { 2924 BitmapFactory.Options options = new BitmapFactory.Options(); 2925 options.inPreferredConfig = Config.HARDWARE; 2926 return options; 2927 } 2928 2929 @Test testCopyAlpha8ToHardware()2930 public void testCopyAlpha8ToHardware() { 2931 Bitmap bm = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8); 2932 assertNotNull(bm); 2933 Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false /* mutable */); 2934 // Some devices may not support ALPHA_8 + HARDWARE 2935 if (hwBitmap != null) { 2936 assertNull(hwBitmap.getColorSpace()); 2937 } 2938 2939 bm.recycle(); 2940 } 2941 } 2942