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 org.robolectric.integrationtests.nativegraphics; 17 18 import static android.os.Build.VERSION_CODES.O; 19 import static android.os.Build.VERSION_CODES.Q; 20 import static com.google.common.truth.Truth.assertThat; 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 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 30 import android.content.res.Resources; 31 import android.graphics.Bitmap; 32 import android.graphics.Bitmap.CompressFormat; 33 import android.graphics.Bitmap.Config; 34 import android.graphics.BitmapFactory; 35 import android.graphics.BitmapFactory.Options; 36 import android.graphics.Canvas; 37 import android.graphics.Color; 38 import android.graphics.ColorSpace; 39 import android.graphics.ColorSpace.Named; 40 import android.graphics.Paint; 41 import android.hardware.HardwareBuffer; 42 import android.os.Parcel; 43 import android.os.StrictMode; 44 import android.util.DisplayMetrics; 45 import java.io.ByteArrayInputStream; 46 import java.io.ByteArrayOutputStream; 47 import java.nio.ByteBuffer; 48 import java.nio.CharBuffer; 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.Objects; 52 import org.junit.Before; 53 import org.junit.Ignore; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.robolectric.RobolectricTestRunner; 57 import org.robolectric.RuntimeEnvironment; 58 import org.robolectric.shadow.api.Shadow; 59 import org.robolectric.shadows.ShadowBitmap; 60 import org.robolectric.shadows.ShadowNativeBitmap; 61 62 @org.robolectric.annotation.Config(minSdk = O) 63 @RunWith(RobolectricTestRunner.class) 64 public class ShadowNativeBitmapTest { 65 // small alpha values cause color values to be pre-multiplied down, losing accuracy 66 private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253); 67 private static final int PREMUL_ROUNDED_COLOR = Color.argb(2, 255, 255, 255); 68 private static final int PREMUL_STORED_COLOR = Color.argb(2, 2, 2, 2); 69 70 private static final BitmapFactory.Options HARDWARE_OPTIONS = createHardwareBitmapOptions(); 71 72 private Resources res; 73 private Bitmap bitmap; 74 private BitmapFactory.Options options; 75 getRgbColorSpaces()76 public static List<ColorSpace> getRgbColorSpaces() { 77 List<ColorSpace> rgbColorSpaces = new ArrayList<ColorSpace>(); 78 for (ColorSpace.Named e : ColorSpace.Named.values()) { 79 ColorSpace cs = ColorSpace.get(e); 80 if (cs.getModel() != ColorSpace.Model.RGB) { 81 continue; 82 } 83 if (((ColorSpace.Rgb) cs).getTransferParameters() == null) { 84 continue; 85 } 86 rgbColorSpaces.add(cs); 87 } 88 return rgbColorSpaces; 89 } 90 91 @Before setup()92 public void setup() { 93 res = RuntimeEnvironment.getApplication().getResources(); 94 options = new BitmapFactory.Options(); 95 options.inScaled = false; 96 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 97 } 98 99 @Test testCompressRecycled()100 public void testCompressRecycled() { 101 bitmap.recycle(); 102 assertThrows(IllegalStateException.class, () -> bitmap.compress(CompressFormat.JPEG, 0, null)); 103 } 104 105 @Test testCompressNullStream()106 public void testCompressNullStream() { 107 assertThrows(NullPointerException.class, () -> bitmap.compress(CompressFormat.JPEG, 0, null)); 108 } 109 110 @Test testCompressQualityTooLow()111 public void testCompressQualityTooLow() { 112 assertThrows( 113 IllegalArgumentException.class, 114 () -> bitmap.compress(CompressFormat.JPEG, -1, new ByteArrayOutputStream())); 115 } 116 117 @Test testCompressQualityTooHigh()118 public void testCompressQualityTooHigh() { 119 assertThrows( 120 IllegalArgumentException.class, 121 () -> bitmap.compress(CompressFormat.JPEG, 101, new ByteArrayOutputStream())); 122 } 123 124 @Test testCopyRecycled()125 public void testCopyRecycled() { 126 bitmap.recycle(); 127 assertThrows(IllegalStateException.class, () -> bitmap.copy(Config.RGB_565, false)); 128 } 129 130 @Test testCopyConfigs()131 public void testCopyConfigs() { 132 Config[] supportedConfigs = 133 new Config[] { 134 Config.ALPHA_8, Config.RGB_565, Config.ARGB_8888, Config.RGBA_F16, 135 }; 136 for (Config src : supportedConfigs) { 137 for (Config dst : supportedConfigs) { 138 Bitmap srcBitmap = Bitmap.createBitmap(1, 1, src); 139 srcBitmap.eraseColor(Color.WHITE); 140 Bitmap dstBitmap = srcBitmap.copy(dst, false); 141 assertNotNull("Should support copying from " + src + " to " + dst, dstBitmap); 142 if (Config.ALPHA_8 == dst || Config.ALPHA_8 == src) { 143 // Color will be opaque but color information will be lost. 144 assertEquals( 145 "Color should be black when copying from " + src + " to " + dst, 146 Color.BLACK, 147 dstBitmap.getPixel(0, 0)); 148 } else { 149 assertEquals( 150 "Color should be preserved when copying from " + src + " to " + dst, 151 Color.WHITE, 152 dstBitmap.getPixel(0, 0)); 153 } 154 } 155 } 156 } 157 158 @Test testCopyMutableHwBitmap()159 public void testCopyMutableHwBitmap() { 160 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 161 assertThrows(IllegalArgumentException.class, () -> bitmap.copy(Config.HARDWARE, true)); 162 } 163 164 @Test testCopyPixelsToBufferUnsupportedBufferClass()165 public void testCopyPixelsToBufferUnsupportedBufferClass() { 166 final int pixSize = bitmap.getRowBytes() * bitmap.getHeight(); 167 168 assertThrows( 169 RuntimeException.class, () -> bitmap.copyPixelsToBuffer(CharBuffer.allocate(pixSize))); 170 } 171 172 @Test testCopyPixelsToBufferBufferTooSmall()173 public void testCopyPixelsToBufferBufferTooSmall() { 174 final int pixSize = bitmap.getRowBytes() * bitmap.getHeight(); 175 final int tooSmall = pixSize / 2; 176 177 assertThrows( 178 RuntimeException.class, () -> bitmap.copyPixelsToBuffer(ByteBuffer.allocate(tooSmall))); 179 } 180 181 @Test testCreateBitmap1()182 public void testCreateBitmap1() { 183 int[] colors = createColors(100); 184 Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565); 185 assertFalse(bitmap.isMutable()); 186 Bitmap ret = Bitmap.createBitmap(bitmap); 187 assertNotNull(ret); 188 assertFalse(ret.isMutable()); 189 assertEquals(10, ret.getWidth()); 190 assertEquals(10, ret.getHeight()); 191 assertEquals(Config.RGB_565, ret.getConfig()); 192 } 193 194 @Test testCreateBitmapNegativeX()195 public void testCreateBitmapNegativeX() { 196 assertThrows( 197 IllegalArgumentException.class, () -> Bitmap.createBitmap(bitmap, -100, 50, 50, 200)); 198 } 199 200 @Test testCreateBitmapNegativeXY()201 public void testCreateBitmapNegativeXY() { 202 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 203 204 // abnormal case: x and/or y less than 0 205 assertThrows( 206 IllegalArgumentException.class, 207 () -> Bitmap.createBitmap(bitmap, -1, -1, 10, 10, null, false)); 208 } 209 210 @Test testCreateBitmapNegativeWidthHeight()211 public void testCreateBitmapNegativeWidthHeight() { 212 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 213 214 // abnormal case: width and/or height less than 0 215 assertThrows( 216 IllegalArgumentException.class, 217 () -> Bitmap.createBitmap(bitmap, 1, 1, -10, -10, null, false)); 218 } 219 220 @Test testCreateBitmapXRegionTooWide()221 public void testCreateBitmapXRegionTooWide() { 222 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 223 224 // abnormal case: (x + width) bigger than source bitmap's width 225 assertThrows( 226 IllegalArgumentException.class, 227 () -> Bitmap.createBitmap(bitmap, 10, 10, 95, 50, null, false)); 228 } 229 230 @Test testCreateBitmapYRegionTooTall()231 public void testCreateBitmapYRegionTooTall() { 232 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 233 234 // abnormal case: (y + height) bigger than source bitmap's height 235 assertThrows( 236 IllegalArgumentException.class, 237 () -> Bitmap.createBitmap(bitmap, 10, 10, 50, 95, null, false)); 238 } 239 240 @Test testCreateMutableBitmapWithHardwareConfig()241 public void testCreateMutableBitmapWithHardwareConfig() { 242 assertThrows( 243 IllegalArgumentException.class, () -> Bitmap.createBitmap(100, 100, Config.HARDWARE)); 244 } 245 246 @Test testCreateBitmap4()247 public void testCreateBitmap4() { 248 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 249 assertNotNull(ret); 250 assertTrue(ret.isMutable()); 251 assertEquals(100, ret.getWidth()); 252 assertEquals(200, ret.getHeight()); 253 assertEquals(Config.RGB_565, ret.getConfig()); 254 } 255 256 @Test testCreateBitmapFromColorsNegativeWidthHeight()257 public void testCreateBitmapFromColorsNegativeWidthHeight() { 258 int[] colors = createColors(100); 259 260 // abnormal case: width and/or height less than 0 261 assertThrows( 262 IllegalArgumentException.class, 263 () -> Bitmap.createBitmap(colors, 0, 100, -1, 100, Config.RGB_565)); 264 } 265 266 @Test testCreateBitmapFromColorsIllegalStride()267 public void testCreateBitmapFromColorsIllegalStride() { 268 int[] colors = createColors(100); 269 270 // abnormal case: stride less than width and bigger than -width 271 assertThrows( 272 IllegalArgumentException.class, 273 () -> Bitmap.createBitmap(colors, 10, 10, 100, 100, Config.RGB_565)); 274 } 275 276 @Test testCreateBitmapFromColorsNegativeOffset()277 public void testCreateBitmapFromColorsNegativeOffset() { 278 int[] colors = createColors(100); 279 280 // abnormal case: offset less than 0 281 assertThrows( 282 ArrayIndexOutOfBoundsException.class, 283 () -> Bitmap.createBitmap(colors, -10, 100, 100, 100, Config.RGB_565)); 284 } 285 286 @Test testCreateBitmapFromColorsOffsetTooLarge()287 public void testCreateBitmapFromColorsOffsetTooLarge() { 288 int[] colors = createColors(100); 289 290 // abnormal case: (offset + width) bigger than colors' length 291 assertThrows( 292 ArrayIndexOutOfBoundsException.class, 293 () -> Bitmap.createBitmap(colors, 10, 100, 100, 100, Config.RGB_565)); 294 } 295 296 @Test testCreateBitmapFromColorsScalnlineTooLarge()297 public void testCreateBitmapFromColorsScalnlineTooLarge() { 298 int[] colors = createColors(100); 299 300 // abnormal case: (lastScanline + width) bigger than colors' length 301 assertThrows( 302 ArrayIndexOutOfBoundsException.class, 303 () -> Bitmap.createBitmap(colors, 10, 100, 50, 100, Config.RGB_565)); 304 } 305 306 @Test testCreateBitmap6()307 public void testCreateBitmap6() { 308 int[] colors = createColors(100); 309 310 // normal case 311 Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565); 312 assertNotNull(ret); 313 assertFalse(ret.isMutable()); 314 assertEquals(10, ret.getWidth()); 315 assertEquals(5, ret.getHeight()); 316 assertEquals(Config.RGB_565, ret.getConfig()); 317 } 318 319 @Test testCreateBitmap_displayMetrics_mutable()320 public void testCreateBitmap_displayMetrics_mutable() { 321 DisplayMetrics metrics = RuntimeEnvironment.getApplication().getResources().getDisplayMetrics(); 322 323 Bitmap bitmap; 324 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888); 325 assertTrue(bitmap.isMutable()); 326 assertEquals(metrics.densityDpi, bitmap.getDensity()); 327 328 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888); 329 assertTrue(bitmap.isMutable()); 330 assertEquals(metrics.densityDpi, bitmap.getDensity()); 331 332 bitmap = Bitmap.createBitmap(metrics, 10, 10, Config.ARGB_8888, true); 333 assertTrue(bitmap.isMutable()); 334 assertEquals(metrics.densityDpi, bitmap.getDensity()); 335 336 bitmap = 337 Bitmap.createBitmap( 338 metrics, 10, 10, Config.ARGB_8888, true, ColorSpace.get(ColorSpace.Named.SRGB)); 339 340 assertTrue(bitmap.isMutable()); 341 assertEquals(metrics.densityDpi, bitmap.getDensity()); 342 343 int[] colors = createColors(100); 344 bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888); 345 assertNotNull(bitmap); 346 assertFalse(bitmap.isMutable()); 347 348 bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888); 349 assertNotNull(bitmap); 350 assertFalse(bitmap.isMutable()); 351 } 352 353 @Test testCreateBitmap_noDisplayMetrics_mutable()354 public void testCreateBitmap_noDisplayMetrics_mutable() { 355 Bitmap bitmap; 356 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 357 assertTrue(bitmap.isMutable()); 358 359 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true); 360 assertTrue(bitmap.isMutable()); 361 362 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB)); 363 assertTrue(bitmap.isMutable()); 364 } 365 366 @Test testCreateBitmap_displayMetrics_immutable()367 public void testCreateBitmap_displayMetrics_immutable() { 368 DisplayMetrics metrics = RuntimeEnvironment.getApplication().getResources().getDisplayMetrics(); 369 int[] colors = createColors(100); 370 371 Bitmap bitmap; 372 bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888); 373 assertFalse(bitmap.isMutable()); 374 assertEquals(metrics.densityDpi, bitmap.getDensity()); 375 376 bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888); 377 assertFalse(bitmap.isMutable()); 378 assertEquals(metrics.densityDpi, bitmap.getDensity()); 379 } 380 381 @Test testCreateBitmap_noDisplayMetrics_immutable()382 public void testCreateBitmap_noDisplayMetrics_immutable() { 383 int[] colors = createColors(100); 384 Bitmap bitmap; 385 bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888); 386 assertFalse(bitmap.isMutable()); 387 388 bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888); 389 assertFalse(bitmap.isMutable()); 390 } 391 392 @SuppressWarnings("UnusedVariable") 393 @org.robolectric.annotation.Config(minSdk = Q) 394 @Test testWrapHardwareBufferWithInvalidUsageFails()395 public void testWrapHardwareBufferWithInvalidUsageFails() { 396 assertThrows( 397 IllegalArgumentException.class, 398 () -> { 399 try (HardwareBuffer hwBuffer = 400 HardwareBuffer.create( 401 512, 512, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_CPU_WRITE_RARELY)) { 402 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.SRGB)); 403 } 404 }); 405 } 406 407 @SuppressWarnings("UnusedVariable") 408 @org.robolectric.annotation.Config(minSdk = Q) 409 @Test testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails()410 public void testWrapHardwareBufferWithRgbBufferButNonRgbColorSpaceFails() { 411 assertThrows( 412 IllegalArgumentException.class, 413 () -> { 414 try (HardwareBuffer hwBuffer = 415 HardwareBuffer.create( 416 512, 512, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)) { 417 Bitmap bitmap = Bitmap.wrapHardwareBuffer(hwBuffer, ColorSpace.get(Named.CIE_LAB)); 418 } 419 }); 420 } 421 422 @Test testGenerationId()423 public void testGenerationId() { 424 Bitmap bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 425 int genId = bitmap.getGenerationId(); 426 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 427 bitmap.setDensity(bitmap.getDensity() + 4); 428 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 429 bitmap.getPixel(0, 0); 430 assertEquals("not expected to change", genId, bitmap.getGenerationId()); 431 432 int beforeGenId = bitmap.getGenerationId(); 433 bitmap.eraseColor(Color.WHITE); 434 int afterGenId = bitmap.getGenerationId(); 435 assertTrue("expected to increase", afterGenId > beforeGenId); 436 437 beforeGenId = bitmap.getGenerationId(); 438 bitmap.setPixel(4, 4, Color.BLUE); 439 afterGenId = bitmap.getGenerationId(); 440 assertTrue("expected to increase again", afterGenId > beforeGenId); 441 } 442 443 @Test testDescribeContents()444 public void testDescribeContents() { 445 assertEquals(0, bitmap.describeContents()); 446 } 447 448 @Test testEraseColorOnRecycled()449 public void testEraseColorOnRecycled() { 450 bitmap.recycle(); 451 452 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(0)); 453 } 454 455 @org.robolectric.annotation.Config(minSdk = Q) 456 @Test testEraseColorLongOnRecycled()457 public void testEraseColorLongOnRecycled() { 458 bitmap.recycle(); 459 460 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(Color.pack(0))); 461 } 462 463 @Test testEraseColorOnImmutable()464 public void testEraseColorOnImmutable() { 465 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 466 467 // abnormal case: bitmap is immutable 468 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(0)); 469 } 470 471 @org.robolectric.annotation.Config(minSdk = Q) 472 @Test testEraseColorLongOnImmutable()473 public void testEraseColorLongOnImmutable() { 474 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 475 476 // abnormal case: bitmap is immutable 477 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(Color.pack(0))); 478 } 479 480 @Test testEraseColor()481 public void testEraseColor() { 482 // normal case 483 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 484 bitmap.eraseColor(0xffff0000); 485 assertEquals(0xffff0000, bitmap.getPixel(10, 10)); 486 assertEquals(0xffff0000, bitmap.getPixel(50, 50)); 487 } 488 489 @org.robolectric.annotation.Config(minSdk = Q) 490 @Test testGetColorOOB()491 public void testGetColorOOB() { 492 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 493 assertThrows(IllegalArgumentException.class, () -> bitmap.getColor(-1, 0)); 494 } 495 496 @org.robolectric.annotation.Config(minSdk = Q) 497 @Test testGetColorOOB2()498 public void testGetColorOOB2() { 499 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 500 assertThrows(IllegalArgumentException.class, () -> bitmap.getColor(5, -10)); 501 } 502 503 @org.robolectric.annotation.Config(minSdk = Q) 504 @Test testGetColorOOB3()505 public void testGetColorOOB3() { 506 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 507 assertThrows(IllegalArgumentException.class, () -> bitmap.getColor(100, 10)); 508 } 509 510 @org.robolectric.annotation.Config(minSdk = Q) 511 @Test testGetColorOOB4()512 public void testGetColorOOB4() { 513 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 514 assertThrows(IllegalArgumentException.class, () -> bitmap.getColor(99, 1000)); 515 } 516 517 @Test 518 @org.robolectric.annotation.Config(minSdk = Q) testGetColorRecycled()519 public void testGetColorRecycled() { 520 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 521 bitmap.recycle(); 522 assertThrows(IllegalStateException.class, () -> bitmap.getColor(0, 0)); 523 } 524 clamp(float f)525 private static float clamp(float f) { 526 return clamp(f, 0.0f, 1.0f); 527 } 528 clamp(float f, float min, float max)529 private static float clamp(float f, float min, float max) { 530 return Math.min(Math.max(f, min), max); 531 } 532 533 @org.robolectric.annotation.Config(minSdk = Q) 534 @Test testGetColor()535 public void testGetColor() { 536 final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 537 List<ColorSpace> rgbColorSpaces = getRgbColorSpaces(); 538 for (Config config : new Config[] {Config.ARGB_8888, Config.RGBA_F16, Config.RGB_565}) { 539 for (ColorSpace bitmapColorSpace : rgbColorSpaces) { 540 bitmap = Bitmap.createBitmap(1, 1, config, /*hasAlpha*/ false, bitmapColorSpace); 541 bitmapColorSpace = bitmap.getColorSpace(); 542 for (ColorSpace eraseColorSpace : rgbColorSpaces) { 543 for (long wideGamutLong : 544 new long[] { 545 Color.pack(1.0f, 0.0f, 0.0f, 1.0f, eraseColorSpace), 546 Color.pack(0.0f, 1.0f, 0.0f, 1.0f, eraseColorSpace), 547 Color.pack(0.0f, 0.0f, 1.0f, 1.0f, eraseColorSpace) 548 }) { 549 bitmap.eraseColor(wideGamutLong); 550 551 Color result = bitmap.getColor(0, 0); 552 if (bitmap.getColorSpace().equals(sRGB)) { 553 assertEquals(bitmap.getPixel(0, 0), result.toArgb()); 554 } 555 if (eraseColorSpace.equals(bitmapColorSpace)) { 556 final Color wideGamutColor = Color.valueOf(wideGamutLong); 557 ColorUtils.verifyColor( 558 "Erasing to Bitmap's ColorSpace " + bitmapColorSpace, 559 wideGamutColor, 560 result, 561 .001f); 562 563 } else { 564 Color convertedColor = Color.valueOf(Color.convert(wideGamutLong, bitmapColorSpace)); 565 if (bitmap.getConfig() != Config.RGBA_F16) { 566 // It's possible that we have to clip to fit into the Config. 567 convertedColor = 568 Color.valueOf( 569 clamp(convertedColor.red()), 570 clamp(convertedColor.green()), 571 clamp(convertedColor.blue()), 572 convertedColor.alpha(), 573 bitmapColorSpace); 574 } 575 ColorUtils.verifyColor( 576 "Bitmap(Config: " 577 + bitmap.getConfig() 578 + ", ColorSpace: " 579 + bitmapColorSpace 580 + ") erasing to " 581 + Color.valueOf(wideGamutLong), 582 convertedColor, 583 result, 584 .03f); 585 } 586 } 587 } 588 } 589 } 590 } 591 592 private static class ARGB { 593 public final float alpha; 594 public final float red; 595 public final float green; 596 public final float blue; 597 ARGB(float alpha, float red, float green, float blue)598 ARGB(float alpha, float red, float green, float blue) { 599 this.alpha = alpha; 600 this.red = red; 601 this.green = green; 602 this.blue = blue; 603 } 604 } 605 606 @org.robolectric.annotation.Config(minSdk = Q) 607 @Test testEraseColorLong()608 public void testEraseColorLong() { 609 List<ColorSpace> rgbColorSpaces = getRgbColorSpaces(); 610 for (Config config : new Config[] {Config.ARGB_8888, Config.RGB_565, Config.RGBA_F16}) { 611 bitmap = Bitmap.createBitmap(100, 100, config); 612 // pack SRGB colors into ColorLongs. 613 for (int color : 614 new int[] { 615 Color.RED, Color.BLUE, Color.GREEN, Color.BLACK, Color.WHITE, Color.TRANSPARENT 616 }) { 617 if (config.equals(Config.RGB_565) && Float.compare(Color.alpha(color), 1.0f) != 0) { 618 // 565 doesn't support alpha. 619 continue; 620 } 621 bitmap.eraseColor(Color.pack(color)); 622 // The Bitmap is either SRGB or SRGBLinear (F16). getPixel(), which retrieves the 623 // color in SRGB, should match exactly. 624 ColorUtils.verifyColor( 625 "Config " + config + " mismatch at 10, 10 ", color, bitmap.getPixel(10, 10), 0); 626 ColorUtils.verifyColor( 627 "Config " + config + " mismatch at 50, 50 ", color, bitmap.getPixel(50, 50), 0); 628 } 629 630 // Use arbitrary colors in various ColorSpaces. getPixel() should approximately match 631 // the SRGB version of the color. 632 for (ARGB color : 633 new ARGB[] { 634 new ARGB(1.0f, .5f, .5f, .5f), 635 new ARGB(1.0f, .3f, .6f, .9f), 636 new ARGB(0.5f, .2f, .8f, .7f) 637 }) { 638 if (config.equals(Config.RGB_565) && Float.compare(color.alpha, 1.0f) != 0) { 639 continue; 640 } 641 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue); 642 for (ColorSpace cs : rgbColorSpaces) { 643 long longColor = Color.convert(srgbColor, cs); 644 bitmap.eraseColor(longColor); 645 // These tolerances were chosen by trial and error. It is expected that 646 // some conversions do not round-trip perfectly. 647 int tolerance = 1; 648 if (config.equals(Config.RGB_565)) { 649 tolerance = 4; 650 } else if (cs.equals(ColorSpace.get(ColorSpace.Named.SMPTE_C))) { 651 tolerance = 3; 652 } 653 654 ColorUtils.verifyColor( 655 "Config " + config + ", ColorSpace " + cs + ", mismatch at 10, 10 ", 656 srgbColor, 657 bitmap.getPixel(10, 10), 658 tolerance); 659 ColorUtils.verifyColor( 660 "Config " + config + ", ColorSpace " + cs + ", mismatch at 50, 50 ", 661 srgbColor, 662 bitmap.getPixel(50, 50), 663 tolerance); 664 } 665 } 666 } 667 } 668 669 @org.robolectric.annotation.Config(minSdk = Q) 670 @Test testEraseColorOnP3()671 public void testEraseColorOnP3() { 672 // Use a ColorLong with a different ColorSpace than the Bitmap. getPixel() should 673 // approximately match the SRGB version of the color. 674 bitmap = 675 Bitmap.createBitmap( 676 100, 100, Config.ARGB_8888, true, ColorSpace.get(ColorSpace.Named.DISPLAY_P3)); 677 int srgbColor = Color.argb(.5f, .3f, .6f, .7f); 678 long acesColor = Color.convert(srgbColor, ColorSpace.get(ColorSpace.Named.ACES)); 679 bitmap.eraseColor(acesColor); 680 ColorUtils.verifyColor("Mismatch at 15, 15", srgbColor, bitmap.getPixel(15, 15), 1); 681 } 682 683 @org.robolectric.annotation.Config(minSdk = Q) 684 @Test testEraseColorXYZ()685 public void testEraseColorXYZ() { 686 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 687 assertThrows( 688 IllegalArgumentException.class, 689 () -> 690 bitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_XYZ)))); 691 } 692 693 @org.robolectric.annotation.Config(minSdk = Q) 694 @Test testEraseColorLAB()695 public void testEraseColorLAB() { 696 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 697 assertThrows( 698 IllegalArgumentException.class, 699 () -> 700 bitmap.eraseColor(Color.convert(Color.BLUE, ColorSpace.get(ColorSpace.Named.CIE_LAB)))); 701 } 702 703 @org.robolectric.annotation.Config(minSdk = Q) 704 @Test testEraseColorUnknown()705 public void testEraseColorUnknown() { 706 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 707 assertThrows(IllegalArgumentException.class, () -> bitmap.eraseColor(-1L)); 708 } 709 710 @Test testExtractAlphaFromRecycled()711 public void testExtractAlphaFromRecycled() { 712 bitmap.recycle(); 713 714 assertThrows(IllegalStateException.class, () -> bitmap.extractAlpha()); 715 } 716 717 @Test testExtractAlpha()718 public void testExtractAlpha() { 719 // normal case 720 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 721 Bitmap ret = bitmap.extractAlpha(); 722 assertNotNull(ret); 723 int source = bitmap.getPixel(10, 20); 724 int result = ret.getPixel(10, 20); 725 assertEquals(Color.alpha(source), Color.alpha(result)); 726 assertEquals(0xFF, Color.alpha(result)); 727 } 728 729 @Test testExtractAlphaWithPaintAndOffsetFromRecycled()730 public void testExtractAlphaWithPaintAndOffsetFromRecycled() { 731 bitmap.recycle(); 732 733 assertThrows( 734 IllegalStateException.class, () -> bitmap.extractAlpha(new Paint(), new int[] {0, 1})); 735 } 736 737 @Test testGetAllocationByteCount()738 public void testGetAllocationByteCount() { 739 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); 740 int alloc = bitmap.getAllocationByteCount(); 741 assertEquals(bitmap.getByteCount(), alloc); 742 743 // reconfigure same size 744 bitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888); 745 assertEquals(bitmap.getByteCount(), alloc); 746 assertEquals(bitmap.getAllocationByteCount(), alloc); 747 748 // reconfigure different size 749 bitmap.reconfigure(10, 10, Bitmap.Config.ALPHA_8); 750 assertEquals(100, bitmap.getByteCount()); 751 assertEquals(bitmap.getAllocationByteCount(), alloc); 752 } 753 754 @Test testGetHeight()755 public void testGetHeight() { 756 assertEquals(31, bitmap.getHeight()); 757 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 758 assertEquals(200, bitmap.getHeight()); 759 } 760 761 @Test testGetNinePatchChunk()762 public void testGetNinePatchChunk() { 763 assertNull(bitmap.getNinePatchChunk()); 764 } 765 766 @Test testGetPixelFromRecycled()767 public void testGetPixelFromRecycled() { 768 bitmap.recycle(); 769 770 assertThrows(IllegalStateException.class, () -> bitmap.getPixel(10, 16)); 771 } 772 773 @Test testGetPixelXTooLarge()774 public void testGetPixelXTooLarge() { 775 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 776 777 // abnormal case: x bigger than the source bitmap's width 778 assertThrows(IllegalArgumentException.class, () -> bitmap.getPixel(200, 16)); 779 } 780 781 @Test testGetPixelYTooLarge()782 public void testGetPixelYTooLarge() { 783 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 784 785 // abnormal case: y bigger than the source bitmap's height 786 assertThrows(IllegalArgumentException.class, () -> bitmap.getPixel(10, 300)); 787 } 788 789 @Test testGetPixel()790 public void testGetPixel() { 791 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 792 793 // normal case 565 794 bitmap.setPixel(10, 16, 0xFF << 24); 795 assertEquals(0xFF << 24, bitmap.getPixel(10, 16)); 796 797 // normal case A_8 798 bitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8); 799 bitmap.setPixel(5, 5, 0xFFFFFFFF); 800 assertEquals(0xFF000000, bitmap.getPixel(5, 5)); 801 bitmap.setPixel(5, 5, 0xA8A8A8A8); 802 assertEquals(0xA8000000, bitmap.getPixel(5, 5)); 803 bitmap.setPixel(5, 5, 0x00000000); 804 assertEquals(0x00000000, bitmap.getPixel(5, 5)); 805 bitmap.setPixel(5, 5, 0x1F000000); 806 assertEquals(0x1F000000, bitmap.getPixel(5, 5)); 807 } 808 809 @Test testGetRowBytes()810 public void testGetRowBytes() { 811 Bitmap bm0 = Bitmap.createBitmap(100, 200, Bitmap.Config.ALPHA_8); 812 Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 813 Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 814 Bitmap bm3 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_4444); 815 816 assertEquals(100, bm0.getRowBytes()); 817 assertEquals(400, bm1.getRowBytes()); 818 assertEquals(200, bm2.getRowBytes()); 819 // Attempting to create a 4444 bitmap actually creates an 8888 bitmap. 820 assertEquals(400, bm3.getRowBytes()); 821 } 822 823 @Test testGetWidth()824 public void testGetWidth() { 825 assertEquals(31, bitmap.getWidth()); 826 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 827 assertEquals(100, bitmap.getWidth()); 828 } 829 830 @Test testHasAlpha()831 public void testHasAlpha() { 832 assertFalse(bitmap.hasAlpha()); 833 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 834 assertTrue(bitmap.hasAlpha()); 835 } 836 837 @Test testIsMutable()838 public void testIsMutable() { 839 assertFalse(bitmap.isMutable()); 840 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 841 assertTrue(bitmap.isMutable()); 842 } 843 844 @Test testIsRecycled()845 public void testIsRecycled() { 846 assertFalse(bitmap.isRecycled()); 847 bitmap.recycle(); 848 assertTrue(bitmap.isRecycled()); 849 } 850 851 @Test testReconfigure()852 public void testReconfigure() { 853 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 854 int alloc = bitmap.getAllocationByteCount(); 855 856 // test shrinking 857 bitmap.reconfigure(50, 100, Bitmap.Config.ALPHA_8); 858 assertEquals(bitmap.getAllocationByteCount(), alloc); 859 assertEquals(bitmap.getByteCount() * 8, alloc); 860 } 861 862 @Test testReconfigureExpanding()863 public void testReconfigureExpanding() { 864 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 865 assertThrows( 866 IllegalArgumentException.class, 867 () -> bitmap.reconfigure(101, 201, Bitmap.Config.ARGB_8888)); 868 } 869 870 @Test testReconfigureMutable()871 public void testReconfigureMutable() { 872 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 873 assertThrows( 874 IllegalStateException.class, () -> bitmap.reconfigure(1, 1, Bitmap.Config.ALPHA_8)); 875 } 876 877 // Used by testAlphaAndPremul. 878 private static final Config[] CONFIGS = 879 new Config[] {Config.ALPHA_8, Config.ARGB_4444, Config.ARGB_8888, Config.RGB_565}; 880 881 // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with 882 // respect to alpha and premultiplied. 883 @Test testAlphaAndPremul()884 public void testAlphaAndPremul() { 885 boolean[] falseTrue = new boolean[] {false, true}; 886 for (Config fromConfig : CONFIGS) { 887 for (Config toConfig : CONFIGS) { 888 for (boolean hasAlpha : falseTrue) { 889 for (boolean isPremul : falseTrue) { 890 Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig); 891 892 // 4444 is deprecated, and will convert to 8888. No need to 893 // attempt a reconfigure, which will be tested when fromConfig 894 // is 8888. 895 if (fromConfig == Config.ARGB_4444) { 896 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 897 break; 898 } 899 900 bitmap.setHasAlpha(hasAlpha); 901 bitmap.setPremultiplied(isPremul); 902 903 verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, false); 904 905 // reconfigure to a smaller size so the function will still succeed when 906 // going to a Config that requires more bits. 907 bitmap.reconfigure(1, 1, toConfig); 908 if (toConfig == Config.ARGB_4444) { 909 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 910 } else { 911 assertEquals(toConfig, bitmap.getConfig()); 912 } 913 914 // Check that the alpha and premultiplied state has not changed (unless 915 // we expected it to). 916 verifyAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565); 917 } 918 } 919 } 920 } 921 } 922 923 /** 924 * Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied(). 925 * 926 * @param bitmap Bitmap to check. 927 * @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based on 928 * what was set, but may be different from the actual return value depending on the Config and 929 * convertedFrom565. 930 * @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to 931 * expectedAlpha, this is based on what was set, but may be different from the actual return 932 * value depending on the Config. 933 * @param convertedFrom565 Whether bitmap was converted to its current Config by being 934 * reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha, 935 * hasAlpha() is expected to be true even if expectedAlpha is false. 936 */ 937 @SuppressWarnings("MissingCasesInEnumSwitch") verifyAlphaAndPremul( Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565)938 private void verifyAlphaAndPremul( 939 Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul, boolean convertedFrom565) { 940 switch (bitmap.getConfig()) { 941 case ARGB_4444: 942 // This shouldn't happen, since we don't allow creating or converting 943 // to 4444. 944 assertFalse(true); 945 break; 946 case RGB_565: 947 assertFalse(bitmap.hasAlpha()); 948 assertFalse(bitmap.isPremultiplied()); 949 break; 950 case ALPHA_8: 951 // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through. 952 case ARGB_8888: 953 // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type 954 // that can have alpha. 955 if (convertedFrom565) { 956 assertTrue(bitmap.hasAlpha()); 957 } else { 958 assertEquals(expectedAlpha, bitmap.hasAlpha()); 959 } 960 961 if (bitmap.hasAlpha()) { 962 // ALPHA_8's premultiplied status is undefined. 963 if (bitmap.getConfig() != Config.ALPHA_8) { 964 assertEquals(expectedPremul, bitmap.isPremultiplied()); 965 } 966 } else { 967 // Opaque bitmap is never considered premultiplied. 968 assertFalse(bitmap.isPremultiplied()); 969 } 970 break; 971 } 972 } 973 974 @org.robolectric.annotation.Config(minSdk = Q) 975 @Test testSetColorSpace()976 public void testSetColorSpace() { 977 // Use arbitrary colors and assign to various ColorSpaces. 978 for (ARGB color : 979 new ARGB[] { 980 new ARGB(1.0f, .5f, .5f, .5f), 981 new ARGB(1.0f, .3f, .6f, .9f), 982 new ARGB(0.5f, .2f, .8f, .7f) 983 }) { 984 985 int srgbColor = Color.argb(color.alpha, color.red, color.green, color.blue); 986 for (ColorSpace cs : getRgbColorSpaces()) { 987 for (Config config : 988 new Config[] { 989 // F16 is tested elsewhere, since it defaults to EXTENDED_SRGB, and 990 // many of these calls to setColorSpace would reduce the range, resulting 991 // in an Exception. 992 Config.ARGB_8888, Config.RGB_565, 993 }) { 994 bitmap = Bitmap.createBitmap(10, 10, config); 995 bitmap.eraseColor(srgbColor); 996 bitmap.setColorSpace(cs); 997 ColorSpace actual = bitmap.getColorSpace(); 998 if (Objects.equals(cs, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB))) { 999 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual); 1000 } else if (Objects.equals(cs, ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB))) { 1001 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual); 1002 } else { 1003 assertSame(cs, actual); 1004 } 1005 1006 // This tolerance was chosen by trial and error. It is expected that 1007 // some conversions do not round-trip perfectly. 1008 int tolerance = 2; 1009 Color c = Color.valueOf(color.red, color.green, color.blue, color.alpha, cs); 1010 ColorUtils.verifyColor( 1011 "Mismatch after setting the colorSpace to " + cs.getName(), 1012 c.convert(bitmap.getColorSpace()), 1013 bitmap.getColor(5, 5), 1014 tolerance); 1015 } 1016 } 1017 } 1018 } 1019 1020 @org.robolectric.annotation.Config(minSdk = Q) 1021 @Test testSetColorSpaceRecycled()1022 public void testSetColorSpaceRecycled() { 1023 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1024 bitmap.recycle(); 1025 assertThrows( 1026 IllegalStateException.class, () -> bitmap.setColorSpace(ColorSpace.get(Named.DISPLAY_P3))); 1027 } 1028 1029 @org.robolectric.annotation.Config(minSdk = Q) 1030 @Test testSetColorSpaceNull()1031 public void testSetColorSpaceNull() { 1032 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1033 assertThrows(IllegalArgumentException.class, () -> bitmap.setColorSpace(null)); 1034 } 1035 1036 @org.robolectric.annotation.Config(minSdk = Q) 1037 @Test testSetColorSpaceXYZ()1038 public void testSetColorSpaceXYZ() { 1039 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1040 assertThrows( 1041 IllegalArgumentException.class, () -> bitmap.setColorSpace(ColorSpace.get(Named.CIE_XYZ))); 1042 } 1043 1044 @org.robolectric.annotation.Config(minSdk = Q) 1045 @Test testSetColorSpaceNoTransferParameters()1046 public void testSetColorSpaceNoTransferParameters() { 1047 bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888); 1048 ColorSpace cs = 1049 new ColorSpace.Rgb( 1050 "NoTransferParams", 1051 new float[] {0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f}, 1052 ColorSpace.ILLUMINANT_D50, 1053 x -> Math.pow(x, 1.0f / 2.2f), 1054 x -> Math.pow(x, 2.2f), 1055 0, 1056 1); 1057 assertThrows(IllegalArgumentException.class, () -> bitmap.setColorSpace(cs)); 1058 } 1059 1060 @org.robolectric.annotation.Config(minSdk = Q) 1061 @Test testSetColorSpaceAlpha8()1062 public void testSetColorSpaceAlpha8() { 1063 bitmap = Bitmap.createBitmap(10, 10, Config.ALPHA_8); 1064 assertNull(bitmap.getColorSpace()); 1065 assertThrows( 1066 IllegalArgumentException.class, 1067 () -> bitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))); 1068 } 1069 1070 @org.robolectric.annotation.Config(minSdk = Q) 1071 @Test testSetColorSpaceReducedRange()1072 public void testSetColorSpaceReducedRange() { 1073 ColorSpace aces = ColorSpace.get(Named.ACES); 1074 bitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, aces); 1075 try { 1076 bitmap.setColorSpace(ColorSpace.get(Named.SRGB)); 1077 fail("Expected IllegalArgumentException!"); 1078 } catch (IllegalArgumentException e) { 1079 assertSame(aces, bitmap.getColorSpace()); 1080 } 1081 } 1082 1083 @org.robolectric.annotation.Config(minSdk = Q) 1084 @Test testSetColorSpaceNotReducedRange()1085 public void testSetColorSpaceNotReducedRange() { 1086 ColorSpace extended = ColorSpace.get(Named.EXTENDED_SRGB); 1087 bitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, extended); 1088 bitmap.setColorSpace(ColorSpace.get(Named.SRGB)); 1089 assertSame(bitmap.getColorSpace(), extended); 1090 } 1091 1092 @org.robolectric.annotation.Config(minSdk = Q) 1093 @Test testSetColorSpaceNotReducedRangeLinear()1094 public void testSetColorSpaceNotReducedRangeLinear() { 1095 ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB); 1096 bitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, linearExtended); 1097 bitmap.setColorSpace(ColorSpace.get(Named.LINEAR_SRGB)); 1098 assertSame(bitmap.getColorSpace(), linearExtended); 1099 } 1100 1101 @org.robolectric.annotation.Config(minSdk = Q) 1102 @Test testSetColorSpaceIncreasedRange()1103 public void testSetColorSpaceIncreasedRange() { 1104 bitmap = Bitmap.createBitmap(10, 10, Config.RGBA_F16, true, ColorSpace.get(Named.DISPLAY_P3)); 1105 ColorSpace linearExtended = ColorSpace.get(Named.LINEAR_EXTENDED_SRGB); 1106 bitmap.setColorSpace(linearExtended); 1107 assertSame(bitmap.getColorSpace(), linearExtended); 1108 } 1109 1110 @Test testSetConfig()1111 public void testSetConfig() { 1112 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1113 int alloc = bitmap.getAllocationByteCount(); 1114 1115 // test shrinking 1116 bitmap.setConfig(Bitmap.Config.ALPHA_8); 1117 assertEquals(bitmap.getAllocationByteCount(), alloc); 1118 assertEquals(bitmap.getByteCount() * 2, alloc); 1119 } 1120 1121 @Test testSetConfigExpanding()1122 public void testSetConfigExpanding() { 1123 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1124 // test expanding 1125 assertThrows(IllegalArgumentException.class, () -> bitmap.setConfig(Bitmap.Config.ARGB_8888)); 1126 } 1127 1128 @Test testSetConfigMutable()1129 public void testSetConfigMutable() { 1130 // test mutable 1131 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 1132 assertThrows(IllegalStateException.class, () -> bitmap.setConfig(Bitmap.Config.ALPHA_8)); 1133 } 1134 1135 @Test testSetHeight()1136 public void testSetHeight() { 1137 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1138 int alloc = bitmap.getAllocationByteCount(); 1139 1140 // test shrinking 1141 bitmap.setHeight(100); 1142 assertEquals(bitmap.getAllocationByteCount(), alloc); 1143 assertEquals(bitmap.getByteCount() * 2, alloc); 1144 } 1145 1146 @Test testSetHeightExpanding()1147 public void testSetHeightExpanding() { 1148 // test expanding 1149 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1150 assertThrows(IllegalArgumentException.class, () -> bitmap.setHeight(201)); 1151 } 1152 1153 @Test testSetHeightMutable()1154 public void testSetHeightMutable() { 1155 // test mutable 1156 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 1157 assertThrows(IllegalStateException.class, () -> bitmap.setHeight(1)); 1158 } 1159 1160 @Test testSetPixelOnRecycled()1161 public void testSetPixelOnRecycled() { 1162 int color = 0xff << 24; 1163 1164 bitmap.recycle(); 1165 assertThrows(IllegalStateException.class, () -> bitmap.setPixel(10, 16, color)); 1166 } 1167 1168 @Test testSetPixelOnImmutable()1169 public void testSetPixelOnImmutable() { 1170 int color = 0xff << 24; 1171 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 1172 1173 assertThrows(IllegalStateException.class, () -> bitmap.setPixel(10, 16, color)); 1174 } 1175 1176 @Test testSetPixelXIsTooLarge()1177 public void testSetPixelXIsTooLarge() { 1178 int color = 0xff << 24; 1179 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1180 1181 // abnormal case: x bigger than the source bitmap's width 1182 assertThrows(IllegalArgumentException.class, () -> bitmap.setPixel(200, 16, color)); 1183 } 1184 1185 @Test testSetPixelYIsTooLarge()1186 public void testSetPixelYIsTooLarge() { 1187 int color = 0xff << 24; 1188 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1189 1190 // abnormal case: y bigger than the source bitmap's height 1191 assertThrows(IllegalArgumentException.class, () -> bitmap.setPixel(10, 300, color)); 1192 } 1193 1194 @Test testSetPixel()1195 public void testSetPixel() { 1196 int color = 0xff << 24; 1197 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); 1198 1199 // normal case 1200 bitmap.setPixel(10, 16, color); 1201 assertEquals(color, bitmap.getPixel(10, 16)); 1202 } 1203 1204 @Test testSetPixelsOnRecycled()1205 public void testSetPixelsOnRecycled() { 1206 int[] colors = createColors(100); 1207 1208 bitmap.recycle(); 1209 assertThrows(IllegalStateException.class, () -> bitmap.setPixels(colors, 0, 0, 0, 0, 0, 0)); 1210 } 1211 1212 @Test testSetPixelsOnImmutable()1213 public void testSetPixelsOnImmutable() { 1214 int[] colors = createColors(100); 1215 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 1216 1217 assertThrows(IllegalStateException.class, () -> bitmap.setPixels(colors, 0, 0, 0, 0, 0, 0)); 1218 } 1219 1220 @Test testSetPixelsXYNegative()1221 public void testSetPixelsXYNegative() { 1222 int[] colors = createColors(100); 1223 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1224 1225 // abnormal case: x and/or y less than 0 1226 assertThrows( 1227 IllegalArgumentException.class, () -> bitmap.setPixels(colors, 0, 0, -1, -1, 200, 16)); 1228 } 1229 1230 @Test testSetPixelsWidthHeightNegative()1231 public void testSetPixelsWidthHeightNegative() { 1232 int[] colors = createColors(100); 1233 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1234 1235 // abnormal case: width and/or height less than 0 1236 assertThrows( 1237 IllegalArgumentException.class, () -> bitmap.setPixels(colors, 0, 0, 0, 0, -1, -1)); 1238 } 1239 1240 @Test testSetPixelsXTooHigh()1241 public void testSetPixelsXTooHigh() { 1242 int[] colors = createColors(100); 1243 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1244 1245 // abnormal case: (x + width) bigger than the source bitmap's width 1246 assertThrows( 1247 IllegalArgumentException.class, () -> bitmap.setPixels(colors, 0, 0, 10, 10, 95, 50)); 1248 } 1249 1250 @Test testSetPixelsYTooHigh()1251 public void testSetPixelsYTooHigh() { 1252 int[] colors = createColors(100); 1253 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1254 1255 // abnormal case: (y + height) bigger than the source bitmap's height 1256 assertThrows( 1257 IllegalArgumentException.class, () -> bitmap.setPixels(colors, 0, 0, 10, 10, 50, 95)); 1258 } 1259 1260 @Test testSetPixelsStrideIllegal()1261 public void testSetPixelsStrideIllegal() { 1262 int[] colors = createColors(100); 1263 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1264 1265 // abnormal case: stride less than width and bigger than -width 1266 assertThrows( 1267 IllegalArgumentException.class, () -> bitmap.setPixels(colors, 0, 10, 10, 10, 50, 50)); 1268 } 1269 1270 @Test testSetPixelsOffsetNegative()1271 public void testSetPixelsOffsetNegative() { 1272 int[] colors = createColors(100); 1273 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1274 1275 // abnormal case: offset less than 0 1276 assertThrows( 1277 ArrayIndexOutOfBoundsException.class, 1278 () -> bitmap.setPixels(colors, -1, 50, 10, 10, 50, 50)); 1279 } 1280 1281 @Test testSetPixelsOffsetTooBig()1282 public void testSetPixelsOffsetTooBig() { 1283 int[] colors = createColors(100); 1284 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1285 1286 // abnormal case: (offset + width) bigger than the length of colors 1287 assertThrows( 1288 ArrayIndexOutOfBoundsException.class, 1289 () -> bitmap.setPixels(colors, 60, 50, 10, 10, 50, 50)); 1290 } 1291 1292 @Test testSetPixelsLastScanlineNegative()1293 public void testSetPixelsLastScanlineNegative() { 1294 int[] colors = createColors(100); 1295 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1296 1297 // abnormal case: lastScanline less than 0 1298 assertThrows( 1299 ArrayIndexOutOfBoundsException.class, 1300 () -> bitmap.setPixels(colors, 10, -50, 10, 10, 50, 50)); 1301 } 1302 1303 @Test testSetPixelsLastScanlineTooBig()1304 public void testSetPixelsLastScanlineTooBig() { 1305 int[] colors = createColors(100); 1306 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1307 1308 // abnormal case: (lastScanline + width) bigger than the length of colors 1309 assertThrows( 1310 ArrayIndexOutOfBoundsException.class, 1311 () -> bitmap.setPixels(colors, 10, 50, 10, 10, 50, 50)); 1312 } 1313 1314 @Test testSetPixels()1315 public void testSetPixels() { 1316 int[] colors = createColors(100 * 100); 1317 bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 1318 bitmap.setPixels(colors, 0, 100, 0, 0, 100, 100); 1319 int[] ret = new int[100 * 100]; 1320 bitmap.getPixels(ret, 0, 100, 0, 0, 100, 100); 1321 1322 for (int i = 0; i < 10000; i++) { 1323 assertEquals(ret[i], colors[i]); 1324 } 1325 } 1326 verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul)1327 private void verifyPremultipliedBitmapConfig(Config config, boolean expectedPremul) { 1328 Bitmap bitmap = Bitmap.createBitmap(1, 1, config); 1329 bitmap.setPremultiplied(true); 1330 bitmap.setPixel(0, 0, Color.TRANSPARENT); 1331 assertTrue(bitmap.isPremultiplied() == expectedPremul); 1332 1333 bitmap.setHasAlpha(false); 1334 assertFalse(bitmap.isPremultiplied()); 1335 } 1336 1337 @Test testSetPremultipliedSimple()1338 public void testSetPremultipliedSimple() { 1339 verifyPremultipliedBitmapConfig(Bitmap.Config.ALPHA_8, true); 1340 verifyPremultipliedBitmapConfig(Bitmap.Config.RGB_565, false); 1341 verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_4444, true); 1342 verifyPremultipliedBitmapConfig(Bitmap.Config.ARGB_8888, true); 1343 } 1344 1345 @Test testSetPremultipliedData()1346 public void testSetPremultipliedData() { 1347 // with premul, will store 2,2,2,2, so it doesn't get value correct 1348 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1349 bitmap.setPixel(0, 0, PREMUL_COLOR); 1350 assertEquals(bitmap.getPixel(0, 0), PREMUL_ROUNDED_COLOR); 1351 1352 // read premultiplied value directly 1353 bitmap.setPremultiplied(false); 1354 assertEquals(bitmap.getPixel(0, 0), PREMUL_STORED_COLOR); 1355 1356 // value can now be stored/read correctly 1357 bitmap.setPixel(0, 0, PREMUL_COLOR); 1358 assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR); 1359 1360 // verify with array methods 1361 int[] testArray = new int[] {PREMUL_COLOR}; 1362 bitmap.setPixels(testArray, 0, 1, 0, 0, 1, 1); 1363 bitmap.getPixels(testArray, 0, 1, 0, 0, 1, 1); 1364 assertEquals(bitmap.getPixel(0, 0), PREMUL_COLOR); 1365 } 1366 1367 @Test testPremultipliedCanvas()1368 public void testPremultipliedCanvas() { 1369 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 1370 bitmap.setHasAlpha(true); 1371 bitmap.setPremultiplied(false); 1372 assertFalse(bitmap.isPremultiplied()); 1373 1374 Canvas c = new Canvas(); 1375 assertThrows(RuntimeException.class, () -> c.drawBitmap(bitmap, 0, 0, null)); 1376 } 1377 1378 @Test testSetWidth()1379 public void testSetWidth() { 1380 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1381 int alloc = bitmap.getAllocationByteCount(); 1382 1383 // test shrinking 1384 bitmap.setWidth(50); 1385 assertEquals(bitmap.getAllocationByteCount(), alloc); 1386 assertEquals(bitmap.getByteCount() * 2, alloc); 1387 } 1388 1389 @Test testSetWidthExpanding()1390 public void testSetWidthExpanding() { 1391 // test expanding 1392 bitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); 1393 1394 assertThrows(IllegalArgumentException.class, () -> bitmap.setWidth(101)); 1395 } 1396 1397 @Test testSetWidthMutable()1398 public void testSetWidthMutable() { 1399 // test mutable 1400 bitmap = BitmapFactory.decodeResource(res, R.drawable.start, options); 1401 1402 assertThrows(IllegalStateException.class, () -> bitmap.setWidth(1)); 1403 } 1404 1405 @Test testWriteToParcelRecycled()1406 public void testWriteToParcelRecycled() { 1407 bitmap.recycle(); 1408 1409 assertThrows(IllegalStateException.class, () -> bitmap.writeToParcel(null, 0)); 1410 } 1411 1412 @Test testGetScaledHeight1()1413 public void testGetScaledHeight1() { 1414 int dummyDensity = 5; 1415 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1416 int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), dummyDensity); 1417 assertNotNull(ret); 1418 assertEquals(scaledHeight, ret.getScaledHeight(dummyDensity)); 1419 } 1420 1421 @Test testGetScaledHeight2()1422 public void testGetScaledHeight2() { 1423 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1424 DisplayMetrics metrics = RuntimeEnvironment.getApplication().getResources().getDisplayMetrics(); 1425 int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), metrics.densityDpi); 1426 assertEquals(scaledHeight, ret.getScaledHeight(metrics)); 1427 } 1428 1429 @Test testGetScaledHeight3()1430 public void testGetScaledHeight3() { 1431 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1432 Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 1433 Canvas mCanvas = new Canvas(mMutableBitmap); 1434 // set Density 1435 mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH); 1436 int scaledHeight = scaleFromDensity(ret.getHeight(), ret.getDensity(), mCanvas.getDensity()); 1437 assertEquals(scaledHeight, ret.getScaledHeight(mCanvas)); 1438 } 1439 1440 @Test testGetScaledWidth1()1441 public void testGetScaledWidth1() { 1442 int dummyDensity = 5; 1443 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1444 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), dummyDensity); 1445 assertNotNull(ret); 1446 assertEquals(scaledWidth, ret.getScaledWidth(dummyDensity)); 1447 } 1448 1449 @Test testGetScaledWidth2()1450 public void testGetScaledWidth2() { 1451 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1452 DisplayMetrics metrics = RuntimeEnvironment.getApplication().getResources().getDisplayMetrics(); 1453 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), metrics.densityDpi); 1454 assertEquals(scaledWidth, ret.getScaledWidth(metrics)); 1455 } 1456 1457 @Test testGetScaledWidth3()1458 public void testGetScaledWidth3() { 1459 Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565); 1460 Bitmap mMutableBitmap = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 1461 Canvas mCanvas = new Canvas(mMutableBitmap); 1462 // set Density 1463 mCanvas.setDensity(DisplayMetrics.DENSITY_HIGH); 1464 int scaledWidth = scaleFromDensity(ret.getWidth(), ret.getDensity(), mCanvas.getDensity()); 1465 assertEquals(scaledWidth, ret.getScaledWidth(mCanvas)); 1466 } 1467 1468 @Test testSameAs_simpleSuccess()1469 public void testSameAs_simpleSuccess() { 1470 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1471 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1472 bitmap1.eraseColor(Color.BLACK); 1473 bitmap2.eraseColor(Color.BLACK); 1474 assertTrue(bitmap1.sameAs(bitmap2)); 1475 assertTrue(bitmap2.sameAs(bitmap1)); 1476 } 1477 1478 @Test testSameAs_simpleFail()1479 public void testSameAs_simpleFail() { 1480 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1481 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1482 bitmap1.eraseColor(Color.BLACK); 1483 bitmap2.eraseColor(Color.BLACK); 1484 bitmap2.setPixel(20, 10, Color.WHITE); 1485 assertFalse(bitmap1.sameAs(bitmap2)); 1486 assertFalse(bitmap2.sameAs(bitmap1)); 1487 } 1488 1489 @Test testSameAs_reconfigure()1490 public void testSameAs_reconfigure() { 1491 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1492 Bitmap bitmap2 = Bitmap.createBitmap(150, 150, Config.ARGB_8888); 1493 bitmap2.reconfigure(100, 100, Config.ARGB_8888); // now same size, so should be same 1494 bitmap1.eraseColor(Color.BLACK); 1495 bitmap2.eraseColor(Color.BLACK); 1496 assertTrue(bitmap1.sameAs(bitmap2)); 1497 assertTrue(bitmap2.sameAs(bitmap1)); 1498 } 1499 1500 @Test testSameAs_config()1501 public void testSameAs_config() { 1502 Bitmap bitmap1 = Bitmap.createBitmap(100, 200, Config.RGB_565); 1503 Bitmap bitmap2 = Bitmap.createBitmap(100, 200, Config.ARGB_8888); 1504 1505 // both bitmaps can represent black perfectly 1506 bitmap1.eraseColor(Color.BLACK); 1507 bitmap2.eraseColor(Color.BLACK); 1508 1509 // but not same due to config 1510 assertFalse(bitmap1.sameAs(bitmap2)); 1511 assertFalse(bitmap2.sameAs(bitmap1)); 1512 } 1513 1514 @Test testSameAs_width()1515 public void testSameAs_width() { 1516 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1517 Bitmap bitmap2 = Bitmap.createBitmap(101, 100, Config.ARGB_8888); 1518 bitmap1.eraseColor(Color.BLACK); 1519 bitmap2.eraseColor(Color.BLACK); 1520 assertFalse(bitmap1.sameAs(bitmap2)); 1521 assertFalse(bitmap2.sameAs(bitmap1)); 1522 } 1523 1524 @Test testSameAs_height()1525 public void testSameAs_height() { 1526 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1527 Bitmap bitmap2 = Bitmap.createBitmap(102, 100, Config.ARGB_8888); 1528 bitmap1.eraseColor(Color.BLACK); 1529 bitmap2.eraseColor(Color.BLACK); 1530 assertFalse(bitmap1.sameAs(bitmap2)); 1531 assertFalse(bitmap2.sameAs(bitmap1)); 1532 } 1533 1534 @Test testSameAs_opaque()1535 public void testSameAs_opaque() { 1536 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1537 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1538 bitmap1.eraseColor(Color.BLACK); 1539 bitmap2.eraseColor(Color.BLACK); 1540 bitmap1.setHasAlpha(true); 1541 bitmap2.setHasAlpha(false); 1542 assertFalse(bitmap1.sameAs(bitmap2)); 1543 assertFalse(bitmap2.sameAs(bitmap1)); 1544 } 1545 1546 @Test testSameAs_hardware()1547 public void testSameAs_hardware() { 1548 Bitmap bitmap1 = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1549 Bitmap bitmap2 = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1550 Bitmap bitmap3 = BitmapFactory.decodeResource(res, R.drawable.robot); 1551 Bitmap bitmap4 = BitmapFactory.decodeResource(res, R.drawable.start, HARDWARE_OPTIONS); 1552 assertTrue(bitmap1.sameAs(bitmap2)); 1553 assertTrue(bitmap2.sameAs(bitmap1)); 1554 assertFalse(bitmap1.sameAs(bitmap3)); 1555 assertFalse(bitmap1.sameAs(bitmap4)); 1556 } 1557 1558 @Test testHardwareSetWidth()1559 public void testHardwareSetWidth() { 1560 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1561 assertThrows(IllegalStateException.class, () -> bitmap.setWidth(30)); 1562 } 1563 1564 @Test testHardwareSetHeight()1565 public void testHardwareSetHeight() { 1566 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1567 assertThrows(IllegalStateException.class, () -> bitmap.setHeight(30)); 1568 } 1569 1570 @Test testHardwareSetConfig()1571 public void testHardwareSetConfig() { 1572 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1573 assertThrows(IllegalStateException.class, () -> bitmap.setConfig(Config.ARGB_8888)); 1574 } 1575 1576 @Test testHardwareReconfigure()1577 public void testHardwareReconfigure() { 1578 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1579 assertThrows(IllegalStateException.class, () -> bitmap.reconfigure(30, 30, Config.ARGB_8888)); 1580 } 1581 1582 @Test testHardwareSetPixels()1583 public void testHardwareSetPixels() { 1584 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1585 assertThrows( 1586 IllegalStateException.class, () -> bitmap.setPixels(new int[10], 0, 1, 0, 0, 1, 1)); 1587 } 1588 1589 @Test testHardwareSetPixel()1590 public void testHardwareSetPixel() { 1591 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1592 assertThrows(IllegalStateException.class, () -> bitmap.setPixel(1, 1, 0)); 1593 } 1594 1595 @Test testHardwareEraseColor()1596 public void testHardwareEraseColor() { 1597 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1598 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(0)); 1599 } 1600 1601 @org.robolectric.annotation.Config(minSdk = Q) 1602 @Test testHardwareEraseColorLong()1603 public void testHardwareEraseColorLong() { 1604 Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.robot, HARDWARE_OPTIONS); 1605 assertThrows(IllegalStateException.class, () -> bitmap.eraseColor(Color.pack(0))); 1606 } 1607 1608 @Test testUseMetadataAfterRecycle()1609 public void testUseMetadataAfterRecycle() { 1610 Bitmap bitmap = Bitmap.createBitmap(10, 20, Config.RGB_565); 1611 bitmap.recycle(); 1612 assertEquals(10, bitmap.getWidth()); 1613 assertEquals(20, bitmap.getHeight()); 1614 assertEquals(Config.RGB_565, bitmap.getConfig()); 1615 } 1616 1617 @Test 1618 @Ignore("TODO(b/hoisie): re-enable when HW bitmaps are better supported") testCreateScaledFromHWInStrictMode()1619 public void testCreateScaledFromHWInStrictMode() { 1620 strictModeTest( 1621 () -> { 1622 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1623 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 1624 Bitmap.createScaledBitmap(hwBitmap, 200, 200, false); 1625 }); 1626 } 1627 1628 @Test testCompressInStrictMode()1629 public void testCompressInStrictMode() { 1630 strictModeTest( 1631 () -> { 1632 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1633 bitmap.compress(CompressFormat.JPEG, 90, new ByteArrayOutputStream()); 1634 }); 1635 } 1636 1637 @Test legacyShadowAPIs_throwException()1638 public void legacyShadowAPIs_throwException() { 1639 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1640 ShadowBitmap shadowBitmap = Shadow.extract(bitmap); 1641 assertThrows(UnsupportedOperationException.class, () -> shadowBitmap.setDescription("hello")); 1642 } 1643 1644 @Ignore("TODO(b/hoisie): re-enable when HW bitmaps are better supported") 1645 @Test testParcelHWInStrictMode()1646 public void testParcelHWInStrictMode() { 1647 strictModeTest( 1648 () -> { 1649 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1650 Bitmap hwBitmap = bitmap.copy(Config.HARDWARE, false); 1651 hwBitmap.writeToParcel(Parcel.obtain(), 0); 1652 }); 1653 } 1654 1655 @Test getCreatedFromResId()1656 public void getCreatedFromResId() { 1657 assertThat(((ShadowNativeBitmap) Shadow.extract(bitmap)).getCreatedFromResId()) 1658 .isEqualTo(R.drawable.start); 1659 } 1660 1661 @Test testWriteToParcel()1662 public void testWriteToParcel() { 1663 Parcel p = Parcel.obtain(); 1664 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1665 bitmap.eraseColor(Color.GREEN); 1666 bitmap.writeToParcel(p, 0); 1667 p.setDataPosition(0); 1668 Bitmap fromParcel = Bitmap.CREATOR.createFromParcel(p); 1669 assertTrue(bitmap.sameAs(fromParcel)); 1670 assertThat(fromParcel.isMutable()).isTrue(); 1671 p.recycle(); 1672 } 1673 1674 @Test testWriteImmutableToParcel()1675 public void testWriteImmutableToParcel() { 1676 Parcel p = Parcel.obtain(); 1677 bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 1678 bitmap.eraseColor(Color.GREEN); 1679 Bitmap immutable = bitmap.copy(Config.ARGB_8888, /*isMutable=*/ false); 1680 assertThat(immutable.isMutable()).isFalse(); 1681 immutable.writeToParcel(p, 0); 1682 p.setDataPosition(0); 1683 Bitmap fromParcel = Bitmap.CREATOR.createFromParcel(p); 1684 assertTrue(immutable.sameAs(fromParcel)); 1685 assertThat(fromParcel.isMutable()).isFalse(); 1686 p.recycle(); 1687 } 1688 1689 @Test createBitmap_colorSpace_customColorSpace()1690 public void createBitmap_colorSpace_customColorSpace() { 1691 Bitmap bitmap = 1692 Bitmap.createBitmap( 1693 100, 100, Bitmap.Config.ARGB_8888, true, ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 1694 1695 assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 1696 } 1697 1698 @Test compress_thenDecodeStream_sameAs()1699 public void compress_thenDecodeStream_sameAs() { 1700 Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Bitmap.Config.ARGB_8888); 1701 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 1702 bitmap.compress(CompressFormat.PNG, /* quality= */ 100, outStream); 1703 byte[] outBytes = outStream.toByteArray(); 1704 ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes); 1705 BitmapFactory.Options options = new Options(); 1706 Bitmap bitmap2 = BitmapFactory.decodeStream(inStream, null, options); 1707 assertThat(bitmap.sameAs(bitmap2)).isTrue(); 1708 } 1709 1710 @Test parcelRoundTripWithoutColorSpace_isSuccessful()1711 public void parcelRoundTripWithoutColorSpace_isSuccessful() { 1712 // Importantly, ALPHA_8 doesn't have an associated color space. 1713 Bitmap orig = Bitmap.createBitmap(/* width= */ 314, /* height= */ 159, Bitmap.Config.ALPHA_8); 1714 1715 Parcel parcel = Parcel.obtain(); 1716 parcel.writeParcelable(orig, /* parcelableFlags= */ 0); 1717 parcel.setDataPosition(0); 1718 Bitmap copy = parcel.readParcelable(Bitmap.class.getClassLoader()); 1719 1720 assertThat(copy.sameAs(orig)).isTrue(); 1721 } 1722 strictModeTest(Runnable runnable)1723 private void strictModeTest(Runnable runnable) { 1724 StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy(); 1725 StrictMode.setThreadPolicy( 1726 new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls().penaltyDeath().build()); 1727 try { 1728 runnable.run(); 1729 fail("Shouldn't reach it"); 1730 } catch (RuntimeException expected) { 1731 // expect to receive StrictModeViolation 1732 } finally { 1733 StrictMode.setThreadPolicy(originalPolicy); 1734 } 1735 } 1736 1737 static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1; 1738 scaleFromDensity(int size, int sdensity, int tdensity)1739 private static int scaleFromDensity(int size, int sdensity, int tdensity) { 1740 if (sdensity == Bitmap.DENSITY_NONE || sdensity == tdensity) { 1741 return size; 1742 } 1743 1744 // Scale by tdensity / sdensity, rounding up. 1745 return ((size * tdensity) + (sdensity >> 1)) / sdensity; 1746 } 1747 createColors(int size)1748 private static int[] createColors(int size) { 1749 int[] colors = new int[size]; 1750 1751 for (int i = 0; i < size; i++) { 1752 colors[i] = (0xFF << 24) | (i << 16) | (i << 8) | i; 1753 } 1754 1755 return colors; 1756 } 1757 createHardwareBitmapOptions()1758 private static BitmapFactory.Options createHardwareBitmapOptions() { 1759 BitmapFactory.Options options = new BitmapFactory.Options(); 1760 options.inPreferredConfig = Config.HARDWARE; 1761 return options; 1762 } 1763 } 1764