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