Home | History | Annotate | Download | only in cts
      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.assertNotNull;
     21 import static org.junit.Assert.assertNull;
     22 import static org.junit.Assert.assertSame;
     23 import static org.junit.Assert.assertTrue;
     24 import static org.junit.Assert.fail;
     25 
     26 import android.content.res.Resources;
     27 import android.graphics.Bitmap;
     28 import android.graphics.BitmapFactory;
     29 import android.graphics.Canvas;
     30 import android.graphics.Color;
     31 import android.graphics.ColorSpace;
     32 import android.graphics.ImageDecoder;
     33 import android.graphics.Matrix;
     34 import android.os.Parcel;
     35 import android.util.Log;
     36 
     37 import androidx.annotation.ColorInt;
     38 import androidx.annotation.NonNull;
     39 import androidx.test.InstrumentationRegistry;
     40 import androidx.test.filters.RequiresDevice;
     41 import androidx.test.filters.SmallTest;
     42 import androidx.test.runner.AndroidJUnit4;
     43 
     44 import com.android.compatibility.common.util.ColorUtils;
     45 
     46 import org.junit.Before;
     47 import org.junit.Test;
     48 import org.junit.runner.RunWith;
     49 
     50 import java.io.ByteArrayOutputStream;
     51 import java.io.IOException;
     52 import java.io.InputStream;
     53 import java.nio.ByteBuffer;
     54 import java.nio.IntBuffer;
     55 import java.util.Arrays;
     56 
     57 @SmallTest
     58 @RunWith(AndroidJUnit4.class)
     59 public class BitmapColorSpaceTest {
     60     private static final String LOG_TAG = "BitmapColorSpaceTest";
     61 
     62     private Resources mResources;
     63 
     64     @Before
     65     public void setup() {
     66         mResources = InstrumentationRegistry.getTargetContext().getResources();
     67     }
     68 
     69     @SuppressWarnings("deprecation")
     70     @Test
     71     public void createWithColorSpace() {
     72         // We don't test HARDWARE configs because they are not compatible with mutable bitmaps
     73 
     74         Bitmap.Config[] configs = new Bitmap.Config[] {
     75                 Bitmap.Config.ARGB_8888,
     76                 Bitmap.Config.RGB_565,
     77                 Bitmap.Config.ARGB_4444,
     78                 Bitmap.Config.RGBA_F16,
     79         };
     80         // in most cases, createBitmap respects the ColorSpace
     81         for (Bitmap.Config config : configs) {
     82             for (ColorSpace.Named e : new ColorSpace.Named[] {
     83                     ColorSpace.Named.PRO_PHOTO_RGB,
     84                     ColorSpace.Named.ADOBE_RGB,
     85                     ColorSpace.Named.DISPLAY_P3,
     86                     ColorSpace.Named.DCI_P3,
     87                     ColorSpace.Named.BT709,
     88                     ColorSpace.Named.BT2020,
     89             }) {
     90                 ColorSpace requested = ColorSpace.get(e);
     91                 Bitmap b = Bitmap.createBitmap(32, 32, config, false, requested);
     92                 ColorSpace cs = b.getColorSpace();
     93                 assertNotNull(cs);
     94                 assertSame(requested, cs);
     95             }
     96 
     97             // SRGB and LINEAR_SRGB are special.
     98             ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
     99             ColorSpace extendedSrgb = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
    100             for (ColorSpace requested : new ColorSpace[] {
    101                     sRGB,
    102                     extendedSrgb,
    103             }) {
    104                 Bitmap b = Bitmap.createBitmap(32, 32, config, false, requested);
    105                 ColorSpace cs = b.getColorSpace();
    106                 assertNotNull(cs);
    107                 if (config == Bitmap.Config.RGBA_F16) {
    108                     assertSame(extendedSrgb, cs);
    109                 } else {
    110                     assertSame(sRGB, cs);
    111                 }
    112             }
    113 
    114             ColorSpace linearRgb = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
    115             ColorSpace linearExtendedSrgb = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
    116             for (ColorSpace requested : new ColorSpace[] {
    117                     linearRgb,
    118                     linearExtendedSrgb,
    119             }) {
    120                 Bitmap b = Bitmap.createBitmap(32, 32, config, false, requested);
    121                 ColorSpace cs = b.getColorSpace();
    122                 assertNotNull(cs);
    123                 if (config == Bitmap.Config.RGBA_F16) {
    124                     assertSame(linearExtendedSrgb, cs);
    125                 } else {
    126                     assertSame(linearRgb, cs);
    127                 }
    128             }
    129         }
    130     }
    131 
    132     @Test
    133     public void createAlpha8ColorSpace() {
    134         Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8);
    135         assertNull(bitmap.getColorSpace());
    136 
    137         for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
    138             bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true, cs);
    139             assertNull(bitmap.getColorSpace());
    140         }
    141     }
    142 
    143     @Test
    144     public void createDefaultColorSpace() {
    145         ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
    146         Bitmap.Config[] configs = new Bitmap.Config[] {
    147                 Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888
    148         };
    149         for (Bitmap.Config config : configs) {
    150             Bitmap bitmap = Bitmap.createBitmap(32, 32, config, true);
    151             assertSame(sRGB, bitmap.getColorSpace());
    152         }
    153     }
    154 
    155     @Test(expected = IllegalArgumentException.class)
    156     public void createWithoutColorSpace() {
    157         Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, null);
    158     }
    159 
    160     @Test(expected = IllegalArgumentException.class)
    161     public void createWithNonRgbColorSpace() {
    162         Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
    163                 ColorSpace.get(ColorSpace.Named.CIE_LAB));
    164     }
    165 
    166     @Test(expected = IllegalArgumentException.class)
    167     public void createWithNoTransferParameters() {
    168         Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
    169                 new ColorSpace.Rgb("NoTransferParams",
    170                     new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
    171                     ColorSpace.ILLUMINANT_D50,
    172                     x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f),
    173                     0, 1));
    174     }
    175 
    176     @Test
    177     public void createFromSourceWithColorSpace() {
    178         for (ColorSpace rgb : BitmapTest.getRgbColorSpaces()) {
    179             Bitmap.Config[] configs = new Bitmap.Config[] {
    180                     Bitmap.Config.ARGB_8888,
    181                     Bitmap.Config.RGB_565,
    182                     Bitmap.Config.ALPHA_8,
    183                     Bitmap.Config.ARGB_4444,
    184                     Bitmap.Config.RGBA_F16,
    185             };
    186             for (Bitmap.Config config : configs) {
    187                 Bitmap orig = Bitmap.createBitmap(32, 32, config, false, rgb);
    188                 Bitmap cropped = Bitmap.createBitmap(orig, 0, 0, orig.getWidth() / 2,
    189                         orig.getHeight() / 2, null, false);
    190                 assertSame(orig.getColorSpace(), cropped.getColorSpace());
    191                 if (config == Bitmap.Config.ALPHA_8) {
    192                     assertNull(cropped.getColorSpace());
    193                 }
    194 
    195                 Matrix m = new Matrix();
    196                 m.setRotate(45, orig.getWidth() / 2, orig.getHeight() / 2);
    197                 Bitmap rotated = Bitmap.createBitmap(orig, 0, 0, orig.getWidth(),
    198                         orig.getHeight(), m, false);
    199                 switch (config) {
    200                     case ALPHA_8:
    201                         assertSame(Bitmap.Config.ARGB_8888, rotated.getConfig());
    202                         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), rotated.getColorSpace());
    203                         break;
    204                     case RGB_565:
    205                         assertSame(Bitmap.Config.ARGB_8888, rotated.getConfig());
    206                         // Fallthrough.
    207                     default:
    208                         assertSame("Mismatch with Config " + config,
    209                                 orig.getColorSpace(), rotated.getColorSpace());
    210                         break;
    211                 }
    212             }
    213         }
    214     }
    215 
    216     @Test
    217     public void sRGB() {
    218         Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
    219         ColorSpace cs = b.getColorSpace();
    220         assertNotNull(cs);
    221         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    222 
    223         b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    224         cs = b.getColorSpace();
    225         assertNotNull(cs);
    226         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    227 
    228         b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    229         cs = b.getColorSpace();
    230         assertNotNull(cs);
    231         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    232     }
    233 
    234     @Test
    235     public void p3() {
    236         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    237             Bitmap b = BitmapFactory.decodeStream(in);
    238             ColorSpace cs = b.getColorSpace();
    239             assertNotNull(cs);
    240             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    241 
    242             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    243             cs = b.getColorSpace();
    244             assertNotNull(cs);
    245             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    246 
    247             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    248             cs = b.getColorSpace();
    249             assertNotNull(cs);
    250             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    251         } catch (IOException e) {
    252             fail();
    253         }
    254     }
    255 
    256     @Test
    257     public void extendedSRGB() {
    258         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
    259             Bitmap b = BitmapFactory.decodeStream(in);
    260             ColorSpace cs = b.getColorSpace();
    261             assertNotNull(cs);
    262             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
    263 
    264             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    265             cs = b.getColorSpace();
    266             assertNotNull(cs);
    267             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
    268 
    269             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    270             cs = b.getColorSpace();
    271             assertNotNull(cs);
    272             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
    273         } catch (IOException e) {
    274             fail();
    275         }
    276     }
    277 
    278     @Test
    279     public void linearSRGB() {
    280         String assetInLinearSRGB = "grayscale-linearSrgb.png";
    281         try (InputStream in = mResources.getAssets().open(assetInLinearSRGB)) {
    282             Bitmap b = BitmapFactory.decodeStream(in);
    283             ColorSpace cs = b.getColorSpace();
    284             assertNotNull(cs);
    285             assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), cs);
    286         } catch (IOException e) {
    287             fail();
    288         }
    289 
    290         try (InputStream in = mResources.getAssets().open(assetInLinearSRGB)) {
    291             BitmapFactory.Options options = new BitmapFactory.Options();
    292             options.inPreferredConfig = Bitmap.Config.RGBA_F16;
    293             Bitmap b = BitmapFactory.decodeStream(in, null, options);
    294             ColorSpace cs = b.getColorSpace();
    295             assertNotNull(cs);
    296             assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
    297         } catch (IOException e) {
    298             fail();
    299         }
    300     }
    301 
    302     private static class Asset {
    303         public final String name;
    304         public final ColorSpace colorSpace;
    305         Asset(String name, ColorSpace.Named e) {
    306             this.name = name;
    307             this.colorSpace = ColorSpace.get(e);
    308         }
    309     };
    310 
    311     @Test
    312     public void reconfigure() {
    313         Asset[] assets = new Asset[] {
    314                 new Asset("green-p3.png", ColorSpace.Named.DISPLAY_P3),
    315                 new Asset("red-adobergb.png", ColorSpace.Named.ADOBE_RGB),
    316         };
    317         for (Asset asset : assets) {
    318             for (Bitmap.Config config : new Bitmap.Config[] {
    319                     Bitmap.Config.ARGB_8888,
    320                     Bitmap.Config.RGB_565,
    321             }) {
    322                 try (InputStream in = mResources.getAssets().open(asset.name)) {
    323                     BitmapFactory.Options opts = new BitmapFactory.Options();
    324                     opts.inMutable = true;
    325                     opts.inPreferredConfig = config;
    326 
    327                     Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    328                     ColorSpace cs = b.getColorSpace();
    329                     assertNotNull(cs);
    330                     assertSame(asset.colorSpace, cs);
    331 
    332                     b.reconfigure(b.getWidth() / 4, b.getHeight() / 4, Bitmap.Config.RGBA_F16);
    333                     cs = b.getColorSpace();
    334                     assertNotNull(cs);
    335                     assertSame(asset.colorSpace, cs);
    336 
    337                     b.reconfigure(b.getWidth(), b.getHeight(), config);
    338                     cs = b.getColorSpace();
    339                     assertNotNull(cs);
    340                     assertSame(asset.colorSpace, cs);
    341                 } catch (IOException e) {
    342                     fail();
    343                 }
    344             }
    345         }
    346     }
    347 
    348     @Test
    349     public void reuse() {
    350         BitmapFactory.Options opts = new BitmapFactory.Options();
    351         opts.inMutable = true;
    352 
    353         Bitmap bitmap1 = null;
    354         try (InputStream in = mResources.getAssets().open("green-srgb.png")) {
    355             bitmap1 = BitmapFactory.decodeStream(in, null, opts);
    356             ColorSpace cs = bitmap1.getColorSpace();
    357             assertNotNull(cs);
    358             assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    359         } catch (IOException e) {
    360             fail();
    361         }
    362 
    363         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    364             opts.inBitmap = bitmap1;
    365 
    366             Bitmap bitmap2 = BitmapFactory.decodeStream(in, null, opts);
    367             assertSame(bitmap1, bitmap2);
    368             ColorSpace cs = bitmap2.getColorSpace();
    369             assertNotNull(cs);
    370             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    371         } catch (IOException e) {
    372             fail();
    373         }
    374     }
    375 
    376     @Test
    377     public void getPixel() {
    378         verifyGetPixel("green-p3.png", 0x75fb4cff);
    379         verifyGetPixel("translucent-green-p3.png", 0x3a7d267f); // 50% translucent
    380     }
    381 
    382     private void verifyGetPixel(@NonNull String fileName, @ColorInt int rawColor) {
    383         try (InputStream in = mResources.getAssets().open(fileName)) {
    384             Bitmap b = BitmapFactory.decodeStream(in);
    385             ColorSpace cs = b.getColorSpace();
    386             assertNotNull(cs);
    387             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    388 
    389             verifyGetPixel(b, rawColor);
    390 
    391             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    392             verifyGetPixel(b, rawColor);
    393 
    394             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    395             verifyGetPixel(b, rawColor);
    396         } catch (IOException e) {
    397             fail();
    398         }
    399     }
    400 
    401     private static void verifyGetPixel(@NonNull Bitmap b, @ColorInt int rawColor) {
    402         ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
    403         b.copyPixelsToBuffer(dst);
    404         dst.rewind();
    405 
    406         // Stored as RGBA
    407         assertEquals(rawColor, dst.asIntBuffer().get());
    408 
    409         int srgbColor = convertPremulColorToColorInt(rawColor, b.getColorSpace());
    410         int srgb = b.getPixel(15, 15);
    411         almostEqual(srgbColor, srgb, 3, 15 * b.getWidth() + 15);
    412     }
    413 
    414     private static int convertPremulColorToColorInt(int premulColor, ColorSpace premulCS) {
    415         float alpha = (premulColor & 0xff) / 255.0f;
    416         return Color.toArgb(Color.convert((premulColor >>> 24) / 255.0f / alpha,
    417                 ((premulColor >> 16) & 0xff) / 255.0f / alpha,
    418                 ((premulColor >> 8) & 0xff) / 255.0f / alpha,
    419                 alpha, premulCS, ColorSpace.get(ColorSpace.Named.SRGB)));
    420     }
    421 
    422     @Test
    423     public void getPixels() {
    424         verifyGetPixels("green-p3.png");
    425         verifyGetPixels("translucent-green-p3.png"); // 50% translucent
    426     }
    427 
    428     private void verifyGetPixels(@NonNull String fileName) {
    429         try (InputStream in = mResources.getAssets().open(fileName)) {
    430             Bitmap b = BitmapFactory.decodeStream(in);
    431             ColorSpace cs = b.getColorSpace();
    432             assertNotNull(cs);
    433             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    434 
    435             ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
    436             b.copyPixelsToBuffer(dst);
    437             dst.rewind();
    438 
    439             // Stored as RGBA
    440             int expected = convertPremulColorToColorInt(dst.asIntBuffer().get(), b.getColorSpace());
    441 
    442             verifyGetPixels(b, expected);
    443 
    444             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    445             verifyGetPixels(b, expected);
    446 
    447             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    448             verifyGetPixels(b, expected);
    449         } catch (IOException e) {
    450             fail();
    451         }
    452     }
    453 
    454     private static void verifyGetPixels(@NonNull Bitmap b, @ColorInt int expected) {
    455         int[] pixels = new int[b.getWidth() * b.getHeight()];
    456         b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
    457 
    458         for (int i = 0; i < pixels.length; i++) {
    459             int pixel = pixels[i];
    460             almostEqual(expected, pixel, 3, i);
    461         }
    462     }
    463 
    464     @Test
    465     public void setPixel() {
    466         verifySetPixel("green-p3.png", 0xffff0000, 0xea3323ff);
    467         verifySetPixel("translucent-green-p3.png", 0x7fff0000, 0x7519127f);
    468     }
    469 
    470     private void verifySetPixel(@NonNull String fileName,
    471             @ColorInt int newColor, @ColorInt int expectedColor) {
    472         try (InputStream in = mResources.getAssets().open(fileName)) {
    473             BitmapFactory.Options opts = new BitmapFactory.Options();
    474             opts.inMutable = true;
    475 
    476             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    477             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    478             assertTrue(b.isMutable());
    479             verifySetPixel(b, newColor, expectedColor);
    480 
    481             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    482             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    483             assertTrue(b.isMutable());
    484             verifySetPixel(b, newColor, expectedColor);
    485 
    486             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    487             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    488             assertTrue(b.isMutable());
    489             verifySetPixel(b, newColor, expectedColor);
    490         } catch (IOException e) {
    491             fail();
    492         }
    493     }
    494 
    495     private static void verifySetPixel(@NonNull Bitmap b,
    496             @ColorInt int newColor, @ColorInt int expectedColor) {
    497         assertTrue(b.isMutable());
    498         b.setPixel(0, 0, newColor);
    499 
    500         ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
    501         b.copyPixelsToBuffer(dst);
    502         dst.rewind();
    503         // Stored as RGBA
    504         ColorUtils.verifyColor(expectedColor, dst.asIntBuffer().get(), 1);
    505     }
    506 
    507     @Test
    508     public void setPixels() {
    509         verifySetPixels("green-p3.png", 0xffff0000, 0xea3323ff);
    510         verifySetPixels("translucent-green-p3.png", 0x7fff0000, 0x7519127f);
    511     }
    512 
    513     private void verifySetPixels(@NonNull String fileName,
    514             @ColorInt int newColor, @ColorInt int expectedColor) {
    515         try (InputStream in = mResources.getAssets().open(fileName)) {
    516             BitmapFactory.Options opts = new BitmapFactory.Options();
    517             opts.inMutable = true;
    518 
    519             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    520             assertNotNull(b.getColorSpace());
    521             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    522 
    523             verifySetPixels(b, newColor, expectedColor);
    524 
    525             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
    526             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    527             assertTrue(b.isMutable());
    528             verifySetPixels(b, newColor, expectedColor);
    529 
    530             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
    531             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
    532             assertTrue(b.isMutable());
    533             verifySetPixels(b, newColor, expectedColor);
    534         } catch (IOException e) {
    535             fail();
    536         }
    537     }
    538 
    539     private static void verifySetPixels(@NonNull Bitmap b,
    540             @ColorInt int newColor, @ColorInt int expectedColor) {
    541         assertTrue(b.isMutable());
    542         int[] pixels = new int[b.getWidth() * b.getHeight()];
    543         Arrays.fill(pixels, newColor);
    544         b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
    545 
    546         ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
    547         b.copyPixelsToBuffer(dst);
    548         dst.rewind();
    549 
    550         IntBuffer buffer = dst.asIntBuffer();
    551         //noinspection ForLoopReplaceableByForEach
    552         for (int i = 0; i < pixels.length; i++) {
    553             // Stored as RGBA
    554             ColorUtils.verifyColor(expectedColor, buffer.get(), 1);
    555         }
    556     }
    557 
    558     @Test
    559     public void writeColorSpace() {
    560         verifyColorSpaceMarshalling("green-srgb.png", ColorSpace.get(ColorSpace.Named.SRGB));
    561         verifyColorSpaceMarshalling("green-p3.png", ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
    562         verifyColorSpaceMarshalling("blue-16bit-srgb.png",
    563                 ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
    564 
    565         Bitmap bitmapIn = BitmapFactory.decodeResource(mResources, R.drawable.robot);
    566         verifyParcelUnparcel(bitmapIn, ColorSpace.get(ColorSpace.Named.SRGB));
    567     }
    568 
    569     private void verifyColorSpaceMarshalling(
    570             @NonNull String fileName, @NonNull ColorSpace colorSpace) {
    571         try (InputStream in = mResources.getAssets().open(fileName)) {
    572             Bitmap bitmapIn = BitmapFactory.decodeStream(in);
    573             verifyParcelUnparcel(bitmapIn, colorSpace);
    574         } catch (IOException e) {
    575             fail();
    576         }
    577     }
    578 
    579     private void verifyParcelUnparcel(Bitmap bitmapIn, ColorSpace expected) {
    580         ColorSpace cs = bitmapIn.getColorSpace();
    581         assertNotNull(cs);
    582         assertSame(expected, cs);
    583 
    584         Parcel p = Parcel.obtain();
    585         bitmapIn.writeToParcel(p, 0);
    586         p.setDataPosition(0);
    587 
    588         Bitmap bitmapOut = Bitmap.CREATOR.createFromParcel(p);
    589         cs = bitmapOut.getColorSpace();
    590         assertNotNull(cs);
    591         assertSame(expected, cs);
    592 
    593         p.recycle();
    594     }
    595 
    596     @Test
    597     public void p3rgb565() {
    598         BitmapFactory.Options opts = new BitmapFactory.Options();
    599         opts.inPreferredConfig = Bitmap.Config.RGB_565;
    600 
    601         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    602             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    603             ColorSpace cs = b.getColorSpace();
    604             assertNotNull(cs);
    605             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    606         } catch (IOException e) {
    607             fail();
    608         }
    609     }
    610 
    611     @Test
    612     public void p3hardware() {
    613         BitmapFactory.Options opts = new BitmapFactory.Options();
    614         opts.inPreferredConfig = Bitmap.Config.HARDWARE;
    615 
    616         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    617             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    618             ColorSpace cs = b.getColorSpace();
    619             assertNotNull(cs);
    620             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    621         } catch (IOException e) {
    622             fail();
    623         }
    624     }
    625 
    626     @Test
    627     public void guessSRGB() {
    628         BitmapFactory.Options opts = new BitmapFactory.Options();
    629         opts.inJustDecodeBounds = true;
    630 
    631         try (InputStream in = mResources.getAssets().open("green-srgb.png")) {
    632             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    633             ColorSpace cs = opts.outColorSpace;
    634             assertNull(b);
    635             assertNotNull(cs);
    636             assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    637         } catch (IOException e) {
    638             fail();
    639         }
    640     }
    641 
    642     @Test
    643     public void guess16bitUntagged() {
    644         BitmapFactory.Options opts = new BitmapFactory.Options();
    645         opts.inJustDecodeBounds = true;
    646 
    647         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
    648             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    649             ColorSpace cs = opts.outColorSpace;
    650             assertNull(b);
    651             assertNotNull(cs);
    652             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
    653         } catch (IOException e) {
    654             fail();
    655         }
    656     }
    657 
    658     @Test
    659     public void guessProPhotoRGB() {
    660         BitmapFactory.Options opts = new BitmapFactory.Options();
    661         opts.inJustDecodeBounds = true;
    662 
    663         try (InputStream in = mResources.getAssets().open("blue-16bit-prophoto.png")) {
    664             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    665             ColorSpace cs = opts.outColorSpace;
    666             assertNull(b);
    667             assertNotNull(cs);
    668             assertSame(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), cs);
    669         } catch (IOException e) {
    670             fail();
    671         }
    672     }
    673 
    674     @Test
    675     public void guessP3() {
    676         BitmapFactory.Options opts = new BitmapFactory.Options();
    677         opts.inJustDecodeBounds = true;
    678 
    679         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    680             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    681             ColorSpace cs = opts.outColorSpace;
    682             assertNull(b);
    683             assertNotNull(cs);
    684             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    685         } catch (IOException e) {
    686             fail();
    687         }
    688     }
    689 
    690     @Test
    691     public void guessAdobeRGB() {
    692         BitmapFactory.Options opts = new BitmapFactory.Options();
    693         opts.inJustDecodeBounds = true;
    694 
    695         try (InputStream in = mResources.getAssets().open("red-adobergb.png")) {
    696             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    697             ColorSpace cs = opts.outColorSpace;
    698             assertNull(b);
    699             assertNotNull(cs);
    700             assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs);
    701         } catch (IOException e) {
    702             fail();
    703         }
    704     }
    705 
    706     @Test
    707     public void guessUnknown() {
    708         BitmapFactory.Options opts = new BitmapFactory.Options();
    709         opts.inJustDecodeBounds = true;
    710 
    711         try (InputStream in = mResources.getAssets().open("purple-displayprofile.png")) {
    712             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    713             ColorSpace cs = opts.outColorSpace;
    714             assertNull(b);
    715             assertNotNull(cs);
    716             assertEquals("Unknown", cs.getName());
    717         } catch (IOException e) {
    718             fail();
    719         }
    720     }
    721 
    722     @Test
    723     public void guessCMYK() {
    724         BitmapFactory.Options opts = new BitmapFactory.Options();
    725         opts.inJustDecodeBounds = true;
    726 
    727         try (InputStream in = mResources.getAssets().open("purple-cmyk.png")) {
    728             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    729             ColorSpace cs = opts.outColorSpace;
    730             assertNull(b);
    731             assertNotNull(cs);
    732             assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    733         } catch (IOException e) {
    734             fail();
    735         }
    736     }
    737 
    738     @Test
    739     public void inColorSpaceP3ToSRGB() {
    740         BitmapFactory.Options opts = new BitmapFactory.Options();
    741         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
    742 
    743         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    744             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    745             ColorSpace cs = b.getColorSpace();
    746             assertNotNull(cs);
    747             assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    748             assertEquals(opts.inPreferredColorSpace, opts.outColorSpace);
    749 
    750             verifyGetPixel(b, 0x2ff00ff);
    751         } catch (IOException e) {
    752             fail();
    753         }
    754     }
    755 
    756     @Test
    757     public void inColorSpaceSRGBToP3() {
    758         BitmapFactory.Options opts = new BitmapFactory.Options();
    759         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
    760 
    761         try (InputStream in = mResources.getAssets().open("green-srgb.png")) {
    762             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    763             ColorSpace cs = b.getColorSpace();
    764             assertNotNull(cs);
    765             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    766             assertEquals(opts.inPreferredColorSpace, opts.outColorSpace);
    767 
    768             verifyGetPixel(b, 0x75fb4cff);
    769         } catch (IOException e) {
    770             fail();
    771         }
    772     }
    773 
    774     @Test
    775     public void inColorSpaceWith16BitSrc() {
    776         BitmapFactory.Options opts = new BitmapFactory.Options();
    777         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB);
    778 
    779         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
    780             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    781             ColorSpace cs = b.getColorSpace();
    782             assertNotNull(cs);
    783             assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs);
    784             assertSame(opts.inPreferredColorSpace, opts.outColorSpace);
    785         } catch (IOException e) {
    786             fail();
    787         }
    788     }
    789 
    790     @Test
    791     public void inColorSpaceWith16BitDst() {
    792         BitmapFactory.Options opts = new BitmapFactory.Options();
    793         opts.inPreferredConfig = Bitmap.Config.RGBA_F16;
    794 
    795         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
    796             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    797             ColorSpace cs = b.getColorSpace();
    798             assertNotNull(cs);
    799             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
    800         } catch (IOException e) {
    801             fail();
    802         }
    803     }
    804 
    805     @Test
    806     public void inColorSpaceWith16BitSrcAndDst() {
    807         BitmapFactory.Options opts = new BitmapFactory.Options();
    808         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB);
    809         opts.inPreferredConfig = Bitmap.Config.RGBA_F16;
    810 
    811         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
    812             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    813             ColorSpace cs = b.getColorSpace();
    814             assertNotNull(cs);
    815             assertSame(opts.inPreferredColorSpace, cs);
    816             assertSame(opts.inPreferredColorSpace, opts.outColorSpace);
    817         } catch (IOException e) {
    818             fail();
    819         }
    820     }
    821 
    822     @Test
    823     public void inColorSpaceWith16BitWithDecreasedGamut() {
    824         final String asset = "blue-16bit-prophoto.png";
    825         BitmapFactory.Options opts = new BitmapFactory.Options();
    826         opts.inJustDecodeBounds = true;
    827         try (InputStream in = mResources.getAssets().open(asset)) {
    828             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    829             assertNull(b);
    830             assertEquals(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), opts.outColorSpace);
    831             assertEquals(Bitmap.Config.RGBA_F16, opts.outConfig);
    832         } catch (IOException e) {
    833             fail();
    834         }
    835 
    836         opts.inJustDecodeBounds = false;
    837         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
    838 
    839         try (InputStream in = mResources.getAssets().open(asset)) {
    840             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    841             ColorSpace cs = b.getColorSpace();
    842             assertNotNull(cs);
    843             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    844         } catch (IOException e) {
    845             fail();
    846         }
    847     }
    848 
    849     @Test
    850     public void inColorSpace565() {
    851         BitmapFactory.Options opts = new BitmapFactory.Options();
    852         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.ADOBE_RGB);
    853         opts.inPreferredConfig = Bitmap.Config.RGB_565;
    854 
    855         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    856             Bitmap b = BitmapFactory.decodeStream(in, null, opts);
    857             ColorSpace cs = b.getColorSpace();
    858             assertNotNull(cs);
    859             assertSame(opts.inPreferredColorSpace, cs);
    860             assertSame(opts.inPreferredColorSpace, opts.outColorSpace);
    861         } catch (IOException e) {
    862             fail();
    863         }
    864     }
    865 
    866     @Test(expected = IllegalArgumentException.class)
    867     public void inColorSpaceNotRGB() {
    868         BitmapFactory.Options opts = new BitmapFactory.Options();
    869         opts.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.CIE_LAB);
    870 
    871         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    872             BitmapFactory.decodeStream(in, null, opts);
    873         } catch (IOException e) {
    874             fail();
    875         }
    876     }
    877 
    878     @Test(expected = IllegalArgumentException.class)
    879     public void inColorSpaceNoTransferParameters() {
    880         BitmapFactory.Options opts = new BitmapFactory.Options();
    881         opts.inPreferredColorSpace = new ColorSpace.Rgb("NoTransferParams",
    882                 new float[]{ 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
    883                 ColorSpace.ILLUMINANT_D50,
    884                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f),
    885                 0, 1);
    886 
    887         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    888             BitmapFactory.decodeStream(in, null, opts);
    889         } catch (IOException e) {
    890             fail();
    891         }
    892     }
    893 
    894     @Test
    895     public void copyF16() {
    896         // Copying from (LINEAR_)SRGB to RGBA_F16 results in (LINEAR_)EXTENDED_SRGB.
    897         ColorSpace[] srcCS = new ColorSpace[] { ColorSpace.get(ColorSpace.Named.SRGB),
    898             ColorSpace.get(ColorSpace.Named.LINEAR_SRGB) };
    899         ColorSpace[] dstCS = new ColorSpace[] { ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB),
    900             ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB) };
    901 
    902         for (int i = 0; i < srcCS.length; ++i) {
    903             for (Bitmap.Config config : new Bitmap.Config[] { Bitmap.Config.ARGB_8888,
    904                     Bitmap.Config.RGB_565 }) {
    905                 Bitmap b = Bitmap.createBitmap(10, 10, config, false, srcCS[i]);
    906                 assertSame(srcCS[i], b.getColorSpace());
    907 
    908                 for (boolean mutable : new boolean[] { true, false }) {
    909                     Bitmap copy = b.copy(Bitmap.Config.RGBA_F16, mutable);
    910                     assertSame(dstCS[i], copy.getColorSpace());
    911                 }
    912             }
    913         }
    914 
    915         // The same is true for the reverse
    916         for (int i = 0; i < srcCS.length; ++i) {
    917             Bitmap b = Bitmap.createBitmap(10, 10, Bitmap.Config.RGBA_F16, false, dstCS[i]);
    918             assertSame(dstCS[i], b.getColorSpace());
    919             for (Bitmap.Config config : new Bitmap.Config[] { Bitmap.Config.ARGB_8888,
    920                     Bitmap.Config.RGB_565 }) {
    921                 for (boolean mutable : new boolean[] { true, false }) {
    922                     Bitmap copy = b.copy(config, mutable);
    923                     assertSame(srcCS[i], copy.getColorSpace());
    924                 }
    925             }
    926         }
    927     }
    928 
    929     @Test
    930     public void copyAlpha8() {
    931         for (Bitmap.Config srcConfig : new Bitmap.Config[] {
    932                 Bitmap.Config.ALPHA_8,
    933                 Bitmap.Config.RGB_565,
    934                 Bitmap.Config.ARGB_8888,
    935                 Bitmap.Config.RGBA_F16,
    936         }) {
    937             Bitmap b = Bitmap.createBitmap(1, 1, srcConfig);
    938             assertNotNull(b);
    939             if (srcConfig == Bitmap.Config.ALPHA_8) {
    940                 assertNull(b.getColorSpace());
    941             } else {
    942                 assertNotNull(b.getColorSpace());
    943             }
    944 
    945             Bitmap copy = b.copy(Bitmap.Config.ALPHA_8, false);
    946             assertNotNull(copy);
    947             assertNull(copy.getColorSpace());
    948 
    949             Bitmap copy2 = copy.copy(srcConfig, false);
    950             switch (srcConfig) {
    951                 case RGBA_F16:
    952                     assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB),
    953                             copy2.getColorSpace());
    954                     break;
    955                 case ALPHA_8:
    956                     assertNull(b.getColorSpace());
    957                     break;
    958                 default:
    959                     assertSame("Copied from ALPHA_8 to " + srcConfig,
    960                             ColorSpace.get(ColorSpace.Named.SRGB), copy2.getColorSpace());
    961             }
    962         }
    963     }
    964 
    965     @Test
    966     public void copyHardwareToAlpha8() {
    967         BitmapFactory.Options options = new BitmapFactory.Options();
    968         options.inPreferredConfig = Bitmap.Config.HARDWARE;
    969         Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot, options);
    970         assertSame(Bitmap.Config.HARDWARE, b.getConfig());
    971         assertNotNull(b.getColorSpace());
    972 
    973         Bitmap copy = b.copy(Bitmap.Config.ALPHA_8, false);
    974         assertNull(copy.getColorSpace());
    975     }
    976 
    977     @Test
    978     public void copy() {
    979         Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
    980         Bitmap c;
    981         ColorSpace cs;
    982         boolean[] trueFalse = new boolean[] { true, false };
    983 
    984         for (boolean mutable : trueFalse) {
    985             c = b.copy(Bitmap.Config.ARGB_8888, mutable);
    986             cs = c.getColorSpace();
    987             assertNotNull(cs);
    988             assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
    989         }
    990 
    991         try (InputStream in = mResources.getAssets().open("green-p3.png")) {
    992             b = BitmapFactory.decodeStream(in);
    993             c = b.copy(Bitmap.Config.ARGB_8888, false);
    994             cs = c.getColorSpace();
    995             assertNotNull(cs);
    996             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
    997 
    998             c = b.copy(Bitmap.Config.ARGB_8888, true);
    999             cs = c.getColorSpace();
   1000             assertNotNull(cs);
   1001             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
   1002         } catch (IOException e) {
   1003             fail();
   1004         }
   1005 
   1006         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
   1007             b = BitmapFactory.decodeStream(in);
   1008             c = b.copy(Bitmap.Config.RGBA_F16, false);
   1009             cs = c.getColorSpace();
   1010             assertNotNull(cs);
   1011             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
   1012 
   1013             c = b.copy(Bitmap.Config.RGBA_F16, true);
   1014             cs = c.getColorSpace();
   1015             assertNotNull(cs);
   1016             assertSame(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), cs);
   1017         } catch (IOException e) {
   1018             fail();
   1019         }
   1020     }
   1021 
   1022     @SuppressWarnings("SameParameterValue")
   1023     private static void almostEqual(@ColorInt int expected,
   1024             @ColorInt int pixel, int threshold, int index) {
   1025         int diffA = Math.abs((expected >>> 24) - (pixel >>> 24));
   1026         int diffR = Math.abs(((expected >> 16) & 0xff) - ((pixel >> 16) & 0xff));
   1027         int diffG = Math.abs(((expected >>  8) & 0xff) - ((pixel >>  8) & 0xff));
   1028         int diffB = Math.abs((expected & 0xff) - (pixel & 0xff));
   1029 
   1030         boolean pass = diffA + diffR + diffG + diffB <= threshold;
   1031         if (!pass) {
   1032             Log.d(LOG_TAG, "Expected 0x" + Integer.toHexString(expected) +
   1033                     " but was 0x" + Integer.toHexString(pixel) + " with index " + index);
   1034         }
   1035 
   1036         assertTrue(pass);
   1037     }
   1038 
   1039     @Test
   1040     public void testEncodeP3() {
   1041         Bitmap b = null;
   1042         ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(),
   1043                 "blue-16bit-srgb.png");
   1044         try {
   1045             b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
   1046                 decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
   1047             });
   1048             assertNotNull(b);
   1049             assertEquals(Bitmap.Config.RGBA_F16, b.getConfig());
   1050         } catch (IOException e) {
   1051             fail("Failed with " + e);
   1052         }
   1053 
   1054         for (Bitmap.CompressFormat format : new Bitmap.CompressFormat[] {
   1055                 Bitmap.CompressFormat.JPEG,
   1056                 Bitmap.CompressFormat.WEBP,
   1057                 Bitmap.CompressFormat.PNG,
   1058         }) {
   1059             ByteArrayOutputStream out = new ByteArrayOutputStream();
   1060             assertTrue("Failed to encode F16 to " + format, b.compress(format, 100, out));
   1061 
   1062             byte[] array = out.toByteArray();
   1063             src = ImageDecoder.createSource(ByteBuffer.wrap(array));
   1064 
   1065             try {
   1066                 Bitmap b2 = ImageDecoder.decodeBitmap(src);
   1067                 assertEquals("Wrong color space for " + format,
   1068                         ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace());
   1069             } catch (IOException e) {
   1070                 fail("Failed with " + e);
   1071             }
   1072         }
   1073     }
   1074 
   1075     @Test
   1076     public void testEncodeP3hardware() {
   1077         Bitmap b = null;
   1078         ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(),
   1079                 "green-p3.png");
   1080         try {
   1081             b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
   1082                 decoder.setAllocator(ImageDecoder.ALLOCATOR_HARDWARE);
   1083             });
   1084             assertNotNull(b);
   1085             assertEquals(Bitmap.Config.HARDWARE, b.getConfig());
   1086             assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
   1087         } catch (IOException e) {
   1088             fail("Failed with " + e);
   1089         }
   1090 
   1091         for (Bitmap.CompressFormat format : new Bitmap.CompressFormat[] {
   1092                 Bitmap.CompressFormat.JPEG,
   1093                 Bitmap.CompressFormat.WEBP,
   1094                 Bitmap.CompressFormat.PNG,
   1095         }) {
   1096             ByteArrayOutputStream out = new ByteArrayOutputStream();
   1097             assertTrue("Failed to encode 8888 to " + format, b.compress(format, 100, out));
   1098 
   1099             byte[] array = out.toByteArray();
   1100             src = ImageDecoder.createSource(ByteBuffer.wrap(array));
   1101 
   1102             try {
   1103                 Bitmap b2 = ImageDecoder.decodeBitmap(src);
   1104                 assertEquals("Wrong color space for " + format,
   1105                         ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace());
   1106             } catch (IOException e) {
   1107                 fail("Failed with " + e);
   1108             }
   1109         }
   1110     }
   1111 
   1112     @Test
   1113     @RequiresDevice // SwiftShader does not yet have support for F16 in HARDWARE b/75778024
   1114     public void test16bitHardware() {
   1115         // Decoding to HARDWARE may use EXTENDED_SRGB or SRGB, depending
   1116         // on whether F16 is supported in HARDWARE.
   1117         try (InputStream in = mResources.getAssets().open("blue-16bit-srgb.png")) {
   1118             BitmapFactory.Options options = new BitmapFactory.Options();
   1119             options.inPreferredConfig = Bitmap.Config.HARDWARE;
   1120             Bitmap b = BitmapFactory.decodeStream(in, null, options);
   1121             assertEquals(Bitmap.Config.HARDWARE, b.getConfig());
   1122 
   1123             final ColorSpace cs = b.getColorSpace();
   1124             if (cs != ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
   1125                     && cs != ColorSpace.get(ColorSpace.Named.SRGB)) {
   1126                 fail("Unexpected color space " + cs);
   1127             }
   1128         } catch (Exception e) {
   1129             fail("Failed with " + e);
   1130         }
   1131     }
   1132 
   1133     @Test
   1134     public void testProPhoto() throws IOException {
   1135         ColorSpace extendedSrgb = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
   1136         Color blue = Color.valueOf(0, 0, 1, 1, ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
   1137         Color expected = blue.convert(extendedSrgb);
   1138         try (InputStream in = mResources.getAssets().open("blue-16bit-prophoto.png")) {
   1139             Bitmap src = BitmapFactory.decodeStream(in, null, null);
   1140 
   1141             Bitmap dst = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
   1142                     Bitmap.Config.RGBA_F16, true, extendedSrgb);
   1143             Canvas c = new Canvas(dst);
   1144             c.drawBitmap(src, 0, 0, null);
   1145             ColorUtils.verifyColor("PRO_PHOTO image did not convert properly", expected,
   1146                     dst.getColor(0, 0), .001f);
   1147         }
   1148     }
   1149 }
   1150