• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
setup()65     public void setup() {
66         mResources = InstrumentationRegistry.getTargetContext().getResources();
67     }
68 
69     @SuppressWarnings("deprecation")
70     @Test
createWithColorSpace()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
createAlpha8ColorSpace()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
createDefaultColorSpace()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)
createWithoutColorSpace()156     public void createWithoutColorSpace() {
157         Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, null);
158     }
159 
160     @Test(expected = IllegalArgumentException.class)
createWithNonRgbColorSpace()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)
createWithNoTransferParameters()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
createFromSourceWithColorSpace()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
sRGB()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
p3()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
extendedSRGB()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
linearSRGB()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;
Asset(String name, ColorSpace.Named e)305         Asset(String name, ColorSpace.Named e) {
306             this.name = name;
307             this.colorSpace = ColorSpace.get(e);
308         }
309     };
310 
311     @Test
reconfigure()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
reuse()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
getPixel()377     public void getPixel() {
378         verifyGetPixel("green-p3.png", 0x75fb4cff);
379         verifyGetPixel("translucent-green-p3.png", 0x3a7d267f); // 50% translucent
380     }
381 
verifyGetPixel(@onNull String fileName, @ColorInt int rawColor)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 
verifyGetPixel(@onNull Bitmap b, @ColorInt int rawColor)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 
convertPremulColorToColorInt(int premulColor, ColorSpace premulCS)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
getPixels()423     public void getPixels() {
424         verifyGetPixels("green-p3.png");
425         verifyGetPixels("translucent-green-p3.png"); // 50% translucent
426     }
427 
verifyGetPixels(@onNull String fileName)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 
verifyGetPixels(@onNull Bitmap b, @ColorInt int expected)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
setPixel()465     public void setPixel() {
466         verifySetPixel("green-p3.png", 0xffff0000, 0xea3323ff);
467         verifySetPixel("translucent-green-p3.png", 0x7fff0000, 0x7519127f);
468     }
469 
verifySetPixel(@onNull String fileName, @ColorInt int newColor, @ColorInt int expectedColor)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 
verifySetPixel(@onNull Bitmap b, @ColorInt int newColor, @ColorInt int expectedColor)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
setPixels()508     public void setPixels() {
509         verifySetPixels("green-p3.png", 0xffff0000, 0xea3323ff);
510         verifySetPixels("translucent-green-p3.png", 0x7fff0000, 0x7519127f);
511     }
512 
verifySetPixels(@onNull String fileName, @ColorInt int newColor, @ColorInt int expectedColor)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 
verifySetPixels(@onNull Bitmap b, @ColorInt int newColor, @ColorInt int expectedColor)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
writeColorSpace()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 
verifyColorSpaceMarshalling( @onNull String fileName, @NonNull ColorSpace colorSpace)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 
verifyParcelUnparcel(Bitmap bitmapIn, ColorSpace expected)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
p3rgb565()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
p3hardware()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
guessSRGB()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
guess16bitUntagged()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
guessProPhotoRGB()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
guessP3()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
guessAdobeRGB()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
guessUnknown()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
guessCMYK()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
inColorSpaceP3ToSRGB()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
inColorSpaceSRGBToP3()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
inColorSpaceWith16BitSrc()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
inColorSpaceWith16BitDst()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
inColorSpaceWith16BitSrcAndDst()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
inColorSpaceWith16BitWithDecreasedGamut()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
inColorSpace565()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)
inColorSpaceNotRGB()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)
inColorSpaceNoTransferParameters()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
copyF16()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
copyAlpha8()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
copyHardwareToAlpha8()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
copy()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")
almostEqual(@olorInt int expected, @ColorInt int pixel, int threshold, int index)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
testEncodeP3()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
testEncodeP3hardware()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
test16bitHardware()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
testProPhoto()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