1 /* 2 * Copyright (C) 2017 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 17 package android.graphics.cts; 18 19 import static org.junit.Assert.assertEquals; 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.BitmapFactory; 30 import android.graphics.ColorSpace; 31 import android.graphics.ImageDecoder; 32 import android.os.Parcel; 33 import android.support.test.InstrumentationRegistry; 34 import android.support.test.filters.RequiresDevice; 35 import android.support.test.filters.SmallTest; 36 import android.support.test.runner.AndroidJUnit4; 37 import android.util.Log; 38 39 import androidx.annotation.ColorInt; 40 import androidx.annotation.NonNull; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.io.ByteArrayOutputStream; 47 import java.io.IOException; 48 import java.io.InputStream; 49 import java.nio.ByteBuffer; 50 import java.nio.IntBuffer; 51 import java.util.Arrays; 52 53 @SmallTest 54 @RunWith(AndroidJUnit4.class) 55 public class BitmapColorSpaceTest { 56 private static final String LOG_TAG = "BitmapColorSpaceTest"; 57 58 private Resources mResources; 59 60 @Before setup()61 public void setup() { 62 mResources = InstrumentationRegistry.getTargetContext().getResources(); 63 } 64 65 @SuppressWarnings("deprecation") 66 @Test createWithColorSpace()67 public void createWithColorSpace() { 68 Bitmap b; 69 ColorSpace cs; 70 ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 71 72 // We don't test HARDWARE configs because they are not compatible with mutable bitmaps 73 74 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, sRGB); 75 cs = b.getColorSpace(); 76 assertNotNull(cs); 77 assertSame(sRGB, cs); 78 79 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, 80 ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 81 cs = b.getColorSpace(); 82 assertNotNull(cs); 83 assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs); 84 85 b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true, sRGB); 86 cs = b.getColorSpace(); 87 assertNotNull(cs); 88 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 89 90 b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true, 91 ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 92 cs = b.getColorSpace(); 93 assertNotNull(cs); 94 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 95 96 b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true, sRGB); 97 cs = b.getColorSpace(); 98 assertNotNull(cs); 99 assertSame(sRGB, cs); 100 101 b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true, 102 ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 103 cs = b.getColorSpace(); 104 assertNotNull(cs); 105 assertSame(sRGB, cs); 106 107 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true, sRGB); 108 cs = b.getColorSpace(); 109 assertNotNull(cs); 110 assertSame(sRGB, cs); 111 112 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true, 113 ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 114 cs = b.getColorSpace(); 115 assertNotNull(cs); 116 assertSame(sRGB, cs); 117 118 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true, sRGB); 119 cs = b.getColorSpace(); 120 assertNotNull(cs); 121 assertSame(sRGB, cs); 122 123 b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true, 124 ColorSpace.get(ColorSpace.Named.ADOBE_RGB)); 125 cs = b.getColorSpace(); 126 assertNotNull(cs); 127 assertSame(sRGB, cs); 128 } 129 130 @Test createDefaultColorSpace()131 public void createDefaultColorSpace() { 132 ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 133 Bitmap.Config[] configs = new Bitmap.Config[] { 134 Bitmap.Config.ALPHA_8, Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 135 }; 136 for (Bitmap.Config config : configs) { 137 Bitmap bitmap = Bitmap.createBitmap(32, 32, config, true); 138 assertSame(sRGB, bitmap.getColorSpace()); 139 } 140 } 141 142 @Test(expected = IllegalArgumentException.class) createWithoutColorSpace()143 public void createWithoutColorSpace() { 144 Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, null); 145 } 146 147 @Test(expected = IllegalArgumentException.class) createWithNonRgbColorSpace()148 public void createWithNonRgbColorSpace() { 149 Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, 150 ColorSpace.get(ColorSpace.Named.CIE_LAB)); 151 } 152 153 @Test sRGB()154 public void sRGB() { 155 Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot); 156 ColorSpace cs = b.getColorSpace(); 157 assertNotNull(cs); 158 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 159 160 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 161 cs = b.getColorSpace(); 162 assertNotNull(cs); 163 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 164 165 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 166 cs = b.getColorSpace(); 167 assertNotNull(cs); 168 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 169 } 170 171 @Test p3()172 public void p3() { 173 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 174 Bitmap b = BitmapFactory.decodeStream(in); 175 ColorSpace cs = b.getColorSpace(); 176 assertNotNull(cs); 177 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 178 179 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 180 cs = b.getColorSpace(); 181 assertNotNull(cs); 182 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 183 184 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 185 cs = b.getColorSpace(); 186 assertNotNull(cs); 187 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 188 } catch (IOException e) { 189 fail(); 190 } 191 } 192 193 @Test extendedSRGB()194 public void extendedSRGB() { 195 try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) { 196 Bitmap b = BitmapFactory.decodeStream(in); 197 ColorSpace cs = b.getColorSpace(); 198 assertNotNull(cs); 199 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 200 201 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 202 cs = b.getColorSpace(); 203 assertNotNull(cs); 204 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 205 206 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 207 cs = b.getColorSpace(); 208 assertNotNull(cs); 209 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 210 } catch (IOException e) { 211 fail(); 212 } 213 } 214 215 @Test reconfigure()216 public void reconfigure() { 217 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 218 BitmapFactory.Options opts = new BitmapFactory.Options(); 219 opts.inMutable = true; 220 221 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 222 ColorSpace cs = b.getColorSpace(); 223 assertNotNull(cs); 224 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 225 226 b.reconfigure(b.getWidth() / 2, b.getHeight() / 2, Bitmap.Config.RGBA_F16); 227 cs = b.getColorSpace(); 228 assertNotNull(cs); 229 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 230 231 b.reconfigure(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888); 232 cs = b.getColorSpace(); 233 assertNotNull(cs); 234 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 235 } catch (IOException e) { 236 fail(); 237 } 238 } 239 240 @Test reuse()241 public void reuse() { 242 BitmapFactory.Options opts = new BitmapFactory.Options(); 243 opts.inMutable = true; 244 245 Bitmap bitmap1 = null; 246 try (InputStream in = mResources.getAssets().open("green-srgb.png")) { 247 bitmap1 = BitmapFactory.decodeStream(in, null, opts); 248 ColorSpace cs = bitmap1.getColorSpace(); 249 assertNotNull(cs); 250 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 251 } catch (IOException e) { 252 fail(); 253 } 254 255 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 256 opts.inBitmap = bitmap1; 257 258 Bitmap bitmap2 = BitmapFactory.decodeStream(in, null, opts); 259 assertSame(bitmap1, bitmap2); 260 ColorSpace cs = bitmap2.getColorSpace(); 261 assertNotNull(cs); 262 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 263 } catch (IOException e) { 264 fail(); 265 } 266 } 267 268 @Test getPixel()269 public void getPixel() { 270 verifyGetPixel("green-p3.png", 0x75fb4cff, 0xff00ff00); 271 verifyGetPixel("translucent-green-p3.png", 0x3a7d267f, 0x7f00ff00); // 50% translucent 272 } 273 verifyGetPixel(@onNull String fileName, @ColorInt int rawColor, @ColorInt int srgbColor)274 private void verifyGetPixel(@NonNull String fileName, 275 @ColorInt int rawColor, @ColorInt int srgbColor) { 276 try (InputStream in = mResources.getAssets().open(fileName)) { 277 Bitmap b = BitmapFactory.decodeStream(in); 278 ColorSpace cs = b.getColorSpace(); 279 assertNotNull(cs); 280 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 281 282 verifyGetPixel(b, rawColor, srgbColor); 283 284 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 285 verifyGetPixel(b, rawColor, srgbColor); 286 287 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 288 verifyGetPixel(b, rawColor, srgbColor); 289 } catch (IOException e) { 290 fail(); 291 } 292 } 293 verifyGetPixel(@onNull Bitmap b, @ColorInt int rawColor, @ColorInt int srgbColor)294 private static void verifyGetPixel(@NonNull Bitmap b, 295 @ColorInt int rawColor, @ColorInt int srgbColor) { 296 ByteBuffer dst = ByteBuffer.allocate(b.getByteCount()); 297 b.copyPixelsToBuffer(dst); 298 dst.rewind(); 299 300 // Stored as RGBA 301 assertEquals(rawColor, dst.asIntBuffer().get()); 302 303 int srgb = b.getPixel(15, 15); 304 almostEqual(srgbColor, srgb, 3, 15 * b.getWidth() + 15); 305 } 306 307 @Test getPixels()308 public void getPixels() { 309 verifyGetPixels("green-p3.png", 0xff00ff00); 310 verifyGetPixels("translucent-green-p3.png", 0x7f00ff00); // 50% translucent 311 } 312 verifyGetPixels(@onNull String fileName, @ColorInt int expected)313 private void verifyGetPixels(@NonNull String fileName, @ColorInt int expected) { 314 try (InputStream in = mResources.getAssets().open(fileName)) { 315 Bitmap b = BitmapFactory.decodeStream(in); 316 ColorSpace cs = b.getColorSpace(); 317 assertNotNull(cs); 318 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 319 320 verifyGetPixels(b, expected); 321 322 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 323 verifyGetPixels(b, expected); 324 325 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 326 verifyGetPixels(b, expected); 327 } catch (IOException e) { 328 fail(); 329 } 330 } 331 verifyGetPixels(@onNull Bitmap b, @ColorInt int expected)332 private static void verifyGetPixels(@NonNull Bitmap b, @ColorInt int expected) { 333 int[] pixels = new int[b.getWidth() * b.getHeight()]; 334 b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight()); 335 336 for (int i = 0; i < pixels.length; i++) { 337 int pixel = pixels[i]; 338 almostEqual(expected, pixel, 3, i); 339 } 340 } 341 342 @Test setPixel()343 public void setPixel() { 344 verifySetPixel("green-p3.png", 0xffff0000, 0xea3323ff); 345 verifySetPixel("translucent-green-p3.png", 0x7fff0000, 0x7519117f); 346 } 347 verifySetPixel(@onNull String fileName, @ColorInt int newColor, @ColorInt int expectedColor)348 private void verifySetPixel(@NonNull String fileName, 349 @ColorInt int newColor, @ColorInt int expectedColor) { 350 try (InputStream in = mResources.getAssets().open(fileName)) { 351 BitmapFactory.Options opts = new BitmapFactory.Options(); 352 opts.inMutable = true; 353 354 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 355 ColorSpace cs = b.getColorSpace(); 356 assertNotNull(cs); 357 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 358 359 verifySetPixel(b, newColor, expectedColor); 360 361 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 362 verifySetPixel(b, newColor, expectedColor); 363 364 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 365 verifySetPixel(b, newColor, expectedColor); 366 } catch (IOException e) { 367 fail(); 368 } 369 } 370 verifySetPixel(@onNull Bitmap b, @ColorInt int newColor, @ColorInt int expectedColor)371 private static void verifySetPixel(@NonNull Bitmap b, 372 @ColorInt int newColor, @ColorInt int expectedColor) { 373 b.setPixel(0, 0, newColor); 374 375 ByteBuffer dst = ByteBuffer.allocate(b.getByteCount()); 376 b.copyPixelsToBuffer(dst); 377 dst.rewind(); 378 // Stored as RGBA 379 assertEquals(expectedColor, dst.asIntBuffer().get()); 380 } 381 382 @Test setPixels()383 public void setPixels() { 384 verifySetPixels("green-p3.png", 0xffff0000, 0xea3323ff); 385 verifySetPixels("translucent-green-p3.png", 0x7fff0000, 0x7519117f); 386 } 387 verifySetPixels(@onNull String fileName, @ColorInt int newColor, @ColorInt int expectedColor)388 private void verifySetPixels(@NonNull String fileName, 389 @ColorInt int newColor, @ColorInt int expectedColor) { 390 try (InputStream in = mResources.getAssets().open(fileName)) { 391 BitmapFactory.Options opts = new BitmapFactory.Options(); 392 opts.inMutable = true; 393 394 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 395 ColorSpace cs = b.getColorSpace(); 396 assertNotNull(cs); 397 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 398 399 verifySetPixels(b, newColor, expectedColor); 400 401 b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2); 402 verifySetPixels(b, newColor, expectedColor); 403 404 b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true); 405 verifySetPixels(b, newColor, expectedColor); 406 } catch (IOException e) { 407 fail(); 408 } 409 } 410 verifySetPixels(@onNull Bitmap b, @ColorInt int newColor, @ColorInt int expectedColor)411 private static void verifySetPixels(@NonNull Bitmap b, 412 @ColorInt int newColor, @ColorInt int expectedColor) { 413 int[] pixels = new int[b.getWidth() * b.getHeight()]; 414 Arrays.fill(pixels, newColor); 415 b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight()); 416 417 ByteBuffer dst = ByteBuffer.allocate(b.getByteCount()); 418 b.copyPixelsToBuffer(dst); 419 dst.rewind(); 420 421 IntBuffer buffer = dst.asIntBuffer(); 422 //noinspection ForLoopReplaceableByForEach 423 for (int i = 0; i < pixels.length; i++) { 424 // Stored as RGBA 425 assertEquals(expectedColor, buffer.get()); 426 } 427 } 428 429 @Test writeColorSpace()430 public void writeColorSpace() { 431 verifyColorSpaceMarshalling("green-srgb.png", ColorSpace.get(ColorSpace.Named.SRGB)); 432 verifyColorSpaceMarshalling("green-p3.png", ColorSpace.get(ColorSpace.Named.DISPLAY_P3)); 433 verifyColorSpaceMarshalling("prophoto-rgba16f.png", 434 ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)); 435 436 // Special case where the color space will be null in native 437 Bitmap bitmapIn = BitmapFactory.decodeResource(mResources, R.drawable.robot); 438 verifyParcelUnparcel(bitmapIn, ColorSpace.get(ColorSpace.Named.SRGB)); 439 } 440 verifyColorSpaceMarshalling( @onNull String fileName, @NonNull ColorSpace colorSpace)441 private void verifyColorSpaceMarshalling( 442 @NonNull String fileName, @NonNull ColorSpace colorSpace) { 443 try (InputStream in = mResources.getAssets().open(fileName)) { 444 Bitmap bitmapIn = BitmapFactory.decodeStream(in); 445 verifyParcelUnparcel(bitmapIn, colorSpace); 446 } catch (IOException e) { 447 fail(); 448 } 449 } 450 verifyParcelUnparcel(Bitmap bitmapIn, ColorSpace expected)451 private void verifyParcelUnparcel(Bitmap bitmapIn, ColorSpace expected) { 452 ColorSpace cs = bitmapIn.getColorSpace(); 453 assertNotNull(cs); 454 assertSame(expected, cs); 455 456 Parcel p = Parcel.obtain(); 457 bitmapIn.writeToParcel(p, 0); 458 p.setDataPosition(0); 459 460 Bitmap bitmapOut = Bitmap.CREATOR.createFromParcel(p); 461 cs = bitmapOut.getColorSpace(); 462 assertNotNull(cs); 463 assertSame(expected, cs); 464 465 p.recycle(); 466 } 467 468 @Test p3rgb565()469 public void p3rgb565() { 470 BitmapFactory.Options opts = new BitmapFactory.Options(); 471 opts.inPreferredConfig = Bitmap.Config.RGB_565; 472 473 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 474 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 475 ColorSpace cs = b.getColorSpace(); 476 assertNotNull(cs); 477 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 478 } catch (IOException e) { 479 fail(); 480 } 481 } 482 483 @Test p3hardware()484 public void p3hardware() { 485 BitmapFactory.Options opts = new BitmapFactory.Options(); 486 opts.inPreferredConfig = Bitmap.Config.HARDWARE; 487 488 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 489 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 490 ColorSpace cs = b.getColorSpace(); 491 assertNotNull(cs); 492 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 493 } catch (IOException e) { 494 fail(); 495 } 496 } 497 498 @Test guessSRGB()499 public void guessSRGB() { 500 BitmapFactory.Options opts = new BitmapFactory.Options(); 501 opts.inJustDecodeBounds = true; 502 503 try (InputStream in = mResources.getAssets().open("green-srgb.png")) { 504 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 505 ColorSpace cs = opts.outColorSpace; 506 assertNull(b); 507 assertNotNull(cs); 508 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 509 } catch (IOException e) { 510 fail(); 511 } 512 } 513 514 @Test guessProPhotoRGB()515 public void guessProPhotoRGB() { 516 BitmapFactory.Options opts = new BitmapFactory.Options(); 517 opts.inJustDecodeBounds = true; 518 519 try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) { 520 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 521 ColorSpace cs = opts.outColorSpace; 522 assertNull(b); 523 assertNotNull(cs); 524 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 525 } catch (IOException e) { 526 fail(); 527 } 528 } 529 530 @Test guessP3()531 public void guessP3() { 532 BitmapFactory.Options opts = new BitmapFactory.Options(); 533 opts.inJustDecodeBounds = true; 534 535 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 536 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 537 ColorSpace cs = opts.outColorSpace; 538 assertNull(b); 539 assertNotNull(cs); 540 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 541 } catch (IOException e) { 542 fail(); 543 } 544 } 545 546 @Test guessAdobeRGB()547 public void guessAdobeRGB() { 548 BitmapFactory.Options opts = new BitmapFactory.Options(); 549 opts.inJustDecodeBounds = true; 550 551 try (InputStream in = mResources.getAssets().open("red-adobergb.png")) { 552 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 553 ColorSpace cs = opts.outColorSpace; 554 assertNull(b); 555 assertNotNull(cs); 556 assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs); 557 } catch (IOException e) { 558 fail(); 559 } 560 } 561 562 @Test guessUnknown()563 public void guessUnknown() { 564 BitmapFactory.Options opts = new BitmapFactory.Options(); 565 opts.inJustDecodeBounds = true; 566 567 try (InputStream in = mResources.getAssets().open("purple-displayprofile.png")) { 568 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 569 ColorSpace cs = opts.outColorSpace; 570 assertNull(b); 571 assertNotNull(cs); 572 assertEquals("Unknown", cs.getName()); 573 } catch (IOException e) { 574 fail(); 575 } 576 } 577 578 @Test guessCMYK()579 public void guessCMYK() { 580 BitmapFactory.Options opts = new BitmapFactory.Options(); 581 opts.inJustDecodeBounds = true; 582 583 try (InputStream in = mResources.getAssets().open("purple-cmyk.png")) { 584 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 585 ColorSpace cs = opts.outColorSpace; 586 assertNull(b); 587 assertNotNull(cs); 588 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 589 } catch (IOException e) { 590 fail(); 591 } 592 } 593 594 @Test inColorSpaceP3ToSRGB()595 public void inColorSpaceP3ToSRGB() { 596 BitmapFactory.Options opts = new BitmapFactory.Options(); 597 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB); 598 599 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 600 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 601 ColorSpace cs = b.getColorSpace(); 602 assertNotNull(cs); 603 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 604 assertEquals(opts.inPreferredColorSpace, opts.outColorSpace); 605 606 verifyGetPixel(b, 0x3ff00ff, 0xff00ff00); 607 } catch (IOException e) { 608 fail(); 609 } 610 } 611 612 @Test inColorSpaceSRGBToP3()613 public void inColorSpaceSRGBToP3() { 614 BitmapFactory.Options opts = new BitmapFactory.Options(); 615 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3); 616 617 try (InputStream in = mResources.getAssets().open("green-srgb.png")) { 618 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 619 ColorSpace cs = b.getColorSpace(); 620 assertNotNull(cs); 621 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 622 assertEquals(opts.inPreferredColorSpace, opts.outColorSpace); 623 624 verifyGetPixel(b, 0x75fb4cff, 0xff00ff00); 625 } catch (IOException e) { 626 fail(); 627 } 628 } 629 630 @Test inColorSpaceRGBA16F()631 public void inColorSpaceRGBA16F() { 632 BitmapFactory.Options opts = new BitmapFactory.Options(); 633 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB); 634 635 try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) { 636 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 637 ColorSpace cs = b.getColorSpace(); 638 assertNotNull(cs); 639 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 640 assertNotEquals(opts.inPreferredColorSpace, opts.outColorSpace); 641 } catch (IOException e) { 642 fail(); 643 } 644 } 645 646 @Test inColorSpace565()647 public void inColorSpace565() { 648 BitmapFactory.Options opts = new BitmapFactory.Options(); 649 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB); 650 opts.inPreferredConfig = Bitmap.Config.RGB_565; 651 652 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 653 Bitmap b = BitmapFactory.decodeStream(in, null, opts); 654 ColorSpace cs = b.getColorSpace(); 655 assertNotNull(cs); 656 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 657 assertNotEquals(opts.inPreferredColorSpace, opts.outColorSpace); 658 } catch (IOException e) { 659 fail(); 660 } 661 } 662 663 @Test(expected = IllegalArgumentException.class) inColorSpaceNotRGB()664 public void inColorSpaceNotRGB() { 665 BitmapFactory.Options opts = new BitmapFactory.Options(); 666 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.CIE_LAB); 667 668 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 669 BitmapFactory.decodeStream(in, null, opts); 670 } catch (IOException e) { 671 fail(); 672 } 673 } 674 675 @Test(expected = IllegalArgumentException.class) inColorSpaceNoTransferParameters()676 public void inColorSpaceNoTransferParameters() { 677 BitmapFactory.Options opts = new BitmapFactory.Options(); 678 opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB); 679 680 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 681 BitmapFactory.decodeStream(in, null, opts); 682 } catch (IOException e) { 683 fail(); 684 } 685 } 686 687 @Test copy()688 public void copy() { 689 Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot); 690 Bitmap c = b.copy(Bitmap.Config.ARGB_8888, false); 691 ColorSpace cs = c.getColorSpace(); 692 assertNotNull(cs); 693 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 694 695 c = b.copy(Bitmap.Config.ARGB_8888, true); 696 cs = c.getColorSpace(); 697 assertNotNull(cs); 698 assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs); 699 700 try (InputStream in = mResources.getAssets().open("green-p3.png")) { 701 b = BitmapFactory.decodeStream(in); 702 c = b.copy(Bitmap.Config.ARGB_8888, false); 703 cs = c.getColorSpace(); 704 assertNotNull(cs); 705 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 706 707 c = b.copy(Bitmap.Config.ARGB_8888, true); 708 cs = c.getColorSpace(); 709 assertNotNull(cs); 710 assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs); 711 } catch (IOException e) { 712 fail(); 713 } 714 715 try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) { 716 b = BitmapFactory.decodeStream(in); 717 c = b.copy(Bitmap.Config.RGBA_F16, false); 718 cs = c.getColorSpace(); 719 assertNotNull(cs); 720 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 721 722 c = b.copy(Bitmap.Config.RGBA_F16, true); 723 cs = c.getColorSpace(); 724 assertNotNull(cs); 725 assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs); 726 } catch (IOException e) { 727 fail(); 728 } 729 } 730 731 @SuppressWarnings("SameParameterValue") almostEqual(@olorInt int expected, @ColorInt int pixel, int threshold, int index)732 private static void almostEqual(@ColorInt int expected, 733 @ColorInt int pixel, int threshold, int index) { 734 int diffA = Math.abs(expected >>> 24 - pixel >>> 24); 735 int diffR = Math.abs((expected >> 16) & 0xff - (pixel >> 16) & 0xff); 736 int diffG = Math.abs((expected >> 8) & 0xff - (pixel >> 8) & 0xff); 737 int diffB = Math.abs((expected ) & 0xff - (pixel ) & 0xff); 738 739 boolean pass = diffA + diffR + diffG + diffB < threshold; 740 if (!pass) { 741 Log.d(LOG_TAG, "Expected 0x" + Integer.toHexString(expected) + 742 " but was 0x" + Integer.toHexString(pixel) + " with index " + index); 743 } 744 745 assertTrue(pass); 746 } 747 748 @Test 749 public void testEncodeP3() { 750 Bitmap b = null; 751 ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(), 752 "prophoto-rgba16f.png"); 753 try { 754 b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> { 755 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); 756 }); 757 assertNotNull(b); 758 assertEquals(Bitmap.Config.RGBA_F16, b.getConfig()); 759 } catch (IOException e) { 760 fail("Failed with " + e); 761 } 762 763 for (Bitmap.CompressFormat format : new Bitmap.CompressFormat[] { 764 Bitmap.CompressFormat.JPEG, 765 Bitmap.CompressFormat.WEBP, 766 Bitmap.CompressFormat.PNG, 767 }) { 768 ByteArrayOutputStream out = new ByteArrayOutputStream(); 769 assertTrue("Failed to encode F16 to " + format, b.compress(format, 100, out)); 770 771 byte[] array = out.toByteArray(); 772 src = ImageDecoder.createSource(ByteBuffer.wrap(array)); 773 774 try { 775 Bitmap b2 = ImageDecoder.decodeBitmap(src); 776 assertEquals("Wrong color space for " + format, 777 ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace()); 778 } catch (IOException e) { 779 fail("Failed with " + e); 780 } 781 } 782 } 783 784 @Test testEncodeP3hardware()785 public void testEncodeP3hardware() { 786 Bitmap b = null; 787 ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(), 788 "green-p3.png"); 789 try { 790 b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> { 791 decoder.setAllocator(ImageDecoder.ALLOCATOR_HARDWARE); 792 }); 793 assertNotNull(b); 794 assertEquals(Bitmap.Config.HARDWARE, b.getConfig()); 795 assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace()); 796 } catch (IOException e) { 797 fail("Failed with " + e); 798 } 799 800 for (Bitmap.CompressFormat format : new Bitmap.CompressFormat[] { 801 Bitmap.CompressFormat.JPEG, 802 Bitmap.CompressFormat.WEBP, 803 Bitmap.CompressFormat.PNG, 804 }) { 805 ByteArrayOutputStream out = new ByteArrayOutputStream(); 806 assertTrue("Failed to encode 8888 to " + format, b.compress(format, 100, out)); 807 808 byte[] array = out.toByteArray(); 809 src = ImageDecoder.createSource(ByteBuffer.wrap(array)); 810 811 try { 812 Bitmap b2 = ImageDecoder.decodeBitmap(src); 813 assertEquals("Wrong color space for " + format, 814 ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace()); 815 } catch (IOException e) { 816 fail("Failed with " + e); 817 } 818 } 819 } 820 821 @Test 822 @RequiresDevice // SwiftShader does not yet have support for F16 in HARDWARE b/75778024 test16bitHardware()823 public void test16bitHardware() { 824 // Decoding to HARDWARE may use LINEAR_EXTENDED_SRGB or SRGB, depending 825 // on whether F16 is supported in HARDWARE. 826 try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) { 827 BitmapFactory.Options options = new BitmapFactory.Options(); 828 options.inPreferredConfig = Bitmap.Config.HARDWARE; 829 Bitmap b = BitmapFactory.decodeStream(in, null, options); 830 assertEquals(Bitmap.Config.HARDWARE, b.getConfig()); 831 832 final ColorSpace cs = b.getColorSpace(); 833 if (cs != ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB) 834 && cs != ColorSpace.get(ColorSpace.Named.SRGB)) { 835 fail("Unexpected color space " + cs); 836 } 837 } catch (Exception e) { 838 fail("Failed with " + e); 839 } 840 } 841 } 842