parse profile following user-supplied A2B priority
This let skcms users indicate which A2B profile to prefer,
preserving skcms_Parse()'s hard-coded priorities.
Change-Id: Idd5e9c04e2c9eb5c8fc628365282c6884df42d24
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/388202
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/skcms.cc b/skcms.cc
index 4f98343..58deef8 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -1027,7 +1027,9 @@
|| (profile->has_trc && profile->has_toXYZD50);
}
-bool skcms_Parse(const void* buf, size_t len, skcms_ICCProfile* profile) {
+bool skcms_ParseWithA2BPriority(const void* buf, size_t len,
+ const int priority[], const int priorities,
+ skcms_ICCProfile* profile) {
assert(SAFE_SIZEOF(header_Layout) == 132);
if (!profile) {
@@ -1132,13 +1134,13 @@
skcms_ICCTag a2b_tag;
- // For now, we're preferring A2B0, like Skia does and the ICC spec tells us to.
- // TODO: prefer A2B1 (relative colormetric) over A2B0 (perceptual)?
- // This breaks with the ICC spec, but we think it's a good idea, given that TRC curves
- // and all our known users are thinking exclusively in terms of relative colormetric.
- const uint32_t sigs[] = { skcms_Signature_A2B0, skcms_Signature_A2B1 };
- for (int i = 0; i < ARRAY_COUNT(sigs); i++) {
- if (skcms_GetTagBySignature(profile, sigs[i], &a2b_tag)) {
+ for (int i = 0; i < priorities; i++) {
+ // enum { perceptual, relative_colormetric, saturation }
+ if (priority[i] < 0 || priority[i] > 2) {
+ return false;
+ }
+ uint32_t sig = skcms_Signature_A2B0 + static_cast<uint32_t>(priority[i]);
+ if (skcms_GetTagBySignature(profile, sig, &a2b_tag)) {
if (!read_a2b(&a2b_tag, &profile->A2B, pcs_is_xyz)) {
// Malformed A2B tag
return false;
diff --git a/skcms.h b/skcms.h
index edaa5d5..cfc2596 100644
--- a/skcms.h
+++ b/skcms.h
@@ -146,8 +146,9 @@
bool has_toXYZD50;
skcms_Matrix3x3 toXYZD50;
- // If the profile has a valid A2B0 tag, skcms_Parse() sets A2B to that data,
- // and has_A2B to true.
+ // If the profile has a valid A2B0 or A2B1 tag, skcms_Parse() sets A2B to
+ // that data, and has_A2B to true. skcms_ParseWithA2BPriority() does the
+ // same following any user-provided prioritization of A2B0, A2B1, or A2B2.
bool has_A2B;
skcms_A2B A2B;
} skcms_ICCProfile;
@@ -180,9 +181,20 @@
const skcms_TransferFunction* inv_tf);
// Parse an ICC profile and return true if possible, otherwise return false.
-// The buffer is not copied, it must remain valid as long as the skcms_ICCProfile
-// will be used.
-SKCMS_API bool skcms_Parse(const void*, size_t, skcms_ICCProfile*);
+// Selects an A2B profile (if present) according to priority list (each entry 0-2).
+// The buffer is not copied; it must remain valid as long as the skcms_ICCProfile will be used.
+SKCMS_API bool skcms_ParseWithA2BPriority(const void*, size_t,
+ const int priority[], int priorities,
+ skcms_ICCProfile*);
+
+static inline bool skcms_Parse(const void* buf, size_t len, skcms_ICCProfile* profile) {
+ // For continuity of existing user expectations,
+ // prefer A2B0 (perceptual) over A2B1 (relative colormetric), and ignore A2B2 (saturation).
+ const int priority[] = {0,1};
+ return skcms_ParseWithA2BPriority(buf, len,
+ priority, sizeof(priority)/sizeof(*priority),
+ profile);
+}
SKCMS_API bool skcms_ApproximateCurve(const skcms_Curve* curve,
skcms_TransferFunction* approx,
diff --git a/tests.c b/tests.c
index 86ef3e1..b4f41fe 100644
--- a/tests.c
+++ b/tests.c
@@ -1769,6 +1769,36 @@
free(ptr);
}
+static void test_ParseWithA2BPriority() {
+ void* ptr;
+ size_t len;
+ expect(load_file("profiles/misc/US_Web_Coated_SWOP_CMYK.icc", &ptr,&len));
+
+ skcms_ICCProfile simple;
+ expect(skcms_Parse(ptr, len, &simple)); // This will pick up A2B0.
+ expect(simple.has_A2B);
+
+ for (int priority = -1; priority < 4; priority++) {
+ skcms_ICCProfile profile;
+
+ bool ok = skcms_ParseWithA2BPriority(ptr, len, &priority, 1, &profile);
+ if (priority < 0 || priority > 2) {
+ expect(!ok);
+ continue;
+ }
+ expect(ok);
+ if (priority == 0 || priority == 2) {
+ // A2B0 and A2B2 are the same in this profile.
+ expect(0 == memcmp(&profile, &simple, sizeof(profile)));
+ } else {
+ // A2B1 is different.
+ expect(0 != memcmp(&profile, &simple, sizeof(profile)));
+ }
+ }
+
+ free(ptr);
+}
+
int main(int argc, char** argv) {
bool regenTestData = false;
for (int i = 1; i < argc; ++i) {
@@ -1811,6 +1841,7 @@
test_PQ_invert();
test_HLG_invert();
test_RGBA_8888_sRGB();
+ test_ParseWithA2BPriority();
// Temporarily disable some tests while getting FP16 compute working.
if (!kFP16) {