Use coverage stages for supersample AA resolve, edgeAA, and glyph masks. Expose a mask stage through GrPaint

Review URL: http://codereview.appspot.com/4548048/



git-svn-id: http://skia.googlecode.com/svn/trunk@1356 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index c29526a..2fecca5 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -579,11 +579,6 @@
     GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill);
 
     struct OffscreenRecord;
-    // we currently only expose stage 0 through the paint so use stage 1. We
-    // use stage 1 for the offscreen.
-    enum {
-        kOffscreenStage = 1,
-    };
 
     bool doOffscreenAA(GrDrawTarget* target, 
                        const GrPaint& paint,
@@ -602,6 +597,15 @@
                           const GrIRect& boundRect,
                           OffscreenRecord* record);
     
+    // computes vertex layout bits based on the paint. If paint expresses
+    // a texture for a stage, the stage coords will be bound to postitions
+    // unless hasTexCoords[s]==true in which case stage s's input coords
+    // are bound to tex coord index s. hasTexCoords == NULL is a shortcut
+    // for an array where all the values are false.
+    static int PaintStageVertexLayoutBits(
+                                    const GrPaint& paint,
+                                    const bool hasTexCoords[GrPaint::kTotalStages]);
+    
 };
 
 /**
diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h
index fae4e92..c79a191 100644
--- a/gpu/include/GrContext_impl.h
+++ b/gpu/include/GrContext_impl.h
@@ -40,19 +40,15 @@
                                           const COL_SRC* colorSrc,
                                           const IDX_SRC* idxSrc) {
 
-    GrVertexLayout layout = 0;
-
     GrDrawTarget::AutoReleaseGeometry geo;
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
 
-    if (NULL != paint.getTexture()) {
-        if (NULL != texCoordSrc) {
-            layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
-        } else {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        }
-    }
+    bool hasTexCoords[GrPaint::kTotalStages] = {
+        NULL != texCoordSrc, // texCoordSrc provides explicit stage 0 coords
+        0                    // remaining stages use positions
+    };
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
 
     if (NULL != colorSrc) {
         layout |= GrDrawTarget::kColor_VertexLayoutBit;
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 9a00b57..985cca7 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -50,7 +50,7 @@
      * or not.
      */
     enum {
-        kNumStages = 2,
+        kNumStages = 3,
         kMaxTexCoords = kNumStages
     };
 
@@ -147,6 +147,7 @@
 
             // default stencil setting should be disabled
             GrAssert(fStencilSettings.isDisabled());
+            fFirstCoverageStage = kNumStages;
         }
         uint32_t                fFlagBits;
         GrBlendCoeff            fSrcBlend;
@@ -250,6 +251,18 @@
     }
 
     /**
+     * Shortcut for preConcatSamplerMatrix on all stages in mask with same 
+     * matrix
+     */
+    void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) {
+        for (int i = 0; i < kNumStages; ++i) {
+            if ((1 << i) & stageMask) {
+                this->preConcatSamplerMatrix(i, matrix);
+            }
+        }
+    }
+
+    /**
      * Gets the matrix of a stage's sampler
      *
      * @param stage     the stage to of sampler to get
diff --git a/gpu/include/GrPaint.h b/gpu/include/GrPaint.h
index 3035ca1..f7ca34c 100644
--- a/gpu/include/GrPaint.h
+++ b/gpu/include/GrPaint.h
@@ -25,12 +25,17 @@
 
 /**
  * The paint describes how pixels are colored when the context draws to
- * them.
+ * them. TODO: Make this a "real" class with getters and setters, default
+ * values, and documentation.
  */
 class GrPaint {
 public:
+    enum {
+        kMaxTextures = 1,
+        kMaxMasks    = 1,
+    };
 
-    // All the paint fields are public except texture (it's ref-counted)
+    // All the paint fields are public except textures/samplers
     GrBlendCoeff                fSrcBlendCoeff;
     GrBlendCoeff                fDstBlendCoeff;
     bool                        fAntiAlias;
@@ -38,22 +43,75 @@
 
     GrColor                     fColor;
 
-    GrSamplerState              fSampler;
-
     GrColor                     fColorFilterColor;
     SkXfermode::Mode            fColorFilterXfermode;
 
-    void setTexture(GrTexture* texture) {
+    void setTexture(int i, GrTexture* texture) {
+        GrAssert((unsigned)i < kMaxTextures);
         GrSafeRef(texture);
-        GrSafeUnref(fTexture);
-        fTexture = texture;
+        GrSafeUnref(fTextures[i]);
+        fTextures[i] = texture;
     }
 
-    GrTexture* getTexture() const { return fTexture; }
+    GrTexture* getTexture(int i) const { 
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextures[i]; 
+    }
+
+    GrSamplerState* getTextureSampler(int i) {
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextureSamplers + i;
+    }
+
+    const GrSamplerState* getTextureSampler(int i) const {
+        GrAssert((unsigned)i < kMaxTextures);
+        return fTextureSamplers + i;
+    }
+
+    // The mask can be alpha-only or per channel. It is applied
+    // after the colorfilter
+    void setMask(int i, GrTexture* mask) {
+        GrAssert((unsigned)i < kMaxMasks);
+        GrSafeRef(mask);
+        GrSafeUnref(fMaskTextures[i]);
+        fMaskTextures[i] = mask;
+    }
+
+    GrTexture* getMask(int i) const { 
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskTextures[i]; 
+    }
+
+    // mask's sampler matrix is always applied to the positions
+    // (i.e. no explicit texture coordinates)
+    GrSamplerState* getMaskSampler(int i) {
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskSamplers + i;
+    }
+
+    const GrSamplerState* getMaskSampler(int i) const {
+        GrAssert((unsigned)i < kMaxMasks);
+        return fMaskSamplers + i;
+    }
+
+    // pre-concats sampler matrices for non-NULL textures and masks
+    void preConcatActiveSamplerMatrices(const GrMatrix& matrix) {
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextureSamplers[i].preConcatMatrix(matrix);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskSamplers[i].preConcatMatrix(matrix);
+        }
+    }
 
     // uninitialized
     GrPaint() {
-        fTexture = NULL;
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextures[i] = NULL;
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskTextures[i] = NULL;
+        }
     }
 
     GrPaint(const GrPaint& paint) {
@@ -67,22 +125,35 @@
         fColorFilterColor = paint.fColorFilterColor;
         fColorFilterXfermode = paint.fColorFilterXfermode;
 
-        fSampler = paint.fSampler;
-        fTexture = paint.fTexture;
-        GrSafeRef(fTexture);
+        for (int i = 0; i < kMaxTextures; ++i) {
+            fTextureSamplers[i] = paint.fTextureSamplers[i];
+            fTextures[i] = paint.fTextures[i];
+            GrSafeRef(fTextures[i]);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            fMaskSamplers[i] = paint.fMaskSamplers[i];
+            fMaskTextures[i] = paint.fMaskTextures[i];
+            GrSafeRef(fMaskTextures[i]);
+        }
     }
 
     ~GrPaint() {
-        GrSafeUnref(fTexture);
+        for (int i = 0; i < kMaxTextures; ++i) {
+            GrSafeUnref(fTextures[i]);
+        }
+        for (int i = 0; i < kMaxMasks; ++i) {
+            GrSafeUnref(fMaskTextures[i]);
+        }
     }
 
-    // sets paint to src-over, solid white, no texture
+    // sets paint to src-over, solid white, no texture, no mask
     void reset() {
-        resetBlend();
-        resetOptions();
-        resetColor();
-        resetTexture();
-        resetColorFilter();
+        this->resetBlend();
+        this->resetOptions();
+        this->resetColor();
+        this->resetTextures();
+        this->resetColorFilter();
+        this->resetMasks();
     }
 
     void resetColorFilter() {
@@ -90,8 +161,60 @@
         fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
+    bool hasTexture() const {
+        return 0 != this->getActiveTextureStageMask();
+    }
+
+    bool hasMask() const {
+        return 0 != this->getActiveMaskStageMask();
+    }
+
+    bool hasTextureOrMask() const {
+        return this->hasTexture() || this->hasMask();
+    }
+
+    // helpers for GrContext, GrTextContext
+    int getActiveTextureStageMask() const {
+        int mask = 0;
+        for (int i = 0; i < kMaxTextures; ++i) {
+            if (NULL != fTextures[i]) {
+                mask |= 1 << (i + kFirstTextureStage);
+            }
+        }
+        return mask;
+    }
+
+    int getActiveMaskStageMask() const {
+        int mask;
+        for (int i = 0; i < kMaxMasks; ++i) {
+            if (NULL != fMaskTextures[i]) {
+                mask |= 1 << (i + kFirstMaskStage);
+            }
+        }
+        return mask;
+    }
+    
+    int getActiveStageMask() const {
+        return this->getActiveTextureStageMask() |
+                this->getActiveMaskStageMask();
+    }
+
+    // internal use
+    // GrPaint's textures and masks map to the first N stages
+    // of GrDrawTarget in that order (textures followed by masks)
+    enum {
+        kFirstTextureStage = 0,
+        kFirstMaskStage = kMaxTextures,
+        kTotalStages = kMaxTextures + kMaxMasks,
+    };
+
 private:
-    GrTexture*      fTexture;
+
+    GrSamplerState              fTextureSamplers[kMaxTextures];
+    GrSamplerState              fMaskSamplers[kMaxMasks];
+
+    GrTexture*      fTextures[kMaxTextures];
+    GrTexture*      fMaskTextures[kMaxMasks];
 
     void resetBlend() {
         fSrcBlendCoeff = kOne_BlendCoeff;
@@ -107,11 +230,19 @@
         fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
-    void resetTexture() {
-        setTexture(NULL);
-        fSampler.setClampNoFilter();
+    void resetTextures() {
+        for (int i = 0; i < kMaxTextures; ++i) {
+            this->setTexture(i, NULL);
+            fTextureSamplers[i].setClampNoFilter();
+        }
     }
 
+    void resetMasks() {
+        for (int i = 0; i < kMaxMasks; ++i) {
+            this->setMask(i, NULL);
+            fMaskSamplers[i].setClampNoFilter();
+        }
+    }
 };
 
 #endif
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 3a4233a..af777c1 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -106,6 +106,26 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+int GrContext::PaintStageVertexLayoutBits(
+                            const GrPaint& paint,
+                            const bool hasTexCoords[GrPaint::kTotalStages]) {
+    int stageMask = paint.getActiveStageMask();
+    int layout = 0;
+    for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+        if ((1 << i) & stageMask) {
+            if (NULL != hasTexCoords && hasTexCoords[i]) {
+                layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i);
+            } else {
+                layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+            }
+        }
+    }
+    return layout;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
 enum {
     kNPOTBit    = 0x1,
     kFilterBit  = 0x2,
@@ -576,6 +596,10 @@
     GrTexture* src = record->fEntry0->texture();
     int scale;
 
+    enum {
+        kOffscreenStage = GrPaint::kTotalStages,
+    };
+
     if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
         GrAssert(NULL != record->fEntry1);
         scale = 2;
@@ -605,11 +629,14 @@
     }
 
     // setup for draw back to main RT
+    int stageMask = paint.getActiveStageMask();
+
     target->restoreDrawState(record->fSavedState);
-    if (NULL != paint.getTexture()) {
+
+    if (stageMask) {
         GrMatrix invVM;
         if (target->getViewInverse(&invVM)) {
-            target->preConcatSamplerMatrix(0, invVM);
+            target->preConcatSamplerMatrices(stageMask, invVM);
         }
     }
     target->setViewMatrix(GrMatrix::I());
@@ -623,7 +650,7 @@
     target->setSamplerState(kOffscreenStage, sampler);
 
     GrRect dstRect;
-    int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1);
+    int stages = (1 << kOffscreenStage) | stageMask;
     dstRect.set(boundRect);
     target->drawSimpleRect(dstRect, NULL, stages);
 
@@ -661,11 +688,14 @@
 }
 
 static GrColor getColorForMesh(const GrPaint& paint) {
-    if (NULL == paint.getTexture()) {
-        return paint.fColor;
-    } else {
+    // FIXME: This was copied from SkGpuDevice, seems like
+    // we should have already smeared a in caller if that
+    // is what is desired.
+    if (paint.hasTexture()) {
         unsigned a = GrColorUnpackA(paint.fColor);
         return GrColorPackRGBA(a, a, a, a);
+    } else {
+        return paint.fColor;
     }
 }
 
@@ -741,10 +771,8 @@
                            const GrPaint& paint,
                            const GrRect& devRect) {
 
-    GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
-    if (NULL != paint.getTexture()) {
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-    }
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+                            GrDrawTarget::kColor_VertexLayoutBit;
 
     size_t vsize = GrDrawTarget::VertexSize(layout);
 
@@ -782,11 +810,8 @@
     const GrScalar rx = GrMul(dx, GR_ScalarHalf);
     const GrScalar ry = GrMul(dy, GR_ScalarHalf);
 
-    GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
-
-    if (NULL != paint.getTexture()) {
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-    }
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
+                            GrDrawTarget::kColor_VertexLayoutBit;
 
     GrScalar spare;
     {
@@ -902,9 +927,9 @@
                          GrScalar width,
                          const GrMatrix* matrix) {
 
-    bool textured = NULL != paint.getTexture();
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    int stageMask = paint.getActiveStageMask();
 
     GrRect devRect = rect;
     GrMatrix combinedMatrix;
@@ -913,10 +938,10 @@
 
     if (doAA) {
         GrDrawTarget::AutoViewMatrixRestore avm(target);
-        if (textured) {
+        if (stageMask) {
             GrMatrix inv;
             if (combinedMatrix.invert(&inv)) {
-                target->preConcatSamplerMatrix(0, inv);
+                target->preConcatSamplerMatrices(stageMask, inv);
             }
         }
         target->setViewMatrix(GrMatrix::I());
@@ -940,9 +965,8 @@
         // TODO: consider making static vertex buffers for these cases.
         // Hairline could be done by just adding closing vertex to
         // unitSquareVertexBuffer()
-        GrVertexLayout layout = textured ?
-                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
-                            0;
+        GrVertexLayout layout =  PaintStageVertexLayoutBits(paint, NULL);
+
         static const int worstCaseVertCount = 10;
         GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
 
@@ -973,17 +997,14 @@
         if (NULL != matrix) {
             avmr.set(target);
             target->preConcatViewMatrix(*matrix);
-            if (textured) {
-                target->preConcatSamplerMatrix(0, *matrix);
-            }
+            target->preConcatSamplerMatrices(stageMask, *matrix);
         }
 
         target->drawNonIndexed(primType, 0, vertCount);
     } else {
         #if GR_STATIC_RECT_VB
-            GrVertexLayout layout = (textured) ?
-                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
-                            0;
+            GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
+
             target->setVertexSourceToBuffer(layout,
                                             fGpu->getUnitSquareVertexBuffer());
             GrDrawTarget::AutoViewMatrixRestore avmr(target);
@@ -997,13 +1018,11 @@
             }
 
             target->preConcatViewMatrix(m);
-
-            if (textured) {
-                target->preConcatSamplerMatrix(0, m);
-            }
+            target->preConcatSamplerMatrices(stageMask, m);
+ 
             target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
         #else
-            target->drawSimpleRect(rect, matrix, textured ? 1 : 0);
+            target->drawSimpleRect(rect, matrix, stageMask);
         #endif
     }
 }
@@ -1014,7 +1033,8 @@
                                const GrMatrix* dstMatrix,
                                const GrMatrix* srcMatrix) {
 
-    if (NULL == paint.getTexture()) {
+    // srcRect refers to paint's first texture
+    if (NULL == paint.getTexture(0)) {
         drawRect(paint, dstRect, -1, dstMatrix);
         return;
     }
@@ -1023,8 +1043,8 @@
 
 #if GR_STATIC_RECT_VB
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-
-    GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+    
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
     GrDrawTarget::AutoViewMatrixRestore avmr(target);
 
     GrMatrix m;
@@ -1037,13 +1057,20 @@
     }
     target->preConcatViewMatrix(m);
 
+    // srcRect refers to first stage
+    int otherStageMask = paint.getActiveStageMask() & 
+                         (~(1 << GrPaint::kFirstTextureStage));
+    if (otherStageMask) {
+        target->preConcatSamplerMatrices(otherStageMask, m);
+    }
+
     m.setAll(srcRect.width(), 0,                srcRect.fLeft,
              0,               srcRect.height(), srcRect.fTop,
              0,               0,                GrMatrix::I()[8]);
     if (NULL != srcMatrix) {
         m.postConcat(*srcMatrix);
     }
-    target->preConcatSamplerMatrix(0, m);
+    target->preConcatSamplerMatrix(GrPaint::kFirstTextureStage, m);
 
     target->setVertexSourceToBuffer(layout, fGpu->getUnitSquareVertexBuffer());
     target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
@@ -1073,26 +1100,22 @@
                              const GrColor colors[],
                              const uint16_t indices[],
                              int indexCount) {
-    GrVertexLayout layout = 0;
-    int vertexSize = sizeof(GrPoint);
 
     GrDrawTarget::AutoReleaseGeometry geo;
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
 
-    if (NULL != paint.getTexture()) {
-        if (NULL == texCoords) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        } else {
-            layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
-            vertexSize += sizeof(GrPoint);
-        }
-    }
+    bool hasTexCoords[GrPaint::kTotalStages] = {
+        NULL != texCoords,   // texCoordSrc provides explicit stage 0 coords
+        0                    // remaining stages use positions
+    };
+
+    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
 
     if (NULL != colors) {
         layout |= GrDrawTarget::kColor_VertexLayoutBit;
-        vertexSize += sizeof(GrColor);
     }
+    int vertexSize = GrDrawTarget::VertexSize(layout);
 
     bool doAA = false;
     OffscreenRecord record;
@@ -1105,9 +1128,9 @@
         }
         int texOffsets[GrDrawTarget::kMaxTexCoords];
         int colorOffset;
-        int vsize = GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
-                                                            texOffsets,
-                                                            &colorOffset);
+        GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+                                                texOffsets,
+                                                &colorOffset);
         void* curVertex = geo.vertices();
 
         for (int i = 0; i < vertexCount; ++i) {
@@ -1119,7 +1142,7 @@
             if (colorOffset > 0) {
                 *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
             }
-            curVertex = (void*)((intptr_t)curVertex + vsize);
+            curVertex = (void*)((intptr_t)curVertex + vertexSize);
         }
     } else {
         // we don't do offscreen AA when we have per-vertex tex coords or colors
@@ -1193,10 +1216,7 @@
             return;
         }
     } 
-    GrDrawTarget::StageBitfield enabledStages = 0;
-    if (NULL != paint.getTexture()) {
-        enabledStages |= 1;
-    }
+    GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
 
     pr->drawPath(target, enabledStages, path, fill, translate);
 }
@@ -1308,8 +1328,21 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) {
-    target->setTexture(0, paint.getTexture());
-    target->setSamplerState(0, paint.fSampler);
+
+    for (int i = 0; i < GrPaint::kMaxTextures; ++i) {
+        int s = i + GrPaint::kFirstTextureStage;
+        target->setTexture(s, paint.getTexture(i));
+        target->setSamplerState(s, *paint.getTextureSampler(i));
+    }
+
+    target->setFirstCoverageStage(GrPaint::kFirstMaskStage);
+
+    for (int i = 0; i < GrPaint::kMaxMasks; ++i) {
+        int s = i + GrPaint::kFirstMaskStage;
+        target->setTexture(s, paint.getMask(i));
+        target->setSamplerState(s, *paint.getMaskSampler(i));
+    }
+
     target->setColor(paint.fColor);
 
     if (paint.fDither) {
diff --git a/gpu/src/GrTextContext.cpp b/gpu/src/GrTextContext.cpp
index 9e8e40a..2aacfa2 100644
--- a/gpu/src/GrTextContext.cpp
+++ b/gpu/src/GrTextContext.cpp
@@ -25,11 +25,9 @@
 #include "GrGpuVertex.h"
 #include "GrDrawTarget.h"
 
-static const int TEXT_STAGE = 1;
-
-static const GrVertexLayout BASE_VLAYOUT =
-                    GrDrawTarget::kTextFormat_VertexLayoutBit |
-                    GrDrawTarget::StageTexCoordVertexLayoutBit(TEXT_STAGE,0);
+enum {
+    kGlyphMaskStage = GrPaint::kTotalStages,
+};
 
 void GrTextContext::flushGlyphs() {
     if (fCurrVertex > 0) {
@@ -45,17 +43,17 @@
         GrSamplerState sampler(GrSamplerState::kRepeat_WrapMode,
                                GrSamplerState::kRepeat_WrapMode,
                                filter);
-        fDrawTarget->setSamplerState(TEXT_STAGE, sampler);
+        fDrawTarget->setSamplerState(kGlyphMaskStage, sampler);
 
         GrAssert(GrIsALIGN4(fCurrVertex));
         int nIndices = fCurrVertex + (fCurrVertex >> 1);
         GrAssert(fCurrTexture);
-        fDrawTarget->setTexture(TEXT_STAGE, fCurrTexture);
+        fDrawTarget->setTexture(kGlyphMaskStage, fCurrTexture);
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
             if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
                 kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
-                NULL != fPaint.getTexture()) {
+                fPaint.hasTexture()) {
                 GrPrintf("LCD Text will not draw correctly.\n");
             }
             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
@@ -117,18 +115,31 @@
     fOrigViewMatrix = fContext->getMatrix();
     fContext->setMatrix(fExtMatrix);
 
-    fVertexLayout = BASE_VLAYOUT;
-    if (NULL != paint.getTexture()) {
-        fVertexLayout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-        GrMatrix inverseViewMatrix;
-        if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
-            fPaint.fSampler.preConcatMatrix(inverseViewMatrix);
-        }
-    }
+    fDrawTarget = fContext->getTextTarget(fPaint);
 
     fVertices = NULL;
     fMaxVertices = 0;
-    fDrawTarget = fContext->getTextTarget(fPaint);
+
+    fVertexLayout = 
+        GrDrawTarget::kTextFormat_VertexLayoutBit |
+        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+
+    int stageMask = paint.getActiveStageMask();
+    if (stageMask) {
+        GrMatrix inverseViewMatrix;
+        if (fOrigViewMatrix.invert(&inverseViewMatrix)) {
+            fDrawTarget->preConcatSamplerMatrices(stageMask, 
+                                                  inverseViewMatrix);
+        }
+        for (int i = 0; i < GrPaint::kTotalStages; ++i) {
+            if ((1 << i) & stageMask) {
+                fVertexLayout |= 
+                    GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
+                GrAssert(i != kGlyphMaskStage);
+            }
+        }
+    }
+
 }
 
 GrTextContext::~GrTextContext() {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 508e493..624cc68 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -40,6 +40,13 @@
     #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
 #endif
 
+// we use the same texture slot on GrPaint for bitmaps and shaders
+// (since drawBitmap, drawSprite, and drawDevice ignore skia's shader)
+enum {
+    kBitmapTextureIdx = 0,
+    kShaderTextureIdx = 0
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGpuDevice::SkAutoCachedTexture::
@@ -316,7 +323,7 @@
 
 bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
     if (NULL != fTexture) {
-        paint->setTexture(fTexture);
+        paint->setTexture(kBitmapTextureIdx, fTexture);
         return true;
     }
     return false;
@@ -371,7 +378,7 @@
         GrAssert(!constantColor);
     } else {
         grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
-        grPaint->setTexture(NULL);
+        grPaint->setTexture(kShaderTextureIdx, NULL);
     }
     SkColorFilter* colorFilter = skPaint.getColorFilter();
     SkColor color;
@@ -423,26 +430,27 @@
         SkDebugf("shader->asABitmap() == kNone_BitmapType\n");
         return false;
     }
-    grPaint->fSampler.setSampleMode(sampleMode);
+    GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx);
+    sampler->setSampleMode(sampleMode);
     if (skPaint.isFilterBitmap()) {
-        grPaint->fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
     } else {
-        grPaint->fSampler.setFilter(GrSamplerState::kNearest_Filter);
+        sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
-    grPaint->fSampler.setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
-    grPaint->fSampler.setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
+    sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
+    sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
     if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
-        grPaint->fSampler.setRadial2Params(twoPointParams[0],
-                                           twoPointParams[1],
-                                           twoPointParams[2] < 0);
+        sampler->setRadial2Params(twoPointParams[0],
+                                  twoPointParams[1],
+                                  twoPointParams[2] < 0);
     }
 
-    GrTexture* texture = act->set(this, bitmap, grPaint->fSampler);
+    GrTexture* texture = act->set(this, bitmap, *sampler);
     if (NULL == texture) {
         SkDebugf("Couldn't convert bitmap to texture.\n");
         return false;
     }
-    grPaint->setTexture(texture);
+    grPaint->setTexture(kShaderTextureIdx, texture);
 
     // since our texture coords will be in local space, we wack the texture
     // matrix to map them back into 0...1 before we load it
@@ -461,7 +469,7 @@
         GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
         matrix.postScale(s, s);
     }
-    grPaint->fSampler.setMatrix(matrix);
+    sampler->setMatrix(matrix);
 
     return true;
 }
@@ -737,6 +745,9 @@
     // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
     // the current clip (and identity matrix) and grpaint settings
 
+    // used to compute inverse view, if necessary
+    GrMatrix ivm = context->getMatrix();
+
     GrAutoMatrix avm(context, GrMatrix::I());
 
     const GrTextureDesc desc = {
@@ -753,18 +764,29 @@
         return false;
     }
 
-    grp->setTexture(texture);
+    if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
+        grp->preConcatActiveSamplerMatrices(ivm);
+    }
+
+    static const int MASK_IDX = GrPaint::kMaxMasks - 1;
+    // we assume the last mask index is available for use
+    GrAssert(NULL == grp->getMask(MASK_IDX));
+    grp->setMask(MASK_IDX, texture);
     texture->unref();
-    grp->fSampler.setClampNoFilter();
+    grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
 
     GrRect d;
     d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
               GrIntToScalar(dstM.fBounds.fTop),
               GrIntToScalar(dstM.fBounds.fRight),
               GrIntToScalar(dstM.fBounds.fBottom));
-    GrRect s;
-    s.setLTRB(0, 0, GR_Scalar1, GR_Scalar1);
-    context->drawRectToRect(*grp, d, s);
+
+    GrMatrix m;
+    m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop);
+    m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height());
+    grp->getMaskSampler(MASK_IDX)->setMatrix(m);
+    
+    context->drawRect(*grp, d);
     return true;
 }
 
@@ -881,12 +903,12 @@
     if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
         return;
     }
+    GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
     if (paint.isFilterBitmap()) {
-        grPaint.fSampler.setFilter(GrSamplerState::kBilinear_Filter);
+        sampler->setFilter(GrSamplerState::kBilinear_Filter);
     } else {
-        grPaint.fSampler.setFilter(GrSamplerState::kNearest_Filter);
+        sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
-    
 
     const int maxTextureDim = fContext->getMaxTextureDimension();
     if (bitmap.getTexture() || (bitmap.width() <= maxTextureDim &&
@@ -968,18 +990,20 @@
         return;
     }
 
-    grPaint->fSampler.setWrapX(GrSamplerState::kClamp_WrapMode);
-    grPaint->fSampler.setWrapY(GrSamplerState::kClamp_WrapMode);
-    grPaint->fSampler.setSampleMode(GrSamplerState::kNormal_SampleMode);
-    grPaint->fSampler.setMatrix(GrMatrix::I());
+    GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx);
+
+    sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
+    sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
+    sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
+    sampler->setMatrix(GrMatrix::I());
 
     GrTexture* texture;
-    SkAutoCachedTexture act(this, bitmap, grPaint->fSampler, &texture);
+    SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
     if (NULL == texture) {
         return;
     }
 
-    grPaint->setTexture(texture);
+    grPaint->setTexture(kShaderTextureIdx, texture);
 
     GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
                                     GrIntToScalar(srcRect.height()));
@@ -989,9 +1013,9 @@
                       GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
                       GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
 
-    if (GrSamplerState::kNearest_Filter != grPaint->fSampler.getFilter() &&
+    if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
         (srcRect.width() < bitmap.width() || 
-        srcRect.height() < bitmap.height())) {
+         srcRect.height() < bitmap.height())) {
         // If drawing a subrect of the bitmap and filtering is enabled,
         // use a constrained texture domain to avoid color bleeding
         GrScalar left, top, right, bottom;
@@ -1011,7 +1035,7 @@
         }
         GrRect textureDomain;
         textureDomain.setLTRB(left, top, right, bottom);
-        grPaint->fSampler.setTextureDomain(textureDomain);
+        sampler->setTextureDomain(textureDomain);
     }
 
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
@@ -1033,11 +1057,13 @@
 
     GrAutoMatrix avm(fContext, GrMatrix::I());
 
-    GrTexture* texture;
-    grPaint.fSampler.setClampNoFilter();
-    SkAutoCachedTexture act(this, bitmap, grPaint.fSampler, &texture);
+    GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
 
-    grPaint.setTexture(texture);
+    GrTexture* texture;
+    sampler->setClampNoFilter();
+    SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
+
+    grPaint.setTexture(kBitmapTextureIdx, texture);
 
     fContext->drawRectToRect(grPaint,
                              GrRect::MakeXYWH(GrIntToScalar(left),
@@ -1057,7 +1083,7 @@
         return;
     }
 
-    SkASSERT(NULL != grPaint.getTexture());
+    SkASSERT(NULL != grPaint.getTexture(0));
 
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
@@ -1065,7 +1091,7 @@
 
     GrAutoMatrix avm(fContext, GrMatrix::I());
 
-    grPaint.fSampler.setClampNoFilter();
+    grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
 
     fContext->drawRectToRect(grPaint,
                              GrRect::MakeXYWH(GrIntToScalar(x),