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