Home | History | Annotate | Download | only in cts
      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.support.test.filters.SmallTest;
     28 import android.support.test.runner.AndroidJUnit4;
     29 
     30 import org.junit.Test;
     31 import org.junit.runner.RunWith;
     32 
     33 import java.util.Arrays;
     34 import java.util.function.DoubleUnaryOperator;
     35 
     36 @SmallTest
     37 @RunWith(AndroidJUnit4.class)
     38 public class ColorSpaceTest {
     39     // Column-major RGB->XYZ transform matrix for the sRGB color space
     40     private static final float[] SRGB_TO_XYZ = {
     41             0.412391f, 0.212639f, 0.019331f,
     42             0.357584f, 0.715169f, 0.119195f,
     43             0.180481f, 0.072192f, 0.950532f
     44     };
     45     // Column-major XYZ->RGB transform matrix for the sRGB color space
     46     private static final float[] XYZ_TO_SRGB = {
     47             3.240970f, -0.969244f,  0.055630f,
     48            -1.537383f,  1.875968f, -0.203977f,
     49            -0.498611f,  0.041555f,  1.056971f
     50     };
     51 
     52     // Column-major RGB->XYZ transform matrix for the sRGB color space and a D50 white point
     53     private static final float[] SRGB_TO_XYZ_D50 = {
     54             0.4360747f, 0.2225045f, 0.0139322f,
     55             0.3850649f, 0.7168786f, 0.0971045f,
     56             0.1430804f, 0.0606169f, 0.7141733f
     57     };
     58 
     59     private static final float[] SRGB_PRIMARIES_xyY =
     60             { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
     61     private static final float[] SRGB_WHITE_POINT_xyY = { 0.3127f, 0.3290f };
     62 
     63     private static final float[] SRGB_PRIMARIES_XYZ = {
     64             1.939394f, 1.000000f, 0.090909f,
     65             0.500000f, 1.000000f, 0.166667f,
     66             2.500000f, 1.000000f, 13.166667f
     67     };
     68     private static final float[] SRGB_WHITE_POINT_XYZ = { 0.950456f, 1.000f, 1.089058f };
     69 
     70     private static final DoubleUnaryOperator sIdentity = DoubleUnaryOperator.identity();
     71 
     72     @Test
     73     public void testNamedColorSpaces() {
     74         for (ColorSpace.Named named : ColorSpace.Named.values()) {
     75             ColorSpace colorSpace = ColorSpace.get(named);
     76             assertNotNull(colorSpace.getName());
     77             assertNotNull(colorSpace);
     78             assertEquals(named.ordinal(), colorSpace.getId());
     79             assertTrue(colorSpace.getComponentCount() >= 1);
     80             assertTrue(colorSpace.getComponentCount() <= 4);
     81         }
     82     }
     83 
     84     @Test(expected = IllegalArgumentException.class)
     85     public void testNullName() {
     86         new ColorSpace.Rgb(null, new float[6], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
     87     }
     88 
     89     @Test(expected = IllegalArgumentException.class)
     90     public void testEmptyName() {
     91         new ColorSpace.Rgb("", new float[6], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
     92     }
     93 
     94     @Test
     95     public void testName() {
     96         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
     97                 sIdentity, sIdentity, 0.0f, 1.0f);
     98         assertEquals("Test", cs.getName());
     99     }
    100 
    101     @Test(expected = IllegalArgumentException.class)
    102     public void testPrimariesLength() {
    103         new ColorSpace.Rgb("Test", new float[7], new float[2], sIdentity, sIdentity, 0.0f, 1.0f);
    104     }
    105 
    106     @Test(expected = IllegalArgumentException.class)
    107     public void testWhitePointLength() {
    108         new ColorSpace.Rgb("Test", new float[6], new float[1], sIdentity, sIdentity, 0.0f, 1.0f);
    109     }
    110 
    111     @Test(expected = IllegalArgumentException.class)
    112     public void testNullOETF() {
    113         new ColorSpace.Rgb("Test", new float[6], new float[2], null, sIdentity, 0.0f, 1.0f);
    114     }
    115 
    116     @Test
    117     public void testOETF() {
    118         DoubleUnaryOperator op = Math::sqrt;
    119         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
    120                 op, sIdentity, 0.0f, 1.0f);
    121         assertEquals(0.5, cs.getOetf().applyAsDouble(0.25), 1e-5);
    122     }
    123 
    124     @Test(expected = IllegalArgumentException.class)
    125     public void testNullEOTF() {
    126         new ColorSpace.Rgb("Test", new float[6], new float[2], sIdentity, null, 0.0f, 1.0f);
    127     }
    128 
    129     @Test
    130     public void testEOTF() {
    131         DoubleUnaryOperator op = x -> x * x;
    132         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", new float[6], new float[2],
    133                 sIdentity, op, 0.0f, 1.0f);
    134         assertEquals(0.0625, cs.getEotf().applyAsDouble(0.25), 1e-5);
    135     }
    136 
    137     @Test(expected = IllegalArgumentException.class)
    138     public void testInvalidRange() {
    139         new ColorSpace.Rgb("Test", new float[6], new float[2], sIdentity, sIdentity, 2.0f, 1.0f);
    140     }
    141 
    142     @Test
    143     public void testRanges() {
    144         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
    145 
    146         float m1 = cs.getMinValue(0);
    147         float m2 = cs.getMinValue(1);
    148         float m3 = cs.getMinValue(2);
    149 
    150         assertEquals(0.0f, m1, 1e-9f);
    151         assertEquals(0.0f, m2, 1e-9f);
    152         assertEquals(0.0f, m3, 1e-9f);
    153 
    154         m1 = cs.getMaxValue(0);
    155         m2 = cs.getMaxValue(1);
    156         m3 = cs.getMaxValue(2);
    157 
    158         assertEquals(1.0f, m1, 1e-9f);
    159         assertEquals(1.0f, m2, 1e-9f);
    160         assertEquals(1.0f, m3, 1e-9f);
    161 
    162         cs = ColorSpace.get(ColorSpace.Named.CIE_LAB);
    163 
    164         m1 = cs.getMinValue(0);
    165         m2 = cs.getMinValue(1);
    166         m3 = cs.getMinValue(2);
    167 
    168         assertEquals(0.0f, m1, 1e-9f);
    169         assertEquals(-128.0f, m2, 1e-9f);
    170         assertEquals(-128.0f, m3, 1e-9f);
    171 
    172         m1 = cs.getMaxValue(0);
    173         m2 = cs.getMaxValue(1);
    174         m3 = cs.getMaxValue(2);
    175 
    176         assertEquals(100.0f, m1, 1e-9f);
    177         assertEquals(128.0f, m2, 1e-9f);
    178         assertEquals(128.0f, m3, 1e-9f);
    179 
    180         cs = ColorSpace.get(ColorSpace.Named.CIE_XYZ);
    181 
    182         m1 = cs.getMinValue(0);
    183         m2 = cs.getMinValue(1);
    184         m3 = cs.getMinValue(2);
    185 
    186         assertEquals(-2.0f, m1, 1e-9f);
    187         assertEquals(-2.0f, m2, 1e-9f);
    188         assertEquals(-2.0f, m3, 1e-9f);
    189 
    190         m1 = cs.getMaxValue(0);
    191         m2 = cs.getMaxValue(1);
    192         m3 = cs.getMaxValue(2);
    193 
    194         assertEquals(2.0f, m1, 1e-9f);
    195         assertEquals(2.0f, m2, 1e-9f);
    196         assertEquals(2.0f, m3, 1e-9f);
    197     }
    198 
    199     @Test
    200     public void testMat3x3() {
    201         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
    202 
    203         float[] rgbToXYZ = cs.getTransform();
    204         for (int i = 0; i < 9; i++) {
    205             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
    206         }
    207     }
    208 
    209     @Test
    210     public void testMat3x3Inverse() {
    211         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
    212 
    213         float[] xyzToRGB = cs.getInverseTransform();
    214         for (int i = 0; i < 9; i++) {
    215             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
    216         }
    217     }
    218 
    219     @Test
    220     public void testMat3x3Primaries() {
    221         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
    222 
    223         float[] primaries = cs.getPrimaries();
    224 
    225         assertNotNull(primaries);
    226         assertEquals(6, primaries.length);
    227 
    228         assertEquals(SRGB_PRIMARIES_xyY[0], primaries[0], 1e-5f);
    229         assertEquals(SRGB_PRIMARIES_xyY[1], primaries[1], 1e-5f);
    230         assertEquals(SRGB_PRIMARIES_xyY[2], primaries[2], 1e-5f);
    231         assertEquals(SRGB_PRIMARIES_xyY[3], primaries[3], 1e-5f);
    232         assertEquals(SRGB_PRIMARIES_xyY[4], primaries[4], 1e-5f);
    233         assertEquals(SRGB_PRIMARIES_xyY[5], primaries[5], 1e-5f);
    234     }
    235 
    236     @Test
    237     public void testMat3x3WhitePoint() {
    238         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_TO_XYZ, sIdentity, sIdentity);
    239 
    240         float[] whitePoint = cs.getWhitePoint();
    241 
    242         assertNotNull(whitePoint);
    243         assertEquals(2, whitePoint.length);
    244 
    245         assertEquals(SRGB_WHITE_POINT_xyY[0], whitePoint[0], 1e-5f);
    246         assertEquals(SRGB_WHITE_POINT_xyY[1], whitePoint[1], 1e-5f);
    247     }
    248 
    249     @Test
    250     public void testXYZFromPrimaries_xyY() {
    251         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_PRIMARIES_xyY, SRGB_WHITE_POINT_xyY,
    252                 sIdentity, sIdentity, 0.0f, 1.0f);
    253 
    254         float[] rgbToXYZ = cs.getTransform();
    255         for (int i = 0; i < 9; i++) {
    256             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
    257         }
    258 
    259         float[] xyzToRGB = cs.getInverseTransform();
    260         for (int i = 0; i < 9; i++) {
    261             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
    262         }
    263     }
    264 
    265     @Test
    266     public void testXYZFromPrimaries_XYZ() {
    267         ColorSpace.Rgb cs = new ColorSpace.Rgb("Test", SRGB_PRIMARIES_XYZ, SRGB_WHITE_POINT_XYZ,
    268                 sIdentity, sIdentity, 0.0f, 1.0f);
    269 
    270         float[] primaries = cs.getPrimaries();
    271 
    272         assertNotNull(primaries);
    273         assertEquals(6, primaries.length);
    274 
    275         // SRGB_PRIMARIES_xyY only has 1e-3 of precision, match it
    276         assertEquals(SRGB_PRIMARIES_xyY[0], primaries[0], 1e-3f);
    277         assertEquals(SRGB_PRIMARIES_xyY[1], primaries[1], 1e-3f);
    278         assertEquals(SRGB_PRIMARIES_xyY[2], primaries[2], 1e-3f);
    279         assertEquals(SRGB_PRIMARIES_xyY[3], primaries[3], 1e-3f);
    280         assertEquals(SRGB_PRIMARIES_xyY[4], primaries[4], 1e-3f);
    281         assertEquals(SRGB_PRIMARIES_xyY[5], primaries[5], 1e-3f);
    282 
    283         float[] whitePoint = cs.getWhitePoint();
    284 
    285         assertNotNull(whitePoint);
    286         assertEquals(2, whitePoint.length);
    287 
    288         // SRGB_WHITE_POINT_xyY only has 1e-3 of precision, match it
    289         assertEquals(SRGB_WHITE_POINT_xyY[0], whitePoint[0], 1e-3f);
    290         assertEquals(SRGB_WHITE_POINT_xyY[1], whitePoint[1], 1e-3f);
    291 
    292         float[] rgbToXYZ = cs.getTransform();
    293         for (int i = 0; i < 9; i++) {
    294             assertEquals(SRGB_TO_XYZ[i], rgbToXYZ[i], 1e-5f);
    295         }
    296 
    297         float[] xyzToRGB = cs.getInverseTransform();
    298         for (int i = 0; i < 9; i++) {
    299             assertEquals(XYZ_TO_SRGB[i], xyzToRGB[i], 1e-5f);
    300         }
    301     }
    302 
    303     @Test
    304     public void testGetComponentCount() {
    305         assertEquals(3, ColorSpace.get(ColorSpace.Named.SRGB).getComponentCount());
    306         assertEquals(3, ColorSpace.get(ColorSpace.Named.LINEAR_SRGB).getComponentCount());
    307         assertEquals(3, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB).getComponentCount());
    308         assertEquals(3, ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB).getComponentCount());
    309         assertEquals(3, ColorSpace.get(ColorSpace.Named.DISPLAY_P3).getComponentCount());
    310         assertEquals(3, ColorSpace.get(ColorSpace.Named.CIE_LAB).getComponentCount());
    311         assertEquals(3, ColorSpace.get(ColorSpace.Named.CIE_XYZ).getComponentCount());
    312     }
    313 
    314     @Test
    315     public void testIsSRGB() {
    316         assertTrue(ColorSpace.get(ColorSpace.Named.SRGB).isSrgb());
    317         assertFalse(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB).isSrgb());
    318         assertFalse(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB).isSrgb());
    319         assertFalse(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB).isSrgb());
    320         assertFalse(ColorSpace.get(ColorSpace.Named.DISPLAY_P3).isSrgb());
    321         assertFalse(ColorSpace.get(ColorSpace.Named.CIE_LAB).isSrgb());
    322         assertFalse(ColorSpace.get(ColorSpace.Named.CIE_XYZ).isSrgb());
    323 
    324         ColorSpace.Rgb cs = new ColorSpace.Rgb("My sRGB", SRGB_TO_XYZ,
    325                 x -> Math.pow(x, 1.0f / 2.2f), x -> Math.pow(x, 2.2f));
    326         assertTrue(cs.isSrgb());
    327     }
    328 
    329     @Test
    330     public void testIsWideGamut() {
    331         assertFalse(ColorSpace.get(ColorSpace.Named.SRGB).isWideGamut());
    332         assertFalse(ColorSpace.get(ColorSpace.Named.BT709).isWideGamut());
    333         assertTrue(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB).isWideGamut());
    334         assertTrue(ColorSpace.get(ColorSpace.Named.DCI_P3).isWideGamut());
    335         assertTrue(ColorSpace.get(ColorSpace.Named.BT2020).isWideGamut());
    336         assertTrue(ColorSpace.get(ColorSpace.Named.ACES).isWideGamut());
    337         assertTrue(ColorSpace.get(ColorSpace.Named.CIE_LAB).isWideGamut());
    338         assertTrue(ColorSpace.get(ColorSpace.Named.CIE_XYZ).isWideGamut());
    339     }
    340 
    341     @Test
    342     public void testWhitePoint() {
    343         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    344 
    345         float[] whitePoint = cs.getWhitePoint();
    346 
    347         assertNotNull(whitePoint);
    348         assertEquals(2, whitePoint.length);
    349 
    350         // Make sure a copy is returned
    351         Arrays.fill(whitePoint, Float.NaN);
    352         assertArrayNotEquals(whitePoint, cs.getWhitePoint(), 1e-5f);
    353         assertSame(whitePoint, cs.getWhitePoint(whitePoint));
    354         assertArrayEquals(whitePoint, cs.getWhitePoint(), 1e-5f);
    355     }
    356 
    357     @Test
    358     public void testPrimaries() {
    359         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    360 
    361         float[] primaries = cs.getPrimaries();
    362 
    363         assertNotNull(primaries);
    364         assertEquals(6, primaries.length);
    365 
    366         // Make sure a copy is returned
    367         Arrays.fill(primaries, Float.NaN);
    368         assertArrayNotEquals(primaries, cs.getPrimaries(), 1e-5f);
    369         assertSame(primaries, cs.getPrimaries(primaries));
    370         assertArrayEquals(primaries, cs.getPrimaries(), 1e-5f);
    371     }
    372 
    373     @Test
    374     public void testRGBtoXYZMatrix() {
    375         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    376 
    377         float[] rgbToXYZ = cs.getTransform();
    378 
    379         assertNotNull(rgbToXYZ);
    380         assertEquals(9, rgbToXYZ.length);
    381 
    382         // Make sure a copy is returned
    383         Arrays.fill(rgbToXYZ, Float.NaN);
    384         assertArrayNotEquals(rgbToXYZ, cs.getTransform(), 1e-5f);
    385         assertSame(rgbToXYZ, cs.getTransform(rgbToXYZ));
    386         assertArrayEquals(rgbToXYZ, cs.getTransform(), 1e-5f);
    387     }
    388 
    389     @Test
    390     public void testXYZtoRGBMatrix() {
    391         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    392 
    393         float[] xyzToRGB = cs.getInverseTransform();
    394 
    395         assertNotNull(xyzToRGB);
    396         assertEquals(9, xyzToRGB.length);
    397 
    398         // Make sure a copy is returned
    399         Arrays.fill(xyzToRGB, Float.NaN);
    400         assertArrayNotEquals(xyzToRGB, cs.getInverseTransform(), 1e-5f);
    401         assertSame(xyzToRGB, cs.getInverseTransform(xyzToRGB));
    402         assertArrayEquals(xyzToRGB, cs.getInverseTransform(), 1e-5f);
    403     }
    404 
    405     @Test
    406     public void testRGBtoXYZ() {
    407         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
    408 
    409         float[] source = { 0.75f, 0.5f, 0.25f };
    410         float[] expected = { 0.3012f, 0.2679f, 0.0840f };
    411 
    412         float[] r1 = cs.toXyz(source[0], source[1], source[2]);
    413         assertNotNull(r1);
    414         assertEquals(3, r1.length);
    415         assertArrayNotEquals(source, r1, 1e-5f);
    416         assertArrayEquals(expected, r1, 1e-3f);
    417 
    418         float[] r3 = { source[0], source[1], source[2] };
    419         assertSame(r3, cs.toXyz(r3));
    420         assertEquals(3, r3.length);
    421         assertArrayEquals(r1, r3, 1e-5f);
    422     }
    423 
    424     @Test
    425     public void testXYZtoRGB() {
    426         ColorSpace cs = ColorSpace.get(ColorSpace.Named.SRGB);
    427 
    428         float[] source = { 0.3012f, 0.2679f, 0.0840f };
    429         float[] expected = { 0.75f, 0.5f, 0.25f };
    430 
    431         float[] r1 = cs.fromXyz(source[0], source[1], source[2]);
    432         assertNotNull(r1);
    433         assertEquals(3, r1.length);
    434         assertArrayNotEquals(source, r1, 1e-5f);
    435         assertArrayEquals(expected, r1, 1e-3f);
    436 
    437         float[] r3 = { source[0], source[1], source[2] };
    438         assertSame(r3, cs.fromXyz(r3));
    439         assertEquals(3, r3.length);
    440         assertArrayEquals(r1, r3, 1e-5f);
    441     }
    442 
    443     @Test
    444     public void testConnect() {
    445         ColorSpace.Connector connector = ColorSpace.connect(
    446                 ColorSpace.get(ColorSpace.Named.SRGB),
    447                 ColorSpace.get(ColorSpace.Named.DCI_P3));
    448 
    449         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector.getSource());
    450         assertSame(ColorSpace.get(ColorSpace.Named.DCI_P3), connector.getDestination());
    451         assertSame(ColorSpace.RenderIntent.PERCEPTUAL, connector.getRenderIntent());
    452 
    453         connector = ColorSpace.connect(
    454                 ColorSpace.get(ColorSpace.Named.SRGB),
    455                 ColorSpace.get(ColorSpace.Named.SRGB));
    456 
    457         assertSame(connector.getDestination(), connector.getSource());
    458         assertSame(ColorSpace.RenderIntent.RELATIVE, connector.getRenderIntent());
    459 
    460         connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.DCI_P3));
    461         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector.getDestination());
    462 
    463         connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.SRGB));
    464         assertSame(connector.getSource(), connector.getDestination());
    465     }
    466 
    467     @Test
    468     public void testConnector() {
    469         // Connect color spaces with same white points
    470         ColorSpace.Connector connector = ColorSpace.connect(
    471                 ColorSpace.get(ColorSpace.Named.SRGB),
    472                 ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
    473 
    474         float[] source = { 1.0f, 0.5f, 0.0f };
    475         float[] expected = { 0.8912f, 0.4962f, 0.1164f };
    476 
    477         float[] r1 = connector.transform(source[0], source[1], source[2]);
    478         assertNotNull(r1);
    479         assertEquals(3, r1.length);
    480         assertArrayNotEquals(source, r1, 1e-5f);
    481         assertArrayEquals(expected, r1, 1e-3f);
    482 
    483         float[] r3 = { source[0], source[1], source[2] };
    484         assertSame(r3, connector.transform(r3));
    485         assertEquals(3, r3.length);
    486         assertArrayEquals(r1, r3, 1e-5f);
    487 
    488         connector = ColorSpace.connect(
    489                 ColorSpace.get(ColorSpace.Named.ADOBE_RGB),
    490                 ColorSpace.get(ColorSpace.Named.SRGB));
    491 
    492         float[] tmp = source;
    493         source = expected;
    494         expected = tmp;
    495 
    496         r1 = connector.transform(source[0], source[1], source[2]);
    497         assertNotNull(r1);
    498         assertEquals(3, r1.length);
    499         assertArrayNotEquals(source, r1, 1e-5f);
    500         assertArrayEquals(expected, r1, 1e-3f);
    501 
    502         r3 = new float[] { source[0], source[1], source[2] };
    503         assertSame(r3, connector.transform(r3));
    504         assertEquals(3, r3.length);
    505         assertArrayEquals(r1, r3, 1e-5f);
    506     }
    507 
    508     @Test
    509     public void testAdaptedConnector() {
    510         // Connect color spaces with different white points
    511         ColorSpace.Connector connector = ColorSpace.connect(
    512                 ColorSpace.get(ColorSpace.Named.SRGB),
    513                 ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB));
    514 
    515         float[] source = new float[] { 1.0f, 0.0f, 0.0f };
    516         float[] expected = new float[] { 0.70226f, 0.2757f, 0.1036f };
    517 
    518         float[] r = connector.transform(source[0], source[1], source[2]);
    519         assertNotNull(r);
    520         assertEquals(3, r.length);
    521         assertArrayNotEquals(source, r, 1e-5f);
    522         assertArrayEquals(expected, r, 1e-4f);
    523     }
    524 
    525     @Test
    526     public void testAdaptedConnectorWithRenderIntent() {
    527         // Connect a wider color space to a narrow color space
    528         ColorSpace.Connector connector = ColorSpace.connect(
    529                 ColorSpace.get(ColorSpace.Named.DCI_P3),
    530                 ColorSpace.get(ColorSpace.Named.SRGB),
    531                 ColorSpace.RenderIntent.RELATIVE);
    532 
    533         float[] source = { 0.9f, 0.9f, 0.9f };
    534 
    535         float[] relative = connector.transform(source[0], source[1], source[2]);
    536         assertNotNull(relative);
    537         assertEquals(3, relative.length);
    538         assertArrayNotEquals(source, relative, 1e-5f);
    539         assertArrayEquals(new float[] { 0.8862f, 0.8862f, 0.8862f }, relative, 1e-4f);
    540 
    541         connector = ColorSpace.connect(
    542                 ColorSpace.get(ColorSpace.Named.DCI_P3),
    543                 ColorSpace.get(ColorSpace.Named.SRGB),
    544                 ColorSpace.RenderIntent.ABSOLUTE);
    545 
    546         float[] absolute = connector.transform(source[0], source[1], source[2]);
    547         assertNotNull(absolute);
    548         assertEquals(3, absolute.length);
    549         assertArrayNotEquals(source, absolute, 1e-5f);
    550         assertArrayNotEquals(relative, absolute, 1e-5f);
    551         assertArrayEquals(new float[] { 0.8475f, 0.9217f, 0.8203f }, absolute, 1e-4f);
    552     }
    553 
    554     @Test
    555     public void testIdentityConnector() {
    556         ColorSpace.Connector connector = ColorSpace.connect(
    557                 ColorSpace.get(ColorSpace.Named.SRGB),
    558                 ColorSpace.get(ColorSpace.Named.SRGB));
    559 
    560         assertSame(connector.getSource(), connector.getDestination());
    561         assertSame(ColorSpace.RenderIntent.RELATIVE, connector.getRenderIntent());
    562 
    563         float[] source = new float[] { 0.11112f, 0.22227f, 0.444448f };
    564 
    565         float[] r = connector.transform(source[0], source[1], source[2]);
    566         assertNotNull(r);
    567         assertEquals(3, r.length);
    568         assertArrayEquals(source, r, 1e-5f);
    569     }
    570 
    571     @Test
    572     public void testConnectorTransformIdentity() {
    573         ColorSpace.Connector connector = ColorSpace.connect(
    574                 ColorSpace.get(ColorSpace.Named.DCI_P3),
    575                 ColorSpace.get(ColorSpace.Named.DCI_P3));
    576 
    577         float[] source = { 1.0f, 0.0f, 0.0f };
    578         float[] expected = { 1.0f, 0.0f, 0.0f };
    579 
    580         float[] r1 = connector.transform(source[0], source[1], source[2]);
    581         assertNotNull(r1);
    582         assertEquals(3, r1.length);
    583         assertArrayEquals(expected, r1, 1e-3f);
    584 
    585         float[] r3 = { source[0], source[1], source[2] };
    586         assertSame(r3, connector.transform(r3));
    587         assertEquals(3, r3.length);
    588         assertArrayEquals(r1, r3, 1e-5f);
    589     }
    590 
    591     @Test
    592     public void testAdaptation() {
    593         ColorSpace adapted = ColorSpace.adapt(
    594                 ColorSpace.get(ColorSpace.Named.SRGB),
    595                 ColorSpace.ILLUMINANT_D50);
    596 
    597         float[] sRGBD50 = {
    598                 0.43602175f, 0.22247513f, 0.01392813f,
    599                 0.38510883f, 0.71690667f, 0.09710153f,
    600                 0.14308129f, 0.06061824f, 0.71415880f
    601         };
    602 
    603         assertArrayEquals(sRGBD50, ((ColorSpace.Rgb) adapted).getTransform(), 1e-7f);
    604 
    605         adapted = ColorSpace.adapt(
    606                 ColorSpace.get(ColorSpace.Named.SRGB),
    607                 ColorSpace.ILLUMINANT_D50,
    608                 ColorSpace.Adaptation.BRADFORD);
    609         assertArrayEquals(sRGBD50, ((ColorSpace.Rgb) adapted).getTransform(), 1e-7f);
    610     }
    611 
    612     @Test
    613     public void testImplicitSRGBConnector() {
    614         ColorSpace.Connector connector1 = ColorSpace.connect(
    615                 ColorSpace.get(ColorSpace.Named.DCI_P3));
    616 
    617         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), connector1.getDestination());
    618 
    619         ColorSpace.Connector connector2 = ColorSpace.connect(
    620                 ColorSpace.get(ColorSpace.Named.DCI_P3),
    621                 ColorSpace.get(ColorSpace.Named.SRGB));
    622 
    623         float[] source = { 0.6f, 0.9f, 0.7f };
    624         assertArrayEquals(
    625                 connector1.transform(source[0], source[1], source[2]),
    626                 connector2.transform(source[0], source[1], source[2]), 1e-7f);
    627     }
    628 
    629     @Test
    630     public void testLab() {
    631         ColorSpace.Connector connector = ColorSpace.connect(
    632                 ColorSpace.get(ColorSpace.Named.CIE_LAB));
    633 
    634         float[] source = { 100.0f, 0.0f, 0.0f };
    635         float[] expected = { 1.0f, 1.0f, 1.0f };
    636 
    637         float[] r1 = connector.transform(source[0], source[1], source[2]);
    638         assertNotNull(r1);
    639         assertEquals(3, r1.length);
    640         assertArrayEquals(expected, r1, 1e-3f);
    641 
    642         source = new float[] { 100.0f, 0.0f, 54.0f };
    643         expected = new float[] { 1.0f, 0.9925f, 0.5762f };
    644 
    645         float[] r2 = connector.transform(source[0], source[1], source[2]);
    646         assertNotNull(r2);
    647         assertEquals(3, r2.length);
    648         assertArrayEquals(expected, r2, 1e-3f);
    649 
    650         connector = ColorSpace.connect(
    651                 ColorSpace.get(ColorSpace.Named.CIE_LAB), ColorSpace.RenderIntent.ABSOLUTE);
    652 
    653         source = new float[] { 100.0f, 0.0f, 0.0f };
    654         expected = new float[] { 1.0f, 0.9910f, 0.8651f };
    655 
    656         r1 = connector.transform(source[0], source[1], source[2]);
    657         assertNotNull(r1);
    658         assertEquals(3, r1.length);
    659         assertArrayEquals(expected, r1, 1e-3f);
    660 
    661         source = new float[] { 100.0f, 0.0f, 54.0f };
    662         expected = new float[] { 1.0f, 0.9853f, 0.4652f };
    663 
    664         r2 = connector.transform(source[0], source[1], source[2]);
    665         assertNotNull(r2);
    666         assertEquals(3, r2.length);
    667         assertArrayEquals(expected, r2, 1e-3f);
    668     }
    669 
    670     @Test
    671     public void testXYZ() {
    672         ColorSpace xyz = ColorSpace.get(ColorSpace.Named.CIE_XYZ);
    673 
    674         float[] source = { 0.32f, 0.43f, 0.54f };
    675 
    676         float[] r1 = xyz.toXyz(source[0], source[1], source[2]);
    677         assertNotNull(r1);
    678         assertEquals(3, r1.length);
    679         assertArrayEquals(source, r1, 1e-7f);
    680 
    681         float[] r2 = xyz.fromXyz(source[0], source[1], source[2]);
    682         assertNotNull(r2);
    683         assertEquals(3, r2.length);
    684         assertArrayEquals(source, r2, 1e-7f);
    685 
    686         ColorSpace.Connector connector =
    687                 ColorSpace.connect(ColorSpace.get(ColorSpace.Named.CIE_XYZ));
    688 
    689         float[] expected = { 0.2280f, 0.7541f, 0.8453f };
    690 
    691         float[] r3 = connector.transform(source[0], source[1], source[2]);
    692         assertNotNull(r3);
    693         assertEquals(3, r3.length);
    694         assertArrayEquals(expected, r3, 1e-3f);
    695     }
    696 
    697     @Test
    698     public void testIDs() {
    699         // These cannot change
    700         assertEquals(0, ColorSpace.get(ColorSpace.Named.SRGB).getId());
    701         assertEquals(-1, ColorSpace.MIN_ID);
    702         assertEquals(63, ColorSpace.MAX_ID);
    703     }
    704 
    705     @Test
    706     public void testFromLinear() {
    707         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    708 
    709         float[] source = { 0.0f, 0.5f, 1.0f };
    710         float[] expected = { 0.0f, 0.7354f, 1.0f };
    711 
    712         float[] r1 = colorSpace.fromLinear(source[0], source[1], source[2]);
    713         assertNotNull(r1);
    714         assertEquals(3, r1.length);
    715         assertArrayEquals(expected, r1, 1e-3f);
    716 
    717         float[] r2 = { source[0], source[1], source[2] };
    718         assertSame(r2, colorSpace.fromLinear(r2));
    719         assertEquals(3, r2.length);
    720         assertArrayEquals(r1, r2, 1e-5f);
    721     }
    722 
    723     @Test
    724     public void testToLinear() {
    725         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    726 
    727         float[] source = { 0.0f, 0.5f, 1.0f };
    728         float[] expected = new float[] { 0.0f, 0.2140f, 1.0f };
    729 
    730         float[] r1 = colorSpace.toLinear(source[0], source[1], source[2]);
    731         assertNotNull(r1);
    732         assertEquals(3, r1.length);
    733         assertArrayEquals(expected, r1, 1e-3f);
    734 
    735         float[] r2 = new float[] { source[0], source[1], source[2] };
    736         assertSame(r2, colorSpace.toLinear(r2));
    737         assertEquals(3, r2.length);
    738         assertArrayEquals(r1, r2, 1e-5f);
    739     }
    740 
    741     @Test
    742     public void testTransferParameters() {
    743         ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
    744         assertNotNull(colorSpace.getTransferParameters());
    745 
    746         colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
    747         assertNull(colorSpace.getTransferParameters());
    748     }
    749 
    750     @Test
    751     public void testIdempotentTransferFunctions() {
    752         Arrays.stream(ColorSpace.Named.values())
    753                 .map(ColorSpace::get)
    754                 .filter(cs -> cs.getModel() == ColorSpace.Model.RGB)
    755                 .map(cs -> (ColorSpace.Rgb) cs)
    756                 .forEach(cs -> {
    757                         float[] source = { 0.0f, 0.5f, 1.0f };
    758                         float[] r = cs.fromLinear(cs.toLinear(source[0], source[1], source[2]));
    759                         assertArrayEquals(source, r, 1e-3f);
    760                 });
    761     }
    762 
    763     @Test
    764     public void testMatch() {
    765         for (ColorSpace.Named named : ColorSpace.Named.values()) {
    766             ColorSpace cs = ColorSpace.get(named);
    767             if (cs.getModel() == ColorSpace.Model.RGB) {
    768                 ColorSpace.Rgb rgb = (ColorSpace.Rgb) cs;
    769                 // match() cannot match extended sRGB
    770                 if (rgb != ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) &&
    771                         rgb != ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
    772 
    773                     // match() uses CIE XYZ D50
    774                     rgb = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
    775                     assertSame(cs,
    776                             ColorSpace.match(rgb.getTransform(), rgb.getTransferParameters()));
    777                 }
    778             }
    779         }
    780 
    781         assertSame(ColorSpace.get(ColorSpace.Named.SRGB),
    782                 ColorSpace.match(SRGB_TO_XYZ_D50, new ColorSpace.Rgb.TransferParameters(
    783                         1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4)));
    784     }
    785 
    786 
    787     @SuppressWarnings("SameParameterValue")
    788     private static void assertArrayNotEquals(float[] a, float[] b, float eps) {
    789         for (int i = 0; i < a.length; i++) {
    790             if (Float.compare(a[i], b[i]) == 0 || Math.abs(a[i] - b[i]) < eps) {
    791                 fail("Expected " + a[i] + ", received " + b[i]);
    792             }
    793         }
    794     }
    795 
    796     private static void assertArrayEquals(float[] a, float[] b, float eps) {
    797         for (int i = 0; i < a.length; i++) {
    798             if (Float.compare(a[i], b[i]) != 0 && Math.abs(a[i] - b[i]) > eps) {
    799                 fail("Expected " + a[i] + ", received " + b[i]);
    800             }
    801         }
    802     }
    803 }
    804