1 package android.graphics; 2 3 import static android.os.Build.VERSION_CODES.M; 4 import static android.os.Build.VERSION_CODES.O; 5 import static android.os.Build.VERSION_CODES.P; 6 import static android.os.Build.VERSION_CODES.Q; 7 import static androidx.test.InstrumentationRegistry.getTargetContext; 8 import static com.google.common.truth.Truth.assertThat; 9 import static com.google.common.truth.TruthJUnit.assume; 10 import static org.junit.Assert.assertThrows; 11 12 import android.content.res.Resources; 13 import android.graphics.Bitmap.CompressFormat; 14 import android.graphics.drawable.BitmapDrawable; 15 import android.graphics.drawable.ColorDrawable; 16 import android.graphics.drawable.Drawable; 17 import android.os.Build; 18 import android.util.DisplayMetrics; 19 import androidx.test.ext.junit.runners.AndroidJUnit4; 20 import androidx.test.filters.SdkSuppress; 21 import androidx.test.platform.graphics.HardwareRendererCompat; 22 import java.io.ByteArrayInputStream; 23 import java.io.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.nio.ByteBuffer; 27 import java.nio.IntBuffer; 28 import org.junit.Before; 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 import org.robolectric.annotation.Config; 32 import org.robolectric.annotation.internal.DoNotInstrument; 33 import org.robolectric.testapp.R; 34 35 /** Compatibility test for {@link Bitmap} */ 36 @DoNotInstrument 37 @RunWith(AndroidJUnit4.class) 38 public class BitmapTest { 39 40 private Resources resources; 41 42 @Before setUp()43 public void setUp() { 44 resources = getTargetContext().getResources(); 45 } 46 47 @Config(minSdk = P) 48 @SdkSuppress(minSdkVersion = P) createBitmap()49 @Test public void createBitmap() { 50 assume().that(System.getProperty("robolectric.graphicsMode")).isNotEqualTo("NATIVE"); 51 // Bitmap.createBitmap(Picture) requires hardware-backed bitmaps 52 HardwareRendererCompat.setDrawingEnabled(true); 53 Picture picture = new Picture(); 54 Canvas canvas = picture.beginRecording(200, 100); 55 56 Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); 57 58 p.setColor(0x88FF0000); 59 canvas.drawCircle(50, 50, 40, p); 60 61 p.setColor(Color.GREEN); 62 p.setTextSize(30); 63 canvas.drawText("Pictures", 60, 60, p); 64 picture.endRecording(); 65 66 Bitmap bitmap = Bitmap.createBitmap(picture); 67 assertThat(bitmap).isNotNull(); 68 } 69 70 @Test testEraseColor()71 public void testEraseColor() { 72 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 73 bitmap.eraseColor(0xffff0000); 74 assertThat(bitmap.getPixel(10, 10)).isEqualTo(0xffff0000); 75 assertThat(bitmap.getPixel(50, 50)).isEqualTo(0xffff0000); 76 } 77 78 @Test 79 @SdkSuppress(minSdkVersion = M) // getAlpha() returns 0 on less than M testExtractAlpha()80 public void testExtractAlpha() { 81 // normal case 82 Bitmap bitmap = 83 BitmapFactory.decodeResource(resources, R.drawable.an_image, new BitmapFactory.Options()); 84 Bitmap ret = bitmap.extractAlpha(); 85 int source = bitmap.getPixel(10, 20); 86 int result = ret.getPixel(10, 20); 87 assertThat(Color.alpha(result)).isEqualTo(Color.alpha(source)); 88 } 89 90 @Test testCopy()91 public void testCopy() { 92 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 93 Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false); 94 assertThat(copy.getWidth()).isEqualTo(bitmap.getWidth()); 95 assertThat(copy.getHeight()).isEqualTo(bitmap.getHeight()); 96 assertThat(copy.getConfig()).isEqualTo(bitmap.getConfig()); 97 } 98 99 @Test testCopyAndEraseColor()100 public void testCopyAndEraseColor() { 101 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 102 bitmap.eraseColor(0xffffff00); 103 assertThat(bitmap.getPixel(10, 10)).isEqualTo(0xffffff00); 104 assertThat(bitmap.getPixel(50, 50)).isEqualTo(0xffffff00); 105 106 Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true); 107 assertThat(copy.getPixel(10, 10)).isEqualTo(0xffffff00); 108 assertThat(copy.getPixel(50, 50)).isEqualTo(0xffffff00); 109 110 copy.eraseColor(0xffff0000); 111 assertThat(copy.getPixel(10, 10)).isEqualTo(0xffff0000); 112 assertThat(copy.getPixel(50, 50)).isEqualTo(0xffff0000); 113 } 114 115 @Test compress()116 public void compress() { 117 Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.an_image); 118 119 ByteArrayOutputStream stm = new ByteArrayOutputStream(); 120 assertThat(bitmap.compress(CompressFormat.JPEG, 0, stm)).isTrue(); 121 assertThat(stm.toByteArray()).isNotEmpty(); 122 } 123 124 @Test getConfigAfterCompress()125 public void getConfigAfterCompress() throws IOException { 126 InputStream inputStream = resources.getAssets().open("robolectric.png"); 127 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 128 Matrix matrix = new Matrix(); 129 matrix.setScale(0.5f, 0.5f); 130 Bitmap scaledBitmap = 131 Bitmap.createBitmap( 132 bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, /* filter */ true); 133 assertThat(scaledBitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888); 134 } 135 136 @Test getConfigAfterCreateScaledBitmap()137 public void getConfigAfterCreateScaledBitmap() throws IOException { 138 InputStream inputStream = resources.getAssets().open("robolectric.png"); 139 Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 140 Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, /* filter= */ false); 141 assertThat(scaledBitmap.getConfig()).isEqualTo(Bitmap.Config.ARGB_8888); 142 } 143 144 @Test scaledBitmap_sameAs()145 public void scaledBitmap_sameAs() { 146 Bitmap bitmap1 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 147 bitmap1.eraseColor(0xffff0000); 148 Bitmap bitmap2 = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 149 bitmap2.eraseColor(0xff00ff00); 150 assertThat(bitmap1.sameAs(bitmap2)).isFalse(); 151 152 Bitmap scaled1 = Bitmap.createScaledBitmap(bitmap1, 200, 200, false); 153 Bitmap scaled2 = Bitmap.createScaledBitmap(bitmap2, 200, 200, false); 154 assertThat(scaled1.sameAs(scaled2)).isFalse(); 155 } 156 157 @Test checkBitmapNotRecycled()158 public void checkBitmapNotRecycled() throws IOException { 159 InputStream inputStream = resources.getAssets().open("robolectric.png"); 160 BitmapFactory.Options options = new BitmapFactory.Options(); 161 options.inScaled = true; 162 options.inDensity = 100; 163 options.inTargetDensity = 500; 164 Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); 165 assertThat(bitmap.isRecycled()).isFalse(); 166 } 167 168 @Test decodeResource_withMutableOpt_isMutable()169 public void decodeResource_withMutableOpt_isMutable() { 170 BitmapFactory.Options opt = new BitmapFactory.Options(); 171 opt.inMutable = true; 172 Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.an_image, opt); 173 assertThat(bitmap.isMutable()).isTrue(); 174 } 175 176 @Test scaledBitmap_isMutable()177 public void scaledBitmap_isMutable() throws IOException { 178 InputStream inputStream = resources.getAssets().open("robolectric.png"); 179 BitmapFactory.Options opt = new BitmapFactory.Options(); 180 opt.inMutable = true; 181 Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, opt); 182 Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, false); 183 assertThat(scaledBitmap.isMutable()).isTrue(); 184 } 185 186 @Test colorDrawable_drawToBitmap()187 public void colorDrawable_drawToBitmap() { 188 Drawable colorDrawable = new ColorDrawable(Color.RED); 189 Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 190 Canvas canvas = new Canvas(bitmap); 191 assertThat(canvas.getWidth()).isEqualTo(1); 192 assertThat(canvas.getHeight()).isEqualTo(1); 193 colorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 194 colorDrawable.draw(canvas); 195 assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED); 196 } 197 198 @Test drawCanvas_bitmap_sameSize()199 public void drawCanvas_bitmap_sameSize() { 200 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 201 bitmap.eraseColor(0xff00ff00); 202 Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 203 Canvas canvas = new Canvas(output); 204 canvas.drawBitmap(bitmap, 0, 0, null); 205 assertThat(bitmap.sameAs(output)).isTrue(); 206 } 207 208 @Test drawCanvas_bitmap_centered()209 public void drawCanvas_bitmap_centered() { 210 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 211 bitmap.eraseColor(0xff00ff00); 212 Bitmap output = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); 213 Canvas canvas = new Canvas(output); 214 canvas.drawBitmap(bitmap, 50, 50, null); 215 assertThat(output.getPixel(49, 49)).isEqualTo(0); 216 assertThat(output.getPixel(50, 50)).isEqualTo(0xff00ff00); 217 assertThat(output.getPixel(149, 149)).isEqualTo(0xff00ff00); 218 assertThat(output.getPixel(150, 150)).isEqualTo(0); 219 } 220 221 @Test drawCanvas_overflow_topLeft()222 public void drawCanvas_overflow_topLeft() { 223 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 224 bitmap.eraseColor(0xff00ff00); 225 Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 226 Canvas canvas = new Canvas(output); 227 canvas.drawBitmap(bitmap, -50, -50, null); 228 assertThat(output.getPixel(0, 0)).isEqualTo(0xff00ff00); 229 assertThat(output.getPixel(49, 49)).isEqualTo(0xff00ff00); 230 assertThat(output.getPixel(50, 50)).isEqualTo(0); 231 } 232 233 @Test drawCanvas_overflow_topRight()234 public void drawCanvas_overflow_topRight() { 235 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 236 bitmap.eraseColor(0xff00ff00); 237 Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 238 Canvas canvas = new Canvas(output); 239 canvas.drawBitmap(bitmap, 50, -50, null); 240 assertThat(output.getPixel(99, 0)).isEqualTo(0xff00ff00); 241 assertThat(output.getPixel(50, 49)).isEqualTo(0xff00ff00); 242 assertThat(output.getPixel(49, 50)).isEqualTo(0); 243 } 244 245 @Test drawCanvas_overflow_bottomLeft()246 public void drawCanvas_overflow_bottomLeft() { 247 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 248 bitmap.eraseColor(0xff00ff00); 249 Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 250 Canvas canvas = new Canvas(output); 251 canvas.drawBitmap(bitmap, -50, 50, null); 252 assertThat(output.getPixel(0, 99)).isEqualTo(0xff00ff00); 253 assertThat(output.getPixel(49, 50)).isEqualTo(0xff00ff00); 254 assertThat(output.getPixel(50, 49)).isEqualTo(0); 255 } 256 257 @Test drawCanvas_overflow_bottomRight()258 public void drawCanvas_overflow_bottomRight() { 259 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 260 bitmap.eraseColor(0xff00ff00); 261 Bitmap output = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 262 Canvas canvas = new Canvas(output); 263 canvas.drawBitmap(bitmap, 50, 50, null); 264 assertThat(output.getPixel(99, 99)).isEqualTo(0xff00ff00); 265 assertThat(output.getPixel(50, 50)).isEqualTo(0xff00ff00); 266 assertThat(output.getPixel(49, 49)).isEqualTo(0); 267 } 268 269 @Test createScaledBitmap_zeroWidthAndHeight_error()270 public void createScaledBitmap_zeroWidthAndHeight_error() { 271 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 272 Exception exception = 273 assertThrows( 274 IllegalArgumentException.class, () -> Bitmap.createScaledBitmap(bitmap, 0, 0, false)); 275 276 assertThat(exception).hasMessageThat().contains("width and height must be > 0"); 277 } 278 279 @Test getBitmapPixels_strideTooLong()280 public void getBitmapPixels_strideTooLong() { 281 int[] bitmapPixels = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 282 Bitmap bitmap = Bitmap.createBitmap(bitmapPixels, 3, 3, Bitmap.Config.ARGB_8888); 283 int[] pixelsCopy = new int[bitmap.getHeight() * bitmap.getWidth()]; 284 assertThrows( 285 ArrayIndexOutOfBoundsException.class, 286 () -> 287 bitmap.getPixels( 288 pixelsCopy, 0, bitmap.getRowBytes(), 0, 0, bitmap.getWidth(), bitmap.getHeight())); 289 } 290 291 @Test eraseColor_toTransparent()292 public void eraseColor_toTransparent() { 293 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 294 bitmap.eraseColor(0); 295 assertThat(bitmap.getPixel(0, 0)).isEqualTo(0); 296 } 297 298 @Test reconfigure_drawPixel()299 public void reconfigure_drawPixel() { 300 Bitmap bitmap = Bitmap.createBitmap(100, 50, Bitmap.Config.ARGB_8888); 301 bitmap.reconfigure(50, 100, Bitmap.Config.ARGB_8888); 302 bitmap.setPixel(0, 99, Color.RED); 303 assertThat(bitmap.getPixel(0, 99)).isEqualTo(Color.RED); 304 } 305 306 /** 307 * Questionable ARGB_8888 pixel values like '10' may be simplified by some graphics engines to 308 * '0'. This happens because '10' has alpha transparency '0', so the values for RGB don't matter. 309 * This happens when Java's Graphics2d is used for certain. 310 */ 311 @Test recompress_png100_samePixelss()312 public void recompress_png100_samePixelss() { 313 Bitmap applicationIconBitmap = 314 Bitmap.createBitmap(new int[] {10, 11, 12, 13}, 2, 2, Bitmap.Config.ARGB_8888); 315 316 BitmapDrawable applicationIcon = new BitmapDrawable(resources, applicationIconBitmap); 317 318 ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream(); 319 applicationIconBitmap.compress(CompressFormat.PNG, 100, outputStream1); 320 321 Bitmap bitmap = 322 Bitmap.createBitmap( 323 applicationIcon.getIntrinsicWidth(), 324 applicationIcon.getIntrinsicHeight(), 325 Bitmap.Config.ARGB_8888); 326 Canvas canvas = new Canvas(bitmap); 327 applicationIcon.draw(canvas); 328 ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream(); 329 bitmap.compress(CompressFormat.PNG, 100, outputStream2); 330 assertThat(outputStream2.toByteArray()).isEqualTo(outputStream1.toByteArray()); 331 } 332 333 @Test compress_thenDecodeStream_sameAs()334 public void compress_thenDecodeStream_sameAs() { 335 Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Bitmap.Config.ARGB_8888); 336 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 337 bitmap.compress(CompressFormat.PNG, /* quality= */ 100, outStream); 338 byte[] outBytes = outStream.toByteArray(); 339 ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes); 340 Bitmap bitmap2 = BitmapFactory.decodeStream(inStream); 341 assertThat(bitmap.sameAs(bitmap2)).isTrue(); 342 } 343 344 @Test compress_asJpeg_convertsTransparentToBlack()345 public void compress_asJpeg_convertsTransparentToBlack() { 346 Bitmap bitmap = Bitmap.createBitmap(/* width= */ 10, /* height= */ 10, Bitmap.Config.ARGB_8888); 347 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 348 bitmap.compress(CompressFormat.JPEG, /* quality= */ 90, outStream); 349 byte[] outBytes = outStream.toByteArray(); 350 assertThat(outBytes).isNotEmpty(); 351 ByteArrayInputStream inStream = new ByteArrayInputStream(outBytes); 352 Bitmap bitmap2 = BitmapFactory.decodeStream(inStream); 353 assertThat(bitmap2.getPixel(0, 0)).isEqualTo(Color.BLACK); 354 } 355 356 @Test createBitmapWithOffsetAndStride()357 public void createBitmapWithOffsetAndStride() { 358 int[] pixels = new int[10]; 359 Bitmap result = Bitmap.createBitmap(pixels, 0, 2, 2, 5, Bitmap.Config.ARGB_8888); 360 assertThat(result).isNotNull(); 361 } 362 363 @Test createBitmap_mutability()364 public void createBitmap_mutability() { 365 // Mutable constructor variants. 366 assertThat( 367 Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888) 368 .isMutable()) 369 .isTrue(); 370 assertThat( 371 Bitmap.createBitmap( 372 (DisplayMetrics) null, /* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888) 373 .isMutable()) 374 .isTrue(); 375 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 376 assertThat( 377 Bitmap.createBitmap( 378 /* width= */ 1, 379 /* height= */ 1, 380 Bitmap.Config.ARGB_8888, 381 /* hasAlpha= */ true) 382 .isMutable()) 383 .isTrue(); 384 assertThat( 385 Bitmap.createBitmap( 386 /* display= */ null, 387 /* width= */ 1, 388 /* height= */ 1, 389 Bitmap.Config.ARGB_8888, 390 /* hasAlpha= */ true) 391 .isMutable()) 392 .isTrue(); 393 } 394 395 // Immutable constructor variants. 396 assertThat( 397 Bitmap.createBitmap( 398 /* colors= */ new int[] {0}, 399 /* width= */ 1, 400 /* height= */ 1, 401 Bitmap.Config.ARGB_8888) 402 .isMutable()) 403 .isFalse(); 404 assertThat( 405 Bitmap.createBitmap( 406 /* colors= */ new int[] {0}, 407 /* offset= */ 0, 408 /* stride= */ 1, 409 /* width= */ 1, 410 /* height= */ 1, 411 Bitmap.Config.ARGB_8888) 412 .isMutable()) 413 .isFalse(); 414 assertThat( 415 Bitmap.createBitmap( 416 /* display= */ null, 417 /* colors= */ new int[] {0}, 418 /* width= */ 1, 419 /* height= */ 1, 420 Bitmap.Config.ARGB_8888) 421 .isMutable()) 422 .isFalse(); 423 assertThat( 424 Bitmap.createBitmap( 425 /* display= */ null, 426 /* colors= */ new int[] {0}, 427 /* offset= */ 0, 428 /* stride= */ 1, 429 /* width= */ 1, 430 /* height= */ 1, 431 Bitmap.Config.ARGB_8888) 432 .isMutable()) 433 .isFalse(); 434 } 435 436 @Test createBitmap_hasAlpha()437 public void createBitmap_hasAlpha() { 438 // ARGB_8888 has alpha by default. 439 assertThat( 440 Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888) 441 .hasAlpha()) 442 .isTrue(); 443 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 444 assertThat( 445 Bitmap.createBitmap( 446 /* width= */ 1, 447 /* height= */ 1, 448 Bitmap.Config.ARGB_8888, 449 /* hasAlpha= */ true) 450 .hasAlpha()) 451 .isTrue(); 452 assertThat( 453 Bitmap.createBitmap( 454 /* display= */ null, 455 /* width= */ 1, 456 /* height= */ 1, 457 Bitmap.Config.ARGB_8888, 458 /* hasAlpha= */ true) 459 .hasAlpha()) 460 .isTrue(); 461 } 462 assertThat( 463 Bitmap.createBitmap( 464 /* colors= */ new int[] {0}, 465 /* width= */ 1, 466 /* height= */ 1, 467 Bitmap.Config.ARGB_8888) 468 .hasAlpha()) 469 .isTrue(); 470 471 // Doesn't have alpha 472 assertThat( 473 Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.RGB_565).hasAlpha()) 474 .isFalse(); 475 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 476 assertThat( 477 Bitmap.createBitmap( 478 /* width= */ 1, 479 /* height= */ 1, 480 Bitmap.Config.ARGB_8888, 481 /* hasAlpha= */ false) 482 .hasAlpha()) 483 .isFalse(); 484 assertThat( 485 Bitmap.createBitmap( 486 /* display= */ null, 487 /* width= */ 1, 488 /* height= */ 1, 489 Bitmap.Config.ARGB_8888, 490 /* hasAlpha= */ false) 491 .hasAlpha()) 492 .isFalse(); 493 } 494 } 495 496 @Test createBitmap_premultiplied()497 public void createBitmap_premultiplied() { 498 // ARGB_8888 has alpha by default, is premultiplied. 499 assertThat( 500 Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.ARGB_8888) 501 .isPremultiplied()) 502 .isTrue(); 503 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 504 assertThat( 505 Bitmap.createBitmap( 506 /* width= */ 1, 507 /* height= */ 1, 508 Bitmap.Config.ARGB_8888, 509 /* hasAlpha= */ true) 510 .isPremultiplied()) 511 .isTrue(); 512 assertThat( 513 Bitmap.createBitmap( 514 /* display= */ null, 515 /* width= */ 1, 516 /* height= */ 1, 517 Bitmap.Config.ARGB_8888, 518 /* hasAlpha= */ true) 519 .isPremultiplied()) 520 .isTrue(); 521 } 522 assertThat( 523 Bitmap.createBitmap( 524 /* colors= */ new int[] {0}, 525 /* width= */ 1, 526 /* height= */ 1, 527 Bitmap.Config.ARGB_8888) 528 .isPremultiplied()) 529 .isTrue(); 530 531 // Doesn't have alpha, is not premultiplied 532 assertThat( 533 Bitmap.createBitmap(/* width= */ 1, /* height= */ 1, Bitmap.Config.RGB_565) 534 .isPremultiplied()) 535 .isFalse(); 536 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 537 assertThat( 538 Bitmap.createBitmap( 539 /* width= */ 1, 540 /* height= */ 1, 541 Bitmap.Config.ARGB_8888, 542 /* hasAlpha= */ false) 543 .isPremultiplied()) 544 .isFalse(); 545 assertThat( 546 Bitmap.createBitmap( 547 /* display= */ null, 548 /* width= */ 1, 549 /* height= */ 1, 550 Bitmap.Config.ARGB_8888, 551 /* hasAlpha= */ false) 552 .isPremultiplied()) 553 .isFalse(); 554 } 555 } 556 557 @Test extractAlpha_isMutable()558 public void extractAlpha_isMutable() { 559 Bitmap result = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 560 Bitmap alphaBitmap = result.extractAlpha(); 561 assertThat(alphaBitmap.isMutable()).isTrue(); 562 } 563 564 @Test createBitmap_withBitmap_containsImageData()565 public void createBitmap_withBitmap_containsImageData() { 566 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 567 bitmap.eraseColor(Color.BLUE); 568 Bitmap cropped = Bitmap.createBitmap(bitmap, 0, 0, 50, 50); 569 assertThat(cropped.isMutable()).isTrue(); 570 assertThat(cropped.getPixel(0, 0)).isEqualTo(Color.BLUE); 571 } 572 573 @Test createBitmap_withBitmap_thenCopy_isValid()574 public void createBitmap_withBitmap_thenCopy_isValid() { 575 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 576 bitmap.eraseColor(Color.BLUE); 577 Bitmap cropped = Bitmap.createBitmap(bitmap, 50, 50, 50, 50); 578 Bitmap copy = cropped.copy(Bitmap.Config.ARGB_8888, true); 579 assertThat(copy.isMutable()).isTrue(); 580 assertThat(copy.getPixel(0, 0)).isEqualTo(Color.BLUE); 581 } 582 583 @Test copyPixelsFromBuffer_intBuffer()584 public void copyPixelsFromBuffer_intBuffer() { 585 Bitmap bitmap = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888); 586 IntBuffer input = IntBuffer.allocate(bitmap.getWidth() * bitmap.getHeight()); 587 for (int i = 0; i < input.capacity(); i++) { 588 // IntBuffer is interpreted as ABGR. Use A=255 to avoid premultiplication. 589 input.put((0xFF << 24) | (i + 2) << 16 | (i + 1) << 8 | i); 590 } 591 input.rewind(); 592 bitmap.copyPixelsFromBuffer(input); 593 594 IntBuffer output = IntBuffer.allocate(input.capacity()); 595 bitmap.copyPixelsToBuffer(output); 596 597 input.rewind(); 598 output.rewind(); 599 600 assertThat(output).isEqualTo(input); 601 } 602 603 @Test copyPixelsFromBuffer_byteBuffer()604 public void copyPixelsFromBuffer_byteBuffer() { 605 Bitmap bitmap = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888); 606 ByteBuffer input = ByteBuffer.allocate(bitmap.getWidth() * bitmap.getHeight() * 4); 607 for (int i = 0; i < bitmap.getWidth() * bitmap.getHeight(); i++) { 608 // ByteBuffer is interpreted as RGBA. Use A=255 to avoid premultiplication. 609 input.put((byte) i); 610 input.put((byte) (i + 1)); 611 input.put((byte) (i + 2)); 612 input.put((byte) 0xFF); 613 } 614 input.rewind(); 615 bitmap.copyPixelsFromBuffer(input); 616 617 ByteBuffer output = ByteBuffer.allocate(input.capacity()); 618 bitmap.copyPixelsToBuffer(output); 619 620 input.rewind(); 621 output.rewind(); 622 623 assertThat(output).isEqualTo(input); 624 } 625 626 @SdkSuppress(minSdkVersion = O) 627 @Config(minSdk = O) 628 @Test createBitmap_colorSpace_defaultColorSpace()629 public void createBitmap_colorSpace_defaultColorSpace() { 630 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 631 632 assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.SRGB)); 633 } 634 635 @SdkSuppress(minSdkVersion = O) 636 @Config(minSdk = O) 637 @Test createBitmap_colorSpace_customColorSpace()638 public void createBitmap_colorSpace_customColorSpace() { 639 Bitmap bitmap = 640 Bitmap.createBitmap( 641 100, 100, Bitmap.Config.ARGB_8888, true, ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 642 643 assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 644 } 645 646 @SdkSuppress(minSdkVersion = Q) 647 @Config(minSdk = Q) 648 @Test setColorSpace()649 public void setColorSpace() { 650 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 651 bitmap.setColorSpace(ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 652 653 assertThat(bitmap.getColorSpace()).isEqualTo(ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 654 } 655 656 @Test bitmapDrawable_mutate()657 public void bitmapDrawable_mutate() { 658 BitmapDrawable drawable1 = (BitmapDrawable) resources.getDrawable(R.drawable.an_image); 659 BitmapDrawable drawable2 = (BitmapDrawable) resources.getDrawable(R.drawable.an_image); 660 661 Drawable mutated1 = drawable1.mutate(); 662 Drawable mutated2 = drawable2.mutate(); 663 mutated1.setAlpha(100); 664 mutated1.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN); 665 mutated2.setAlpha(200); 666 mutated2.setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN); 667 assertThat(mutated1.getAlpha()).isEqualTo(100); 668 // ColorFilter is part of the Drawable paint, so BLUE is overridden by RED. 669 assertThat(mutated1.getColorFilter()) 670 .isEqualTo(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)); 671 assertThat(mutated2.getAlpha()).isEqualTo(200); 672 assertThat(mutated2.getColorFilter()) 673 .isEqualTo(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN)); 674 } 675 676 @Test null_bitmapConfig_throwsNPE()677 public void null_bitmapConfig_throwsNPE() { 678 assertThrows(NullPointerException.class, () -> Bitmap.createBitmap(100, 100, null)); 679 } 680 } 681