Added asNewCustomStage to SkShader and implemented it for all the gradient shaders.

Not actually hooked up yet, but it should be ready to replace asABitmap for a future CL.
Review URL: https://codereview.appspot.com/6351113

git-svn-id: http://skia.googlecode.com/svn/trunk@4702 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 7108c34..1ba1d67 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -17,6 +17,9 @@
 #include "SkPaint.h"
 
 class SkPath;
+class GrContext;
+class GrCustomStage;
+class GrSamplerState;
 
 /** \class SkShader
  *
@@ -299,6 +302,16 @@
 
     virtual GradientType asAGradient(GradientInfo* info) const;
 
+    /**
+     *  If the shader subclass has a GrCustomStage implementation, this returns
+     *  a new custom stage (the caller assumes ownership, and will need to
+     *  unref it). A GrContext pointer is required since custom stages may
+     *  need to create textures. The sampler parameter is necessary to set
+     *  up matrix/tile modes/etc, and will eventually be removed.
+     */
+    virtual GrCustomStage* asNewCustomStage(GrContext* context, 
+                                            GrSamplerState* sampler) const;
+
     //////////////////////////////////////////////////////////////////////////
     //  Factory methods for stock shaders
 
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index cd40b7f..a0ae49d 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -200,6 +200,11 @@
     return kNone_GradientType;
 }
 
+GrCustomStage* SkShader::asNewCustomStage(GrContext* context, 
+                                          GrSamplerState* sampler) const {
+    return NULL;
+}
+
 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
                                        TileMode tmx, TileMode tmy) {
     return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index b86c4e2..8838e77 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -15,6 +15,9 @@
 #include "SkUtils.h"
 #include "SkTemplates.h"
 #include "SkBitmapCache.h"
+#include "../gpu/effects/GrGradientEffects.h"
+#include "../gpu/GrSamplerState.h"
+#include "../gpu/SkGr.h"
 
 #ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
     #define USE_DITHER_32BIT_GRADIENT
@@ -796,6 +799,8 @@
     virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
                              SkScalar* twoPointRadialParams) const SK_OVERRIDE;
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    virtual GrCustomStage* asNewCustomStage(GrContext* context, 
+                                            GrSamplerState* sampler) const SK_OVERRIDE;
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Linear_Gradient)
 
@@ -1044,6 +1049,16 @@
     return kLinear_GradientType;
 }
 
+GrCustomStage* Linear_Gradient::asNewCustomStage(GrContext* context,
+                                                 GrSamplerState* sampler) const {
+    SkASSERT(NULL != context && NULL != sampler);
+    sampler->matrix()->preConcat(fPtsToUnit);
+    sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
+    sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
+    sampler->setFilter(GrSamplerState::kBilinear_Filter);
+    return SkNEW_ARGS(GrLinearGradient, (context, *this));
+}
+
 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
                             int count) {
     if (reinterpret_cast<uintptr_t>(dst) & 2) {
@@ -1444,6 +1459,7 @@
         }
         return kRadial_BitmapType;
     }
+
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
         if (info) {
             commonAsAGradient(info);
@@ -1453,6 +1469,16 @@
         return kRadial_GradientType;
     }
 
+    virtual GrCustomStage* asNewCustomStage(GrContext* context,
+        GrSamplerState* sampler) const SK_OVERRIDE {
+        SkASSERT(NULL != context && NULL != sampler);
+        sampler->matrix()->preConcat(fPtsToUnit);
+        sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
+        sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
+        return SkNEW_ARGS(GrRadialGradient, (context, *this));
+    }
+
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Radial_Gradient)
 
 protected:
@@ -1903,6 +1929,24 @@
         return kRadial2_GradientType;
     }
 
+    virtual GrCustomStage* asNewCustomStage(GrContext* context,
+        GrSamplerState* sampler) const SK_OVERRIDE {
+        SkASSERT(NULL != context && NULL != sampler);
+        SkScalar diffLen = fDiff.length();
+        if (0 != diffLen) {
+            SkScalar invDiffLen = SkScalarInvert(diffLen);
+            sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
+                              SkScalarMul(invDiffLen, fDiff.fX));
+        } else {
+            sampler->matrix()->reset();
+        }
+        sampler->matrix()->preConcat(fPtsToUnit);
+        sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
+        sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
+        return SkNEW_ARGS(GrRadial2Gradient, (context, *this));
+    }
+
     virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
                            int count) SK_OVERRIDE {
         SkASSERT(count > 0);
@@ -2355,6 +2399,25 @@
         return kConical_GradientType;
     }
 
+    virtual GrCustomStage* asNewCustomStage(GrContext* context,
+        GrSamplerState* sampler) const SK_OVERRIDE {
+        SkASSERT(NULL != context && NULL != sampler);
+        SkPoint diff = fCenter2 - fCenter1;
+        SkScalar diffLen = diff.length();
+        if (0 != diffLen) {
+            SkScalar invDiffLen = SkScalarInvert(diffLen);
+            sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
+                              SkScalarMul(invDiffLen, diff.fX));
+        } else {
+            sampler->matrix()->reset();
+        }
+        sampler->matrix()->preTranslate(-fCenter1.fX, -fCenter1.fY);
+        sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
+        sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
+        return SkNEW_ARGS(GrConical2Gradient, (context, *this));
+    }
+
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Conical_Gradient)
     
 protected:
@@ -2422,6 +2485,15 @@
         return kSweep_GradientType;
     }
 
+    virtual GrCustomStage* asNewCustomStage(GrContext* context,
+        GrSamplerState* sampler) const SK_OVERRIDE {
+        sampler->matrix()->preConcat(fPtsToUnit);
+        sampler->setWrapX(sk_tile_mode_to_grwrap(fTileMode));
+        sampler->setWrapY(sk_tile_mode_to_grwrap(kClamp_TileMode));
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
+        return SkNEW_ARGS(GrSweepGradient, (context, *this));
+    }
+
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sweep_Gradient)
 
 protected:
diff --git a/src/gpu/effects/GrGradientEffects.cpp b/src/gpu/effects/GrGradientEffects.cpp
index ec465b8..2faeec0 100644
--- a/src/gpu/effects/GrGradientEffects.cpp
+++ b/src/gpu/effects/GrGradientEffects.cpp
@@ -8,6 +8,7 @@
 #include "GrGradientEffects.h"
 #include "gl/GrGLProgramStage.h"
 #include "GrProgramStageFactory.h"
+#include "SkGr.h"
 
 // Base class for GL gradient custom stages
 class GrGLGradientStage : public GrGLProgramStage {
@@ -50,10 +51,37 @@
     SkSafeRef(fTexture);
 }
 
+GrGradientEffect::GrGradientEffect(GrContext* ctx, const SkShader& shader)
+                                   : fTexture (NULL)
+                                   , fUseTexture (false) {
+    // TODO: check for simple cases where we don't need a texture:
+    //GradientInfo info;
+    //shader.asAGradient(&info);
+    //if (info.fColorCount == 2) { ...
+
+    SkBitmap bitmap;
+    shader.asABitmap(&bitmap, NULL, NULL, NULL);
+
+    // Note: we just construct a default sampler state here, which isn't great,
+    // however, as long as the bitmap has power-of-two dimensions, which should
+    // be the case for gradient bitmaps, it should be fine
+    GrAssert(SkIsPow2(bitmap.width()) && SkIsPow2(bitmap.height()));
+    GrSamplerState sampler;
+
+    GrContext::TextureCacheEntry entry = GrLockCachedBitmapTexture(ctx, bitmap,
+                                                                   &sampler);
+    fTexture = entry.texture();
+    SkSafeRef(fTexture);
+    fUseTexture = true;
+
+    // Unlock immediately, this is not great, but we don't have a way of
+    // knowing when else to unlock it currently, so it may get purged from
+    // the cache, but it'll still be ref'd until it's no longer being used.
+    GrUnlockCachedBitmapTexture(ctx, entry);
+}
+
 GrGradientEffect::~GrGradientEffect() {
-    if (fTexture) {
-        SkSafeUnref(fTexture);
-    }
+    SkSafeUnref(fTexture);
 }
 
 unsigned int GrGradientEffect::numTextures() const {
@@ -105,6 +133,10 @@
                   : INHERITED(texture) { 
 }
 
+GrLinearGradient::GrLinearGradient(GrContext* ctx, const SkShader& shader)
+                  : INHERITED(ctx, shader) {
+}
+
 GrLinearGradient::~GrLinearGradient() {
 
 }
@@ -113,10 +145,6 @@
     return GrTProgramStageFactory<GrLinearGradient>::getInstance();
 }
 
-bool GrLinearGradient::isEqual(const GrCustomStage& sBase) const {
-    return INHERITED::isEqual(sBase);
-}
-
 /////////////////////////////////////////////////////////////////////
 
 class GrGLRadialGradient : public GrGLGradientStage {
@@ -160,19 +188,18 @@
 
 }
 
+GrRadialGradient::GrRadialGradient(GrContext* ctx, const SkShader& shader)
+    : INHERITED(ctx, shader) {
+}
+
 GrRadialGradient::~GrRadialGradient() {
 
 }
 
-
 const GrProgramStageFactory& GrRadialGradient::getFactory() const {
     return GrTProgramStageFactory<GrRadialGradient>::getInstance();
 }
 
-bool GrRadialGradient::isEqual(const GrCustomStage& sBase) const {
-    return INHERITED::isEqual(sBase);
-}
-
 /////////////////////////////////////////////////////////////////////
 
 // For brevity, and these definitions are likely to move to a different class soon.
@@ -418,6 +445,19 @@
 
 }
 
+GrRadial2Gradient::GrRadial2Gradient(GrContext* ctx, const SkShader& shader)
+    : INHERITED(ctx, shader) {
+    SkShader::GradientInfo info;
+    info.fColorCount = 0;
+    shader.asAGradient(&info);
+    fCenterX1 = SkPoint::Distance(info.fPoint[0], info.fPoint[1]);
+    SkScalar diffRadius = info.fRadius[1] - info.fRadius[0];
+    fPosRoot = diffRadius < 0;
+    SkScalar inv = 0 == diffRadius ? 0 : SkScalarInvert(diffRadius);
+    fRadius0 = SkScalarMul(info.fRadius[0], inv);
+    fCenterX1 = SkScalarMul(fCenterX1, inv);
+}
+
 GrRadial2Gradient::~GrRadial2Gradient() {
 
 }
@@ -736,6 +776,16 @@
 
 }
 
+GrConical2Gradient::GrConical2Gradient(GrContext* ctx, const SkShader& shader)
+    : INHERITED(ctx, shader) {
+    SkShader::GradientInfo info;
+    info.fColorCount = 0;
+    shader.asAGradient(&info);
+    fCenterX1 = SkPoint::Distance(info.fPoint[0], info.fPoint[1]);
+    fRadius0 = info.fRadius[0];
+    fDiffRadius = info.fRadius[1] - info.fRadius[0];
+}
+
 GrConical2Gradient::~GrConical2Gradient() {
 
 }
@@ -796,6 +846,10 @@
 
 }
 
+GrSweepGradient::GrSweepGradient(GrContext* ctx, const SkShader& shader) 
+    : INHERITED(ctx, shader) {
+}
+
 GrSweepGradient::~GrSweepGradient() {
 
 }
@@ -804,7 +858,3 @@
     return GrTProgramStageFactory<GrSweepGradient>::getInstance();
 }
 
-bool GrSweepGradient::isEqual(const GrCustomStage& sBase) const {
-    return INHERITED::isEqual(sBase);
-}
-
diff --git a/src/gpu/effects/GrGradientEffects.h b/src/gpu/effects/GrGradientEffects.h
index eb1a8f2..fdd5d52 100644
--- a/src/gpu/effects/GrGradientEffects.h
+++ b/src/gpu/effects/GrGradientEffects.h
@@ -11,6 +11,7 @@
 #include "GrSingleTextureEffect.h"
 #include "GrTypes.h"
 #include "GrScalar.h"
+#include "SkShader.h"
 
 /*
  * The intepretation of the texture matrix depends on the sample mode. The
@@ -40,9 +41,7 @@
 public:
 
     GrGradientEffect(GrTexture* texture);
-    
-    // TODO: Look at a GradientInfo and make the texture only if necessary
-    // GrGradientEffect(GrContext* ctx, GradientInfo* info);
+    GrGradientEffect(GrContext* ctx, const SkShader& shader);
 
     virtual ~GrGradientEffect();
 
@@ -67,11 +66,11 @@
 public:
 
     GrLinearGradient(GrTexture* texture);
+    GrLinearGradient(GrContext* ctx, const SkShader& shader);
     virtual ~GrLinearGradient();
 
     static const char* Name() { return "Linear Gradient"; }
     virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
 
     typedef GrGLLinearGradient GLProgramStage;
 
@@ -87,11 +86,11 @@
 public:
 
     GrRadialGradient(GrTexture* texture);
+    GrRadialGradient(GrContext* ctx, const SkShader& shader);
     virtual ~GrRadialGradient();
 
     static const char* Name() { return "Radial Gradient"; }
     virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
 
     typedef GrGLRadialGradient GLProgramStage;
 
@@ -107,6 +106,7 @@
 public:
 
     GrRadial2Gradient(GrTexture* texture, GrScalar center, GrScalar radius, bool posRoot);
+    GrRadial2Gradient(GrContext* ctx, const SkShader& shader);
     virtual ~GrRadial2Gradient();
 
     static const char* Name() { return "Two-Point Radial Gradient"; }
@@ -143,6 +143,7 @@
 public:
 
     GrConical2Gradient(GrTexture* texture, GrScalar center, GrScalar radius, GrScalar diffRadius);
+    GrConical2Gradient(GrContext* ctx, const SkShader& shader);
     virtual ~GrConical2Gradient();
 
     static const char* Name() { return "Two-Point Conical Gradient"; }
@@ -179,11 +180,11 @@
 public:
 
     GrSweepGradient(GrTexture* texture);
+    GrSweepGradient(GrContext* ctx, const SkShader& shader);
     virtual ~GrSweepGradient();
 
     static const char* Name() { return "Sweep Gradient"; }
     virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
-    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
 
     typedef GrGLSweepGradient GLProgramStage;