diff --git a/third_party/skcms/skcms.c b/third_party/skcms/skcms.c index 0cf621ecc2..54467af9d9 100644 --- a/third_party/skcms/skcms.c +++ b/third_party/skcms/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/third_party/skcms/skcms.gni b/third_party/skcms/skcms.gni index db3bc9e0d2..7729208436 100644 --- a/third_party/skcms/skcms.gni +++ b/third_party/skcms/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/third_party/skcms/skcms.h b/third_party/skcms/skcms.h index 0451a582e3..ffc8901019 100644 --- a/third_party/skcms/skcms.h +++ b/third_party/skcms/skcms.h @@ -61,6 +61,12 @@ typedef union skcms_Curve { }; } 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/third_party/skcms/src/Curve.c b/third_party/skcms/src/Curve.c new file mode 100644 index 0000000000..1e48d65799 --- /dev/null +++ b/third_party/skcms/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/third_party/skcms/src/Curve.h b/third_party/skcms/src/Curve.h new file mode 100644 index 0000000000..af01d53b2f --- /dev/null +++ b/third_party/skcms/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/third_party/skcms/src/GaussNewton.c b/third_party/skcms/src/GaussNewton.c index 11f55b53b4..7e9f774538 100644 --- a/third_party/skcms/src/GaussNewton.c +++ b/third_party/skcms/src/GaussNewton.c @@ -93,23 +93,3 @@ bool skcms_gauss_newton_step(float (*rg)(float x, const void*, const float P[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/third_party/skcms/src/GaussNewton.h b/third_party/skcms/src/GaussNewton.h index 2d5174d328..1c18b9ad93 100644 --- a/third_party/skcms/src/GaussNewton.h +++ b/third_party/skcms/src/GaussNewton.h @@ -23,6 +23,3 @@ bool skcms_gauss_newton_step(float (*rg)(float x, const void*, const float P[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/third_party/skcms/src/PolyTF.c b/third_party/skcms/src/PolyTF.c index 6ffbc5049e..ff93ec5586 100644 --- a/third_party/skcms/src/PolyTF.c +++ b/third_party/skcms/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 @@ static float rg_poly_tf(float x, const void* ctx, const float P[3], float dfdP[3 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 @@ static bool fit_poly_tf(const skcms_Curve* curve, skcms_PolyTF* tf) { 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/third_party/skcms/src/TransferFunction.c b/third_party/skcms/src/TransferFunction.c index 8fd94b49d9..94ad410b40 100644 --- a/third_party/skcms/src/TransferFunction.c +++ b/third_party/skcms/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 @@ typedef struct { 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 @@ int skcms_fit_linear(const skcms_Curve* curve, int N, float tol, float* c, float 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 @@ bool skcms_ApproximateCurve(const skcms_Curve* curve, } 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 @@ bool skcms_ApproximateCurve(const skcms_Curve* curve, 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/third_party/skcms/src/Transform.c b/third_party/skcms/src/Transform.c index f253c72721..79feebcb01 100644 --- a/third_party/skcms/src/Transform.c +++ b/third_party/skcms/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 @@ static float max_roundtrip_error(const skcms_TransferFunction* inv_tf, const skc 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; diff --git a/third_party/skcms/version.sha1 b/third_party/skcms/version.sha1 index 13f363176f..b0e2170f56 100755 --- a/third_party/skcms/version.sha1 +++ b/third_party/skcms/version.sha1 @@ -1 +1 @@ -ce2fabc6e146526bfb843932200ff006f4a2c943 \ No newline at end of file +e040063b5d7d52615d0060bd79cbe90c5e2d90b4 \ No newline at end of file