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