Expose SkColorSpaceTransferFn inversion function
authorBrian Osman <brianosman@google.com>
Tue, 14 Mar 2017 15:27:13 +0000 (11:27 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Tue, 14 Mar 2017 15:57:03 +0000 (15:57 +0000)
Also adds tolerance to checks against zero

BUG=skia:

Change-Id: I2ad5737c6eef7e3ed52a685dceb347a434607336
Reviewed-on: https://skia-review.googlesource.com/9643
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Matt Sarett <msarett@google.com>
include/core/SkColorSpace.h
src/core/SkColorSpace.cpp
src/core/SkColorSpaceXform_A2B.cpp

index c9457035495d3e621035c91fbbb884dfefe63c6c..ebc0ca1d91cf3d13ecd6b18bcfdaa15c1ccc4f5a 100644 (file)
@@ -48,6 +48,12 @@ struct SK_API SkColorSpaceTransferFn {
     float fD;
     float fE;
     float fF;
+
+    /**
+     * Produces a new parametric transfer function equation that is the mathematical inverse of
+     * this one.
+     */
+    SkColorSpaceTransferFn invert() const;
 };
 
 class SK_API SkColorSpace : public SkRefCnt {
index 3677fb6342e1c564d4e7110d6618d20024d91e6e..8ffff5edf371efef7ca5165b8bcc9431107b5e67 100644 (file)
@@ -632,3 +632,47 @@ bool SkColorSpace::Equals(const SkColorSpace* src, const SkColorSpace* dst) {
                                serializedSrcData->size());
     }
 }
+
+SkColorSpaceTransferFn SkColorSpaceTransferFn::invert() const {
+    // Original equation is:       y = (ax + b)^g + e   for x >= d
+    //                             y = cx + f           otherwise
+    //
+    // so 1st inverse is:          (y - e)^(1/g) = ax + b
+    //                             x = ((y - e)^(1/g) - b) / a
+    //
+    // which can be re-written as: x = (1/a)(y - e)^(1/g) - b/a
+    //                             x = ((1/a)^g)^(1/g) * (y - e)^(1/g) - b/a
+    //                             x = ([(1/a)^g]y + [-((1/a)^g)e]) ^ [1/g] + [-b/a]
+    //
+    // and 2nd inverse is:         x = (y - f) / c
+    // which can be re-written as: x = [1/c]y + [-f/c]
+    //
+    // and now both can be expressed in terms of the same parametric form as the
+    // original - parameters are enclosed in square brackets.
+    SkColorSpaceTransferFn inv = { 0, 0, 0, 0, 0, 0, 0 };
+
+    // find inverse for linear segment (if possible)
+    if (!transfer_fn_almost_equal(0.f, fC)) {
+        inv.fC = 1.f / fC;
+        inv.fF = -fF / fC;
+    } else {
+        // otherwise assume it should be 0 as it is the lower segment
+        // as y = f is a constant function
+    }
+
+    // find inverse for the other segment (if possible)
+    if (transfer_fn_almost_equal(0.f, fA) || transfer_fn_almost_equal(0.f, fG)) {
+        // otherwise assume it should be 1 as it is the top segment
+        // as you can't invert the constant functions y = b^g + c, or y = 1 + c
+        inv.fG = 1.f;
+        inv.fE = 1.f;
+    } else {
+        inv.fG = 1.f / fG;
+        inv.fA = powf(1.f / fA, fG);
+        inv.fB = -inv.fA * fE;
+        inv.fE = -fB / fA;
+    }
+    inv.fD = fC * fD + fF;
+
+    return inv;
+}
index 39c352cd8c97405f97b1c3b45c7c1c4897dad834..cde07f57cf61f55f94ec2e62c741acc95c7b0019 100644 (file)
@@ -88,52 +88,6 @@ static inline bool gamma_to_parametric(SkColorSpaceTransferFn* coeffs, const SkG
             return false;
     }
 }
-static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) {
-    // Original equation is:       y = (ax + b)^g + e   for x >= d
-    //                             y = cx + f           otherwise
-    //
-    // so 1st inverse is:          (y - e)^(1/g) = ax + b
-    //                             x = ((y - e)^(1/g) - b) / a
-    //
-    // which can be re-written as: x = (1/a)(y - e)^(1/g) - b/a
-    //                             x = ((1/a)^g)^(1/g) * (y - e)^(1/g) - b/a
-    //                             x = ([(1/a)^g]y + [-((1/a)^g)e]) ^ [1/g] + [-b/a]
-    //
-    // and 2nd inverse is:         x = (y - f) / c
-    // which can be re-written as: x = [1/c]y + [-f/c]
-    //
-    // and now both can be expressed in terms of the same parametric form as the
-    // original - parameters are enclosed in square brackets.
-
-    // find inverse for linear segment (if possible)
-    float c, f;
-    if (0.f == fn.fC) {
-        // otherwise assume it should be 0 as it is the lower segment
-        // as y = f is a constant function
-        c = 0.f;
-        f = 0.f;
-    } else {
-        c = 1.f / fn.fC;
-        f = -fn.fF / fn.fC;
-    }
-    // find inverse for the other segment (if possible)
-    float g, a, b, e;
-    if (0.f == fn.fA || 0.f == fn.fG) {
-        // otherwise assume it should be 1 as it is the top segment
-        // as you can't invert the constant functions y = b^g + c, or y = 1 + c
-        g = 1.f;
-        a = 0.f;
-        b = 0.f;
-        e = 1.f;
-    } else {
-        g = 1.f / fn.fG;
-        a = powf(1.f / fn.fA, fn.fG);
-        b = -a * fn.fE;
-        e = -fn.fB / fn.fA;
-    }
-    const float d = fn.fC * fn.fD + fn.fF;
-    return {g, a, b, c, d, e, f};
-}
 
 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
                                              SkColorSpace_XYZ* dstSpace)
@@ -279,7 +233,7 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
                 } else {
                     SkColorSpaceTransferFn fn;
                     SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
-                    this->addTransferFn(invert_parametric(fn), channel);
+                    this->addTransferFn(fn.invert(), channel);
                 }
             }
         }