1 /*
2  * Copyright 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "ColorSpaceTest"
18 
19 #include <math.h>
20 #include <stdlib.h>
21 
22 #include <ui/ColorSpace.h>
23 
24 #include <gtest/gtest.h>
25 
26 namespace android {
27 
28 class ColorSpaceTest : public testing::Test {
29 protected:
30 };
31 
TEST_F(ColorSpaceTest,XYZ)32 TEST_F(ColorSpaceTest, XYZ) {
33     mat3 sRGBToXYZ(transpose(mat3{
34         0.412391f, 0.357584f, 0.180481f,
35         0.212639f, 0.715169f, 0.072192f,
36         0.019331f, 0.119195f, 0.950532f
37     }));
38 
39     mat3 XYZtoSRGB(inverse(sRGBToXYZ));
40 
41     ColorSpace sRGB("sRGB", sRGBToXYZ);
42 
43     EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ());
44     EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB());
45 }
46 
TEST_F(ColorSpaceTest,XYZPrimaries)47 TEST_F(ColorSpaceTest, XYZPrimaries) {
48     mat3 sRGBToXYZ(transpose(mat3{
49         0.412391f, 0.357584f, 0.180481f,
50         0.212639f, 0.715169f, 0.072192f,
51         0.019331f, 0.119195f, 0.950532f
52     }));
53 
54     ColorSpace sRGB("sRGB", sRGBToXYZ);
55 
56     EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f);
57     EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f);
58 
59     EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f);
60     EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f);
61 
62     EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f);
63     EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f);
64 }
65 
TEST_F(ColorSpaceTest,XYZWhitePoint)66 TEST_F(ColorSpaceTest, XYZWhitePoint) {
67     mat3 sRGBToXYZ(transpose(mat3{
68         0.412391f, 0.357584f, 0.180481f,
69         0.212639f, 0.715169f, 0.072192f,
70         0.019331f, 0.119195f, 0.950532f
71     }));
72 
73     ColorSpace sRGB("sRGB", sRGBToXYZ);
74 
75     EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f);
76     EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f);
77 }
78 
TEST_F(ColorSpaceTest,XYZFromPrimaries)79 TEST_F(ColorSpaceTest, XYZFromPrimaries) {
80     mat3 sRGBToXYZ(transpose(mat3{
81         0.412391f, 0.357584f, 0.180481f,
82         0.212639f, 0.715169f, 0.072192f,
83         0.019331f, 0.119195f, 0.950532f
84     }));
85 
86     ColorSpace sRGB1("sRGB", sRGBToXYZ);
87     ColorSpace sRGB2(
88           "sRGB",
89           {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
90           {0.3127f, 0.3290f}
91     );
92 
93     for (size_t i = 0; i < 3; i++) {
94         for (size_t j= 0; j < 3; j++) {
95             ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f);
96         }
97     }
98 
99     for (size_t i = 0; i < 3; i++) {
100         for (size_t j= 0; j < 3; j++) {
101             ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f);
102         }
103     }
104 }
105 
TEST_F(ColorSpaceTest,TransferFunctions)106 TEST_F(ColorSpaceTest, TransferFunctions) {
107     ColorSpace sRGB = ColorSpace::sRGB();
108 
109     EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f);
110     EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f);
111     EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
112     EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
113 
114     for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
115         ASSERT_TRUE(v >= sRGB.getEOTF()(v));
116         ASSERT_TRUE(v <= sRGB.getOETF()(v));
117     }
118 
119     float previousEOTF = std::numeric_limits<float>::lowest();
120     float previousOETF = std::numeric_limits<float>::lowest();
121     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
122         ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
123         previousEOTF = sRGB.getEOTF()(v);
124         ASSERT_TRUE(previousOETF < sRGB.getOETF()(v));
125         previousOETF = sRGB.getOETF()(v);
126     }
127 
128     ColorSpace sRGB2(
129           "sRGB",
130           {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
131           {0.3127f, 0.3290f}
132           // linear transfer functions
133     );
134     for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
135         ASSERT_EQ(v, sRGB2.getEOTF()(v));
136         ASSERT_EQ(v, sRGB2.getOETF()(v));
137     }
138 }
139 
TEST_F(ColorSpaceTest,Clamping)140 TEST_F(ColorSpaceTest, Clamping) {
141     // Pick a color outside of sRGB
142     float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0}));
143 
144     // The color will be clamped
145     float3 sRGB(ColorSpace::sRGB().xyzToRGB(c));
146     EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0});
147 
148     // The color will not be clamped
149     float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c));
150     EXPECT_TRUE(extendedSRGB.g > 1.0f);
151 }
152 
TEST_F(ColorSpaceTest,Connect)153 TEST_F(ColorSpaceTest, Connect) {
154     // No chromatic adaptation
155     auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
156             .transform({1.0f, 0.5f, 0.0f});
157     EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
158 
159     // Test with chromatic adaptation
160     r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
161             .transform({1.0f, 0.0f, 0.0f});
162     EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
163 }
164 
TEST_F(ColorSpaceTest,LUT)165 TEST_F(ColorSpaceTest, LUT) {
166     auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB());
167     EXPECT_TRUE(lut != nullptr);
168 
169     // {1.0f, 0.5f, 0.0f}
170     auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16];
171     EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
172 
173     // {1.0f, 1.0f, 0.5f}
174     r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
175     EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f})));
176 
177     // {1.0f, 1.0f, 1.0f}
178     r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
179     EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f})));
180 
181 }
182 
183 }; // namespace android
184