Add ColorSpace class
The ColorSpace class can be used to create an RGB color space from
either primaries/whitepoint or an RGB->XYZ matrix.
The primaries and whitepoint are in xyY space. A utility function
is provided to compute xyY coordinates from XYZ coordinats.
The class contains numerous functions to create common RGB color
spaces (sRGB, DCI-P3, etc.).
Test: colorspace_test
Bug: 29940137
Change-Id: Ifba8701377d058f5877176dabf4183e904a4cde0
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 9594466..c4f34d5 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -39,3 +39,9 @@
name: "quat_test",
srcs: ["quat_test.cpp"],
}
+
+cc_test {
+ name: "colorspace_test",
+ shared_libs: ["libui"],
+ srcs: ["colorspace_test.cpp"],
+}
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
new file mode 100644
index 0000000..5c127ad
--- /dev/null
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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();
+
+ 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::DCIP3().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);
+}
+
+}; // namespace android
diff --git a/libs/ui/tests/half_test.cpp b/libs/ui/tests/half_test.cpp
index 0ea9265..b2a5e5c 100644
--- a/libs/ui/tests/half_test.cpp
+++ b/libs/ui/tests/half_test.cpp
@@ -32,7 +32,7 @@
TEST_F(HalfTest, Basics) {
- EXPECT_EQ(2, sizeof(half));
+ EXPECT_EQ(2UL, sizeof(half));
// test +/- zero
EXPECT_EQ(0x0000, half( 0.0f).getBits());