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