Add flipped gradient branch to two point conical gradient
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 21 Apr 2014 13:10:25 +0000 (13:10 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 21 Apr 2014 13:10:25 +0000 (13:10 +0000)
BUG=skia:

Committed: http://code.google.com/p/skia/source/detail?r=14235

R=bsalomon@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/227623004

git-svn-id: http://skia.googlecode.com/svn/trunk@14271 2bbb7eff-a529-9590-31e7-b0007b416f81

expectations/gm/ignored-tests.txt
include/core/SkPicture.h
src/effects/gradients/SkGradientShader.cpp
src/effects/gradients/SkGradientShaderPriv.h
src/effects/gradients/SkTwoPointConicalGradient.cpp
src/effects/gradients/SkTwoPointConicalGradient.h
src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp

index 28cdbf3f0242b3661871a6f6fdea1837e77587b6..dc68fe0623cf9d9cdd0f0665528963aefa8c9938 100644 (file)
@@ -56,3 +56,15 @@ image-surface
 aaclip
 composeshader
 peekpixels
+
+# egdaniel https://codereview.chromium.org/227623004/
+# This change will cause change two point conical gradients to change pixels values
+# but without any real perceptual differences.
+gradients
+gradients_view_perspective
+gradients_local_perspective
+gradients_no_texture
+twopointconical
+lightingcolorfilter
+radial_gradient2
+gradient_dirty_laundry
index 1f588a42b9db0f7b2305f6a34fd556e7280d5cff..bf9ec4f8bbb184e08d665a7abbc4dbe30892c414 100644 (file)
@@ -323,13 +323,14 @@ protected:
     // V21: add pushCull, popCull
     // V22: SK_PICT_FACTORY_TAG's size is now the chunk size in bytes
     // V23: SkPaint::FilterLevel became a real enum
+    // V24: SkTwoPointConicalGradient now has fFlipped flag for gradient flipping
 
     // Note: If the picture version needs to be increased then please follow the
     // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t MIN_PICTURE_VERSION = 19;
-    static const uint32_t CURRENT_PICTURE_VERSION = 23;
+    static const uint32_t CURRENT_PICTURE_VERSION = 24;
 
     mutable uint32_t      fUniqueID;
 
index f155cc5eb770436c9b33b41c0d0bd11e6cdb97dc..7f0840470015df83beb3319c43448c2b7453bf17 100644 (file)
@@ -118,12 +118,13 @@ SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc) {
             SkFixed dp = SK_Fixed1 / (desc.fCount - 1);
             SkFixed p = dp;
             SkFixed scale = (desc.fCount - 1) << 8;  // (1 << 24) / dp
-            for (int i = 1; i < desc.fCount; i++) {
+            for (int i = 1; i < desc.fCount - 1; i++) {
                 recs->fPos   = p;
                 recs->fScale = scale;
                 recs += 1;
                 p += dp;
             }
+            recs->fPos = SK_Fixed1;
         }
     }
     this->initCommon();
@@ -237,6 +238,30 @@ SkGradientShaderBase::GpuColorType SkGradientShaderBase::getGpuColorType(SkColor
     return kTexture_GpuColorType;
 }
 
+void SkGradientShaderBase::FlipGradientColors(SkColor* colorDst, Rec* recDst,
+                                              SkColor* colorSrc, Rec* recSrc,
+                                              int count) {
+    SkAutoSTArray<8, SkColor> colorsTemp(count);
+    for (int i = 0; i < count; ++i) {
+        int offset = count - i - 1;
+        colorsTemp[i] = colorSrc[offset];
+    }
+    if (count > 2) {
+        SkAutoSTArray<8, Rec> recsTemp(count);
+        for (int i = 0; i < count; ++i) {
+            int offset = count - i - 1;
+            recsTemp[i].fPos = SK_Fixed1 - recSrc[offset].fPos;
+            recsTemp[i].fScale = recSrc[offset].fScale;
+        }
+        memcpy(recDst, recsTemp.get(), count * sizeof(Rec));
+    }
+    memcpy(colorDst, colorsTemp.get(), count * sizeof(SkColor));
+}
+
+void SkGradientShaderBase::flipGradientColors() {
+    FlipGradientColors(fOrigColors, fRecs, fOrigColors, fRecs, fColorCount); 
+}
+
 bool SkGradientShaderBase::isOpaque() const {
     return fColorsAreOpaque;
 }
@@ -643,11 +668,23 @@ void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
     }
 }
 
-void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
+void SkGradientShaderBase::commonAsAGradient(GradientInfo* info, bool flipGrad) const {
     if (info) {
         if (info->fColorCount >= fColorCount) {
+            SkColor* colorLoc;
+            Rec*     recLoc;
+            if (flipGrad && (info->fColors || info->fColorOffsets)) {
+                SkAutoSTArray<8, SkColor> colorStorage(fColorCount);
+                SkAutoSTArray<8, Rec> recStorage(fColorCount);
+                colorLoc = colorStorage.get();
+                recLoc = recStorage.get();
+                FlipGradientColors(colorLoc, recLoc, fOrigColors, fRecs, fColorCount);
+            } else {
+                colorLoc = fOrigColors;
+                recLoc = fRecs;
+            }
             if (info->fColors) {
-                memcpy(info->fColors, fOrigColors, fColorCount * sizeof(SkColor));
+                memcpy(info->fColors, colorLoc, fColorCount * sizeof(SkColor));
             }
             if (info->fColorOffsets) {
                 if (fColorCount == 2) {
@@ -655,7 +692,7 @@ void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
                     info->fColorOffsets[1] = SK_Scalar1;
                 } else if (fColorCount > 2) {
                     for (int i = 0; i < fColorCount; ++i) {
-                        info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
+                        info->fColorOffsets[i] = SkFixedToScalar(recLoc[i].fPos);
                     }
                 }
             }
@@ -801,12 +838,36 @@ SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
     if (start == end && startRadius == endRadius) {
         return SkNEW(SkEmptyShader);
     }
+
     EXPAND_1_COLOR(colorCount);
 
+    bool flipGradient = startRadius > endRadius;
+
     SkGradientShaderBase::Descriptor desc;
-    desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
-    return SkNEW_ARGS(SkTwoPointConicalGradient,
-                      (start, startRadius, end, endRadius, desc));
+
+    if (!flipGradient) {
+        desc_init(&desc, colors, pos, colorCount, mode, mapper, flags);
+        return SkNEW_ARGS(SkTwoPointConicalGradient,
+                          (start, startRadius, end, endRadius, flipGradient, desc));
+    } else {
+        SkAutoSTArray<8, SkColor> colorsNew(colorCount);
+        SkAutoSTArray<8, SkScalar> posNew(colorCount);
+        for (int i = 0; i < colorCount; ++i) {
+            colorsNew[i] = colors[colorCount - i - 1];
+        }
+
+        if (pos) {
+            for (int i = 0; i < colorCount; ++i) {
+                posNew[i] = 1 - pos[colorCount - i - 1];
+            }
+            desc_init(&desc, colorsNew.get(), posNew.get(), colorCount, mode, mapper, flags);
+        } else {
+            desc_init(&desc, colorsNew.get(), NULL, colorCount, mode, mapper, flags);
+        }
+
+        return SkNEW_ARGS(SkTwoPointConicalGradient,
+                          (end, endRadius, start, startRadius, flipGradient, desc));
+    }
 }
 
 SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
index 6a24d0f15f03540264dd680c9439152d478b27bb..84fe49880ac60e5ef876e9f421db5e519055eabf 100644 (file)
@@ -166,7 +166,23 @@ protected:
     const uint16_t*     getCache16() const;
     const SkPMColor*    getCache32() const;
 
-    void commonAsAGradient(GradientInfo*) const;
+    void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
+    
+    /*
+     * Takes in pointers to gradient color and Rec info as colorSrc and recSrc respectively.
+     * Count is the number of colors in the gradient
+     * It will then flip all the color and rec information and return in their respective Dst
+     * pointers. It is assumed that space has already been allocated for the Dst pointers.
+     * The rec src and dst are only assumed to be valid if count > 2
+     */
+    static void FlipGradientColors(SkColor* colorDst, Rec* recDst,
+                                   SkColor* colorSrc, Rec* recSrc,
+                                   int count);
+
+    // V23_COMPATIBILITY_CODE
+    // Used for 2-pt conical gradients since we sort start/end cirlces by radius
+    // Assumes space has already been allocated for fOrigColors
+    void flipGradientColors();
 
 private:
     enum {
index de8c2364c364fe5f8081b4a2f91111575e38c6af..1e6a0d81268a28c30e630a3be37039510e761aea 100644 (file)
@@ -20,7 +20,7 @@ static int valid_divide(float numer, float denom, float* ratio) {
 
 // Return the number of distinct real roots, and write them into roots[] in
 // ascending order
-static int find_quad_roots(float A, float B, float C, float roots[2]) {
+static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) {
     SkASSERT(roots);
 
     if (A == 0) {
@@ -54,6 +54,9 @@ static int find_quad_roots(float A, float B, float C, float roots[2]) {
     float r1 = C / Q;
     roots[0] = r0 < r1 ? r0 : r1;
     roots[1] = r0 > r1 ? r0 : r1;
+    if (descendingOrder) {
+        SkTSwap(roots[0], roots[1]);
+    }
     return 2;
 }
 
@@ -64,7 +67,8 @@ static float lerp(float x, float dx, float t) {
 static float sqr(float x) { return x * x; }
 
 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
-                       const SkPoint& center1, SkScalar rad1) {
+                       const SkPoint& center1, SkScalar rad1,
+                       bool flipped) {
     fCenterX = SkScalarToFloat(center0.fX);
     fCenterY = SkScalarToFloat(center0.fY);
     fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
@@ -75,6 +79,8 @@ void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
     fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
     fRadius2 = sqr(fRadius);
     fRDR = fRadius * fDRadius;
+
+    fFlipped = flipped;
 }
 
 void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
@@ -90,7 +96,7 @@ SkFixed TwoPtRadial::nextT() {
     float roots[2];
 
     float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
-    int countRoots = find_quad_roots(fA, fB, C, roots);
+    int countRoots = find_quad_roots(fA, fB, C, roots, fFlipped);
 
     fRelX += fIncX;
     fRelY += fIncY;
@@ -169,7 +175,7 @@ static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
 }
 
 void SkTwoPointConicalGradient::init() {
-    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
+    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad);
     fPtsToUnit.reset();
 }
 
@@ -178,12 +184,13 @@ void SkTwoPointConicalGradient::init() {
 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
         const SkPoint& start, SkScalar startRadius,
         const SkPoint& end, SkScalar endRadius,
-        const Descriptor& desc)
+        bool flippedGrad, const Descriptor& desc)
     : SkGradientShaderBase(desc),
     fCenter1(start),
     fCenter2(end),
     fRadius1(startRadius),
-    fRadius2(endRadius) {
+    fRadius2(endRadius),
+    fFlippedGrad(flippedGrad) {
     // this is degenerate, and should be caught by our caller
     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
     this->init();
@@ -299,14 +306,19 @@ SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
     return kTwoPointConical_BitmapType;
 }
 
+// Returns the original non-sorted version of the gradient
 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
     GradientInfo* info) const {
     if (info) {
-        commonAsAGradient(info);
+        commonAsAGradient(info, fFlippedGrad);
         info->fPoint[0] = fCenter1;
         info->fPoint[1] = fCenter2;
         info->fRadius[0] = fRadius1;
         info->fRadius[1] = fRadius2;
+        if (fFlippedGrad) {
+            SkTSwap(info->fPoint[0], info->fPoint[1]);
+            SkTSwap(info->fRadius[0], info->fRadius[1]);
+        }
     }
     return kConical_GradientType;
 }
@@ -318,6 +330,20 @@ SkTwoPointConicalGradient::SkTwoPointConicalGradient(
     fCenter2(buffer.readPoint()),
     fRadius1(buffer.readScalar()),
     fRadius2(buffer.readScalar()) {
+    if (buffer.pictureVersion() >= 24 || 0 == buffer.pictureVersion()) {
+        fFlippedGrad = buffer.readBool();
+    } else {
+        // V23_COMPATIBILITY_CODE
+        // Sort gradient by radius size for old pictures
+        if (fRadius2 < fRadius1) {
+            SkTSwap(fCenter1, fCenter2);
+            SkTSwap(fRadius1, fRadius2);
+            this->flipGradientColors();
+            fFlippedGrad = true;
+        } else {
+            fFlippedGrad = false;
+        }
+    }
     this->init();
 };
 
@@ -328,6 +354,7 @@ void SkTwoPointConicalGradient::flatten(
     buffer.writePoint(fCenter2);
     buffer.writeScalar(fRadius1);
     buffer.writeScalar(fRadius2);
+    buffer.writeBool(fFlippedGrad);
 }
 
 #if SK_SUPPORT_GPU
index 041f8b8be44cd74e9c75c3da046e44a419689e7a..5548b179883af9107557ffcb4ab1d5a0383686fa 100644 (file)
@@ -23,9 +23,11 @@ struct TwoPtRadial {
     float   fA;
     float   fRadius2;
     float   fRDR;
+    bool    fFlipped;
 
     void init(const SkPoint& center0, SkScalar rad0,
-              const SkPoint& center1, SkScalar rad1);
+              const SkPoint& center1, SkScalar rad1,
+              bool flipped);
 
     // used by setup and nextT
     float   fRelX, fRelY, fIncX, fIncY;
@@ -47,7 +49,7 @@ class SkTwoPointConicalGradient : public SkGradientShaderBase {
 public:
     SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius,
                               const SkPoint& end, SkScalar endRadius,
-                              const Descriptor&);
+                              bool flippedGrad, const Descriptor&);
 
     virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
                            int count) SK_OVERRIDE;
@@ -68,6 +70,7 @@ public:
     const SkPoint& getStartCenter() const { return fCenter1; }
     const SkPoint& getEndCenter() const { return fCenter2; }
     SkScalar getEndRadius() const { return fRadius2; }
+    bool isFlippedGrad() const { return fFlippedGrad; }
 
     SK_TO_STRING_OVERRIDE()
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
@@ -77,11 +80,13 @@ protected:
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
 
 private:
+    SkPoint fCenter1;
+    SkPoint fCenter2;
+    SkScalar fRadius1;
+    SkScalar fRadius2;
+    bool fFlippedGrad;
+    
     typedef SkGradientShaderBase INHERITED;
-    const SkPoint fCenter1;
-    const SkPoint fCenter2;
-    const SkScalar fRadius1;
-    const SkScalar fRadius2;
 };
 
 #endif
index d7b03840172164af52a5ed7d1f68f4a47230165b..7cdb62dc44c516154b24dabacb83c4cddb77172f 100644 (file)
@@ -56,6 +56,7 @@ public:
 
     // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
     bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
+    bool isFlipped() const { return fIsFlipped; }
     SkScalar center() const { return fCenterX1; }
     SkScalar diffRadius() const { return fDiffRadius; }
     SkScalar radius() const { return fRadius0; }
@@ -68,7 +69,8 @@ private:
         return (INHERITED::onIsEqual(sBase) &&
                 this->fCenterX1 == s.fCenterX1 &&
                 this->fRadius0 == s.fRadius0 &&
-                this->fDiffRadius == s.fDiffRadius);
+                this->fDiffRadius == s.fDiffRadius &&
+                this->fIsFlipped == s.fIsFlipped);
     }
 
     Default2PtConicalEffect(GrContext* ctx,
@@ -78,7 +80,8 @@ private:
         : INHERITED(ctx, shader, matrix, tm),
         fCenterX1(shader.getCenterX1()),
         fRadius0(shader.getStartRadius()),
-        fDiffRadius(shader.getDiffRadius()) {
+        fDiffRadius(shader.getDiffRadius()),
+        fIsFlipped(shader.isFlippedGrad()) {
         // We pass the linear part of the quadratic as a varying.
         //    float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
         fBTransform = this->getCoordTransform();
@@ -103,6 +106,7 @@ private:
     SkScalar         fCenterX1;
     SkScalar         fRadius0;
     SkScalar         fDiffRadius;
+    bool             fIsFlipped;
 
     // @}
 
@@ -132,6 +136,7 @@ protected:
     const char* fFSVaryingName;
 
     bool fIsDegenerate;
+    bool fIsFlipped;
 
     // @{
     /// Values last uploaded as uniforms
@@ -194,6 +199,7 @@ GLDefault2PtConicalEffect::GLDefault2PtConicalEffect(const GrBackendEffectFactor
 
     const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>();
     fIsDegenerate = data.isDegenerate();
+    fIsFlipped = data.isFlipped();
 }
 
 void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
@@ -281,9 +287,14 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
         // Note: If there are two roots that both generate radius(t) > 0, the
         // Canvas spec says to choose the larger t.
 
-        // so we'll look at the larger one first:
-        builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
-                               r0Name.c_str(), r1Name.c_str());
+        // so we'll look at the larger one first (or smaller if flipped):
+        if (!fIsFlipped) {
+            builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
+                                   r0Name.c_str(), r1Name.c_str());
+        } else {
+            builder->fsCodeAppendf("\t\tfloat %s = min(%s, %s);\n", tName.c_str(),
+                                   r0Name.c_str(), r1Name.c_str());
+        }
 
         // if r(t) > 0, then we're done; t will be our x coordinate
         builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
@@ -294,8 +305,13 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
 
         // otherwise, if r(t) for the larger root was <= 0, try the other root
         builder->fsCodeAppend("\t\t} else {\n");
-        builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
-                               r0Name.c_str(), r1Name.c_str());
+        if (!fIsFlipped) {
+            builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
+                                   r0Name.c_str(), r1Name.c_str());
+        } else {
+            builder->fsCodeAppendf("\t\t\t%s = max(%s, %s);\n", tName.c_str(),
+                                   r0Name.c_str(), r1Name.c_str());
+        }
 
         // if r(t) > 0 for the smaller root, then t will be our x coordinate
         builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
@@ -330,6 +346,7 @@ void GLDefault2PtConicalEffect::setData(const GrGLUniformManager& uman,
     INHERITED::setData(uman, drawEffect);
     const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>();
     SkASSERT(data.isDegenerate() == fIsDegenerate);
+    SkASSERT(data.isFlipped() == fIsFlipped);
     SkScalar centerX1 = data.center();
     SkScalar radius0 = data.radius();
     SkScalar diffRadius = data.diffRadius();
@@ -365,12 +382,16 @@ GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& draw
                                                    const GrGLCaps&) {
     enum {
         kIsDegenerate = 1 << kBaseKeyBitCnt,
+        kIsFlipped = 1 << (kBaseKeyBitCnt + 1),
     };
 
     EffectKey key = GenBaseGradientKey(drawEffect);
     if (drawEffect.castEffect<Default2PtConicalEffect>().isDegenerate()) {
         key |= kIsDegenerate;
     }
+    if (drawEffect.castEffect<Default2PtConicalEffect>().isFlipped()) {
+        key |= kIsFlipped;
+    }
     return key;
 }