add shader flag kConstInY_Flag
this signals blitters that the shader will return the same results for a given
x value, independent of y. Useful inside blitRect(), where it can cache the
first call to shadeSpan() and reuse it on all subsequent scans. Works with
(non-rotated) linear-gradients, and Nx1 bitmaps.



git-svn-id: http://skia.googlecode.com/svn/trunk@214 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h
index f9c3dc3..7c5f941 100644
--- a/include/core/SkColorShader.h
+++ b/include/core/SkColorShader.h
@@ -29,14 +29,15 @@
     /** Create a ColorShader that will inherit its color from the Paint
         at draw time.
     */
-    SkColorShader() : fInheritColor(true) {}
+    SkColorShader() : fFlags(0), fInheritColor(true) {}
+
     /** Create a ColorShader that ignores the color in the paint, and uses the
         specified color. Note: like all shaders, at draw time the paint's alpha
         will be respected, and is applied to the specified color.
     */
-    SkColorShader(SkColor c) : fColor(c), fInheritColor(false) {}
+    SkColorShader(SkColor c) : fColor(c), fFlags(0), fInheritColor(false) {}
     
-    virtual uint32_t getFlags();
+    virtual uint32_t getFlags() { return fFlags; }
     virtual uint8_t getSpan16Alpha() const;
     virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
                             const SkMatrix& matrix);
@@ -54,6 +55,7 @@
     }
     SkColor     fColor;         // ignored if fInheritColor is true
     SkPMColor   fPMColor;       // cached after setContext()
+    uint32_t    fFlags;         // cached after setContext()
     uint16_t    fColor16;       // cached after setContext()
     SkBool8     fInheritColor;
 
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 7c13e3d..8fac8cb 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -63,14 +63,22 @@
     enum Flags {
         //!< set if all of the colors will be opaque
         kOpaqueAlpha_Flag   = 0x01,
+
         //! set if this shader's shadeSpan16() method can be called
         kHasSpan16_Flag     = 0x02,
+
         /** Set this bit if the shader's native data type is instrinsically 16
             bit, meaning that calling the 32bit shadeSpan() entry point will
             mean the the impl has to up-sample 16bit data into 32bit. Used as a
             a means of clearing a dither request if the it will have no effect
         */
-        kIntrinsicly16_Flag = 0x04
+        kIntrinsicly16_Flag = 0x04,
+
+        /** set (after setContext) if the spans only vary in X (const in Y).
+            e.g. an Nx1 bitmap that is being tiled in Y, or a linear-gradient
+            that varies from left-to-right
+         */
+        kConstInY_Flag = 0x08
     };
 
     /** Called sometimes before drawing with this shader.
diff --git a/samplecode/SampleTestGL.cpp b/samplecode/SampleTestGL.cpp
index c12eb7c..210d779 100644
--- a/samplecode/SampleTestGL.cpp
+++ b/samplecode/SampleTestGL.cpp
@@ -9,6 +9,19 @@
 #include "SkShader.h"
 #include "SkUtils.h"
 
+static void show_ramp(SkCanvas* canvas, const SkRect& r) {
+    SkPoint pts[] = { r.fLeft, 0, r.fRight, 0 };
+    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                 SkShader::kRepeat_TileMode);
+    SkPaint p;
+    p.setShader(s)->unref();
+    canvas->drawRect(r, p);
+    canvas->translate(r.width() + SkIntToScalar(8), 0);
+    p.setDither(true);
+    canvas->drawRect(r, p);
+}
+
 class TestGLView : public SkView {
 public:
 	TestGLView() {
@@ -49,6 +62,9 @@
         canvas->translate(r.width() + SkIntToScalar(20), 0);
         paint.setStrokeWidth(SkIntToScalar(5));
         canvas->drawRect(r, paint);
+        
+        canvas->translate(r.width() * 10/9, 0);
+        show_ramp(canvas, r);
     }
     
 private:
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 8ba57c4..2302e0f 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -67,6 +67,11 @@
     buffer.write8(fState.fTileModeY);
 }
 
+static bool only_scale_and_translate(const SkMatrix& matrix) {
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    return (matrix.getType() & ~mask) == 0;
+}
+
 bool SkBitmapProcShader::setContext(const SkBitmap& device,
                                     const SkPaint& paint,
                                     const SkMatrix& matrix) {
@@ -117,6 +122,12 @@
         default:
             break;
     }
+
+    // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
+    if (1 == fState.fBitmap->height() &&
+            only_scale_and_translate(this->getTotalInverse())) {
+        fFlags |= kConstInY_Flag;
+    }
     return true;
 }
 
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 25303ac..a38b46e 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -977,17 +977,16 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 
 SkShaderBlitter::SkShaderBlitter(const SkBitmap& device, const SkPaint& paint)
-    : INHERITED(device)
-{
+        : INHERITED(device) {
     fShader = paint.getShader();
     SkASSERT(fShader);
 
     fShader->ref();
     fShader->beginSession();
+    fShaderFlags = fShader->getFlags();
 }
 
-SkShaderBlitter::~SkShaderBlitter()
-{
+SkShaderBlitter::~SkShaderBlitter() {
     fShader->endSession();
     fShader->unref();
 }
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
index b253662..40eceec 100644
--- a/src/core/SkBlitter_RGB16.cpp
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -483,7 +483,7 @@
 SkRGB16_Shader16_Blitter::SkRGB16_Shader16_Blitter(const SkBitmap& device,
                                                    const SkPaint& paint)
     : SkRGB16_Shader_Blitter(device, paint) {
-    SkASSERT(SkShader::CanCallShadeSpan16(fShader->getFlags()));
+    SkASSERT(SkShader::CanCallShadeSpan16(fShaderFlags));
 }
 
 void SkRGB16_Shader16_Blitter::blitH(int x, int y, int width) SK_RESTRICT {
@@ -569,7 +569,7 @@
     // compute SkBlitRow::Procs
     unsigned flags = 0;
     
-    uint32_t shaderFlags = fShader->getFlags();
+    uint32_t shaderFlags = fShaderFlags;
     // shaders take care of global alpha, so we never set it in SkBlitRow
     if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) {
         flags |= SkBlitRow::kSrcPixelAlpha_Flag;
@@ -597,6 +597,30 @@
     fOpaqueProc(fDevice.getAddr16(x, y), fBuffer, width, 0xFF, x, y);
 }
 
+void SkRGB16_Shader_Blitter::blitRect(int x, int y, int width, int height) {
+    SkShader*       shader = fShader;
+    SkBlitRow::Proc proc = fOpaqueProc;
+    SkPMColor*      buffer = fBuffer;
+    uint16_t*       dst = fDevice.getAddr16(x, y);
+    size_t          dstRB = fDevice.rowBytes();
+
+    if (fShaderFlags & SkShader::kConstInY_Flag) {
+        shader->shadeSpan(x, y, buffer, width);
+        do {
+            proc(dst, buffer, width, 0xFF, x, y);
+            y += 1;
+            dst = (uint16_t*)((char*)dst + dstRB);
+        } while (--height);
+    } else {
+        do {
+            shader->shadeSpan(x, y, buffer, width);
+            proc(dst, buffer, width, 0xFF, x, y);
+            y += 1;
+            dst = (uint16_t*)((char*)dst + dstRB);
+        } while (--height);
+    }
+}
+
 static inline int count_nonzero_span(const int16_t runs[], const SkAlpha aa[]) {
     int count = 0;
     for (;;) {
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 5b3497e..8f9cfc3 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -38,7 +38,8 @@
     virtual ~SkShaderBlitter();
 
 protected:
-    SkShader* fShader;
+    uint32_t    fShaderFlags;
+    SkShader*   fShader;
 
 private:
     // illegal
@@ -192,7 +193,8 @@
     virtual ~SkRGB16_Shader_Blitter();
     virtual void blitH(int x, int y, int width);
     virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
-    
+    virtual void blitRect(int x, int y, int width, int height);
+
 protected:
     SkPMColor*      fBuffer;
     SkBlitRow::Proc fOpaqueProc;
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index dd9c859..5567914 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -210,6 +210,7 @@
 #include "SkUtils.h"
 
 SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
+    fFlags = 0; // computed in setContext
     fInheritColor = b.readU8(); 
     if (fInheritColor) {
         return;
@@ -226,11 +227,6 @@
     buffer.write32(fColor);
 }
 
-uint32_t SkColorShader::getFlags() {
-    return (SkGetPackedA32(fPMColor) == 255 ? kOpaqueAlpha_Flag : 0) |
-            kHasSpan16_Flag;
-}
-
 uint8_t SkColorShader::getSpan16Alpha() const {
     return SkGetPackedA32(fPMColor);
 }
@@ -267,6 +263,11 @@
     }
     fPMColor = SkPackARGB32(a, r, g, b);
 
+    fFlags = kHasSpan16_Flag | kConstInY_Flag;
+    if (SkGetPackedA32(fPMColor) == 255) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+
     return true;
 }
 
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
index 87d70c2..e1a92ba 100644
--- a/src/effects/SkGradientShader.cpp
+++ b/src/effects/SkGradientShader.cpp
@@ -620,6 +620,7 @@
         }
     }
 
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
     virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
     virtual bool asABitmap(SkBitmap*, SkMatrix*, TileMode*);
@@ -646,6 +647,19 @@
     typedef Gradient_Shader INHERITED;
 };
 
+bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
+                                 const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    if ((fDstToIndex.getType() & ~mask) == 0) {
+        fFlags |= SkShader::kConstInY_Flag;
+    }
+    return true;
+}
+
 //  Return true if fx, fx+dx, fx+2*dx, ... is always in range
 static inline bool no_need_for_clamp(int fx, int dx, int count)
 {
diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
index d3679c4..c670314 100644
--- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
+++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj
@@ -71,7 +71,7 @@
 		00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6760FCCCB01002BD8B4 /* SampleMovie.cpp */; };
 		00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A7282D0FD43D3700D5051F /* SkMovie.cpp */; };
 		00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2762F6740FCCCB01002BD8B4 /* SampleAll.cpp */; };
-		00A729650FD93ED600D5051F /* SampleTestGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A729630FD93ED600D5051F /* SampleTestGL.cpp */; };
+		00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A729630FD93ED600D5051F /* SampleTestGL.cpp */; };
 		00C55DA10F8552DC000CAC09 /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00C55DA00F8552DC000CAC09 /* SampleGradients.cpp */; };
 		00FF39140FC6ED2C00915187 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00FF39130FC6ED2C00915187 /* SampleEffects.cpp */; };
 		0156F80407C56A3000C6122B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0156F80307C56A3000C6122B /* Foundation.framework */; };
@@ -569,8 +569,8 @@
 				00A728270FD43D0400D5051F /* SampleMovie.cpp in Sources */,
 				00A7282F0FD43D3700D5051F /* SkMovie.cpp in Sources */,
 				00A7295D0FD8397600D5051F /* SampleAll.cpp in Sources */,
-				00A729650FD93ED600D5051F /* SampleTestGL.cpp in Sources */,
 				000A99820FD97526007E45BD /* SampleArc.cpp in Sources */,
+				00AF77B00FE2EA2D007F9650 /* SampleTestGL.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};