SkColorSpaceXformer: Add SkShader::makeColorSpace()
authorMatt Sarett <msarett@google.com>
Tue, 18 Apr 2017 22:29:12 +0000 (18:29 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Wed, 19 Apr 2017 16:28:44 +0000 (16:28 +0000)
Just a refactor - reimplements the shader xforms with makeColorSpace().

11 gms have diffs.  Some are down to floating precision.  The old
implementation would go float->fixed->float in some cases.  Others
are due to improvements with gradient shaders inside local matrix
shaders.

Bug: skia:6516
Change-Id: I424406990c5c58a47833cf4c9ef146cd3ea6c37e
Reviewed-on: https://skia-review.googlesource.com/13769
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Matt Sarett <msarett@google.com>

16 files changed:
include/core/SkShader.h
src/core/SkColorShader.cpp
src/core/SkColorShader.h
src/core/SkColorSpaceXformer.cpp
src/core/SkColorSpaceXformer.h
src/core/SkComposeShader.h
src/core/SkLocalMatrixShader.h
src/effects/gradients/SkLinearGradient.cpp
src/effects/gradients/SkLinearGradient.h
src/effects/gradients/SkRadialGradient.cpp
src/effects/gradients/SkRadialGradient.h
src/effects/gradients/SkSweepGradient.cpp
src/effects/gradients/SkSweepGradient.h
src/effects/gradients/SkTwoPointConicalGradient.cpp
src/effects/gradients/SkTwoPointConicalGradient.h
src/image/SkImageShader.h

index e49ad5f..b3b5faa 100644 (file)
@@ -20,6 +20,7 @@
 class SkArenaAlloc;
 class SkColorFilter;
 class SkColorSpace;
+class SkColorSpaceXformer;
 class SkImage;
 class SkPath;
 class SkPicture;
@@ -497,6 +498,16 @@ protected:
                                 const SkMatrix&, const SkPaint&,
                                 const SkMatrix* /*local matrix*/) const;
 
+    /**
+     *  Returns a shader transformed into a new color space via the |xformer|.
+     */
+    sk_sp<SkShader> makeColorSpace(SkColorSpaceXformer* xformer) const {
+        return this->onMakeColorSpace(xformer);
+    }
+    virtual sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer*) const {
+        return sk_ref_sp(const_cast<SkShader*>(this));
+    }
+
 private:
     // This is essentially const, but not officially so it can be modified in
     // constructors.
@@ -505,6 +516,8 @@ private:
     // So the SkLocalMatrixShader can whack fLocalMatrix in its SkReadBuffer constructor.
     friend class SkLocalMatrixShader;
     friend class SkBitmapProcLegacyShader;    // for computeTotalInverse()
+    friend class SkComposeShader;
+    friend class SkColorSpaceXformer;
 
     typedef SkFlattenable INHERITED;
 };
index 5c8bde6..66fc654 100644 (file)
@@ -236,6 +236,10 @@ void SkColor4Shader::toString(SkString* str) const {
 }
 #endif
 
+sk_sp<SkShader> SkColor4Shader::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    return SkShader::MakeColorShader(xformer->apply(fCachedByteColor));
+}
+
 sk_sp<SkShader> SkShader::MakeColorShader(const SkColor4f& color, sk_sp<SkColorSpace> space) {
     if (!SkScalarsAreFinite(color.vec(), 4)) {
         return nullptr;
index b9db657..0a6a935 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef SkColorShader_DEFINED
 #define SkColorShader_DEFINED
 
+#include "SkColorSpaceXformer.h"
 #include "SkShader.h"
 #include "SkPM4f.h"
 
@@ -69,6 +70,10 @@ protected:
     bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
 
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return SkShader::MakeColorShader(xformer->apply(fColor));
+    }
+
 private:
     SkColor fColor;
 
@@ -124,6 +129,8 @@ protected:
     bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
 
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
 private:
     sk_sp<SkColorSpace> fColorSpace;
     const SkColor4f     fColor4;
index 7a9f24b..779fe81 100644 (file)
@@ -75,91 +75,6 @@ SkColor SkColorSpaceXformer::apply(SkColor srgb) {
     return xformed;
 }
 
-// TODO: Is this introspection going to be enough, or do we need a new SkShader method?
-sk_sp<SkShader> SkColorSpaceXformer::apply(const SkShader* shader) {
-    SkColor color;
-    if (shader->isConstant() && shader->asLuminanceColor(&color)) {
-        return SkShader::MakeColorShader(this->apply(color))
-                ->makeWithLocalMatrix(shader->getLocalMatrix());
-    }
-
-    SkShader::TileMode xy[2];
-    SkMatrix local;
-    if (auto img = shader->isAImage(&local, xy)) {
-        return this->apply(img)->makeShader(xy[0], xy[1], &local);
-    }
-
-    SkShader::ComposeRec compose;
-    if (shader->asACompose(&compose)) {
-        auto A = this->apply(compose.fShaderA),
-             B = this->apply(compose.fShaderB);
-        if (A && B) {
-            return SkShader::MakeComposeShader(std::move(A), std::move(B), compose.fBlendMode)
-                    ->makeWithLocalMatrix(shader->getLocalMatrix());
-        }
-    }
-
-    SkShader::GradientInfo gradient;
-    sk_bzero(&gradient, sizeof(gradient));
-    if (auto type = shader->asAGradient(&gradient)) {
-        SkSTArray<8, SkColor>  colors(gradient.fColorCount);
-        SkSTArray<8, SkScalar>    pos(gradient.fColorCount);
-
-        gradient.fColors       = colors.begin();
-        gradient.fColorOffsets =    pos.begin();
-        shader->asAGradient(&gradient);
-
-        SkSTArray<8, SkColor> xformed(gradient.fColorCount);
-        this->apply(xformed.begin(), gradient.fColors, gradient.fColorCount);
-
-        switch (type) {
-            case SkShader::kNone_GradientType:
-            case SkShader::kColor_GradientType:
-                SkASSERT(false);  // Should be unreachable.
-                break;
-
-            case SkShader::kLinear_GradientType:
-                return SkGradientShader::MakeLinear(gradient.fPoint,
-                                                    xformed.begin(),
-                                                    gradient.fColorOffsets,
-                                                    gradient.fColorCount,
-                                                    gradient.fTileMode,
-                                                    gradient.fGradientFlags,
-                                                    &shader->getLocalMatrix());
-            case SkShader::kRadial_GradientType:
-                return SkGradientShader::MakeRadial(gradient.fPoint[0],
-                                                    gradient.fRadius[0],
-                                                    xformed.begin(),
-                                                    gradient.fColorOffsets,
-                                                    gradient.fColorCount,
-                                                    gradient.fTileMode,
-                                                    gradient.fGradientFlags,
-                                                    &shader->getLocalMatrix());
-            case SkShader::kSweep_GradientType:
-                return SkGradientShader::MakeSweep(gradient.fPoint[0].fX,
-                                                   gradient.fPoint[0].fY,
-                                                   xformed.begin(),
-                                                   gradient.fColorOffsets,
-                                                   gradient.fColorCount,
-                                                   gradient.fGradientFlags,
-                                                   &shader->getLocalMatrix());
-            case SkShader::kConical_GradientType:
-                return SkGradientShader::MakeTwoPointConical(gradient.fPoint[0],
-                                                             gradient.fRadius[0],
-                                                             gradient.fPoint[1],
-                                                             gradient.fRadius[1],
-                                                             xformed.begin(),
-                                                             gradient.fColorOffsets,
-                                                             gradient.fColorCount,
-                                                             gradient.fTileMode,
-                                                             gradient.fGradientFlags,
-                                                             &shader->getLocalMatrix());
-        }
-    }
-
-    return sk_ref_sp(const_cast<SkShader*>(shader));
-}
-
 const SkPaint& SkColorSpaceXformer::apply(const SkPaint& src) {
     const SkPaint* result = &src;
     auto get_dst = [&] {
@@ -176,7 +91,8 @@ const SkPaint& SkColorSpaceXformer::apply(const SkPaint& src) {
     }
 
     if (auto shader = src.getShader()) {
-        if (auto replacement = this->apply(shader)) {
+        auto replacement = shader->makeColorSpace(this);
+        if (replacement.get() != shader) {
             get_dst()->setShader(std::move(replacement));
         }
     }
@@ -189,11 +105,17 @@ const SkPaint& SkColorSpaceXformer::apply(const SkPaint& src) {
     }
 
     if (auto looper = src.getDrawLooper()) {
-        get_dst()->setDrawLooper(looper->makeColorSpace(this));
+        auto replacement = looper->makeColorSpace(this);
+        if (replacement.get() != looper) {
+            get_dst()->setDrawLooper(std::move(replacement));
+        }
     }
 
     if (auto imageFilter = src.getImageFilter()) {
-        get_dst()->setImageFilter(imageFilter->makeColorSpace(this));
+        auto replacement = imageFilter->makeColorSpace(this);
+        if (replacement.get() != imageFilter) {
+            get_dst()->setImageFilter(std::move(replacement));
+        }
     }
 
     return *result;
index 5fc62e0..61a0c02 100644 (file)
@@ -18,7 +18,7 @@ public:
 
     sk_sp<SkImage> apply(const SkImage* src);
     sk_sp<SkImage> apply(const SkBitmap& bitmap);
-    sk_sp<SkColorFilter> apply(const SkColorFilter* shader);
+    sk_sp<SkColorFilter> apply(const SkColorFilter* filter);
     const SkPaint* apply(const SkPaint* src);
     const SkPaint& apply(const SkPaint& src);
     void apply(SkColor dst[], const SkColor src[], int n);
@@ -27,8 +27,6 @@ public:
     sk_sp<SkColorSpace> dst() const { return fDst; }
 
 private:
-    sk_sp<SkShader> apply(const SkShader* shader);
-
     SkColorSpaceXformer() {}
 
     sk_sp<SkColorSpace>                fDst;
index be788af..d5905b7 100644 (file)
@@ -11,6 +11,8 @@
 #include "SkShader.h"
 #include "SkBlendMode.h"
 
+class SkColorSpacXformer;
+
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 /** \class SkComposeShader
@@ -68,6 +70,10 @@ protected:
     SkComposeShader(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const override;
     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return SkShader::MakeComposeShader(fShaderA->makeColorSpace(xformer),
+                                           fShaderB->makeColorSpace(xformer), fMode);
+    }
 
 private:
     sk_sp<SkShader>     fShaderA;
index cba1409..97986a6 100644 (file)
@@ -14,6 +14,7 @@
 
 class GrFragmentProcessor;
 class SkArenaAlloc;
+class SkColorSpaceXformer;
 
 class SkLocalMatrixShader : public SkShader {
 public:
@@ -50,6 +51,10 @@ protected:
     bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
 
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return fProxyShader->makeColorSpace(xformer)->makeWithLocalMatrix(this->getLocalMatrix());
+    }
+
 #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP
     bool onIsABitmap(SkBitmap* bitmap, SkMatrix* matrix, TileMode* mode) const override {
         return fProxyShader->isABitmap(bitmap, matrix, mode);
index de6341e..24cb1b5 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "Sk4fLinearGradient.h"
+#include "SkColorSpaceXformer.h"
 #include "SkLinearGradient.h"
 #include "SkRefCnt.h"
 
@@ -257,6 +258,14 @@ bool SkLinearGradient::onAppendStages(SkRasterPipeline* p,
     return true;
 }
 
+sk_sp<SkShader> SkLinearGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkPoint pts[2] = { fStart, fEnd };
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeLinear(pts, xformedColors.begin(), fOrigPos, fColorCount,
+                                        fTileMode, fGradFlags, &this->getLocalMatrix());
+}
+
 // This swizzles SkColor into the same component order as SkPMColor, but does not actually
 // "pre" multiply the color components.
 //
index 4118dee..f860568 100644 (file)
@@ -71,6 +71,8 @@ protected:
     bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         const SkMatrix&, const SkPaint&, const SkMatrix*) const override;
 
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
+
 private:
     class LinearGradient4fContext;
 
index 2caf905..71cdb9f 100644 (file)
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkColorSpaceXformer.h"
 #include "SkRadialGradient.h"
 #include "SkNx.h"
 
@@ -367,6 +368,14 @@ sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs&
 
 #endif
 
+sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos,
+                                        fColorCount, fTileMode, fGradFlags,
+                                        &this->getLocalMatrix());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkRadialGradient::toString(SkString* str) const {
     str->append("SkRadialGradient: (");
index f92fbb3..c43df09 100644 (file)
@@ -36,6 +36,7 @@ protected:
     SkRadialGradient(SkReadBuffer& buffer);
     void flatten(SkWriteBuffer& buffer) const override;
     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
 
 private:
     const SkPoint fCenter;
index 06b9f21..27517bb 100644 (file)
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkColorSpaceXformer.h"
 #include "SkSweepGradient.h"
 
 #include <algorithm>
@@ -280,6 +281,13 @@ sk_sp<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(const AsFPArgs&
 
 #endif
 
+sk_sp<SkShader> SkSweepGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> xformedColors(fColorCount);
+    xformer->apply(xformedColors.begin(), fOrigColors, fColorCount);
+    return SkGradientShader::MakeSweep(fCenter.fX, fCenter.fY, xformedColors.begin(), fOrigPos,
+                                       fColorCount, fGradFlags, &this->getLocalMatrix());
+}
+
 #ifndef SK_IGNORE_TO_STRING
 void SkSweepGradient::toString(SkString* str) const {
     str->append("SkSweepGradient: (");
index 30ebb1a..331e22d 100644 (file)
@@ -36,6 +36,7 @@ public:
 protected:
     void flatten(SkWriteBuffer& buffer) const override;
     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
 
 private:
     const SkPoint fCenter;
index a9740aa..ced299e 100644 (file)
@@ -367,6 +367,33 @@ sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
 
 #endif
 
+sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
+    SkSTArray<8, SkColor> origColorsStorage(fColorCount);
+    SkSTArray<8, SkScalar> origPosStorage(fColorCount);
+    SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
+    SkColor* origColors = origColorsStorage.begin();
+    SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
+    SkColor* xformedColors = xformedColorsStorage.begin();
+
+    // Flip if necessary
+    SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
+    SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
+    SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
+    SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
+    for (int i = 0; i < fColorCount; i++) {
+        origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
+        if (origPos) {
+            origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
+        }
+    }
+
+    xformer->apply(xformedColors, origColors, fColorCount);
+    return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
+                                                 origPos, fColorCount, fTileMode, fGradFlags,
+                                                 &this->getLocalMatrix());
+}
+
+
 #ifndef SK_IGNORE_TO_STRING
 void SkTwoPointConicalGradient::toString(SkString* str) const {
     str->append("SkTwoPointConicalGradient: (");
index d73ba11..b32f52c 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef SkTwoPointConicalGradient_DEFINED
 #define SkTwoPointConicalGradient_DEFINED
 
+#include "SkColorSpaceXformer.h"
 #include "SkGradientShaderPriv.h"
 
 // TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)?
@@ -76,6 +77,7 @@ protected:
     SkTwoPointConicalGradient(SkReadBuffer& buffer);
     void flatten(SkWriteBuffer& buffer) const override;
     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
 
 private:
     SkPoint fCenter1;
index 8960812..5274826 100644 (file)
@@ -8,9 +8,10 @@
 #ifndef SkImageShader_DEFINED
 #define SkImageShader_DEFINED
 
+#include "SkBitmapProcShader.h"
+#include "SkColorSpaceXformer.h"
 #include "SkImage.h"
 #include "SkShader.h"
-#include "SkBitmapProcShader.h"
 
 class SkImageShader : public SkShader {
 public:
@@ -39,6 +40,11 @@ protected:
     bool onAppendStages(SkRasterPipeline*, SkColorSpace*, SkArenaAlloc*,
                         const SkMatrix& ctm, const SkPaint&, const SkMatrix*) const override;
 
+    sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override {
+        return xformer->apply(fImage.get())->makeShader(fTileModeX, fTileModeY,
+                                                        &this->getLocalMatrix());
+    }
+
     sk_sp<SkImage>  fImage;
     const TileMode  fTileModeX;
     const TileMode  fTileModeY;