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