Make transfer function classification part of the public API

Change-Id: I9580a89ab6211b411c3e741b16cd6d0980b806b2
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/616520
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/skcms.cc b/skcms.cc
index 4db244d..c1818c7 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -130,28 +130,39 @@
 // Most transfer functions we work with are sRGBish.
 // For exotic HDR transfer functions, we encode them using a tf.g that makes no sense,
 // and repurpose the other fields to hold the parameters of the HDR functions.
-enum TFKind { Bad, sRGBish, PQish, HLGish, HLGinvish };
 struct TF_PQish  { float A,B,C,D,E,F; };
 struct TF_HLGish { float R,G,a,b,c,K_minus_1; };
 // We didn't originally support a scale factor K for HLG, and instead just stored 0 in
 // the unused `f` field of skcms_TransferFunction for HLGish and HLGInvish transfer functions.
 // By storing f=K-1, those old unusued f=0 values now mean K=1, a noop scale factor.
 
-static float TFKind_marker(TFKind kind) {
+static float TFKind_marker(skcms_TFType kind) {
     // We'd use different NaNs, but those aren't guaranteed to be preserved by WASM.
     return -(float)kind;
 }
 
-static TFKind classify(const skcms_TransferFunction& tf, TF_PQish*   pq = nullptr
-                                                       , TF_HLGish* hlg = nullptr) {
+static skcms_TFType classify(const skcms_TransferFunction& tf, TF_PQish*   pq = nullptr
+                                                             , TF_HLGish* hlg = nullptr) {
     if (tf.g < 0 && static_cast<float>(static_cast<int>(tf.g)) == tf.g) {
         // TODO: soundness checks for PQ/HLG like we do for sRGBish?
         switch ((int)tf.g) {
-            case -PQish:     if (pq ) { memcpy(pq , &tf.a, sizeof(*pq )); } return PQish;
-            case -HLGish:    if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGish;
-            case -HLGinvish: if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGinvish;
+            case -skcms_TFType_PQish:
+                if (pq) {
+                    memcpy(pq , &tf.a, sizeof(*pq ));
+                }
+                return skcms_TFType_PQish;
+            case -skcms_TFType_HLGish:
+                if (hlg) {
+                    memcpy(hlg, &tf.a, sizeof(*hlg));
+                }
+                return skcms_TFType_HLGish;
+            case -skcms_TFType_HLGinvish:
+                if (hlg) {
+                    memcpy(hlg, &tf.a, sizeof(*hlg));
+                }
+                return skcms_TFType_HLGinvish;
         }
-        return Bad;
+        return skcms_TFType_Invalid;
     }
 
     // Basic soundness checks for sRGBish transfer functions.
@@ -163,26 +174,29 @@
             && tf.g >= 0
             // Raising a negative value to a fractional tf->g produces complex numbers.
             && tf.a * tf.d + tf.b >= 0) {
-        return sRGBish;
+        return skcms_TFType_sRGBish;
     }
 
-    return Bad;
+    return skcms_TFType_Invalid;
 }
 
+skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction* tf) {
+    return classify(*tf);
+}
 bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction* tf) {
-    return classify(*tf) == sRGBish;
+    return classify(*tf) == skcms_TFType_sRGBish;
 }
 bool skcms_TransferFunction_isPQish(const skcms_TransferFunction* tf) {
-    return classify(*tf) == PQish;
+    return classify(*tf) == skcms_TFType_PQish;
 }
 bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction* tf) {
-    return classify(*tf) == HLGish;
+    return classify(*tf) == skcms_TFType_HLGish;
 }
 
 bool skcms_TransferFunction_makePQish(skcms_TransferFunction* tf,
                                       float A, float B, float C,
                                       float D, float E, float F) {
-    *tf = { TFKind_marker(PQish), A,B,C,D,E,F };
+    *tf = { TFKind_marker(skcms_TFType_PQish), A,B,C,D,E,F };
     assert(skcms_TransferFunction_isPQish(tf));
     return true;
 }
@@ -190,7 +204,7 @@
 bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction* tf,
                                              float K, float R, float G,
                                              float a, float b, float c) {
-    *tf = { TFKind_marker(HLGish), R,G, a,b,c, K-1.0f };
+    *tf = { TFKind_marker(skcms_TFType_HLGish), R,G, a,b,c, K-1.0f };
     assert(skcms_TransferFunction_isHLGish(tf));
     return true;
 }
@@ -202,29 +216,29 @@
     TF_PQish  pq;
     TF_HLGish hlg;
     switch (classify(*tf, &pq, &hlg)) {
-        case Bad:       break;
+        case skcms_TFType_Invalid: break;
 
-        case HLGish: {
+        case skcms_TFType_HLGish: {
             const float K = hlg.K_minus_1 + 1.0f;
             return K * sign * (x*hlg.R <= 1 ? powf_(x*hlg.R, hlg.G)
                                             : expf_((x-hlg.c)*hlg.a) + hlg.b);
         }
 
         // skcms_TransferFunction_invert() inverts R, G, and a for HLGinvish so this math is fast.
-        case HLGinvish: {
+        case skcms_TFType_HLGinvish: {
             const float K = hlg.K_minus_1 + 1.0f;
             x /= K;
             return sign * (x <= 1 ? hlg.R * powf_(x, hlg.G)
                                   : hlg.a * logf_(x - hlg.b) + hlg.c);
         }
 
+        case skcms_TFType_sRGBish:
+            return sign * (x < tf->d ?       tf->c * x + tf->f
+                                     : powf_(tf->a * x + tf->b, tf->g) + tf->e);
 
-        case sRGBish: return sign * (x < tf->d ?       tf->c * x + tf->f
-                                               : powf_(tf->a * x + tf->b, tf->g) + tf->e);
-
-        case PQish: return sign * powf_(fmaxf_(pq.A + pq.B * powf_(x, pq.C), 0)
-                                            / (pq.D + pq.E * powf_(x, pq.C)),
-                                        pq.F);
+        case skcms_TFType_PQish: return sign * powf_(fmaxf_(pq.A + pq.B * powf_(x, pq.C), 0)
+                                                         / (pq.D + pq.E * powf_(x, pq.C)),
+                                                     pq.F);
     }
     return 0;
 }
@@ -1850,28 +1864,28 @@
     TF_PQish  pq;
     TF_HLGish hlg;
     switch (classify(*src, &pq, &hlg)) {
-        case Bad: return false;
-        case sRGBish: break;  // handled below
+        case skcms_TFType_Invalid: return false;
+        case skcms_TFType_sRGBish: break;  // handled below
 
-        case PQish:
-            *dst = { TFKind_marker(PQish), -pq.A,  pq.D, 1.0f/pq.F
-                                         ,  pq.B, -pq.E, 1.0f/pq.C};
+        case skcms_TFType_PQish:
+            *dst = { TFKind_marker(skcms_TFType_PQish), -pq.A,  pq.D, 1.0f/pq.F
+                                                      ,  pq.B, -pq.E, 1.0f/pq.C};
             return true;
 
-        case HLGish:
-            *dst = { TFKind_marker(HLGinvish), 1.0f/hlg.R, 1.0f/hlg.G
-                                             , 1.0f/hlg.a, hlg.b, hlg.c
-                                             , hlg.K_minus_1 };
+        case skcms_TFType_HLGish:
+            *dst = { TFKind_marker(skcms_TFType_HLGinvish), 1.0f/hlg.R, 1.0f/hlg.G
+                                                          , 1.0f/hlg.a, hlg.b, hlg.c
+                                                          , hlg.K_minus_1 };
             return true;
 
-        case HLGinvish:
-            *dst = { TFKind_marker(HLGish), 1.0f/hlg.R, 1.0f/hlg.G
-                                          , 1.0f/hlg.a, hlg.b, hlg.c
-                                          , hlg.K_minus_1 };
+        case skcms_TFType_HLGinvish:
+            *dst = { TFKind_marker(skcms_TFType_HLGish), 1.0f/hlg.R, 1.0f/hlg.G
+                                                       , 1.0f/hlg.a, hlg.b, hlg.c
+                                                       , hlg.K_minus_1 };
             return true;
     }
 
-    assert (classify(*src) == sRGBish);
+    assert (classify(*src) == skcms_TFType_sRGBish);
 
     // We're inverting this function, solving for x in terms of y.
     //   y = (cx + f)         x < d
@@ -1932,7 +1946,7 @@
 
     // That should usually make classify(inv) == sRGBish true, but there are a couple situations
     // where we might still fail here, like non-finite parameter values.
-    if (classify(inv) != sRGBish) {
+    if (classify(inv) != skcms_TFType_sRGBish) {
         return false;
     }
 
@@ -1956,7 +1970,7 @@
     }
 
     *dst = inv;
-    return classify(*dst) == sRGBish;
+    return classify(*dst) == skcms_TFType_sRGBish;
 }
 
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
@@ -2110,7 +2124,7 @@
 static float max_roundtrip_error_checked(const skcms_Curve* curve,
                                          const skcms_TransferFunction* tf_inv) {
     skcms_TransferFunction tf;
-    if (!skcms_TransferFunction_invert(tf_inv, &tf) || sRGBish != classify(tf)) {
+    if (!skcms_TransferFunction_invert(tf_inv, &tf) || skcms_TFType_sRGBish != classify(tf)) {
         return INFINITY_;
     }
 
@@ -2257,7 +2271,7 @@
         // Other non-Bad TFs would be fine, but we know we've only ever tried to fit sRGBish;
         // anything else is just some accident of math and the way we pun tf.g as a type flag.
         // fit_nonlinear() should guarantee this, but the special cases may fail this test.
-        if (sRGBish != classify(tf)) {
+        if (skcms_TFType_sRGBish != classify(tf)) {
             continue;
         }
 
@@ -2550,11 +2564,11 @@
         }
 
         switch (classify(tf)) {
-            case Bad:        return noop;
-            case sRGBish:    return OpAndArg{op.sRGBish,   &tf};
-            case PQish:      return OpAndArg{op.PQish,     &tf};
-            case HLGish:     return OpAndArg{op.HLGish,    &tf};
-            case HLGinvish:  return OpAndArg{op.HLGinvish, &tf};
+            case skcms_TFType_Invalid:    return noop;
+            case skcms_TFType_sRGBish:    return OpAndArg{op.sRGBish,   &tf};
+            case skcms_TFType_PQish:      return OpAndArg{op.PQish,     &tf};
+            case skcms_TFType_HLGish:     return OpAndArg{op.HLGish,    &tf};
+            case skcms_TFType_HLGinvish:  return OpAndArg{op.HLGinvish, &tf};
         }
     }
     return OpAndArg{op.table, curve};
diff --git a/skcms.h b/skcms.h
index 2ee5693..1510a13 100644
--- a/skcms.h
+++ b/skcms.h
@@ -51,6 +51,17 @@
 SKCMS_API bool  skcms_TransferFunction_invert(const skcms_TransferFunction*,
                                               skcms_TransferFunction*);
 
+typedef enum skcms_TFType {
+    skcms_TFType_Invalid,
+    skcms_TFType_sRGBish,
+    skcms_TFType_PQish,
+    skcms_TFType_HLGish,
+    skcms_TFType_HLGinvish,
+} skcms_TFType;
+
+// Identify which kind of transfer function is encoded in an skcms_TransferFunction
+SKCMS_API skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction*);
+
 // We can jam a couple alternate transfer function forms into skcms_TransferFunction,
 // including those matching the general forms of the SMPTE ST 2084 PQ function or HLG.
 //