blob: 0a4873c8d3c548d4e5791493ddca16bec7aa2e03 [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "ColorSpaceTest"
#include <math.h>
#include <stdlib.h>
#include <ui/ColorSpace.h>
#include <gtest/gtest.h>
namespace android {
class ColorSpaceTest : public testing::Test {
protected:
};
TEST_F(ColorSpaceTest, XYZ) {
mat3 sRGBToXYZ(transpose(mat3{
0.412391f, 0.357584f, 0.180481f,
0.212639f, 0.715169f, 0.072192f,
0.019331f, 0.119195f, 0.950532f
}));
mat3 XYZtoSRGB(inverse(sRGBToXYZ));
ColorSpace sRGB("sRGB", sRGBToXYZ);
EXPECT_EQ(sRGBToXYZ, sRGB.getRGBtoXYZ());
EXPECT_EQ(XYZtoSRGB, sRGB.getXYZtoRGB());
}
TEST_F(ColorSpaceTest, XYZPrimaries) {
mat3 sRGBToXYZ(transpose(mat3{
0.412391f, 0.357584f, 0.180481f,
0.212639f, 0.715169f, 0.072192f,
0.019331f, 0.119195f, 0.950532f
}));
ColorSpace sRGB("sRGB", sRGBToXYZ);
EXPECT_NEAR(0.640f, sRGB.getPrimaries()[0].x, 1e-5f);
EXPECT_NEAR(0.330f, sRGB.getPrimaries()[0].y, 1e-5f);
EXPECT_NEAR(0.300f, sRGB.getPrimaries()[1].x, 1e-5f);
EXPECT_NEAR(0.600f, sRGB.getPrimaries()[1].y, 1e-5f);
EXPECT_NEAR(0.150f, sRGB.getPrimaries()[2].x, 1e-5f);
EXPECT_NEAR(0.060f, sRGB.getPrimaries()[2].y, 1e-5f);
}
TEST_F(ColorSpaceTest, XYZWhitePoint) {
mat3 sRGBToXYZ(transpose(mat3{
0.412391f, 0.357584f, 0.180481f,
0.212639f, 0.715169f, 0.072192f,
0.019331f, 0.119195f, 0.950532f
}));
ColorSpace sRGB("sRGB", sRGBToXYZ);
EXPECT_NEAR(0.3127f, sRGB.getWhitePoint().x, 1e-5f);
EXPECT_NEAR(0.3290f, sRGB.getWhitePoint().y, 1e-5f);
}
TEST_F(ColorSpaceTest, XYZFromPrimaries) {
mat3 sRGBToXYZ(transpose(mat3{
0.412391f, 0.357584f, 0.180481f,
0.212639f, 0.715169f, 0.072192f,
0.019331f, 0.119195f, 0.950532f
}));
ColorSpace sRGB1("sRGB", sRGBToXYZ);
ColorSpace sRGB2(
"sRGB",
{{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
{0.3127f, 0.3290f}
);
for (size_t i = 0; i < 3; i++) {
for (size_t j= 0; j < 3; j++) {
ASSERT_NEAR(sRGB1.getRGBtoXYZ()[i][j], sRGB2.getRGBtoXYZ()[i][j], 1e-5f);
}
}
for (size_t i = 0; i < 3; i++) {
for (size_t j= 0; j < 3; j++) {
ASSERT_NEAR(sRGB2.getXYZtoRGB()[i][j], sRGB2.getXYZtoRGB()[i][j], 1e-5f);
}
}
}
TEST_F(ColorSpaceTest, TransferFunctions) {
ColorSpace sRGB = ColorSpace::sRGB();
EXPECT_NEAR(0.0f, sRGB.getEOTF()(0.0f), 1e-6f);
EXPECT_NEAR(0.0f, sRGB.getOETF()(0.0f), 1e-6f);
EXPECT_NEAR(1.0f, sRGB.getEOTF()(1.0f), 1e-6f);
EXPECT_NEAR(1.0f, sRGB.getOETF()(1.0f), 1e-6f);
for (float v = 0.0f; v <= 0.5f; v += 1e-3f) {
ASSERT_TRUE(v >= sRGB.getEOTF()(v));
ASSERT_TRUE(v <= sRGB.getOETF()(v));
}
float previousEOTF = std::numeric_limits<float>::lowest();
float previousOETF = std::numeric_limits<float>::lowest();
for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
ASSERT_TRUE(previousEOTF < sRGB.getEOTF()(v));
previousEOTF = sRGB.getEOTF()(v);
ASSERT_TRUE(previousOETF < sRGB.getOETF()(v));
previousOETF = sRGB.getOETF()(v);
}
ColorSpace sRGB2(
"sRGB",
{{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
{0.3127f, 0.3290f}
// linear transfer functions
);
for (float v = 0.0f; v <= 1.0f; v += 1e-3f) {
ASSERT_EQ(v, sRGB2.getEOTF()(v));
ASSERT_EQ(v, sRGB2.getOETF()(v));
}
}
TEST_F(ColorSpaceTest, Clamping) {
// Pick a color outside of sRGB
float3 c(ColorSpace::BT2020().rgbToXYZ(float3{0, 1, 0}));
// The color will be clamped
float3 sRGB(ColorSpace::sRGB().xyzToRGB(c));
EXPECT_TRUE(sRGB > float3{0.0} && sRGB < float3{1.0});
// The color will not be clamped
float3 extendedSRGB(ColorSpace::linearExtendedSRGB().xyzToRGB(c));
EXPECT_TRUE(extendedSRGB.g > 1.0f);
}
TEST_F(ColorSpaceTest, Connect) {
// No chromatic adaptation
auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
.transform({1.0f, 0.5f, 0.0f});
EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
// Test with chromatic adaptation
r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
.transform({1.0f, 0.0f, 0.0f});
EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
}
TEST_F(ColorSpaceTest, LUT) {
auto lut = ColorSpace::createLUT(17, ColorSpace::sRGB(), ColorSpace::AdobeRGB());
EXPECT_TRUE(lut != nullptr);
// {1.0f, 0.5f, 0.0f}
auto r = lut.get()[0 * 17 * 17 + 8 * 17 + 16];
EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
// {1.0f, 1.0f, 0.5f}
r = lut.get()[8 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 0.5290f}), float3{1e-4f})));
// {1.0f, 1.0f, 1.0f}
r = lut.get()[16 * 17 * 17 + 0 * 17 + 16]; // y (G) is flipped
EXPECT_TRUE(all(lessThan(abs(r - float3{1.0f, 1.0f, 1.0f}), float3{1e-4f})));
}
}; // namespace android