spin off Curve.[ch]

   - add Curve.[ch], move skcms_eval_curve() there
   - swap argument order of skcms_eval_curve()... much better

TODO:
   - lerp tables in skcms_eval_curve()?
   - implement skcms_ApproximatelyEqualCurves()

Change-Id: Iabb37f20b48e357133245b444d18b0e929b16991
Reviewed-on: https://skia-review.googlesource.com/128401
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: Mike Klein <mtklein@chromium.org>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/skcms.c b/skcms.c
index 0cf621e..54467af 100644
--- a/skcms.c
+++ b/skcms.c
@@ -7,6 +7,7 @@
 
 // skcms.c is a unity build target for skcms, #including every other C source file.
 
+#include "src/Curve.c"
 #include "src/GaussNewton.c"
 #include "src/ICCProfile.c"
 #include "src/LinearAlgebra.c"
diff --git a/skcms.gni b/skcms.gni
index db3bc9e..7729208 100644
--- a/skcms.gni
+++ b/skcms.gni
@@ -4,6 +4,8 @@
 # found in the LICENSE file.
 
 skcms_sources = [
+  "src/Curve.c",
+  "src/Curve.h",
   "src/GaussNewton.c",
   "src/GaussNewton.h",
   "src/ICCProfile.c",
diff --git a/skcms.h b/skcms.h
index 0451a58..ffc8901 100644
--- a/skcms.h
+++ b/skcms.h
@@ -61,6 +61,12 @@
     };
 } skcms_Curve;
 
+// Practical equality test for two skcms_Curves.
+// The implementation is subject to change, but it will always try to answer
+// "can I substitute A for B?" and "can I skip transforming from A to B?".
+SKCMS_API bool skcms_ApproximatelyEqualCurves(const skcms_Curve* A,
+                                              const skcms_Curve* B);
+
 typedef struct skcms_A2B {
     // Optional: N 1D curves, followed by an N-dimensional CLUT.
     // If input_channels == 0, these curves and CLUT are skipped,
diff --git a/src/Curve.c b/src/Curve.c
new file mode 100644
index 0000000..1e48d65
--- /dev/null
+++ b/src/Curve.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Curve.h"
+#include "TransferFunction.h"
+
+float skcms_eval_curve(const skcms_Curve* curve, float x) {
+    if (curve->table_entries == 0) {
+        return skcms_TransferFunction_eval(&curve->parametric, x);
+    }
+
+    // TODO: today we should always hit an entry exactly, but if that changes, lerp?
+    // (We add half to account for slight int -> float -> int round tripping issues.)
+    int ix = (int)( x*(curve->table_entries - 1) + 0.5f );
+
+    if (curve->table_8) {
+        return curve->table_8[ix] * (1/255.0f);
+    } else {
+        uint16_t be;
+        memcpy(&be, curve->table_16 + 2*ix, 2);
+
+        uint16_t le = ((be << 8) | (be >> 8)) & 0xffff;
+        return le * (1/65535.0f);
+    }
+}
diff --git a/src/Curve.h b/src/Curve.h
new file mode 100644
index 0000000..af01d53
--- /dev/null
+++ b/src/Curve.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#pragma once
+
+#include "../skcms.h"
+
+// Evaluate an skcms_Curve at x.
+float skcms_eval_curve(const skcms_Curve*, float x);
diff --git a/src/GaussNewton.c b/src/GaussNewton.c
index 11f55b5..7e9f774 100644
--- a/src/GaussNewton.c
+++ b/src/GaussNewton.c
@@ -93,23 +93,3 @@
     P[2] += dP.vals[2];
     return isfinitef_(P[0]) && isfinitef_(P[1]) && isfinitef_(P[2]);
 }
-
-float skcms_eval_curve(float x, const skcms_Curve* curve) {
-    if (curve->table_entries == 0) {
-        return skcms_TransferFunction_eval(&curve->parametric, x);
-    }
-
-    // TODO: today we should always hit an entry exactly, but if that changes, lerp?
-    // (We add half to account for slight int -> float -> int round tripping issues.)
-    int ix = (int)( x*(curve->table_entries - 1) + 0.5f );
-
-    if (curve->table_8) {
-        return curve->table_8[ix] * (1/255.0f);
-    } else {
-        uint16_t be;
-        memcpy(&be, curve->table_16 + 2*ix, 2);
-
-        uint16_t le = ((be << 8) | (be >> 8)) & 0xffff;
-        return le * (1/65535.0f);
-    }
-}
diff --git a/src/GaussNewton.h b/src/GaussNewton.h
index 2d5174d..1c18b9a 100644
--- a/src/GaussNewton.h
+++ b/src/GaussNewton.h
@@ -23,6 +23,3 @@
                              const void* ctx,
                              float P[3],
                              float x0, float x1, int N);
-
-// Evaluate an skcms_Curve at x.
-float skcms_eval_curve(float x, const skcms_Curve*);
diff --git a/src/PolyTF.c b/src/PolyTF.c
index 6ffbc50..ff93ec5 100644
--- a/src/PolyTF.c
+++ b/src/PolyTF.c
@@ -6,6 +6,7 @@
  */
 
 #include "../skcms.h"
+#include "Curve.h"
 #include "GaussNewton.h"
 #include "Macros.h"
 #include "PortableMath.h"
@@ -42,7 +43,8 @@
 // and not Ax^3 + Bx^2 + (1-A-B) to ensure that f(1.0f) == 1.0f.
 
 
-static float eval_poly_tf(float x, float A, float B, float C, float D) {
+static float eval_poly_tf(float A, float B, float C, float D,
+                          float x) {
     return x < D ? C*x
                  : A*(x*x*x-1) + B*(x*x-1) + 1;
 }
@@ -63,8 +65,8 @@
 
     dfdP[0] = (x*x*x - 1) - (x*x-1)*(D*D*D-1)/(D*D-1);
 
-    return skcms_eval_curve(x, arg->curve)
-         -     eval_poly_tf(x, A,B,C,D);
+    return skcms_eval_curve(arg->curve, x)
+         -     eval_poly_tf(A,B,C,D,    x);
 }
 
 static bool fit_poly_tf(const skcms_Curve* curve, skcms_PolyTF* tf) {
@@ -147,7 +149,7 @@
     for (int i = 0; i < N; i++) {
         float x = i * (1.0f/(N-1));
 
-        float rt = skcms_TransferFunction_eval(&inv, eval_poly_tf(x, A,B,C,D))
+        float rt = skcms_TransferFunction_eval(&inv, eval_poly_tf(A,B,C,D, x))
                  * (N-1) + 0.5f;
         if (!isfinitef_(rt) || rt >= N || rt < 0) {
             return false;
diff --git a/src/TransferFunction.c b/src/TransferFunction.c
index 8fd94b4..94ad410 100644
--- a/src/TransferFunction.c
+++ b/src/TransferFunction.c
@@ -6,6 +6,7 @@
  */
 
 #include "../skcms.h"
+#include "Curve.h"
 #include "GaussNewton.h"
 #include "LinearAlgebra.h"
 #include "Macros.h"
@@ -160,7 +161,7 @@
 static float rg_nonlinear(float x, const void* ctx, const float P[3], float dfdP[3]) {
     const rg_nonlinear_arg* arg = (const rg_nonlinear_arg*)ctx;
 
-    const float y = skcms_eval_curve(x, arg->curve);
+    const float y = skcms_eval_curve(arg->curve, x);
 
     const skcms_TransferFunction* tf = arg->tf;
     const float g = P[0],  a = P[1],  b = P[2],
@@ -200,13 +201,13 @@
     const float x_scale = 1.0f / (N - 1);
 
     int lin_points = 1;
-    *f = skcms_eval_curve(0, curve);
+    *f = skcms_eval_curve(curve, 0);
 
     float slope_min = -INFINITY_;
     float slope_max = +INFINITY_;
     for (int i = 1; i < N; ++i) {
         float x = i * x_scale;
-        float y = skcms_eval_curve(x, curve);
+        float y = skcms_eval_curve(curve, x);
 
         float slope_max_i = (y + tol - *f) / x,
               slope_min_i = (y - tol - *f) / x;
@@ -310,16 +311,17 @@
         } else if (L == N - 1) {
             // Degenerate case with only two points in the nonlinear segment. Solve directly.
             tf.g = 1;
-            tf.a = (skcms_eval_curve((N-1)*x_scale, curve) - skcms_eval_curve((N-2)*x_scale, curve))
+            tf.a = (skcms_eval_curve(curve, (N-1)*x_scale) -
+                    skcms_eval_curve(curve, (N-2)*x_scale))
                  / x_scale;
-            tf.b = skcms_eval_curve((N-2)*x_scale, curve)
+            tf.b = skcms_eval_curve(curve, (N-2)*x_scale)
                  - tf.a * (N-2)*x_scale;
             tf.e = 0;
         } else {
             // Start by guessing a gamma-only curve through the midpoint.
             int mid = (L + N) / 2;
             float mid_x = mid / (N - 1.0f);
-            float mid_y = skcms_eval_curve(mid_x, curve);
+            float mid_y = skcms_eval_curve(curve, mid_x);
             tf.g = log2f_(mid_y) / log2f_(mid_x);;
             tf.a = 1;
             tf.b = 0;
@@ -352,7 +354,7 @@
         float err = 0;
         for (int i = 0; i < N; i++) {
             float x = i * x_scale,
-                  y = skcms_eval_curve(x, curve);
+                  y = skcms_eval_curve(curve, x);
             err = fmaxf_(err, fabsf_(x - skcms_TransferFunction_eval(&tf_inv, y)));
         }
         if (*max_error > err) {
diff --git a/src/Transform.c b/src/Transform.c
index f253c72..79feebc 100644
--- a/src/Transform.c
+++ b/src/Transform.c
@@ -6,7 +6,7 @@
  */
 
 #include "../skcms.h"
-#include "GaussNewton.h"
+#include "Curve.h"
 #include "LinearAlgebra.h"
 #include "Macros.h"
 #include "PortableMath.h"
@@ -619,7 +619,7 @@
     float err = 0;
     for (int i = 0; i < N; i++) {
         float x = i * x_scale,
-              y = skcms_eval_curve(x, curve);
+              y = skcms_eval_curve(curve, x);
         err = fmaxf_(err, fabsf_(x - skcms_TransferFunction_eval(inv_tf, y)));
     }
     return err;