Add skcms_AdaptToXYZD50 to the API
This splits of the part inside skcms_PrimariesToXYZD50 which computes
the chromatic adaptation matrix into a new function
skcms_AdaptToXYZD50.
This is useful when creating an ICC profile, to convert values to D50
which ICC uses.
Change-Id: I2a453ee9f17f7173868bd8341ef793614dde01bb
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/259136
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/skcms.cc b/skcms.cc
index cc5738d..2765567 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -1364,6 +1364,47 @@
return dst;
}
+bool skcms_AdaptToXYZD50(float wx, float wy,
+ skcms_Matrix3x3* toXYZD50) {
+ if (!is_zero_to_one(wx) || !is_zero_to_one(wy) ||
+ !toXYZD50) {
+ return false;
+ }
+
+ // Assumes that Y is 1.0f.
+ skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } };
+
+ // Now convert toXYZ matrix to toXYZD50.
+ skcms_Vector3 wXYZD50 = { { 0.96422f, 1.0f, 0.82521f } };
+
+ // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus
+ // the matrices below. The Bradford method is used by Adobe and is widely considered
+ // to be the best.
+ skcms_Matrix3x3 xyz_to_lms = {{
+ { 0.8951f, 0.2664f, -0.1614f },
+ { -0.7502f, 1.7135f, 0.0367f },
+ { 0.0389f, -0.0685f, 1.0296f },
+ }};
+ skcms_Matrix3x3 lms_to_xyz = {{
+ { 0.9869929f, -0.1470543f, 0.1599627f },
+ { 0.4323053f, 0.5183603f, 0.0492912f },
+ { -0.0085287f, 0.0400428f, 0.9684867f },
+ }};
+
+ skcms_Vector3 srcCone = mv_mul(&xyz_to_lms, &wXYZ);
+ skcms_Vector3 dstCone = mv_mul(&xyz_to_lms, &wXYZD50);
+
+ *toXYZD50 = {{
+ { dstCone.vals[0] / srcCone.vals[0], 0, 0 },
+ { 0, dstCone.vals[1] / srcCone.vals[1], 0 },
+ { 0, 0, dstCone.vals[2] / srcCone.vals[2] },
+ }};
+ *toXYZD50 = skcms_Matrix3x3_concat(toXYZD50, &xyz_to_lms);
+ *toXYZD50 = skcms_Matrix3x3_concat(&lms_to_xyz, toXYZD50);
+
+ return true;
+}
+
bool skcms_PrimariesToXYZD50(float rx, float ry,
float gx, float gy,
float bx, float by,
@@ -1399,33 +1440,10 @@
}};
toXYZ = skcms_Matrix3x3_concat(&primaries, &toXYZ);
- // Now convert toXYZ matrix to toXYZD50.
- skcms_Vector3 wXYZD50 = { { 0.96422f, 1.0f, 0.82521f } };
-
- // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus
- // the matrices below. The Bradford method is used by Adobe and is widely considered
- // to be the best.
- skcms_Matrix3x3 xyz_to_lms = {{
- { 0.8951f, 0.2664f, -0.1614f },
- { -0.7502f, 1.7135f, 0.0367f },
- { 0.0389f, -0.0685f, 1.0296f },
- }};
- skcms_Matrix3x3 lms_to_xyz = {{
- { 0.9869929f, -0.1470543f, 0.1599627f },
- { 0.4323053f, 0.5183603f, 0.0492912f },
- { -0.0085287f, 0.0400428f, 0.9684867f },
- }};
-
- skcms_Vector3 srcCone = mv_mul(&xyz_to_lms, &wXYZ);
- skcms_Vector3 dstCone = mv_mul(&xyz_to_lms, &wXYZD50);
-
- skcms_Matrix3x3 DXtoD50 = {{
- { dstCone.vals[0] / srcCone.vals[0], 0, 0 },
- { 0, dstCone.vals[1] / srcCone.vals[1], 0 },
- { 0, 0, dstCone.vals[2] / srcCone.vals[2] },
- }};
- DXtoD50 = skcms_Matrix3x3_concat(&DXtoD50, &xyz_to_lms);
- DXtoD50 = skcms_Matrix3x3_concat(&lms_to_xyz, &DXtoD50);
+ skcms_Matrix3x3 DXtoD50;
+ if (!skcms_AdaptToXYZD50(wx, wy, &DXtoD50)) {
+ return false;
+ }
*toXYZD50 = skcms_Matrix3x3_concat(&DXtoD50, &toXYZ);
return true;
diff --git a/skcms.h b/skcms.h
index 3df6b44..165264c 100644
--- a/skcms.h
+++ b/skcms.h
@@ -293,6 +293,12 @@
// profile unchanged and return false.
SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile);
+// Returns a matrix to adapt XYZ color from given the whitepoint to D50.
+SKCMS_API bool skcms_AdaptToXYZD50(float wx, float wy,
+ skcms_Matrix3x3* toXYZD50);
+
+// Returns a matrix to convert RGB color into XYZ adapted to D50, given the
+// primaries and whitepoint of the RGB model.
SKCMS_API bool skcms_PrimariesToXYZD50(float rx, float ry,
float gx, float gy,
float bx, float by,
diff --git a/tests.c b/tests.c
index 7a69a31..522cb35 100644
--- a/tests.c
+++ b/tests.c
@@ -1216,6 +1216,24 @@
free(ptr);
}
+static void test_AdaptToD50() {
+ skcms_Matrix3x3 xyz_to_xyzD50;
+ float x_D65 = 0.3127f;
+ float y_D65 = 0.3290f;
+ expect(skcms_AdaptToXYZD50(x_D65, y_D65, &xyz_to_xyzD50));
+ skcms_Matrix3x3 sRGB_D65 = {{
+ { 0.4124564f, 0.3575761f, 0.1804375f },
+ { 0.2126729f, 0.7151522f, 0.0721750f },
+ { 0.0193339f, 0.1191920f, 0.9503041f }
+ }};
+ skcms_Matrix3x3 sRGB_D50 = skcms_Matrix3x3_concat(&xyz_to_xyzD50, &sRGB_D65);
+ skcms_ICCProfile p = *skcms_sRGB_profile();
+ for (int r = 0; r < 3; ++r)
+ for (int c = 0; c < 3; ++c) {
+ expect(fabsf_(sRGB_D50.vals[r][c] - p.toXYZD50.vals[r][c]) < 0.0001f);
+ }
+}
+
static void test_PrimariesToXYZ() {
skcms_Matrix3x3 srgb_to_xyz;
expect(skcms_PrimariesToXYZD50(0.64f, 0.33f,
@@ -1613,6 +1631,7 @@
test_ByteToLinearFloat();
test_MakeUsableAsDestination();
test_MakeUsableAsDestinationAdobe();
+ test_AdaptToD50();
test_PrimariesToXYZ();
test_Programmatic_sRGB();
test_ExactlyEqual();