Extract most of the mutable state of SkShader into a separate Context object.

SkShader currently stores some state during draw calls via setContext(...).
Move that mutable state into a separate SkShader::Context class that is
constructed on demand for the duration of the draw.

Calls to setContext() are replaced with createContext() which returns a context
corresponding to the shader object or NULL if the parameters to createContext
are invalid.

TEST=out/Debug/dm
BUG=skia:1976
R=scroggo@google.com, skyostil@chromium.org, tomhudson@chromium.org, senorblanco@chromium.org, reed@google.com, bungeman@google.com

Author: dominikg@chromium.org

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14216 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index de8c236..b0955a2 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -9,6 +9,18 @@
 
 #include "SkTwoPointConicalGradient_gpu.h"
 
+struct TwoPtRadialContext {
+    const TwoPtRadial&  fRec;
+    float               fRelX, fRelY;
+    const float         fIncX, fIncY;
+    float               fB;
+    const float         fDB;
+
+    TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
+                       SkScalar dfx, SkScalar dfy);
+    SkFixed nextT();
+};
+
 static int valid_divide(float numer, float denom, float* ratio) {
     SkASSERT(ratio);
     if (0 == denom) {
@@ -77,47 +89,48 @@
     fRDR = fRadius * fDRadius;
 }
 
-void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
-    fRelX = SkScalarToFloat(fx) - fCenterX;
-    fRelY = SkScalarToFloat(fy) - fCenterY;
-    fIncX = SkScalarToFloat(dfx);
-    fIncY = SkScalarToFloat(dfy);
-    fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
-    fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
-}
+TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy,
+                                       SkScalar dfx, SkScalar dfy)
+    : fRec(rec)
+    , fRelX(SkScalarToFloat(fx) - rec.fCenterX)
+    , fRelY(SkScalarToFloat(fy) - rec.fCenterY)
+    , fIncX(SkScalarToFloat(dfx))
+    , fIncY(SkScalarToFloat(dfy))
+    , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR))
+    , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {}
 
-SkFixed TwoPtRadial::nextT() {
+SkFixed TwoPtRadialContext::nextT() {
     float roots[2];
 
-    float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
-    int countRoots = find_quad_roots(fA, fB, C, roots);
+    float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
+    int countRoots = find_quad_roots(fRec.fA, fB, C, roots);
 
     fRelX += fIncX;
     fRelY += fIncY;
     fB += fDB;
 
     if (0 == countRoots) {
-        return kDontDrawT;
+        return TwoPtRadial::kDontDrawT;
     }
 
     // Prefer the bigger t value if both give a radius(t) > 0
     // find_quad_roots returns the values sorted, so we start with the last
     float t = roots[countRoots - 1];
-    float r = lerp(fRadius, fDRadius, t);
+    float r = lerp(fRec.fRadius, fRec.fDRadius, t);
     if (r <= 0) {
         t = roots[0];   // might be the same as roots[countRoots-1]
-        r = lerp(fRadius, fDRadius, t);
+        r = lerp(fRec.fRadius, fRec.fDRadius, t);
         if (r <= 0) {
-            return kDontDrawT;
+            return TwoPtRadial::kDontDrawT;
         }
     }
     return SkFloatToFixed(t);
 }
 
-typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC,
+typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC,
                                     const SkPMColor* cache, int toggle, int count);
 
-static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
                            const SkPMColor* SK_RESTRICT cache, int toggle,
                            int count) {
     for (; count > 0; --count) {
@@ -134,7 +147,7 @@
     }
 }
 
-static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
                             const SkPMColor* SK_RESTRICT cache, int toggle,
                             int count) {
     for (; count > 0; --count) {
@@ -151,7 +164,7 @@
     }
 }
 
-static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC,
                             const SkPMColor* SK_RESTRICT cache, int toggle,
                             int count) {
     for (; count > 0; --count) {
@@ -196,8 +209,39 @@
     return false;
 }
 
-void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
-                                          int count) {
+size_t SkTwoPointConicalGradient::contextSize() const {
+    return sizeof(TwoPointConicalGradientContext);
+}
+
+SkShader::Context* SkTwoPointConicalGradient::createContext(
+        const SkBitmap& device, const SkPaint& paint,
+        const SkMatrix& matrix, void* storage) const {
+    if (!this->validContext(device, paint, matrix)) {
+        return NULL;
+    }
+
+    return SkNEW_PLACEMENT_ARGS(storage, TwoPointConicalGradientContext,
+                                (*this, device, paint, matrix));
+}
+
+SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext(
+        const SkTwoPointConicalGradient& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix)
+{
+    // we don't have a span16 proc
+    fFlags &= ~kHasSpan16_Flag;
+
+    // in general, we might discard based on computed-radius, so clear
+    // this flag (todo: sometimes we can detect that we never discard...)
+    fFlags &= ~kOpaqueAlpha_Flag;
+}
+
+void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan(
+        int x, int y, SkPMColor* dstCParam, int count) {
+    const SkTwoPointConicalGradient& twoPointConicalGradient =
+            static_cast<const SkTwoPointConicalGradient&>(fShader);
+
     int toggle = init_dither_toggle(x, y);
 
     SkASSERT(count > 0);
@@ -206,15 +250,15 @@
 
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
 
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
 
     TwoPointConicalProc shadeProc = twopoint_repeat;
-    if (SkShader::kClamp_TileMode == fTileMode) {
+    if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) {
         shadeProc = twopoint_clamp;
-    } else if (SkShader::kMirror_TileMode == fTileMode) {
+    } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) {
         shadeProc = twopoint_mirror;
     } else {
-        SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode);
     }
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -235,16 +279,16 @@
             dy = fDstToIndex.getSkewY();
         }
 
-        fRec.setup(fx, fy, dx, dy);
-        (*shadeProc)(&fRec, dstC, cache, toggle, count);
+        TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy);
+        (*shadeProc)(&rec, dstC, cache, toggle, count);
     } else {    // perspective case
         SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
         SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
         for (; count > 0; --count) {
             SkPoint srcPt;
             dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
-            (*shadeProc)(&fRec, dstC, cache, toggle, 1);
+            TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0);
+            (*shadeProc)(&rec, dstC, cache, toggle, 1);
 
             dstX += SK_Scalar1;
             toggle = next_dither_toggle(toggle);
@@ -253,23 +297,6 @@
     }
 }
 
-bool SkTwoPointConicalGradient::setContext(const SkBitmap& device,
-                                           const SkPaint& paint,
-                                           const SkMatrix& matrix) {
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
-    }
-
-    // we don't have a span16 proc
-    fFlags &= ~kHasSpan16_Flag;
-
-    // in general, we might discard based on computed-radius, so clear
-    // this flag (todo: sometimes we can detect that we never discard...)
-    fFlags &= ~kOpaqueAlpha_Flag;
-
-    return true;
-}
-
 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
     SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
     SkPoint diff = fCenter2 - fCenter1;