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
26namespace android {
27
28class ColorSpaceTest : public testing::Test {
29protected:
30};
31
32TEST_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
47TEST_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
66TEST_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
79TEST_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
106TEST_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
140TEST_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
153TEST_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
165TEST_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