From 73e627074839bc00569117235eb4e7d4fc3cc39f Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Tue, 14 Mar 2017 11:27:13 -0400 Subject: [PATCH] Expose SkColorSpaceTransferFn inversion function Also adds tolerance to checks against zero BUG=skia: Change-Id: I2ad5737c6eef7e3ed52a685dceb347a434607336 Reviewed-on: https://skia-review.googlesource.com/9643 Commit-Queue: Brian Osman Reviewed-by: Matt Sarett --- include/core/SkColorSpace.h | 6 ++++ src/core/SkColorSpace.cpp | 44 +++++++++++++++++++++++++++ src/core/SkColorSpaceXform_A2B.cpp | 48 +----------------------------- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h index c945703549..ebc0ca1d91 100644 --- a/include/core/SkColorSpace.h +++ b/include/core/SkColorSpace.h @@ -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 { diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp index 3677fb6342..8ffff5edf3 100644 --- a/src/core/SkColorSpace.cpp +++ b/src/core/SkColorSpace.cpp @@ -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; +} diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp index 39c352cd8c..cde07f57cf 100644 --- a/src/core/SkColorSpaceXform_A2B.cpp +++ b/src/core/SkColorSpaceXform_A2B.cpp @@ -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); } } }