1 /*
2  * Copyright (C) 2016 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 package android.graphics.cts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
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.graphics.ColorSpace;
27 import android.hardware.DataSpace;
28 import android.platform.test.annotations.RequiresFlagsEnabled;
29 import android.platform.test.flag.junit.CheckFlagsRule;
30 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
31 import android.util.Log;
32 
33 import androidx.test.filters.SmallTest;
34 
35 import com.android.graphics.flags.Flags;
36 
37 import junitparams.JUnitParamsRunner;
38 import junitparams.Parameters;
39 
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.Arrays;
45 import java.util.function.DoubleUnaryOperator;
46 
47 @SmallTest
48 @RunWith(JUnitParamsRunner.class)
49 public class ColorSpaceTest {
50 
51     @Rule
52     public final CheckFlagsRule mCheckFlagsRule =
53             DeviceFlagsValueProvider.createCheckFlagsRule();
54 
55     // Column-major RGB->XYZ transform matrix for the sRGB color space
56     private static final float[] SRGB_TO_XYZ = {
57             0.412391f, 0.212639f, 0.019331f,
58             0.357584f, 0.715169f, 0.119195f,
59             0.180481f, 0.072192f, 0.950532f
60     };
61     // Column-major XYZ->RGB transform matrix for the sRGB color space
62     private static final float[] XYZ_TO_SRGB = {
63             3.240970f, -0.969244f,  0.055630f,
64            -1.537383f,  1.875968f, -0.203977f,
65            -0.498611f,  0.041555f,  1.056971f
66     };
67 
68     // Column-major RGB->XYZ transform matrix for the sRGB color space and a D50 white point
69     private static final float[] SRGB_TO_XYZ_D50 = {
70             0.4360747f, 0.2225045f, 0.0139322f,
71             0.3850649f, 0.7168786f, 0.0971045f,
72             0.1430804f, 0.0606169f, 0.7141733f
73     };
74 
75     private static final float[] SRGB_PRIMARIES_xyY =
76             { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
77     private static final float[] SRGB_WHITE_POINT_xyY = { 0.3127f, 0.3290f };
78 
79     private static final float[] SRGB_PRIMARIES_XYZ = {
80             1.939394f, 1.000000f, 0.090909f,
81             0.500000f, 1.000000f, 0.166667f,
82             2.500000f, 1.000000f, 13.166667f
83     };
84     private static final float[] SRGB_WHITE_POINT_XYZ = { 0.950456f, 1.000f, 1.089058f };
85 
86     private static final DoubleUnaryOperator sIdentity = DoubleUnaryOperator.identity();
87 
88     @Test
testNamedColorSpaces()89     public void testNamedColorSpaces() {
90         ColorSpace.Named[] values = ColorSpace.Named.values();
91         int numColorSpaces;
92         if (Flags.okLabColorspace()) {
93             numColorSpaces = values.length;
94         } else {
95             numColorSpaces = values.length - 1;
96         }
97         for (int i = 0; i < numColorSpaces; i++) {
98             ColorSpace.Named named = values[i];
99             ColorSpace colorSpace = ColorSpace.get(named);
100             Log.v("ResolvedColorSpace", "ColorSpace: " + colorSpace);
101             assertNotNull(colorSpace.getName());
102             assertNotNull(colorSpace);
103             assertEquals(named.ordinal(), colorSpace.getId());
104             assertTrue(colorSpace.getComponentCount() >= 1);
105             assertTrue(colorSpace.getComponentCount() <= 4);
106         }
107     }
108 
109     @Test(expected = IllegalArgumentException.class)
testNullName()110     public void testNullName() {
111         new ColorSpace.Rgb(null, new float[6], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
112     }
113 
114     @Test(expected = IllegalArgumentException.class)
testEmptyName()115     public void testEmptyName() {
116         new ColorSpace.Rgb("", new float[6], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
117     }
118 
119     @Test
testName()120     public void testName() {
121         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
122                 sIdentity, sIdentity, 0.0f, 1.0f);
123         assertEquals("Test", cs.getName());
124     }
125 
126     @Test(expected = IllegalArgumentException.class)
testPrimariesLength()127     public void testPrimariesLength() {
128         new ColorSpace.Rgb("Test", new float[7], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
129     }
130 
131     @Test(expected = IllegalArgumentException.class)
testWhitePointLength()132     public void testWhitePointLength() {
133         new ColorSpace.Rgb("Test", new float[6], new float[1], sIdentity, sIdentity, 0.0f, 1.0f);
134     }
135 
136     @Test(expected = IllegalArgumentException.class)
testNullOETF()137     public void testNullOETF() {
138         new ColorSpace.Rgb("Test", new float[6], new float[2], null, sIdentity, 0.0f, 1.0f);
139     }
140 
141     @Test
testOETF()142     public void testOETF() {
143         DoubleUnaryOperator op = Math::sqrt;
144         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
145                 op, sIdentity, 0.0f, 1.0f);
146         assertEquals(0.5, cs.getOetf().applyAsDouble(0.25), 1e-5);
147     }
148 
149     @Test(expected = IllegalArgumentException.class)
testNullEOTF()150     public void testNullEOTF() {
151         new ColorSpace.Rgb("Test", new float[6], new float[2], sIdentity, null, 0.0f, 1.0f);
152     }
153 
154     @Test
testEOTF()155     public void testEOTF() {
156         DoubleUnaryOperator op = x -> x * x;
157         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
158                 sIdentity, op, 0.0f, 1.0f);
159         assertEquals(0.0625, cs.getEotf().applyAsDouble(0.25), 1e-5);
160     }
161 
162     @Test(expected = IllegalArgumentException.class)
testInvalidRange()163     public void testInvalidRange() {
164         new ColorSpace.Rgb("Test", new float[6], new float[2], sIdentity, sIdentity, 2.0f, 1.0f);
165     }
166 
167     @Test
testRanges()168     public void testRanges() {
169         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
170 
171         float m1 = cs.getMinValue(0);
172         float m2 = cs.getMinValue(1);
173         float m3 = cs.getMinValue(2);
174 
175         assertEquals(0.0f, m1, 1e-9f);
176         assertEquals(0.0f, m2, 1e-9f);
177         assertEquals(0.0f, m3, 1e-9f);
178 
179         m1 = cs.getMaxValue(0);
180         m2 = cs.getMaxValue(1);
181         m3 = cs.getMaxValue(2);
182 
183         assertEquals(1.0f, m1, 1e-9f);
184         assertEquals(1.0f, m2, 1e-9f);
185         assertEquals(1.0f, m3, 1e-9f);
186 
187         cs = ColorSpace.get(ColorSpace.Named.CIE_LAB);
188 
189         m1 = cs.getMinValue(0);
190         m2 = cs.getMinValue(1);
191         m3 = cs.getMinValue(2);
192 
193         assertEquals(0.0f, m1, 1e-9f);
194         assertEquals(-128.0f, m2, 1e-9f);
195         assertEquals(-128.0f, m3, 1e-9f);
196 
197         m1 = cs.getMaxValue(0);
198         m2 = cs.getMaxValue(1);
199         m3 = cs.getMaxValue(2);
200 
201         assertEquals(100.0f, m1, 1e-9f);
202         assertEquals(128.0f, m2, 1e-9f);
203         assertEquals(128.0f, m3, 1e-9f);
204 
205         cs = ColorSpace.get(ColorSpace.Named.CIE_XYZ);
206 
207         m1 = cs.getMinValue(0);
208         m2 = cs.getMinValue(1);
209         m3 = cs.getMinValue(2);
210 
211         assertEquals(-2.0f, m1, 1e-9f);
212         assertEquals(-2.0f, m2, 1e-9f);
213         assertEquals(-2.0f, m3, 1e-9f);
214 
215         m1 = cs.getMaxValue(0);
216         m2 = cs.getMaxValue(1);
217         m3 = cs.getMaxValue(2);
218 
219         assertEquals(2.0f, m1, 1e-9f);
220         assertEquals(2.0f, m2, 1e-9f);
221         assertEquals(2.0f, m3, 1e-9f);
222 
223         if (Flags.okLabColorspace()) {
224             cs = ColorSpace.get(ColorSpace.Named.OK_LAB);
225 
226             m1 = cs.getMinValue(0);
227             m2 = cs.getMinValue(1);
228             m3 = cs.getMinValue(2);
229 
230             assertEquals(0f, m1, 1e-9f);
231             assertEquals(-0.5f, m2, 1e-9f);
232             assertEquals(-0.5f, m3, 1e-9f);
233 
234             m1 = cs.getMaxValue(0);
235             m2 = cs.getMaxValue(1);
236             m3 = cs.getMaxValue(2);
237 
238             assertEquals(1f, m1, 1e-9f);
239             assertEquals(0.5f, m2, 1e-9f);
240             assertEquals(0.5f, m3, 1e-9f);
241         }
242     }
243 
244     @Test
testMat3x3()245     public void testMat3x3() {
246         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
247 
248         float[] rgbToXYZ = cs.getTransform();
249         for (int i = 0; i < 9; i++) {
250             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
251         }
252     }
253 
254     @Test
testMat3x3Inverse()255     public void testMat3x3Inverse() {
256         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
257 
258         float[] xyzToRGB = cs.getInverseTransform();
259         for (int i = 0; i < 9; i++) {
260             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
261         }
262     }
263 
264     @Test
testMat3x3Primaries()265     public void testMat3x3Primaries() {
266         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
267 
268         float[] primaries = cs.getPrimaries();
269 
270         assertNotNull(primaries);
271         assertEquals(6, primaries.length);
272 
273         assertEquals(SRGB_PRIMARIES_xyY[0], primaries[0], 1e-5f);
274         assertEquals(SRGB_PRIMARIES_xyY[1], primaries[1], 1e-5f);
275         assertEquals(SRGB_PRIMARIES_xyY[2], primaries[2], 1e-5f);
276         assertEquals(SRGB_PRIMARIES_xyY[3], primaries[3], 1e-5f);
277         assertEquals(SRGB_PRIMARIES_xyY[4], primaries[4], 1e-5f);
278         assertEquals(SRGB_PRIMARIES_xyY[5], primaries[5], 1e-5f);
279     }
280 
281     @Test
testMat3x3WhitePoint()282     public void testMat3x3WhitePoint() {
283         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
284 
285         float[] whitePoint = cs.getWhitePoint();
286 
287         assertNotNull(whitePoint);
288         assertEquals(2, whitePoint.length);
289 
290         assertEquals(SRGB_WHITE_POINT_xyY[0], whitePoint[0], 1e-5f);
291         assertEquals(SRGB_WHITE_POINT_xyY[1], whitePoint[1], 1e-5f);
292     }
293 
294     @Test
testXYZFromPrimaries_xyY()295     public void testXYZFromPrimaries_xyY() {
296         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_PRIMARIES_xyY, SRGB_WHITE_POINT_xyY,
297                 sIdentity, sIdentity, 0.0f, 1.0f);
298 
299         float[] rgbToXYZ = cs.getTransform();
300         for (int i = 0; i < 9; i++) {
301             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
302         }
303 
304         float[] xyzToRGB = cs.getInverseTransform();
305         for (int i = 0; i < 9; i++) {
306             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
307         }
308     }
309 
310     @Test
testXYZFromPrimaries_XYZ()311     public void testXYZFromPrimaries_XYZ() {
312         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_PRIMARIES_XYZ, SRGB_WHITE_POINT_XYZ,
313                 sIdentity, sIdentity, 0.0f, 1.0f);
314 
315         float[] primaries = cs.getPrimaries();
316 
317         assertNotNull(primaries);
318         assertEquals(6, primaries.length);
319 
320         // SRGB_PRIMARIES_xyY only has 1e-3 of precision, match it
321         assertEquals(SRGB_PRIMARIES_xyY[0], primaries[0], 1e-3f);
322         assertEquals(SRGB_PRIMARIES_xyY[1], primaries[1], 1e-3f);
323         assertEquals(SRGB_PRIMARIES_xyY[2], primaries[2], 1e-3f);
324         assertEquals(SRGB_PRIMARIES_xyY[3], primaries[3], 1e-3f);
325         assertEquals(SRGB_PRIMARIES_xyY[4], primaries[4], 1e-3f);
326         assertEquals(SRGB_PRIMARIES_xyY[5], primaries[5], 1e-3f);
327 
328         float[] whitePoint = cs.getWhitePoint();
329 
330         assertNotNull(whitePoint);
331         assertEquals(2, whitePoint.length);
332 
333         // SRGB_WHITE_POINT_xyY only has 1e-3 of precision, match it
334         assertEquals(SRGB_WHITE_POINT_xyY[0], whitePoint[0], 1e-3f);
335         assertEquals(SRGB_WHITE_POINT_xyY[1], whitePoint[1], 1e-3f);
336 
337         float[] rgbToXYZ = cs.getTransform();
338         for (int i = 0; i < 9; i++) {
339             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
340         }
341 
342         float[] xyzToRGB = cs.getInverseTransform();
343         for (int i = 0; i < 9; i++) {
344             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
345         }
346     }
347 
348     @Test
testGetComponentCount()349     public void testGetComponentCount() {
350         assertEquals(3, ColorSpace.get(ColorSpace.Named.SRGB).getComponentCount());
351         assertEquals(3, ColorSpace.get(ColorSpace.Named.LINEAR_SRGB).getComponentCount());
352         assertEquals(3, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB).getComponentCount());
353         assertEquals(3, ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB).getComponentCount());
354         assertEquals(3, ColorSpace.get(ColorSpace.Named.DISPLAY_P3).getComponentCount());
355         assertEquals(3, ColorSpace.get(ColorSpace.Named.CIE_LAB).getComponentCount());
356         assertEquals(3, ColorSpace.get(ColorSpace.Named.CIE_XYZ).getComponentCount());
357         if (Flags.okLabColorspace()) {
358             assertEquals(3, ColorSpace.get(ColorSpace.Named.OK_LAB).getComponentCount());
359         }
360     }
361 
362     @Test
testIsSRGB()363     public void testIsSRGB() {
364         for (ColorSpace.Named e : ColorSpace.Named.values()) {
365             ColorSpace colorSpace = ColorSpace.get(e);
366             // ColorSpace.get is guaranteed to return non-null. So if this is queried with
367             // a ColorSpace that is flagged, this falls back ot return SRGB as a default.
368             // The values method of an enum will always return the full set of enum values
369             // regardless if they are flagged out or not
370             boolean isSrgbFallback = (colorSpace.getId() == 0 && !Flags.okLabColorspace());
371             if (e == ColorSpace.Named.SRGB || isSrgbFallback) {
372                 assertTrue(colorSpace.isSrgb());
373             } else {
374                 assertFalse("Incorrectly treating " + e + " as SRGB!",
375                             colorSpace.isSrgb());
376             }
377         }
378 
379         ColorSpace.Rgb cs = new ColorSpace.Rgb("Almost sRGB", SRGB_TO_XYZ,
380                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f));
381         assertFalse(cs.isSrgb());
382     }
383 
384     @Test
testIsWideGamut()385     public void testIsWideGamut() {
386         assertFalse(ColorSpace.get(ColorSpace.Named.SRGB).isWideGamut());
387         assertFalse(ColorSpace.get(ColorSpace.Named.BT709).isWideGamut());
388         assertTrue(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB).isWideGamut());
389         assertTrue(ColorSpace.get(ColorSpace.Named.DCI_P3).isWideGamut());
390         assertTrue(ColorSpace.get(ColorSpace.Named.BT2020).isWideGamut());
391         assertTrue(ColorSpace.get(ColorSpace.Named.ACES).isWideGamut());
392         assertTrue(ColorSpace.get(ColorSpace.Named.CIE_LAB).isWideGamut());
393         assertTrue(ColorSpace.get(ColorSpace.Named.CIE_XYZ).isWideGamut());
394         if (Flags.okLabColorspace()) {
395             assertTrue(ColorSpace.get(ColorSpace.Named.OK_LAB).isWideGamut());
396         }
397     }
398 
399     @Test
testWhitePoint()400     public void testWhitePoint() {
401         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
402 
403         float[] whitePoint = cs.getWhitePoint();
404 
405         assertNotNull(whitePoint);
406         assertEquals(2, whitePoint.length);
407 
408         // Make sure a copy is returned
409         Arrays.fill(whitePoint, Float.NaN);
410         assertArrayNotEquals(whitePoint, cs.getWhitePoint(), 1e-5f);
411         assertSame(whitePoint, cs.getWhitePoint(whitePoint));
412         assertArrayEquals(whitePoint, cs.getWhitePoint(), 1e-5f);
413     }
414 
415     @Test
testPrimaries()416     public void testPrimaries() {
417         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
418 
419         float[] primaries = cs.getPrimaries();
420 
421         assertNotNull(primaries);
422         assertEquals(6, primaries.length);
423 
424         // Make sure a copy is returned
425         Arrays.fill(primaries, Float.NaN);
426         assertArrayNotEquals(primaries, cs.getPrimaries(), 1e-5f);
427         assertSame(primaries, cs.getPrimaries(primaries));
428         assertArrayEquals(primaries, cs.getPrimaries(), 1e-5f);
429     }
430 
431     @Test
testRGBtoXYZMatrix()432     public void testRGBtoXYZMatrix() {
433         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
434 
435         float[] rgbToXYZ = cs.getTransform();
436 
437         assertNotNull(rgbToXYZ);
438         assertEquals(9, rgbToXYZ.length);
439 
440         // Make sure a copy is returned
441         Arrays.fill(rgbToXYZ, Float.NaN);
442         assertArrayNotEquals(rgbToXYZ, cs.getTransform(), 1e-5f);
443         assertSame(rgbToXYZ, cs.getTransform(rgbToXYZ));
444         assertArrayEquals(rgbToXYZ, cs.getTransform(), 1e-5f);
445     }
446 
447     @Test
testXYZtoRGBMatrix()448     public void testXYZtoRGBMatrix() {
449         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
450 
451         float[] xyzToRGB = cs.getInverseTransform();
452 
453         assertNotNull(xyzToRGB);
454         assertEquals(9, xyzToRGB.length);
455 
456         // Make sure a copy is returned
457         Arrays.fill(xyzToRGB, Float.NaN);
458         assertArrayNotEquals(xyzToRGB, cs.getInverseTransform(), 1e-5f);
459         assertSame(xyzToRGB, cs.getInverseTransform(xyzToRGB));
460         assertArrayEquals(xyzToRGB, cs.getInverseTransform(), 1e-5f);
461     }
462 
463     @Test
testRGBtoXYZ()464     public void testRGBtoXYZ() {
465         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
466 
467         float[] source = { 0.75f, 0.5f, 0.25f };
468         float[] expected = { 0.3012f, 0.2679f, 0.0840f };
469 
470         float[] r1 = cs.toXyz(source[0], source[1], source[2]);
471         assertNotNull(r1);
472         assertEquals(3, r1.length);
473         assertArrayNotEquals(source, r1, 1e-5f);
474         assertArrayEquals(expected, r1, 1e-3f);
475 
476         float[] r3 = { source[0], source[1], source[2] };
477         assertSame(r3, cs.toXyz(r3));
478         assertEquals(3, r3.length);
479         assertArrayEquals(r1, r3, 1e-5f);
480     }
481 
482     @Test
testXYZtoRGB()483     public void testXYZtoRGB() {
484         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
485 
486         float[] source = { 0.3012f, 0.2679f, 0.0840f };
487         float[] expected = { 0.75f, 0.5f, 0.25f };
488 
489         float[] r1 = cs.fromXyz(source[0], source[1], source[2]);
490         assertNotNull(r1);
491         assertEquals(3, r1.length);
492         assertArrayNotEquals(source, r1, 1e-5f);
493         assertArrayEquals(expected, r1, 1e-3f);
494 
495         float[] r3 = { source[0], source[1], source[2] };
496         assertSame(r3, cs.fromXyz(r3));
497         assertEquals(3, r3.length);
498         assertArrayEquals(r1, r3, 1e-5f);
499     }
500 
501     @Test
testConnect()502     public void testConnect() {
503         ColorSpace.Connector connector = ColorSpace.connect(
504                 ColorSpace.get(ColorSpace.Named.SRGB),
505                 ColorSpace.get(ColorSpace.Named.DCI_P3));
506 
507         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector.getSource());
508         assertSame(ColorSpace.get(ColorSpace.Named.DCI_P3), connector.getDestination());
509         assertSame(ColorSpace.RenderIntent.PERCEPTUAL, connector.getRenderIntent());
510 
511         connector = ColorSpace.connect(
512                 ColorSpace.get(ColorSpace.Named.SRGB),
513                 ColorSpace.get(ColorSpace.Named.SRGB));
514 
515         assertSame(connector.getDestination(), connector.getSource());
516         assertSame(ColorSpace.RenderIntent.RELATIVE, connector.getRenderIntent());
517 
518         connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.DCI_P3));
519         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector.getDestination());
520 
521         connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.SRGB));
522         assertSame(connector.getSource(), connector.getDestination());
523     }
524 
525     @Test
testConnector()526     public void testConnector() {
527         // Connect color spaces with same white points
528         ColorSpace.Connector connector = ColorSpace.connect(
529                 ColorSpace.get(ColorSpace.Named.SRGB),
530                 ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
531 
532         float[] source = { 1.0f, 0.5f, 0.0f };
533         float[] expected = { 0.8912f, 0.4962f, 0.1164f };
534 
535         float[] r1 = connector.transform(source[0], source[1], source[2]);
536         assertNotNull(r1);
537         assertEquals(3, r1.length);
538         assertArrayNotEquals(source, r1, 1e-5f);
539         assertArrayEquals(expected, r1, 1e-3f);
540 
541         float[] r3 = { source[0], source[1], source[2] };
542         assertSame(r3, connector.transform(r3));
543         assertEquals(3, r3.length);
544         assertArrayEquals(r1, r3, 1e-5f);
545 
546         connector = ColorSpace.connect(
547                 ColorSpace.get(ColorSpace.Named.ADOBE_RGB),
548                 ColorSpace.get(ColorSpace.Named.SRGB));
549 
550         float[] tmp = source;
551         source = expected;
552         expected = tmp;
553 
554         r1 = connector.transform(source[0], source[1], source[2]);
555         assertNotNull(r1);
556         assertEquals(3, r1.length);
557         assertArrayNotEquals(source, r1, 1e-5f);
558         assertArrayEquals(expected, r1, 1e-3f);
559 
560         r3 = new float[] { source[0], source[1], source[2] };
561         assertSame(r3, connector.transform(r3));
562         assertEquals(3, r3.length);
563         assertArrayEquals(r1, r3, 1e-5f);
564     }
565 
566     @Test
testAdaptedConnector()567     public void testAdaptedConnector() {
568         // Connect color spaces with different white points
569         ColorSpace.Connector connector = ColorSpace.connect(
570                 ColorSpace.get(ColorSpace.Named.SRGB),
571                 ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
572 
573         float[] source = new float[] { 1.0f, 0.0f, 0.0f };
574         float[] expected = new float[] { 0.70226f, 0.2757f, 0.1036f };
575 
576         float[] r = connector.transform(source[0], source[1], source[2]);
577         assertNotNull(r);
578         assertEquals(3, r.length);
579         assertArrayNotEquals(source, r, 1e-5f);
580         assertArrayEquals(expected, r, 1e-4f);
581     }
582 
583     @Test
testAdaptedConnectorWithRenderIntent()584     public void testAdaptedConnectorWithRenderIntent() {
585         // Connect a wider color space to a narrow color space
586         ColorSpace.Connector connector = ColorSpace.connect(
587                 ColorSpace.get(ColorSpace.Named.DCI_P3),
588                 ColorSpace.get(ColorSpace.Named.SRGB),
589                 ColorSpace.RenderIntent.RELATIVE);
590 
591         float[] source = { 0.9f, 0.9f, 0.9f };
592 
593         float[] relative = connector.transform(source[0], source[1], source[2]);
594         assertNotNull(relative);
595         assertEquals(3, relative.length);
596         assertArrayNotEquals(source, relative, 1e-5f);
597         assertArrayEquals(new float[] { 0.8862f, 0.8862f, 0.8862f }, relative, 1e-4f);
598 
599         connector = ColorSpace.connect(
600                 ColorSpace.get(ColorSpace.Named.DCI_P3),
601                 ColorSpace.get(ColorSpace.Named.SRGB),
602                 ColorSpace.RenderIntent.ABSOLUTE);
603 
604         float[] absolute = connector.transform(source[0], source[1], source[2]);
605         assertNotNull(absolute);
606         assertEquals(3, absolute.length);
607         assertArrayNotEquals(source, absolute, 1e-5f);
608         assertArrayNotEquals(relative, absolute, 1e-5f);
609         assertArrayEquals(new float[] { 0.8475f, 0.9217f, 0.8203f }, absolute, 1e-4f);
610     }
611 
612     @Test
testIdentityConnector()613     public void testIdentityConnector() {
614         ColorSpace.Connector connector = ColorSpace.connect(
615                 ColorSpace.get(ColorSpace.Named.SRGB),
616                 ColorSpace.get(ColorSpace.Named.SRGB));
617 
618         assertSame(connector.getSource(), connector.getDestination());
619         assertSame(ColorSpace.RenderIntent.RELATIVE, connector.getRenderIntent());
620 
621         float[] source = new float[] { 0.11112f, 0.22227f, 0.444448f };
622 
623         float[] r = connector.transform(source[0], source[1], source[2]);
624         assertNotNull(r);
625         assertEquals(3, r.length);
626         assertArrayEquals(source, r, 1e-5f);
627     }
628 
629     @Test
testConnectorTransformIdentity()630     public void testConnectorTransformIdentity() {
631         ColorSpace.Connector connector = ColorSpace.connect(
632                 ColorSpace.get(ColorSpace.Named.DCI_P3),
633                 ColorSpace.get(ColorSpace.Named.DCI_P3));
634 
635         float[] source = { 1.0f, 0.0f, 0.0f };
636         float[] expected = { 1.0f, 0.0f, 0.0f };
637 
638         float[] r1 = connector.transform(source[0], source[1], source[2]);
639         assertNotNull(r1);
640         assertEquals(3, r1.length);
641         assertArrayEquals(expected, r1, 1e-3f);
642 
643         float[] r3 = { source[0], source[1], source[2] };
644         assertSame(r3, connector.transform(r3));
645         assertEquals(3, r3.length);
646         assertArrayEquals(r1, r3, 1e-5f);
647     }
648 
649     @Test
testAdaptation()650     public void testAdaptation() {
651         ColorSpace adapted = ColorSpace.adapt(
652                 ColorSpace.get(ColorSpace.Named.SRGB),
653                 ColorSpace.ILLUMINANT_D50);
654 
655         float[] sRGBD50 = {
656                 0.43602175f, 0.22247513f, 0.01392813f,
657                 0.38510883f, 0.71690667f, 0.09710153f,
658                 0.14308129f, 0.06061824f, 0.71415880f
659         };
660 
661         assertArrayEquals(sRGBD50, ((ColorSpace.Rgb) adapted).getTransform(), 1e-7f);
662 
663         adapted = ColorSpace.adapt(
664                 ColorSpace.get(ColorSpace.Named.SRGB),
665                 ColorSpace.ILLUMINANT_D50,
666                 ColorSpace.Adaptation.BRADFORD);
667         assertArrayEquals(sRGBD50, ((ColorSpace.Rgb) adapted).getTransform(), 1e-7f);
668     }
669 
670     @Test
testImplicitSRGBConnector()671     public void testImplicitSRGBConnector() {
672         ColorSpace.Connector connector1 = ColorSpace.connect(
673                 ColorSpace.get(ColorSpace.Named.DCI_P3));
674 
675         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector1.getDestination());
676 
677         ColorSpace.Connector connector2 = ColorSpace.connect(
678                 ColorSpace.get(ColorSpace.Named.DCI_P3),
679                 ColorSpace.get(ColorSpace.Named.SRGB));
680 
681         float[] source = { 0.6f, 0.9f, 0.7f };
682         assertArrayEquals(
683                 connector1.transform(source[0], source[1], source[2]),
684                 connector2.transform(source[0], source[1], source[2]), 1e-7f);
685     }
686 
687     @Test
testLab()688     public void testLab() {
689         ColorSpace.Connector connector = ColorSpace.connect(
690                 ColorSpace.get(ColorSpace.Named.CIE_LAB));
691 
692         float[] source = { 100.0f, 0.0f, 0.0f };
693         float[] expected = { 1.0f, 1.0f, 1.0f };
694 
695         float[] r1 = connector.transform(source[0], source[1], source[2]);
696         assertNotNull(r1);
697         assertEquals(3, r1.length);
698         assertArrayEquals(expected, r1, 1e-3f);
699 
700         source = new float[] { 100.0f, 0.0f, 54.0f };
701         expected = new float[] { 1.0f, 0.9925f, 0.5762f };
702 
703         float[] r2 = connector.transform(source[0], source[1], source[2]);
704         assertNotNull(r2);
705         assertEquals(3, r2.length);
706         assertArrayEquals(expected, r2, 1e-3f);
707 
708         connector = ColorSpace.connect(
709                 ColorSpace.get(ColorSpace.Named.CIE_LAB), ColorSpace.RenderIntent.ABSOLUTE);
710 
711         source = new float[] { 100.0f, 0.0f, 0.0f };
712         expected = new float[] { 1.0f, 0.9910f, 0.8651f };
713 
714         r1 = connector.transform(source[0], source[1], source[2]);
715         assertNotNull(r1);
716         assertEquals(3, r1.length);
717         assertArrayEquals(expected, r1, 1e-3f);
718 
719         source = new float[] { 100.0f, 0.0f, 54.0f };
720         expected = new float[] { 1.0f, 0.9853f, 0.4652f };
721 
722         r2 = connector.transform(source[0], source[1], source[2]);
723         assertNotNull(r2);
724         assertEquals(3, r2.length);
725         assertArrayEquals(expected, r2, 1e-3f);
726     }
727 
728     @RequiresFlagsEnabled(Flags.FLAG_OK_LAB_COLORSPACE)
729     @Test
testOkLab()730     public void testOkLab() {
731         ColorSpace.Connector connector = ColorSpace.connect(
732                 ColorSpace.get(ColorSpace.Named.OK_LAB));
733 
734         float[] source = { 100.0f, 0.0f, 0.0f };
735         float[] expected = { 1.0f, 1.0f, 1.0f };
736 
737         float[] r1 = connector.transform(source[0], source[1], source[2]);
738         assertNotNull(r1);
739         assertEquals(3, r1.length);
740         assertArrayEquals(expected, r1, 1e-3f);
741 
742         source = new float[] { 100.0f, 0.0f, 54.0f };
743         expected = new float[] { 1.0f, 0.8137f, 0f };
744 
745         float[] r2 = connector.transform(source[0], source[1], source[2]);
746         assertNotNull(r2);
747         assertEquals(3, r2.length);
748         assertArrayEquals(expected, r2, 1e-3f);
749 
750         connector = ColorSpace.connect(
751                 ColorSpace.get(ColorSpace.Named.OK_LAB), ColorSpace.RenderIntent.ABSOLUTE);
752 
753         source = new float[] { 100.0f, 0.0f, 0.0f };
754         expected = new float[] { 1.0f, 0.9910f, 0.8651f };
755 
756         r1 = connector.transform(source[0], source[1], source[2]);
757         assertNotNull(r1);
758         assertEquals(3, r1.length);
759         assertArrayEquals(expected, r1, 1e-3f);
760 
761         source = new float[] { 100.0f, 0.0f, 54.0f };
762         expected = new float[] { 1.0f, 0.80465f, 0.0f };
763 
764         r2 = connector.transform(source[0], source[1], source[2]);
765         assertNotNull(r2);
766         assertEquals(3, r2.length);
767         assertArrayEquals(expected, r2, 1e-3f);
768     }
769 
770     @Test
testXYZ()771     public void testXYZ() {
772         ColorSpace xyz = ColorSpace.get(ColorSpace.Named.CIE_XYZ);
773 
774         float[] source = { 0.32f, 0.43f, 0.54f };
775 
776         float[] r1 = xyz.toXyz(source[0], source[1], source[2]);
777         assertNotNull(r1);
778         assertEquals(3, r1.length);
779         assertArrayEquals(source, r1, 1e-7f);
780 
781         float[] r2 = xyz.fromXyz(source[0], source[1], source[2]);
782         assertNotNull(r2);
783         assertEquals(3, r2.length);
784         assertArrayEquals(source, r2, 1e-7f);
785 
786         ColorSpace.Connector connector =
787                 ColorSpace.connect(ColorSpace.get(ColorSpace.Named.CIE_XYZ));
788 
789         float[] expected = { 0.2280f, 0.7541f, 0.8453f };
790 
791         float[] r3 = connector.transform(source[0], source[1], source[2]);
792         assertNotNull(r3);
793         assertEquals(3, r3.length);
794         assertArrayEquals(expected, r3, 1e-3f);
795     }
796 
797     @Test
testIDs()798     public void testIDs() {
799         // These cannot change
800         assertEquals(0, ColorSpace.get(ColorSpace.Named.SRGB).getId());
801         assertEquals(-1, ColorSpace.MIN_ID);
802         assertEquals(63, ColorSpace.MAX_ID);
803     }
804 
805     @Test
testFromLinear()806     public void testFromLinear() {
807         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
808 
809         float[] source = { 0.0f, 0.5f, 1.0f };
810         float[] expected = { 0.0f, 0.7354f, 1.0f };
811 
812         float[] r1 = colorSpace.fromLinear(source[0], source[1], source[2]);
813         assertNotNull(r1);
814         assertEquals(3, r1.length);
815         assertArrayEquals(expected, r1, 1e-3f);
816 
817         float[] r2 = { source[0], source[1], source[2] };
818         assertSame(r2, colorSpace.fromLinear(r2));
819         assertEquals(3, r2.length);
820         assertArrayEquals(r1, r2, 1e-5f);
821     }
822 
823     @Test
testToLinear()824     public void testToLinear() {
825         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
826 
827         float[] source = { 0.0f, 0.5f, 1.0f };
828         float[] expected = new float[] { 0.0f, 0.2140f, 1.0f };
829 
830         float[] r1 = colorSpace.toLinear(source[0], source[1], source[2]);
831         assertNotNull(r1);
832         assertEquals(3, r1.length);
833         assertArrayEquals(expected, r1, 1e-3f);
834 
835         float[] r2 = new float[] { source[0], source[1], source[2] };
836         assertSame(r2, colorSpace.toLinear(r2));
837         assertEquals(3, r2.length);
838         assertArrayEquals(r1, r2, 1e-5f);
839     }
840 
841     @Test
testTransferParameters()842     public void testTransferParameters() {
843         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
844         assertNotNull(colorSpace.getTransferParameters());
845 
846         colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
847         assertNotNull(colorSpace.getTransferParameters());
848 
849         colorSpace = new ColorSpace.Rgb("Almost sRGB", SRGB_TO_XYZ,
850                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f));
851         assertNull(colorSpace.getTransferParameters());
852     }
853 
854     @Test
testIdempotentTransferFunctions()855     public void testIdempotentTransferFunctions() {
856         Arrays.stream(ColorSpace.Named.values())
857                 .map(ColorSpace::get)
858                 .filter(cs -> cs.getModel() == ColorSpace.Model.RGB)
859                 .map(cs -> (ColorSpace.Rgb) cs)
860                 .forEach(cs -> {
861                         float[] source = { 0.0f, 0.5f, 1.0f };
862                         float[] r = cs.fromLinear(cs.toLinear(source[0], source[1], source[2]));
863                         assertArrayEquals(source, r, 1e-3f);
864                 });
865     }
866 
867     @Test
testMatch()868     public void testMatch() {
869         for (ColorSpace.Named named : ColorSpace.Named.values()) {
870             ColorSpace cs = ColorSpace.get(named);
871             if (cs.getModel() == ColorSpace.Model.RGB) {
872                 ColorSpace.Rgb rgb = (ColorSpace.Rgb) cs;
873                 // match() cannot match extended sRGB, BT2020_HLG, BT2020_PQ
874                 if (rgb != ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
875                         && rgb != ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)
876                         && rgb != ColorSpace.get(ColorSpace.Named.BT2020_HLG)
877                         && rgb != ColorSpace.get(ColorSpace.Named.BT2020_PQ)) {
878 
879                     // match() uses CIE XYZ D50
880                     rgb = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
881                     assertSame(cs,
882                             ColorSpace.match(rgb.getTransform(), rgb.getTransferParameters()));
883                 }
884             }
885         }
886 
887         assertSame(ColorSpace.get(ColorSpace.Named.SRGB),
888                 ColorSpace.match(SRGB_TO_XYZ_D50, new ColorSpace.Rgb.TransferParameters(
889                         1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4)));
890     }
891 
892     @Test(expected = IllegalArgumentException.class)
893     @Parameters({"0", "-1", "-50"})
testInvalidCct(int cct)894     public void testInvalidCct(int cct) {
895         ColorSpace.cctToXyz(cct);
896     }
897 
898     @Test
testCctToXyz()899     public void testCctToXyz() {
900         // Verify that range listed as meaningful by the API return float arrays as expected.
901         for (int i = 1667; i <= 25000; i++) {
902             float[] result = ColorSpace.cctToXyz(i);
903             assertNotNull(result);
904             assertEquals(3, result.length);
905         }
906     }
907 
cctToXyzExpected()908     private static Object[] cctToXyzExpected() {
909         return new Object[] {
910                 // ILLUMINANT_A
911                 new Object[] { 2856, new float[] { 1.0970824f, 1.0f, 0.3568525f }},
912                 // ILLUMINANT_B
913                 new Object[] { 4874, new float[] { 0.98355806f, 1.0f, 0.8376475f }},
914                 // ILLUMINANT_C
915                 new Object[] { 6774, new float[] { 0.9680535f, 1.0f, 1.1603559f }},
916                 // ILLUMINANT_D50
917                 new Object[] { 5003, new float[] { 0.9811904f, 1.0f, 0.86360276f }},
918                 // ILLUMINANT_D55
919                 new Object[] { 5503, new float[] { 0.97444946f, 1.0f, 0.9582717f }},
920                 // ILLUMINANT_D60
921                 new Object[] { 6004, new float[] { 0.9705604f, 1.0f, 1.0441511f }},
922                 // ILLUMINANT_D65
923                 new Object[] { 6504, new float[] { 0.968573f, 1.0f, 1.1216444f }},
924                 // ILLUMINANT_D75
925                 new Object[] { 7504, new float[] { 0.9679457f, 1.0f, 1.2551404f }},
926                 // ILLUMINANT_E
927                 new Object[] { 5454, new float[] { 0.9749648f, 1.0f, 0.9494016f }},
928                 // Test a sample of values in the meaningful range according to the API.
929                 new Object[] { 1667, new float[] { 1.4014802f, 1.0f, 0.08060435f }},
930                 new Object[] { 1668, new float[] { 1.4010513f, 1.0f, 0.08076303f }},
931                 new Object[] { 1700, new float[] { 1.3874257f, 1.0f, 0.08596305f }},
932                 new Object[] { 1701, new float[] { 1.3870035f, 1.0f, 0.08612958f }},
933                 new Object[] { 2020, new float[] { 1.2686056f, 1.0f, 0.14921218f }},
934                 new Object[] { 2102, new float[] { 1.2439337f, 1.0f, 0.1678791f }},
935                 new Object[] { 2360, new float[] { 1.1796018f, 1.0f, 0.2302558f }},
936                 new Object[] { 4688, new float[] { 0.9875373f, 1.0f, 0.79908675f }},
937                 new Object[] { 5797, new float[] { 0.97189087f, 1.0f, 1.0097121f }},
938                 new Object[] { 7625, new float[] { 0.96806175f, 1.0f, 1.2695707f }},
939                 new Object[] { 8222, new float[] { 0.9690009f, 1.0f, 1.3359972f }},
940                 new Object[] { 8330, new float[] { 0.9692224f, 1.0f, 1.3472213f }},
941                 new Object[] { 9374, new float[] { 0.9718307f, 1.0f, 1.4447508f }},
942                 new Object[] { 9604, new float[] { 0.97247595f, 1.0f, 1.4638413f }},
943                 new Object[] { 9894, new float[] { 0.9733059f, 1.0f, 1.4868189f }},
944                 new Object[] { 10764, new float[] { 0.97584003f, 1.0f, 1.5491791f }},
945                 new Object[] { 11735, new float[] { 0.97862047f, 1.0f, 1.6088297f }},
946                 new Object[] { 12819, new float[] { 0.98155034f, 1.0f, 1.6653923f }},
947                 new Object[] { 13607, new float[] { 0.98353446f, 1.0f, 1.7010691f }},
948                 new Object[] { 15185, new float[] { 0.98712224f, 1.0f, 1.7615601f }},
949                 new Object[] { 17474, new float[] { 0.9914801f, 1.0f, 1.8297766f }},
950                 new Object[] { 18788, new float[] { 0.9935937f, 1.0f, 1.8612393f }},
951                 new Object[] { 19119, new float[] { 0.99408686f, 1.0f, 1.8684553f }},
952                 new Object[] { 19174, new float[] { 0.99416786f, 1.0f, 1.8696303f }},
953                 new Object[] { 19437, new float[] { 0.9945476f, 1.0f, 1.8751476f }},
954                 new Object[] { 19533, new float[] { 0.99468416f, 1.0f, 1.8771234f }},
955                 new Object[] { 19548, new float[] { 0.99470526f, 1.0f, 1.8774294f }},
956                 new Object[] { 19762, new float[] { 0.995005f, 1.0f, 1.8817542f }},
957                 new Object[] { 19774, new float[] { 0.9950216f, 1.0f, 1.8819935f }},
958                 new Object[] { 20291, new float[] { 0.99572146f, 1.0f, 1.8920314f }},
959                 new Object[] { 23018, new float[] { 0.99893945f, 1.0f, 1.9371331f }},
960                 new Object[] { 23509, new float[] { 0.999445f, 1.0f, 1.9440757f }},
961                 new Object[] { 24761, new float[] { 1.0006485f, 1.0f, 1.9604537f }},
962 
963         };
964     }
965 
966     @Test
967     @Parameters(method = "cctToXyzExpected")
testCctToXyzValues(int cct, float[] xyz)968     public void testCctToXyzValues(int cct, float[] xyz) {
969         float[] result = ColorSpace.cctToXyz(cct);
970         assertArrayEquals(xyz, result, 1e-3f);
971     }
972 
chromaticAdaptationNullParameters()973     private static Object[] chromaticAdaptationNullParameters() {
974         return new Object[] {
975                 new Object[] { null, ColorSpace.ILLUMINANT_D50, ColorSpace.ILLUMINANT_D60 },
976                 new Object[] { ColorSpace.Adaptation.BRADFORD, null, ColorSpace.ILLUMINANT_D60 },
977                 new Object[] { ColorSpace.Adaptation.BRADFORD, ColorSpace.ILLUMINANT_D60, null },
978         };
979     }
980 
981     @Test(expected = NullPointerException.class)
982     @Parameters(method = "chromaticAdaptationNullParameters")
testChromaticAdaptationNullParameters(ColorSpace.Adaptation adaptation, float[] srcWhitePoint, float[] dstWhitePoint)983     public void testChromaticAdaptationNullParameters(ColorSpace.Adaptation adaptation,
984             float[] srcWhitePoint, float[] dstWhitePoint) {
985         ColorSpace.chromaticAdaptation(adaptation, srcWhitePoint, dstWhitePoint);
986     }
987 
chromaticAdaptationWrongSizedArrays()988     private static Object[] chromaticAdaptationWrongSizedArrays() {
989         return new Object[] {
990                 new Object[] { ColorSpace.Adaptation.BRADFORD, new float[] { 1.0f },
991                         ColorSpace.ILLUMINANT_D60 },
992                 new Object[] { ColorSpace.Adaptation.BRADFORD, ColorSpace.ILLUMINANT_D60,
993                         new float[] { 1.0f, 1.0f, 1.0f, 1.0f }},
994         };
995     }
996 
997     @Test(expected = IllegalArgumentException.class)
998     @Parameters(method = "chromaticAdaptationWrongSizedArrays")
testChromaticAdaptationWrongSizedArrays(ColorSpace.Adaptation adaptation, float[] srcWhitePoint, float[] dstWhitePoint)999     public void testChromaticAdaptationWrongSizedArrays(ColorSpace.Adaptation adaptation,
1000             float[] srcWhitePoint, float[] dstWhitePoint) {
1001         ColorSpace.chromaticAdaptation(adaptation, srcWhitePoint, dstWhitePoint);
1002     }
1003 
1004     private static float[] sIdentityMatrix = new float[] {
1005             1.0f, 0.0f, 0.0f,
1006             0.0f, 1.0f, 0.0f,
1007             0.0f, 0.0f, 1.0f
1008     };
1009 
1010     @Test
testChromaticAdaptation()1011     public void testChromaticAdaptation() {
1012         for (ColorSpace.Adaptation adaptation : ColorSpace.Adaptation.values()) {
1013             float[][] whitePoints = {
1014                     ColorSpace.ILLUMINANT_A,
1015                     ColorSpace.ILLUMINANT_B,
1016                     ColorSpace.ILLUMINANT_C,
1017                     ColorSpace.ILLUMINANT_D50,
1018                     ColorSpace.ILLUMINANT_D55,
1019                     ColorSpace.ILLUMINANT_D60,
1020                     ColorSpace.ILLUMINANT_D65,
1021                     ColorSpace.ILLUMINANT_D75,
1022                     ColorSpace.ILLUMINANT_E,
1023             };
1024             for (float[] srcWhitePoint : whitePoints) {
1025                 for (float[] dstWhitePoint : whitePoints) {
1026                     float[] result = ColorSpace.chromaticAdaptation(adaptation, srcWhitePoint,
1027                             dstWhitePoint);
1028                     assertNotNull(result);
1029                     assertEquals(9, result.length);
1030                     if (Arrays.equals(srcWhitePoint, dstWhitePoint)) {
1031                         assertArrayEquals(sIdentityMatrix, result, 0f);
1032                     }
1033                 }
1034             }
1035         }
1036     }
1037 
1038     @Test
getDataSpaceFromColorSpace()1039     public void getDataSpaceFromColorSpace() {
1040         ColorSpace cs = ColorSpace.get(ColorSpace.Named.BT709);
1041         assertNotNull(cs);
1042         assertEquals(DataSpace.DATASPACE_BT709, cs.getDataSpace());
1043 
1044         cs = ColorSpace.get(ColorSpace.Named.ACES);
1045         assertEquals(DataSpace.DATASPACE_UNKNOWN, cs.getDataSpace());
1046     }
1047 
1048     @Test
getColorSpaceFromDataSpace()1049     public void getColorSpaceFromDataSpace() {
1050         ColorSpace cs = ColorSpace.getFromDataSpace(DataSpace.DATASPACE_SRGB);
1051         assertNotNull(cs);
1052         assertEquals(DataSpace.DATASPACE_SRGB, cs.getDataSpace());
1053 
1054         assertNull(ColorSpace.getFromDataSpace(DataSpace.DATASPACE_JFIF));
1055     }
1056 
1057     @SuppressWarnings("SameParameterValue")
assertArrayNotEquals(float[] a, float[] b, float eps)1058     private void assertArrayNotEquals(float[] a, float[] b, float eps) {
1059         for (int i = 0; i < a.length; i++) {
1060             if (Float.compare(a[i], b[i]) == 0 || Math.abs(a[i] - b[i]) < eps) {
1061                 fail("Expected " + a[i] + ", received " + b[i]);
1062             }
1063         }
1064     }
1065 
assertArrayEquals(float[] a, float[] b, float eps)1066     private void assertArrayEquals(float[] a, float[] b, float eps) {
1067         for (int i = 0; i < a.length; i++) {
1068             if (Float.compare(a[i], b[i]) != 0 && Math.abs(a[i] - b[i]) > eps) {
1069                 fail("Expected " + a[i] + ", received " + b[i]);
1070             }
1071         }
1072     }
1073 }
1074