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