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

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

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@14323 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkPerlinNoiseShader.cpp b/src/effects/SkPerlinNoiseShader.cpp
index ed63faf..5adb582 100644
--- a/src/effects/SkPerlinNoiseShader.cpp
+++ b/src/effects/SkPerlinNoiseShader.cpp
@@ -278,7 +278,6 @@
   , fStitchTiles(!fTileSize.isEmpty())
 {
     SkASSERT(numOctaves >= 0 && numOctaves < 256);
-    fMatrix.reset();
     fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY));
 }
 
@@ -293,7 +292,6 @@
     fStitchTiles    = buffer.readBool();
     fTileSize.fWidth  = buffer.readInt();
     fTileSize.fHeight = buffer.readInt();
-    fMatrix.reset();
     fPaintingData = SkNEW_ARGS(PaintingData, (fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY));
     buffer.validate(perlin_noise_type_is_valid(fType) &&
                     (fNumOctaves >= 0) && (fNumOctaves <= 255) &&
@@ -317,9 +315,9 @@
     buffer.writeInt(fTileSize.fHeight);
 }
 
-SkScalar SkPerlinNoiseShader::noise2D(int channel, const PaintingData& paintingData,
-                                      const StitchData& stitchData,
-                                      const SkPoint& noiseVector) const {
+SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D(
+        int channel, const PaintingData& paintingData,
+        const StitchData& stitchData, const SkPoint& noiseVector) const {
     struct Noise {
         int noisePositionIntegerValue;
         SkScalar noisePositionFractionValue;
@@ -333,8 +331,9 @@
     Noise noiseX(noiseVector.x());
     Noise noiseY(noiseVector.y());
     SkScalar u, v;
+    const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
     // If stitching, adjust lattice points accordingly.
-    if (fStitchTiles) {
+    if (perlinNoiseShader.fStitchTiles) {
         noiseX.noisePositionIntegerValue =
             checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
         noiseY.noisePositionIntegerValue =
@@ -365,11 +364,11 @@
     return SkScalarInterp(a, b, sy);
 }
 
-SkScalar SkPerlinNoiseShader::calculateTurbulenceValueForPoint(int channel,
-                                                               const PaintingData& paintingData,
-                                                               StitchData& stitchData,
-                                                               const SkPoint& point) const {
-    if (fStitchTiles) {
+SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
+        int channel, const PaintingData& paintingData,
+        StitchData& stitchData, const SkPoint& point) const {
+    const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
+    if (perlinNoiseShader.fStitchTiles) {
         // Set up TurbulenceInitial stitch values.
         stitchData = paintingData.fStitchDataInit;
     }
@@ -377,14 +376,14 @@
     SkPoint noiseVector(SkPoint::Make(SkScalarMul(point.x(), paintingData.fBaseFrequency.fX),
                                       SkScalarMul(point.y(), paintingData.fBaseFrequency.fY)));
     SkScalar ratio = SK_Scalar1;
-    for (int octave = 0; octave < fNumOctaves; ++octave) {
+    for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
         SkScalar noise = noise2D(channel, paintingData, stitchData, noiseVector);
         turbulenceFunctionResult += SkScalarDiv(
-            (fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio);
+            (perlinNoiseShader.fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise), ratio);
         noiseVector.fX *= 2;
         noiseVector.fY *= 2;
         ratio *= 2;
-        if (fStitchTiles) {
+        if (perlinNoiseShader.fStitchTiles) {
             // Update stitch values
             stitchData.fWidth  *= 2;
             stitchData.fWrapX   = stitchData.fWidth + kPerlinNoise;
@@ -395,7 +394,7 @@
 
     // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
     // by fractalNoise and (turbulenceFunctionResult) by turbulence.
-    if (fType == kFractalNoise_Type) {
+    if (perlinNoiseShader.fType == kFractalNoise_Type) {
         turbulenceFunctionResult =
             SkScalarMul(turbulenceFunctionResult, SK_ScalarHalf) + SK_ScalarHalf;
     }
@@ -409,7 +408,9 @@
     return SkScalarPin(turbulenceFunctionResult, 0, SK_Scalar1);
 }
 
-SkPMColor SkPerlinNoiseShader::shade(const SkPoint& point, StitchData& stitchData) const {
+SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade(
+        const SkPoint& point, StitchData& stitchData) const {
+    const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
     SkPoint newPoint;
     fMatrix.mapPoints(&newPoint, &point, 1);
     newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
@@ -418,15 +419,32 @@
     U8CPU rgba[4];
     for (int channel = 3; channel >= 0; --channel) {
         rgba[channel] = SkScalarFloorToInt(255 *
-            calculateTurbulenceValueForPoint(channel, *fPaintingData, stitchData, newPoint));
+            calculateTurbulenceValueForPoint(channel, *perlinNoiseShader.fPaintingData,
+                                             stitchData, newPoint));
     }
     return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
 }
 
-bool SkPerlinNoiseShader::setContext(const SkBitmap& device, const SkPaint& paint,
-                                     const SkMatrix& matrix) {
+SkShader::Context* SkPerlinNoiseShader::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, PerlinNoiseShaderContext, (*this, device, paint, matrix));
+}
+
+size_t SkPerlinNoiseShader::contextSize() const {
+    return sizeof(PerlinNoiseShaderContext);
+}
+
+SkPerlinNoiseShader::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
+        const SkPerlinNoiseShader& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix)
+{
     SkMatrix newMatrix = matrix;
-    newMatrix.postConcat(getLocalMatrix());
+    newMatrix.postConcat(shader.getLocalMatrix());
     SkMatrix invMatrix;
     if (!newMatrix.invert(&invMatrix)) {
         invMatrix.reset();
@@ -437,10 +455,10 @@
     newMatrix.postConcat(invMatrix);
     newMatrix.postConcat(invMatrix);
     fMatrix = newMatrix;
-    return INHERITED::setContext(device, paint, matrix);
 }
 
-void SkPerlinNoiseShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
+void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan(
+        int x, int y, SkPMColor result[], int count) {
     SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
     StitchData stitchData;
     for (int i = 0; i < count; ++i) {
@@ -449,7 +467,8 @@
     }
 }
 
-void SkPerlinNoiseShader::shadeSpan16(int x, int y, uint16_t result[], int count) {
+void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan16(
+        int x, int y, uint16_t result[], int count) {
     SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
     StitchData stitchData;
     DITHER_565_SCAN(y);
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
index bd8b99a..0997e62 100644
--- a/src/effects/SkTransparentShader.cpp
+++ b/src/effects/SkTransparentShader.cpp
@@ -11,26 +11,40 @@
 #include "SkColorPriv.h"
 #include "SkString.h"
 
-bool SkTransparentShader::setContext(const SkBitmap& device,
-                                     const SkPaint& paint,
-                                     const SkMatrix& matrix) {
-    fDevice = &device;
-    fAlpha = paint.getAlpha();
+SkShader::Context* SkTransparentShader::createContext(const SkBitmap& device,
+                                                      const SkPaint& paint,
+                                                      const SkMatrix& matrix,
+                                                      void* storage) const {
+    if (!this->validContext(device, paint, matrix)) {
+        return NULL;
+    }
 
-    return this->INHERITED::setContext(device, paint, matrix);
+    return SkNEW_PLACEMENT_ARGS(storage, TransparentShaderContext, (*this, device, paint, matrix));
 }
 
-uint32_t SkTransparentShader::getFlags() {
+size_t SkTransparentShader::contextSize() const {
+    return sizeof(TransparentShaderContext);
+}
+
+SkTransparentShader::TransparentShaderContext::TransparentShaderContext(
+        const SkTransparentShader& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix)
+    , fDevice(&device) {}
+
+SkTransparentShader::TransparentShaderContext::~TransparentShaderContext() {}
+
+uint32_t SkTransparentShader::TransparentShaderContext::getFlags() const {
     uint32_t flags = this->INHERITED::getFlags();
 
     switch (fDevice->colorType()) {
         case kRGB_565_SkColorType:
             flags |= kHasSpan16_Flag;
-            if (fAlpha == 255)
+            if (this->getPaintAlpha() == 255)
                 flags |= kOpaqueAlpha_Flag;
             break;
         case kN32_SkColorType:
-            if (fAlpha == 255 && fDevice->isOpaque())
+            if (this->getPaintAlpha() == 255 && fDevice->isOpaque())
                 flags |= kOpaqueAlpha_Flag;
             break;
         default:
@@ -39,8 +53,9 @@
     return flags;
 }
 
-void SkTransparentShader::shadeSpan(int x, int y, SkPMColor span[], int count) {
-    unsigned scale = SkAlpha255To256(fAlpha);
+void SkTransparentShader::TransparentShaderContext::shadeSpan(int x, int y, SkPMColor span[],
+                                                              int count) {
+    unsigned scale = SkAlpha255To256(this->getPaintAlpha());
 
     switch (fDevice->colorType()) {
         case kN32_SkColorType:
@@ -63,7 +78,7 @@
                     span[i] = SkPixel16ToPixel32(src[i]);
                 }
             } else {
-                unsigned alpha = fAlpha;
+                unsigned alpha = this->getPaintAlpha();
                 for (int i = count - 1; i >= 0; --i) {
                     uint16_t c = src[i];
                     unsigned r = SkPacked16ToR32(c);
@@ -97,7 +112,8 @@
     }
 }
 
-void SkTransparentShader::shadeSpan16(int x, int y, uint16_t span[], int count) {
+void SkTransparentShader::TransparentShaderContext::shadeSpan16(int x, int y, uint16_t span[],
+                                                                int count) {
     SkASSERT(fDevice->colorType() == kRGB_565_SkColorType);
 
     uint16_t* src = fDevice->getAddr16(x, y);
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
index 2e92076..46e0c95 100644
--- a/src/effects/gradients/SkGradientShader.cpp
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -15,8 +15,6 @@
 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc) {
     SkASSERT(desc.fCount > 1);
 
-    fCacheAlpha = 256;  // init to a value that paint.getAlpha() can't return
-
     fMapper = desc.fMapper;
     SkSafeRef(fMapper);
     fGradFlags = SkToU8(desc.fGradFlags);
@@ -26,10 +24,6 @@
     fTileMode = desc.fTileMode;
     fTileProc = gTileProcs[desc.fTileMode];
 
-    fCache16 = fCache16Storage = NULL;
-    fCache32 = NULL;
-    fCache32PixelRef = NULL;
-
     /*  Note: we let the caller skip the first and/or last position.
         i.e. pos[0] = 0.3, pos[1] = 0.7
         In these cases, we insert dummy entries to ensure that the final data
@@ -146,14 +140,8 @@
 }
 
 SkGradientShaderBase::SkGradientShaderBase(SkReadBuffer& buffer) : INHERITED(buffer) {
-    fCacheAlpha = 256;
-
     fMapper = buffer.readUnitMapper();
 
-    fCache16 = fCache16Storage = NULL;
-    fCache32 = NULL;
-    fCache32PixelRef = NULL;
-
     int colorCount = fColorCount = buffer.getArrayCount();
     if (colorCount > kColorStorageCount) {
         size_t allocSize = (sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec)) * colorCount;
@@ -188,10 +176,6 @@
 }
 
 SkGradientShaderBase::~SkGradientShaderBase() {
-    if (fCache16Storage) {
-        sk_free(fCache16Storage);
-    }
-    SkSafeUnref(fCache32PixelRef);
     if (fOrigColors != fStorage) {
         sk_free(fOrigColors);
     }
@@ -199,7 +183,6 @@
 }
 
 void SkGradientShaderBase::initCommon() {
-    fFlags = 0;
     unsigned colorAlpha = 0xFF;
     for (int i = 0; i < fColorCount; i++) {
         colorAlpha &= SkColorGetA(fOrigColors[i]);
@@ -267,49 +250,50 @@
     return fColorsAreOpaque;
 }
 
-bool SkGradientShaderBase::setContext(const SkBitmap& device,
-                                 const SkPaint& paint,
-                                 const SkMatrix& matrix) {
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
-    }
-
+SkGradientShaderBase::GradientShaderBaseContext::GradientShaderBaseContext(
+        const SkGradientShaderBase& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix)
+    , fCache(shader.refCache(getPaintAlpha()))
+{
     const SkMatrix& inverse = this->getTotalInverse();
 
-    fDstToIndex.setConcat(fPtsToUnit, inverse);
+    fDstToIndex.setConcat(shader.fPtsToUnit, inverse);
+
     fDstToIndexProc = fDstToIndex.getMapXYProc();
-    fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
+    fDstToIndexClass = (uint8_t)SkShader::Context::ComputeMatrixClass(fDstToIndex);
 
     // now convert our colors in to PMColors
     unsigned paintAlpha = this->getPaintAlpha();
 
     fFlags = this->INHERITED::getFlags();
-    if (fColorsAreOpaque && paintAlpha == 0xFF) {
+    if (shader.fColorsAreOpaque && paintAlpha == 0xFF) {
         fFlags |= kOpaqueAlpha_Flag;
     }
     // we can do span16 as long as our individual colors are opaque,
     // regardless of the paint's alpha
-    if (fColorsAreOpaque) {
+    if (shader.fColorsAreOpaque) {
         fFlags |= kHasSpan16_Flag;
     }
-
-    this->setCacheAlpha(paintAlpha);
-    return true;
 }
 
-void SkGradientShaderBase::setCacheAlpha(U8CPU alpha) const {
-    // if the new alpha differs from the previous time we were called, inval our cache
-    // this will trigger the cache to be rebuilt.
-    // we don't care about the first time, since the cache ptrs will already be NULL
-    if (fCacheAlpha != alpha) {
-        fCache16 = NULL;            // inval the cache
-        fCache32 = NULL;            // inval the cache
-        fCacheAlpha = alpha;        // record the new alpha
-        // inform our subclasses
-        if (fCache32PixelRef) {
-            fCache32PixelRef->notifyPixelsChanged();
-        }
-    }
+SkGradientShaderBase::GradientShaderCache::GradientShaderCache(
+        U8CPU alpha, const SkGradientShaderBase& shader)
+    : fCacheAlpha(alpha)
+    , fShader(shader)
+    , fCache16Inited(false)
+    , fCache32Inited(false)
+{
+    // Only initialize the cache in getCache16/32.
+    fCache16 = NULL;
+    fCache32 = NULL;
+    fCache16Storage = NULL;
+    fCache32PixelRef = NULL;
+}
+
+SkGradientShaderBase::GradientShaderCache::~GradientShaderCache() {
+    sk_free(fCache16Storage);
+    SkSafeUnref(fCache32PixelRef);
 }
 
 #define Fixed_To_Dot8(x)        (((x) + 0x80) >> 8)
@@ -318,8 +302,8 @@
     build a 16bit table as long as the original colors are opaque, even if the
     paint specifies a non-opaque alpha.
 */
-void SkGradientShaderBase::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
-                                      int count) {
+void SkGradientShaderBase::GradientShaderCache::Build16bitCache(
+        uint16_t cache[], SkColor c0, SkColor c1, int count) {
     SkASSERT(count > 1);
     SkASSERT(SkColorGetA(c0) == 0xFF);
     SkASSERT(SkColorGetA(c1) == 0xFF);
@@ -367,8 +351,9 @@
  */
 typedef uint32_t SkUFixed;
 
-void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
-                                      int count, U8CPU paintAlpha, uint32_t gradFlags) {
+void SkGradientShaderBase::GradientShaderCache::Build32bitCache(
+        SkPMColor cache[], SkColor c0, SkColor c1,
+        int count, U8CPU paintAlpha, uint32_t gradFlags) {
     SkASSERT(count > 1);
 
     // need to apply paintAlpha to our two endpoints
@@ -511,99 +496,123 @@
     return 0;
 }
 
-const uint16_t* SkGradientShaderBase::getCache16() const {
-    if (fCache16 == NULL) {
-        // double the count for dither entries
-        const int entryCount = kCache16Count * 2;
-        const size_t allocSize = sizeof(uint16_t) * entryCount;
-
-        if (fCache16Storage == NULL) { // set the storage and our working ptr
-            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
-        }
-        fCache16 = fCache16Storage;
-        if (fColorCount == 2) {
-            Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
-                            kCache16Count);
-        } else {
-            Rec* rec = fRecs;
-            int prevIndex = 0;
-            for (int i = 1; i < fColorCount; i++) {
-                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
-                SkASSERT(nextIndex < kCache16Count);
-
-                if (nextIndex > prevIndex)
-                    Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
-                prevIndex = nextIndex;
-            }
-        }
-
-        if (fMapper) {
-            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
-            uint16_t* linear = fCache16;         // just computed linear data
-            uint16_t* mapped = fCache16Storage;  // storage for mapped data
-            SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kCache16Count; i++) {
-                int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
-                mapped[i] = linear[index];
-                mapped[i + kCache16Count] = linear[index + kCache16Count];
-            }
-            sk_free(fCache16);
-            fCache16 = fCache16Storage;
-        }
-    }
+const uint16_t* SkGradientShaderBase::GradientShaderCache::getCache16() {
+    SkOnce(&fCache16Inited, &fCache16Mutex, SkGradientShaderBase::GradientShaderCache::initCache16,
+           this);
+    SkASSERT(fCache16);
     return fCache16;
 }
 
-const SkPMColor* SkGradientShaderBase::getCache32() const {
-    if (fCache32 == NULL) {
-        SkImageInfo info;
-        info.fWidth = kCache32Count;
-        info.fHeight = 4;   // for our 4 dither rows
-        info.fAlphaType = kPremul_SkAlphaType;
-        info.fColorType = kN32_SkColorType;
+void SkGradientShaderBase::GradientShaderCache::initCache16(GradientShaderCache* cache) {
+    // double the count for dither entries
+    const int entryCount = kCache16Count * 2;
+    const size_t allocSize = sizeof(uint16_t) * entryCount;
 
-        if (NULL == fCache32PixelRef) {
-            fCache32PixelRef = SkMallocPixelRef::NewAllocate(info, 0, NULL);
-        }
-        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
-        if (fColorCount == 2) {
-            Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
-                            kCache32Count, fCacheAlpha, fGradFlags);
-        } else {
-            Rec* rec = fRecs;
-            int prevIndex = 0;
-            for (int i = 1; i < fColorCount; i++) {
-                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
-                SkASSERT(nextIndex < kCache32Count);
+    SkASSERT(NULL == cache->fCache16Storage);
+    cache->fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+    cache->fCache16 = cache->fCache16Storage;
+    if (cache->fShader.fColorCount == 2) {
+        Build16bitCache(cache->fCache16, cache->fShader.fOrigColors[0],
+                        cache->fShader.fOrigColors[1], kCache16Count);
+    } else {
+        Rec* rec = cache->fShader.fRecs;
+        int prevIndex = 0;
+        for (int i = 1; i < cache->fShader.fColorCount; i++) {
+            int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
+            SkASSERT(nextIndex < kCache16Count);
 
-                if (nextIndex > prevIndex)
-                    Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
-                                    fOrigColors[i], nextIndex - prevIndex + 1,
-                                    fCacheAlpha, fGradFlags);
-                prevIndex = nextIndex;
-            }
-        }
-
-        if (fMapper) {
-            SkMallocPixelRef* newPR = SkMallocPixelRef::NewAllocate(info, 0, NULL);
-            SkPMColor* linear = fCache32;           // just computed linear data
-            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
-            SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kCache32Count; i++) {
-                int index = map->mapUnit16((i << 8) | i) >> 8;
-                mapped[i + kCache32Count*0] = linear[index + kCache32Count*0];
-                mapped[i + kCache32Count*1] = linear[index + kCache32Count*1];
-                mapped[i + kCache32Count*2] = linear[index + kCache32Count*2];
-                mapped[i + kCache32Count*3] = linear[index + kCache32Count*3];
-            }
-            fCache32PixelRef->unref();
-            fCache32PixelRef = newPR;
-            fCache32 = (SkPMColor*)newPR->getAddr();
+            if (nextIndex > prevIndex)
+                Build16bitCache(cache->fCache16 + prevIndex, cache->fShader.fOrigColors[i-1],
+                                cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1);
+            prevIndex = nextIndex;
         }
     }
+
+    if (cache->fShader.fMapper) {
+        cache->fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+        uint16_t* linear = cache->fCache16;         // just computed linear data
+        uint16_t* mapped = cache->fCache16Storage;  // storage for mapped data
+        SkUnitMapper* map = cache->fShader.fMapper;
+        for (int i = 0; i < kCache16Count; i++) {
+            int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
+            mapped[i] = linear[index];
+            mapped[i + kCache16Count] = linear[index + kCache16Count];
+        }
+        sk_free(cache->fCache16);
+        cache->fCache16 = cache->fCache16Storage;
+    }
+}
+
+const SkPMColor* SkGradientShaderBase::GradientShaderCache::getCache32() {
+    SkOnce(&fCache32Inited, &fCache32Mutex, SkGradientShaderBase::GradientShaderCache::initCache32,
+           this);
+    SkASSERT(fCache32);
     return fCache32;
 }
 
+void SkGradientShaderBase::GradientShaderCache::initCache32(GradientShaderCache* cache) {
+    SkImageInfo info;
+    info.fWidth = kCache32Count;
+    info.fHeight = 4;   // for our 4 dither rows
+    info.fAlphaType = kPremul_SkAlphaType;
+    info.fColorType = kN32_SkColorType;
+
+    SkASSERT(NULL == cache->fCache32PixelRef);
+    cache->fCache32PixelRef = SkMallocPixelRef::NewAllocate(info, 0, NULL);
+    cache->fCache32 = (SkPMColor*)cache->fCache32PixelRef->getAddr();
+    if (cache->fShader.fColorCount == 2) {
+        Build32bitCache(cache->fCache32, cache->fShader.fOrigColors[0],
+                        cache->fShader.fOrigColors[1], kCache32Count, cache->fCacheAlpha,
+                        cache->fShader.fGradFlags);
+    } else {
+        Rec* rec = cache->fShader.fRecs;
+        int prevIndex = 0;
+        for (int i = 1; i < cache->fShader.fColorCount; i++) {
+            int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
+            SkASSERT(nextIndex < kCache32Count);
+
+            if (nextIndex > prevIndex)
+                Build32bitCache(cache->fCache32 + prevIndex, cache->fShader.fOrigColors[i-1],
+                                cache->fShader.fOrigColors[i], nextIndex - prevIndex + 1,
+                                cache->fCacheAlpha, cache->fShader.fGradFlags);
+            prevIndex = nextIndex;
+        }
+    }
+
+    if (cache->fShader.fMapper) {
+        SkMallocPixelRef* newPR = SkMallocPixelRef::NewAllocate(info, 0, NULL);
+        SkPMColor* linear = cache->fCache32;           // just computed linear data
+        SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
+        SkUnitMapper* map = cache->fShader.fMapper;
+        for (int i = 0; i < kCache32Count; i++) {
+            int index = map->mapUnit16((i << 8) | i) >> 8;
+            mapped[i + kCache32Count*0] = linear[index + kCache32Count*0];
+            mapped[i + kCache32Count*1] = linear[index + kCache32Count*1];
+            mapped[i + kCache32Count*2] = linear[index + kCache32Count*2];
+            mapped[i + kCache32Count*3] = linear[index + kCache32Count*3];
+        }
+        cache->fCache32PixelRef->unref();
+        cache->fCache32PixelRef = newPR;
+        cache->fCache32 = (SkPMColor*)newPR->getAddr();
+    }
+}
+
+/*
+ *  The gradient holds a cache for the most recent value of alpha. Successive
+ *  callers with the same alpha value will share the same cache.
+ */
+SkGradientShaderBase::GradientShaderCache* SkGradientShaderBase::refCache(U8CPU alpha) const {
+    SkAutoMutexAcquire ama(fCacheMutex);
+    if (!fCache || fCache->getAlpha() != alpha) {
+        fCache.reset(SkNEW_ARGS(GradientShaderCache, (alpha, *this)));
+    }
+    // Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
+    // Otherwise, the pointer may have been overwritten on a different thread before the object's
+    // ref count was incremented.
+    fCache.get()->ref();
+    return fCache;
+}
+
 /*
  *  Because our caller might rebuild the same (logically the same) gradient
  *  over and over, we'd like to return exactly the same "bitmap" if possible,
@@ -615,14 +624,14 @@
 void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
     // our caller assumes no external alpha, so we ensure that our cache is
     // built with 0xFF
-    this->setCacheAlpha(0xFF);
+    SkAutoTUnref<GradientShaderCache> cache(this->refCache(0xFF));
 
     // don't have a way to put the mapper into our cache-key yet
     if (fMapper) {
-        // force our cahce32pixelref to be built
-        (void)this->getCache32();
+        // force our cache32pixelref to be built
+        (void)cache->getCache32();
         bitmap->setConfig(SkImageInfo::MakeN32Premul(kCache32Count, 1));
-        bitmap->setPixelRef(fCache32PixelRef);
+        bitmap->setPixelRef(cache->getCache32PixelRef());
         return;
     }
 
@@ -661,9 +670,9 @@
 
     if (!gCache->find(storage.get(), size, bitmap)) {
         // force our cahce32pixelref to be built
-        (void)this->getCache32();
+        (void)cache->getCache32();
         bitmap->setConfig(SkImageInfo::MakeN32Premul(kCache32Count, 1));
-        bitmap->setPixelRef(fCache32PixelRef);
+        bitmap->setPixelRef(cache->getCache32PixelRef());
 
         gCache->add(storage.get(), size, *bitmap);
     }
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
index 02bb50b..5dec665 100644
--- a/src/effects/gradients/SkGradientShaderPriv.h
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -19,6 +19,7 @@
 #include "SkTemplates.h"
 #include "SkBitmapCache.h"
 #include "SkShader.h"
+#include "SkOnce.h"
 
 static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
                                int count) {
@@ -101,8 +102,64 @@
     SkGradientShaderBase(const Descriptor& desc);
     virtual ~SkGradientShaderBase();
 
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
-    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    // The cache is initialized on-demand when getCache16/32 is called.
+    class GradientShaderCache : public SkRefCnt {
+    public:
+        GradientShaderCache(U8CPU alpha, const SkGradientShaderBase& shader);
+        ~GradientShaderCache();
+
+        const uint16_t*     getCache16();
+        const SkPMColor*    getCache32();
+
+        SkMallocPixelRef* getCache32PixelRef() const { return fCache32PixelRef; }
+
+        unsigned getAlpha() const { return fCacheAlpha; }
+
+    private:
+        // Working pointers. If either is NULL, we need to recompute the corresponding cache values.
+        uint16_t*   fCache16;
+        SkPMColor*  fCache32;
+
+        uint16_t*         fCache16Storage;    // Storage for fCache16, allocated on demand.
+        SkMallocPixelRef* fCache32PixelRef;
+        const unsigned    fCacheAlpha;        // The alpha value we used when we computed the cache.
+                                              // Larger than 8bits so we can store uninitialized
+                                              // value.
+
+        const SkGradientShaderBase& fShader;
+
+        // Make sure we only initialize the caches once.
+        bool    fCache16Inited, fCache32Inited;
+        SkMutex fCache16Mutex, fCache32Mutex;
+
+        static void initCache16(GradientShaderCache* cache);
+        static void initCache32(GradientShaderCache* cache);
+
+        static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
+        static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
+                                    U8CPU alpha, uint32_t gradFlags);
+    };
+
+    class GradientShaderBaseContext : public SkShader::Context {
+    public:
+        GradientShaderBaseContext(const SkGradientShaderBase& shader, const SkBitmap& device,
+                                  const SkPaint& paint, const SkMatrix& matrix);
+        ~GradientShaderBaseContext() {}
+
+        virtual uint32_t getFlags() const SK_OVERRIDE { return fFlags; }
+
+    protected:
+        SkMatrix    fDstToIndex;
+        SkMatrix::MapXYProc fDstToIndexProc;
+        uint8_t     fDstToIndexClass;
+        uint8_t     fFlags;
+
+        SkAutoTUnref<GradientShaderCache> fCache;
+
+    private:
+        typedef SkShader::Context INHERITED;
+    };
+
     virtual bool isOpaque() const SK_OVERRIDE;
 
     void getGradientTableBitmap(SkBitmap*) const;
@@ -148,13 +205,9 @@
 
     SkUnitMapper* fMapper;
     SkMatrix    fPtsToUnit;     // set by subclass
-    SkMatrix    fDstToIndex;
-    SkMatrix::MapXYProc fDstToIndexProc;
     TileMode    fTileMode;
     TileProc    fTileProc;
     int         fColorCount;
-    uint8_t     fDstToIndexClass;
-    uint8_t     fFlags;
     uint8_t     fGradFlags;
 
     struct Rec {
@@ -163,9 +216,6 @@
     };
     Rec*        fRecs;
 
-    const uint16_t*     getCache16() const;
-    const SkPMColor*    getCache32() const;
-
     void commonAsAGradient(GradientInfo*, bool flipGrad = false) const;
 
     /*
@@ -191,20 +241,13 @@
         kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
     };
     SkColor     fStorage[(kStorageSize + 3) >> 2];
-    SkColor*    fOrigColors; // original colors, before modulation by paint in setContext
+    SkColor*    fOrigColors; // original colors, before modulation by paint in context.
     bool        fColorsAreOpaque;
 
-    mutable uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
-    mutable SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
+    GradientShaderCache* refCache(U8CPU alpha) const;
+    mutable SkMutex                           fCacheMutex;
+    mutable SkAutoTUnref<GradientShaderCache> fCache;
 
-    mutable uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
-    mutable SkMallocPixelRef* fCache32PixelRef;
-    mutable unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
-
-    static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
-    static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
-                                U8CPU alpha, uint32_t gradFlags);
-    void setCacheAlpha(U8CPU alpha) const;
     void initCommon();
 
     typedef SkShader INHERITED;
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
index b24a634..e660d7c 100644
--- a/src/effects/gradients/SkLinearGradient.cpp
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -71,12 +71,24 @@
     buffer.writePoint(fEnd);
 }
 
-bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint,
-                                 const SkMatrix& matrix) {
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
+size_t SkLinearGradient::contextSize() const {
+    return sizeof(LinearGradientContext);
+}
+
+SkShader::Context* SkLinearGradient::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, LinearGradientContext, (*this, device, paint, matrix));
+}
+
+SkLinearGradient::LinearGradientContext::LinearGradientContext(
+        const SkLinearGradient& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix)
+{
     unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
     if ((fDstToIndex.getType() & ~mask) == 0) {
         // when we dither, we are (usually) not const-in-Y
@@ -87,7 +99,6 @@
             fFlags |= SkShader::kConstInY16_Flag;
         }
     }
-    return true;
 }
 
 #define NO_CHECK_ITER               \
@@ -196,14 +207,16 @@
 
 }
 
-void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                                int count) {
+void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                                                        int count) {
     SkASSERT(count > 0);
 
+    const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
+
     SkPoint             srcPt;
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    TileProc            proc = linearGradient.fTileProc;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
     int                 toggle = init_dither_toggle(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -223,12 +236,12 @@
         LinearShadeProc shadeProc = shadeSpan_linear_repeat;
         if (0 == dx) {
             shadeProc = shadeSpan_linear_vertical_lerp;
-        } else if (SkShader::kClamp_TileMode == fTileMode) {
+        } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
             shadeProc = shadeSpan_linear_clamp;
-        } else if (SkShader::kMirror_TileMode == fTileMode) {
+        } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
             shadeProc = shadeSpan_linear_mirror;
         } else {
-            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+            SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
         }
         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
     } else {
@@ -381,14 +394,16 @@
     return SkAbs32(x) < (SK_Fixed1 >> 12);
 }
 
-void SkLinearGradient::shadeSpan16(int x, int y,
-                                  uint16_t* SK_RESTRICT dstC, int count) {
+void SkLinearGradient::LinearGradientContext::shadeSpan16(int x, int y,
+                                                          uint16_t* SK_RESTRICT dstC, int count) {
     SkASSERT(count > 0);
 
+    const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&>(fShader);
+
     SkPoint             srcPt;
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    TileProc            proc = linearGradient.fTileProc;
+    const uint16_t* SK_RESTRICT cache = fCache->getCache16();
     int                 toggle = init_dither_toggle16(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -408,12 +423,12 @@
         LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
         if (fixed_nearly_zero(dx)) {
             shadeProc = shadeSpan16_linear_vertical;
-        } else if (SkShader::kClamp_TileMode == fTileMode) {
+        } else if (SkShader::kClamp_TileMode == linearGradient.fTileMode) {
             shadeProc = shadeSpan16_linear_clamp;
-        } else if (SkShader::kMirror_TileMode == fTileMode) {
+        } else if (SkShader::kMirror_TileMode == linearGradient.fTileMode) {
             shadeProc = shadeSpan16_linear_mirror;
         } else {
-            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+            SkASSERT(SkShader::kRepeat_TileMode == linearGradient.fTileMode);
         }
         (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
     } else {
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
index 013c449..8d80667 100644
--- a/src/effects/gradients/SkLinearGradient.h
+++ b/src/effects/gradients/SkLinearGradient.h
@@ -15,9 +15,23 @@
 public:
     SkLinearGradient(const SkPoint pts[2], const Descriptor&);
 
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+    virtual SkShader::Context* createContext(const SkBitmap&, const SkPaint&, const SkMatrix&,
+                                             void* storage) const SK_OVERRIDE;
+    virtual size_t contextSize() const SK_OVERRIDE;
+
+    class LinearGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        LinearGradientContext(const SkLinearGradient& shader, const SkBitmap& device,
+                              const SkPaint& paint, const SkMatrix& matrix);
+        ~LinearGradientContext() {}
+
+        virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+        virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
     virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
     virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
index 1b9e725..bc2ea3b 100644
--- a/src/effects/gradients/SkRadialGradient.cpp
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -157,16 +157,36 @@
     rad_to_unit_matrix(center, radius, &fPtsToUnit);
 }
 
-void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
-                         int count) {
+size_t SkRadialGradient::contextSize() const {
+    return sizeof(RadialGradientContext);
+}
+
+SkShader::Context* SkRadialGradient::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, RadialGradientContext, (*this, device, paint, matrix));
+}
+
+SkRadialGradient::RadialGradientContext::RadialGradientContext(
+        const SkRadialGradient& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix) {}
+
+void SkRadialGradient::RadialGradientContext::shadeSpan16(int x, int y, uint16_t* dstCParam,
+                                                          int count) {
     SkASSERT(count > 0);
 
+    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
+
     uint16_t* SK_RESTRICT dstC = dstCParam;
 
     SkPoint             srcPt;
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    TileProc            proc = radialGradient.fTileProc;
+    const uint16_t* SK_RESTRICT cache = fCache->getCache16();
     int                 toggle = init_dither_toggle16(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -187,12 +207,12 @@
         }
 
         RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
-        if (SkShader::kClamp_TileMode == fTileMode) {
+        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
             shadeProc = shadeSpan16_radial_clamp;
-        } else if (SkShader::kMirror_TileMode == fTileMode) {
+        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
             shadeProc = shadeSpan16_radial_mirror;
         } else {
-            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
         }
         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
                      cache, toggle, count);
@@ -389,14 +409,16 @@
 
 }  // namespace
 
-void SkRadialGradient::shadeSpan(int x, int y,
-                                SkPMColor* SK_RESTRICT dstC, int count) {
+void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y,
+                                                        SkPMColor* SK_RESTRICT dstC, int count) {
     SkASSERT(count > 0);
 
+    const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader);
+
     SkPoint             srcPt;
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    TileProc            proc = radialGradient.fTileProc;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
     int toggle = init_dither_toggle(x, y);
 
     if (fDstToIndexClass != kPerspective_MatrixClass) {
@@ -416,12 +438,12 @@
         }
 
         RadialShadeProc shadeProc = shadeSpan_radial_repeat;
-        if (SkShader::kClamp_TileMode == fTileMode) {
+        if (SkShader::kClamp_TileMode == radialGradient.fTileMode) {
             shadeProc = shadeSpan_radial_clamp;
-        } else if (SkShader::kMirror_TileMode == fTileMode) {
+        } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) {
             shadeProc = shadeSpan_radial_mirror;
         } else {
-            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+            SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode);
         }
         (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
     } else {    // perspective case
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
index 4a72514..a3d04b1 100644
--- a/src/effects/gradients/SkRadialGradient.h
+++ b/src/effects/gradients/SkRadialGradient.h
@@ -14,10 +14,24 @@
 class SkRadialGradient : public SkGradientShaderBase {
 public:
     SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
-    virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
-        SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t* dstCParam,
-                             int count) SK_OVERRIDE;
+
+    virtual SkShader::Context* createContext(const SkBitmap&, const SkPaint&, const SkMatrix&,
+                                             void* storage) const SK_OVERRIDE;
+    virtual size_t contextSize() const SK_OVERRIDE;
+
+    class RadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        RadialGradientContext(const SkRadialGradient& shader, const SkBitmap& device,
+                              const SkPaint& paint, const SkMatrix& matrix);
+        ~RadialGradientContext() {}
+
+        virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+        virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
+
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
                                  TileMode* xy) const SK_OVERRIDE;
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
index 7024945..6dff1e7 100644
--- a/src/effects/gradients/SkSweepGradient.cpp
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -52,6 +52,24 @@
     buffer.writePoint(fCenter);
 }
 
+size_t SkSweepGradient::contextSize() const {
+    return sizeof(SweepGradientContext);
+}
+
+SkShader::Context* SkSweepGradient::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, SweepGradientContext, (*this, device, paint, matrix));
+}
+
+SkSweepGradient::SweepGradientContext::SweepGradientContext(
+        const SkSweepGradient& shader, const SkBitmap& device,
+        const SkPaint& paint, const SkMatrix& matrix)
+    : INHERITED(shader, device, paint, matrix) {}
+
 //  returns angle in a circle [0..2PI) -> [0..255]
 static unsigned SkATan2_255(float y, float x) {
     //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
@@ -69,11 +87,11 @@
     return ir;
 }
 
-void SkSweepGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                               int count) {
+void SkSweepGradient::SweepGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                                                      int count) {
     SkMatrix::MapXYProc proc = fDstToIndexProc;
     const SkMatrix&     matrix = fDstToIndex;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
     int                 toggle = init_dither_toggle(x, y);
     SkPoint             srcPt;
 
@@ -111,11 +129,11 @@
     }
 }
 
-void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
-                                 int count) {
+void SkSweepGradient::SweepGradientContext::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
+                                                        int count) {
     SkMatrix::MapXYProc proc = fDstToIndexProc;
     const SkMatrix&     matrix = fDstToIndex;
-    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    const uint16_t* SK_RESTRICT cache = fCache->getCache16();
     int                 toggle = init_dither_toggle16(x, y);
     SkPoint             srcPt;
 
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
index ca19da2..9998ed1 100644
--- a/src/effects/gradients/SkSweepGradient.h
+++ b/src/effects/gradients/SkSweepGradient.h
@@ -14,8 +14,23 @@
 class SkSweepGradient : public SkGradientShaderBase {
 public:
     SkSweepGradient(SkScalar cx, SkScalar cy, const Descriptor&);
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+    virtual SkShader::Context* createContext(const SkBitmap&, const SkPaint&, const SkMatrix&,
+                                             void* storage) const SK_OVERRIDE;
+    virtual size_t contextSize() const SK_OVERRIDE;
+
+    class SweepGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        SweepGradientContext(const SkSweepGradient& shader, const SkBitmap& device,
+                             const SkPaint& paint, const SkMatrix& matrix);
+        ~SweepGradientContext() {}
+
+        virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+        virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
 
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
@@ -33,8 +48,9 @@
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
 
 private:
-    typedef SkGradientShaderBase INHERITED;
     const SkPoint fCenter;
+
+    typedef SkGradientShaderBase INHERITED;
 };
 
 #endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
index 1e6a0d8..b7aba82 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) {
@@ -83,47 +95,48 @@
     fFlipped = flipped;
 }
 
-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, fFlipped);
+    float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2;
+    int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped);
 
     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) {
@@ -140,7 +153,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) {
@@ -157,7 +170,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) {
@@ -203,8 +216,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);
@@ -213,15 +257,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) {
@@ -242,16 +286,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);
@@ -260,23 +304,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;
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
index b2e258e..80aa6fa 100644
--- a/src/effects/gradients/SkTwoPointConicalGradient.h
+++ b/src/effects/gradients/SkTwoPointConicalGradient.h
@@ -11,6 +11,8 @@
 
 #include "SkGradientShaderPriv.h"
 
+// TODO(dominikg): Worth making it truly immutable (i.e. set values in constructor)?
+// Should only be initialized once via init(). Immutable afterwards.
 struct TwoPtRadial {
     enum {
         kDontDrawT  = 0x80000000
@@ -29,13 +31,6 @@
               const SkPoint& center1, SkScalar rad1,
               bool flipped);
 
-    // used by setup and nextT
-    float   fRelX, fRelY, fIncX, fIncY;
-    float   fB, fDB;
-
-    void setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy);
-    SkFixed nextT();
-
     static bool DontDrawT(SkFixed t) {
         return kDontDrawT == (uint32_t)t;
     }
@@ -51,11 +46,24 @@
                               const SkPoint& end, SkScalar endRadius,
                               bool flippedGrad, const Descriptor&);
 
-    virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
-                           int count) SK_OVERRIDE;
-    virtual bool setContext(const SkBitmap& device,
-                            const SkPaint& paint,
-                            const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual SkShader::Context* createContext(const SkBitmap&, const SkPaint&, const SkMatrix&,
+                                             void* storage) const SK_OVERRIDE;
+    virtual size_t contextSize() const SK_OVERRIDE;
+
+    class TwoPointConicalGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        TwoPointConicalGradientContext(const SkTwoPointConicalGradient& shader,
+                                       const SkBitmap& device,
+                                       const SkPaint& paint,
+                                       const SkMatrix& matrix);
+        ~TwoPointConicalGradientContext() {}
+
+        virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
 
     virtual BitmapType asABitmap(SkBitmap* bitmap,
                                  SkMatrix* matrix,
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
index e1359b1..a598c6e 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.cpp
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -220,23 +220,60 @@
     return kRadial2_GradientType;
 }
 
-void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
-                                         int count) {
+size_t SkTwoPointRadialGradient::contextSize() const {
+    return sizeof(TwoPointRadialGradientContext);
+}
+
+bool SkTwoPointRadialGradient::validContext(const SkBitmap& device, const SkPaint& paint,
+                                            const SkMatrix& matrix, SkMatrix* totalInverse) const {
+    // For now, we might have divided by zero, so detect that.
+    if (0 == fDiffRadius) {
+        return false;
+    }
+
+    return this->INHERITED::validContext(device, paint, matrix, totalInverse);
+}
+
+SkShader::Context* SkTwoPointRadialGradient::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, TwoPointRadialGradientContext,
+                                (*this, device, paint, matrix));
+}
+
+SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
+        const SkTwoPointRadialGradient& 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;
+}
+
+void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
+        int x, int y, SkPMColor* dstCParam, int count) {
     SkASSERT(count > 0);
 
+    const SkTwoPointRadialGradient& twoPointRadialGradient =
+            static_cast<const SkTwoPointRadialGradient&>(fShader);
+
     SkPMColor* SK_RESTRICT dstC = dstCParam;
 
     // Zero difference between radii:  fill with transparent black.
-    if (fDiffRadius == 0) {
+    if (twoPointRadialGradient.fDiffRadius == 0) {
       sk_bzero(dstC, count * sizeof(*dstC));
       return;
     }
     SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    TileProc            proc = twoPointRadialGradient.fTileProc;
+    const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
 
-    SkScalar foura = fA * 4;
-    bool posRoot = fDiffRadius < 0;
+    SkScalar foura = twoPointRadialGradient.fA * 4;
+    bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
     if (fDstToIndexClass != kPerspective_MatrixClass) {
         SkPoint srcPt;
         dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
@@ -254,21 +291,23 @@
             dx = fDstToIndex.getScaleX();
             dy = fDstToIndex.getSkewY();
         }
-        SkScalar b = (SkScalarMul(fDiff.fX, fx) +
-                     SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
-        SkScalar db = (SkScalarMul(fDiff.fX, dx) +
-                      SkScalarMul(fDiff.fY, dy)) * 2;
+        SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
+                     SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
+                     twoPointRadialGradient.fStartRadius) * 2;
+        SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
+                      SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
 
         TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
-        if (SkShader::kClamp_TileMode == fTileMode) {
+        if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
             shadeProc = shadeSpan_twopoint_clamp;
-        } else if (SkShader::kMirror_TileMode == fTileMode) {
+        } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
             shadeProc = shadeSpan_twopoint_mirror;
         } else {
-            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+            SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
         }
         (*shadeProc)(fx, dx, fy, dy, b, db,
-                     fSr2D2, foura, fOneOverTwoA, posRoot,
+                     twoPointRadialGradient.fSr2D2, foura,
+                     twoPointRadialGradient.fOneOverTwoA, posRoot,
                      dstC, cache, count);
     } else {    // perspective case
         SkScalar dstX = SkIntToScalar(x);
@@ -278,10 +317,11 @@
             dstProc(fDstToIndex, dstX, dstY, &srcPt);
             SkScalar fx = srcPt.fX;
             SkScalar fy = srcPt.fY;
-            SkScalar b = (SkScalarMul(fDiff.fX, fx) +
-                         SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
-            SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
-                                         fOneOverTwoA, posRoot);
+            SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
+                         SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
+                         twoPointRadialGradient.fStartRadius) * 2;
+            SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
+                                         twoPointRadialGradient.fOneOverTwoA, posRoot);
             SkFixed index = proc(t);
             SkASSERT(index <= 0xFFFF);
             *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
@@ -290,23 +330,6 @@
     }
 }
 
-bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
-                                          const SkPaint& paint,
-                                          const SkMatrix& matrix){
-    // For now, we might have divided by zero, so detect that
-    if (0 == fDiffRadius) {
-        return false;
-    }
-
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
-    }
-
-    // we don't have a span16 proc
-    fFlags &= ~kHasSpan16_Flag;
-    return true;
-}
-
 #ifndef SK_IGNORE_TO_STRING
 void SkTwoPointRadialGradient::toString(SkString* str) const {
     str->append("SkTwoPointRadialGradient: (");
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.h b/src/effects/gradients/SkTwoPointRadialGradient.h
index ee1b49e..9ba89f2 100644
--- a/src/effects/gradients/SkTwoPointRadialGradient.h
+++ b/src/effects/gradients/SkTwoPointRadialGradient.h
@@ -23,11 +23,26 @@
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
     virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
 
-    virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
-                           int count) SK_OVERRIDE;
-    virtual bool setContext(const SkBitmap& device,
-                            const SkPaint& paint,
-                            const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual size_t contextSize() const SK_OVERRIDE;
+    virtual bool validContext(const SkBitmap&, const SkPaint&,
+                              const SkMatrix&, SkMatrix* totalInverse = NULL) const SK_OVERRIDE;
+    virtual SkShader::Context* createContext(const SkBitmap&, const SkPaint&, const SkMatrix&,
+                                             void* storage) const SK_OVERRIDE;
+
+    class TwoPointRadialGradientContext : public SkGradientShaderBase::GradientShaderBaseContext {
+    public:
+        TwoPointRadialGradientContext(const SkTwoPointRadialGradient& shader,
+                                      const SkBitmap& device,
+                                      const SkPaint& paint,
+                                      const SkMatrix& matrix);
+        ~TwoPointRadialGradientContext() {}
+
+        virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+
+    private:
+        typedef SkGradientShaderBase::GradientShaderBaseContext INHERITED;
+    };
 
     SkScalar getCenterX1() const { return fDiff.length(); }
     SkScalar getStartRadius() const { return fStartRadius; }
@@ -41,7 +56,6 @@
     virtual void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE;
 
 private:
-    typedef SkGradientShaderBase INHERITED;
     const SkPoint fCenter1;
     const SkPoint fCenter2;
     const SkScalar fRadius1;
@@ -50,6 +64,8 @@
     SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
 
     void init();
+
+    typedef SkGradientShaderBase INHERITED;
 };
 
 #endif