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