Add per-vertex coverage field, use in AA rects when alpha tweak is not valid, add relevant sample/gm

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



git-svn-id: http://skia.googlecode.com/svn/trunk@2440 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp
new file mode 100644
index 0000000..ffb267d
--- /dev/null
+++ b/gm/aarectmodes.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+static SkCanvas* create_canvas(int w, int h) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bm.allocPixels();
+    bm.eraseColor(0);
+    return new SkCanvas(bm);
+}
+
+static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
+    return canvas->getDevice()->accessBitmap(false);
+}
+
+static const struct {
+    SkXfermode::Mode  fMode;
+    const char*         fLabel;
+} gModes[] = {
+    { SkXfermode::kClear_Mode,    "Clear"     },
+    { SkXfermode::kSrc_Mode,      "Src"       },
+    { SkXfermode::kDst_Mode,      "Dst"       },
+    { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+    { SkXfermode::kDstOver_Mode,  "DstOver"   },
+    { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+    { SkXfermode::kDstIn_Mode,    "DstIn"     },
+    { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+    { SkXfermode::kDstOut_Mode,   "DstOut"    },
+    { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+    { SkXfermode::kDstATop_Mode,  "DstATop"   },
+    { SkXfermode::kXor_Mode,      "Xor"       },
+};
+
+const int gWidth = 64;
+const int gHeight = 64;
+const SkScalar W = SkIntToScalar(gWidth);
+const SkScalar H = SkIntToScalar(gHeight);
+
+static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode,
+                         SkAlpha a0, SkAlpha a1) {
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkRect r = SkRect::MakeWH(W, H);
+    r.inset(W/10, H/10);
+
+    paint.setColor(SK_ColorBLUE);
+    paint.setAlpha(a0);
+    canvas->drawOval(r, paint);
+
+    paint.setColor(SK_ColorRED);
+    paint.setAlpha(a1);
+    paint.setXfermode(mode);
+
+    SkScalar offset = SK_Scalar1 / 3;
+    SkRect rect = SkRect::MakeXYWH(W / 4 + offset,
+                                   H / 4 + offset,
+                                   W / 2, H / 2);
+    canvas->drawRect(rect, paint);
+    
+    return H;
+}
+
+static SkShader* make_bg_shader() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bm.allocPixels();
+    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
+    *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC,
+                                                             0xCC, 0xCC);
+
+    SkShader* s = SkShader::CreateBitmapShader(bm,
+                                               SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+    
+    SkMatrix m;
+    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+    s->setLocalMatrix(m);
+    return s;
+}
+
+namespace skiagm {
+    
+    class AARectModesGM : public GM {
+        SkPaint fBGPaint;
+    public:
+        AARectModesGM () {
+            fBGPaint.setShader(make_bg_shader())->unref();
+        }
+
+    protected:
+
+        virtual SkString onShortName() {
+            return SkString("aarectmodes");
+        }
+
+        virtual SkISize onISize() { return make_isize(640, 480); }
+
+        virtual void onDraw(SkCanvas* canvas) {
+            canvas->drawColor(SK_ColorWHITE);
+
+            const SkRect bounds = SkRect::MakeWH(W, H);
+            static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
+
+            canvas->translate(SkIntToScalar(4), SkIntToScalar(4));
+
+            for (int alpha = 0; alpha < 4; ++alpha) {
+                canvas->save();
+                canvas->save();
+                for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) {
+                    if (6 == i) {
+                        canvas->restore();
+                        canvas->translate(W * 5, 0);
+                        canvas->save();
+                    }
+                    SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+                
+                    canvas->drawRect(bounds, fBGPaint);
+                    canvas->saveLayer(&bounds, NULL);
+                    SkScalar dy = drawCell(canvas, mode,
+                                           gAlphaValue[alpha & 1],
+                                           gAlphaValue[alpha & 2]);
+                    canvas->restore();
+
+                    canvas->translate(0, dy * 5 / 4);
+                    SkSafeUnref(mode);
+                }
+                canvas->restore();
+                canvas->restore();
+                canvas->translate(W * 5 / 4, 0);
+            }
+        }
+
+        // disable pdf for now, since it crashes on mac
+        virtual uint32_t onGetFlags() const { return kSkipPDF_Flag; }
+
+    private:
+        typedef GM INHERITED;
+    };
+
+//////////////////////////////////////////////////////////////////////////////
+
+    static GM* MyFactory(void*) { return new AARectModesGM; }
+    static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index eeb96cc..4a1f1da 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -563,13 +563,13 @@
     GrContext(GrGpu* gpu);
 
     void fillAARect(GrDrawTarget* target,
-                    const GrPaint& paint,
-                    const GrRect& devRect);
+                    const GrRect& devRect,
+                    bool useVertexCoverage);
 
     void strokeAARect(GrDrawTarget* target,
-                      const GrPaint& paint,
                       const GrRect& devRect,
-                      const GrVec& devStrokeSize);
+                      const GrVec& devStrokeSize,
+                      bool useVertexCoverage);
 
     inline int aaFillRectIndexCount() const;
     GrIndexBuffer* aaFillRectIndexBuffer();
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index d9fb9e8..3f5ab54 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -677,6 +677,7 @@
         return false;
     }
     if (disable_coverage_aa_for_blend(target)) {
+        GrPrintf("Turning off AA to correctly apply blend.\n");
         return false;
     }
     return true;
@@ -937,18 +938,6 @@
     verts[9] = verts[1];
 }
 
-static GrColor getColorForMesh(const GrPaint& paint) {
-    // 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;
-    }
-}
-
 static void setInsetFan(GrPoint* pts, size_t stride,
                         const GrRect& r, GrScalar dx, GrScalar dy) {
     pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
@@ -1019,11 +1008,26 @@
     return fAAStrokeRectIndexBuffer;
 }
 
+static GrVertexLayout aa_rect_layout(const GrDrawTarget* target,
+                                     bool useCoverage) {
+    GrVertexLayout layout = 0;
+    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+        if (NULL != target->getTexture(s)) {
+            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+        }
+    }
+    if (useCoverage) {
+        layout |= GrDrawTarget::kCoverage_VertexLayoutBit;
+    } else {
+        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+    }
+    return layout;
+}
+
 void GrContext::fillAARect(GrDrawTarget* target,
-                           const GrPaint& paint,
-                           const GrRect& devRect) {
-    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
-                            GrDrawTarget::kColor_VertexLayoutBit;
+                           const GrRect& devRect,
+                           bool useVertexCoverage) {
+    GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
 
     size_t vsize = GrDrawTarget::VertexSize(layout);
 
@@ -1051,7 +1055,13 @@
         *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
     }
 
-    GrColor innerColor = getColorForMesh(paint);
+    GrColor innerColor;
+    if (useVertexCoverage) {
+        innerColor = GrColorPackRGBA(0,0,0,0xff);
+    } else {
+        innerColor = target->getColor();
+    }
+
     verts += 4 * vsize;
     for (int i = 0; i < 4; ++i) {
         *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
@@ -1063,16 +1073,15 @@
                          0, 8, this->aaFillRectIndexCount());
 }
 
-void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint,
-                             const GrRect& devRect, const GrVec& devStrokeSize) {
+void GrContext::strokeAARect(GrDrawTarget* target,
+                             const GrRect& devRect,
+                             const GrVec& devStrokeSize,
+                             bool useVertexCoverage) {
     const GrScalar& dx = devStrokeSize.fX;
     const GrScalar& dy = devStrokeSize.fY;
     const GrScalar rx = GrMul(dx, GR_ScalarHalf);
     const GrScalar ry = GrMul(dy, GR_ScalarHalf);
 
-    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL) |
-                            GrDrawTarget::kColor_VertexLayoutBit;
-
     GrScalar spare;
     {
         GrScalar w = devRect.width() - dx;
@@ -1083,10 +1092,10 @@
     if (spare <= 0) {
         GrRect r(devRect);
         r.inset(-rx, -ry);
-        fillAARect(target, paint, r);
+        fillAARect(target, r, useVertexCoverage);
         return;
     }
-
+    GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
     size_t vsize = GrDrawTarget::VertexSize(layout);
 
     GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
@@ -1117,7 +1126,12 @@
         *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
     }
 
-    GrColor innerColor = getColorForMesh(paint);
+    GrColor innerColor;
+    if (useVertexCoverage) {
+        innerColor = GrColorPackRGBA(0,0,0,0xff);
+    } else {
+        innerColor = target->getColor();
+    }
     verts += 4 * vsize;
     for (int i = 0; i < 8; ++i) {
         *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
@@ -1146,7 +1160,8 @@
                              GrScalar width, 
                              const GrMatrix* matrix,
                              GrMatrix* combinedMatrix,
-                             GrRect* devRect) {
+                             GrRect* devRect,
+                             bool* useVertexCoverage) {
     // we use a simple alpha ramp to do aa on axis-aligned rects
     // do AA with alpha ramp if the caller requested AA, the rect 
     // will be axis-aligned,the render target is not
@@ -1156,8 +1171,22 @@
         return false;
     }
 
+    // we are keeping around the "tweak the alpha" trick because
+    // it is our only hope for the fixed-pipe implementation.
+    // In a shader implementation we can give a separate coverage input
+    *useVertexCoverage = false;
     if (!target->canTweakAlphaForCoverage()) {
-        return false;
+        if (target->getCaps().fSupportPerVertexCoverage) {
+            if (disable_coverage_aa_for_blend(target)) {
+                GrPrintf("Turning off AA to correctly apply blend.\n");
+                return false;
+            } else {
+                *useVertexCoverage = true;
+            }
+        } else {
+            GrPrintf("Rect AA dropped because no support for coverage.\n");
+            return false;
+        }
     }
 
     if (target->getRenderTarget()->isMultisampled()) {
@@ -1204,8 +1233,9 @@
 
     GrRect devRect = rect;
     GrMatrix combinedMatrix;
+    bool useVertexCoverage;
     bool doAA = apply_aa_to_rect(target, rect, width, matrix,
-                                 &combinedMatrix, &devRect);
+                                 &combinedMatrix, &devRect, &useVertexCoverage);
 
     if (doAA) {
         GrDrawTarget::AutoViewMatrixRestore avm(target);
@@ -1225,9 +1255,9 @@
             } else {
                 strokeSize.set(GR_Scalar1, GR_Scalar1);
             }
-            strokeAARect(target, paint, devRect, strokeSize);
+            strokeAARect(target, devRect, strokeSize, useVertexCoverage);
         } else {
-            fillAARect(target, paint, devRect);
+            fillAARect(target, devRect, useVertexCoverage);
         }
         return;
     }
@@ -1406,12 +1436,11 @@
         }
         int texOffsets[GrDrawTarget::kMaxTexCoords];
         int colorOffset;
-        int edgeOffset;
         GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
                                                 texOffsets,
                                                 &colorOffset,
-                                                &edgeOffset);
-        GrAssert(-1 == edgeOffset);
+                                                NULL,
+                                                NULL);
         void* curVertex = geo.vertices();
 
         for (int i = 0; i < vertexCount; ++i) {
@@ -1462,6 +1491,7 @@
     // aa. If we have some future driver-mojo path AA that can do the right
     // thing WRT to the blend then we'll need some query on the PR.
     if (disable_coverage_aa_for_blend(target)) {
+        GrPrintf("Turning off AA to correctly apply blend.\n");
         target->disableState(GrDrawTarget::kAntialias_StateBit);
     }
     
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 79b3da8..2169fc7 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -88,12 +88,30 @@
     if (vertexLayout & kColor_VertexLayoutBit) {
         size += sizeof(GrColor);
     }
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
     if (vertexLayout & kEdge_VertexLayoutBit) {
         size += 4 * sizeof(GrScalar);
     }
     return size;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Functions for computing offsets of various components from the layout
+ * bitfield.
+ * 
+ * Order of vertex components:
+ * Position
+ * Tex Coord 0
+ * ...
+ * Tex Coord kMaxTexCoords-1
+ * Color
+ * Coverage
+ */
+
 int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
     GrAssert(check_layout(vertexLayout));
     if (StagePosAsTexCoordVertexLayoutBit(stage) & vertexLayout) {
@@ -121,7 +139,6 @@
 int  GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
     GrAssert(check_layout(vertexLayout));
 
-    // color is after the pos and tex coords
     if (vertexLayout & kColor_VertexLayoutBit) {
         int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
                                     sizeof(GrGpuTextVertex) :
@@ -131,6 +148,23 @@
     return -1;
 }
 
+int  GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
 int  GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
     GrAssert(check_layout(vertexLayout));
 
@@ -143,21 +177,21 @@
         if (vertexLayout & kColor_VertexLayoutBit) {
             offset += sizeof(GrColor);
         }
+        if (vertexLayout & kCoverage_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
         return offset;
     }
     return -1;
 }
 
 int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
-                                             int texCoordOffsetsByIdx[kMaxTexCoords],
-                                             int* colorOffset,
-                                             int* edgeOffset) {
+                                            int texCoordOffsetsByIdx[kMaxTexCoords],
+                                            int* colorOffset,
+                                            int* coverageOffset,
+                                            int* edgeOffset) {
     GrAssert(check_layout(vertexLayout));
 
-    GrAssert(NULL != texCoordOffsetsByIdx);
-    GrAssert(NULL != colorOffset);
-    GrAssert(NULL != edgeOffset);
-
     int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
                                                     sizeof(GrGpuTextVertex) :
                                                     sizeof(GrPoint);
@@ -165,23 +199,45 @@
 
     for (int t = 0; t < kMaxTexCoords; ++t) {
         if (tex_coord_idx_mask(t) & vertexLayout) {
-            texCoordOffsetsByIdx[t] = size;
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = size;
+            }
             size += vecSize;
         } else {
-            texCoordOffsetsByIdx[t] = -1;
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = -1;
+            }
         }
     }
     if (kColor_VertexLayoutBit & vertexLayout) {
-        *colorOffset = size;
+        if (NULL != colorOffset) {
+            *colorOffset = size;
+        }
         size += sizeof(GrColor);
     } else {
-        *colorOffset = -1;
+        if (NULL != colorOffset) {
+            *colorOffset = -1;
+        }
+    }
+    if (kCoverage_VertexLayoutBit & vertexLayout) {
+        if (NULL != coverageOffset) {
+            *coverageOffset = size;
+        }
+        size += sizeof(GrColor);
+    } else {
+        if (NULL != coverageOffset) {
+            *coverageOffset = -1;
+        }
     }
     if (kEdge_VertexLayoutBit & vertexLayout) {
-        *edgeOffset = size;
+        if (NULL != edgeOffset) {
+            *edgeOffset = size;
+        }
         size += 4 * sizeof(GrScalar);
     } else {
-        *edgeOffset = -1;
+        if (NULL != edgeOffset) {
+            *edgeOffset = -1;
+        }
     }
     return size;
 }
@@ -189,31 +245,35 @@
 int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
                                               int texCoordOffsetsByStage[kNumStages],
                                               int* colorOffset,
+                                              int* coverageOffset,
                                               int* edgeOffset) {
     GrAssert(check_layout(vertexLayout));
 
-    GrAssert(NULL != texCoordOffsetsByStage);
-    GrAssert(NULL != colorOffset);
-    GrAssert(NULL != edgeOffset);
-
     int texCoordOffsetsByIdx[kMaxTexCoords];
     int size = VertexSizeAndOffsetsByIdx(vertexLayout,
-                                         texCoordOffsetsByIdx,
+                                         (NULL == texCoordOffsetsByStage) ?
+                                               NULL :
+                                               texCoordOffsetsByIdx,
                                          colorOffset,
+                                         coverageOffset,
                                          edgeOffset);
-    for (int s = 0; s < kNumStages; ++s) {
-        int tcIdx;
-        if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
-            texCoordOffsetsByStage[s] = 0;
-        } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) {
-            texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx];
-        } else {
-            texCoordOffsetsByStage[s] = -1;
+    if (NULL != texCoordOffsetsByStage) {
+        for (int s = 0; s < kNumStages; ++s) {
+            int tcIdx;
+            if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
+                texCoordOffsetsByStage[s] = 0;
+            } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) {
+                texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx];
+            } else {
+                texCoordOffsetsByStage[s] = -1;
+            }
         }
     }
     return size;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 bool GrDrawTarget::VertexUsesStage(int stage, GrVertexLayout vertexLayout) {
     GrAssert(stage < kNumStages);
     GrAssert(check_layout(vertexLayout));
@@ -241,6 +301,8 @@
     return -1;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 void GrDrawTarget::VertexLayoutUnitTest() {
     // not necessarily exhaustive
     static bool run;
@@ -286,9 +348,11 @@
                 }
                 GrAssert(-1 == VertexEdgeOffset(tcMask));
                 GrAssert(-1 == VertexColorOffset(tcMask));
+                GrAssert(-1 == VertexCoverageOffset(tcMask));
             #if GR_DEBUG
                 GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
             #endif
+                GrAssert(-1 == VertexCoverageOffset(withColor));
                 GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
                 GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
             #if GR_DEBUG
@@ -303,6 +367,19 @@
                 GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
                 GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
                 GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
+            #if GR_DEBUG
+                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
+            #if GR_DEBUG
+                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
+                                                      kColor_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
             }
             GrAssert(tex_coord_idx_mask(t) == tcMask);
             GrAssert(check_layout(tcMask));
@@ -310,10 +387,13 @@
             int stageOffsets[kNumStages];
             int colorOffset;
             int edgeOffset;
+            int coverageOffset;
             int size;
-            size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset, &edgeOffset);
+            size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset,
+                                               &coverageOffset, &edgeOffset);
             GrAssert(2*sizeof(GrPoint) == size);
             GrAssert(-1 == colorOffset);
+            GrAssert(-1 == coverageOffset);
             GrAssert(-1 == edgeOffset);
             for (int s = 0; s < kNumStages; ++s) {
                 GrAssert(VertexUsesStage(s, tcMask));
@@ -765,8 +845,9 @@
 bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) {
     // If we compute a coverage value (using edge AA or a coverage stage) then
     // we can't force blending off.
-    if (state.fEdgeAANumEdges > 0 || 
-        layout & kEdge_VertexLayoutBit) {
+    if (state.fEdgeAANumEdges > 0 ||
+        (layout & kEdge_VertexLayoutBit) ||
+        (layout & kCoverage_VertexLayoutBit)) {
         return false;
     }
     for (int s = state.fFirstCoverageStage; s < kNumStages; ++s) {
@@ -910,14 +991,10 @@
 #endif
 
     int stageOffsets[kNumStages];
-    int colorOffset;
-    int edgeOffset;
-    int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, 
-                                            &colorOffset, &edgeOffset);
-    GrAssert(-1 == colorOffset);
-    GrAssert(-1 == edgeOffset);
+    int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
+                                            NULL, NULL, NULL);
 
-    GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop, 
+    GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
                                             rect.fRight, rect.fBottom,
                                             vsize);
     if (NULL != matrix) {
@@ -926,10 +1003,10 @@
 
     for (int i = 0; i < kNumStages; ++i) {
         if (stageOffsets[i] > 0) {
-            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(vertices) + 
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(vertices) +
                                                 stageOffsets[i]);
             coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
-                               srcRects[i]->fRight, srcRects[i]->fBottom, 
+                               srcRects[i]->fRight, srcRects[i]->fBottom,
                                vsize);
             if (NULL != srcMatrices && NULL != srcMatrices[i]) {
                 srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h
index a1667e4..86aa3b4 100644
--- a/gpu/src/GrDrawTarget.h
+++ b/gpu/src/GrDrawTarget.h
@@ -53,6 +53,7 @@
         bool fFSAASupport               : 1;
         bool fDualSourceBlendingSupport : 1;
         bool fBufferLockSupport         : 1;
+        bool fSupportPerVertexCoverage  : 1;
         int fMinRenderTargetWidth;
         int fMinRenderTargetHeight;
         int fMaxRenderTargetSize;
@@ -408,6 +409,12 @@
     void setColor(GrColor);
 
     /**
+     * Gets the currently set color.
+     * @return the current color.
+     */
+    GrColor getColor() const { return fCurrDrawState.fColor; }
+
+    /**
      * Add a color filter that can be represented by a color and a mode.
      */
     void setColorFilter(GrColor, SkXfermode::Mode);
@@ -517,77 +524,6 @@
     GrColor getBlendConstant() const { return fCurrDrawState.fBlendConstant; }
 
     /**
-     * Used to save and restore the GrGpu's drawing state
-     */
-    struct SavedDrawState {
-    private:
-        DrState fState;
-        friend class GrDrawTarget;
-    };
-
-    /**
-     * Saves the current draw state. The state can be restored at a later time
-     * with restoreDrawState.
-     *
-     * See also AutoStateRestore class.
-     *
-     * @param   state will hold the state after the function returns.
-     */
-    void saveCurrentDrawState(SavedDrawState* state) const;
-
-    /**
-     * Restores previously saved draw state. The client guarantees that state
-     * was previously passed to saveCurrentDrawState and that the rendertarget
-     * and texture set at save are still valid.
-     *
-     * See also AutoStateRestore class.
-     *
-     * @param   state the previously saved state to restore.
-     */
-    void restoreDrawState(const SavedDrawState& state);
-
-    /**
-     * Copies the draw state from another target to this target.
-     *
-     * @param srcTarget     draw target used as src of the draw state.
-     */
-    void copyDrawState(const GrDrawTarget& srcTarget);
-
-    /**
-     * The format of vertices is represented as a bitfield of flags.
-     * Flags that indicate the layout of vertex data. Vertices always contain
-     * positions and may also contain up to kMaxTexCoords sets of 2D texture
-     * coordinates and per-vertex colors. Each stage can use any of the texture
-     * coordinates as its input texture coordinates or it may use the positions.
-     *
-     * If no texture coordinates are specified for a stage then the stage is
-     * disabled.
-     *
-     * Only one type of texture coord can be specified per stage. For
-     * example StageTexCoordVertexLayoutBit(0, 2) and
-     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
-     *
-     * The order in memory is always (position, texture coord 0, ..., color)
-     * with any unused fields omitted. Note that this means that if only texture
-     * coordinates 1 is referenced then there is no texture coordinates 0 and
-     * the order would be (position, texture coordinate 1[, color]).
-     */
-
-    /**
-     * Generates a bit indicating that a texture stage uses texture coordinates
-     *
-     * @param stage       the stage that will use texture coordinates.
-     * @param texCoordIdx the index of the texture coordinates to use
-     *
-     * @return the bit to add to a GrVertexLayout bitfield.
-     */
-    static int StageTexCoordVertexLayoutBit(int stage, int texCoordIdx) {
-        GrAssert(stage < kNumStages);
-        GrAssert(texCoordIdx < kMaxTexCoords);
-        return 1 << (stage + (texCoordIdx * kNumStages));
-    }
-
-    /**
      * Determines if blend is effectively disabled.
      *
      * @return true if blend can be disabled without changing the rendering
@@ -642,6 +578,79 @@
      */
      void setEdgeAAData(const Edge* edges, int numEdges);
 
+    /**
+     * Used to save and restore the GrGpu's drawing state
+     */
+    struct SavedDrawState {
+    private:
+        DrState fState;
+        friend class GrDrawTarget;
+    };
+
+    /**
+     * Saves the current draw state. The state can be restored at a later time
+     * with restoreDrawState.
+     *
+     * See also AutoStateRestore class.
+     *
+     * @param   state will hold the state after the function returns.
+     */
+    void saveCurrentDrawState(SavedDrawState* state) const;
+
+    /**
+     * Restores previously saved draw state. The client guarantees that state
+     * was previously passed to saveCurrentDrawState and that the rendertarget
+     * and texture set at save are still valid.
+     *
+     * See also AutoStateRestore class.
+     *
+     * @param   state the previously saved state to restore.
+     */
+    void restoreDrawState(const SavedDrawState& state);
+
+    /**
+     * Copies the draw state from another target to this target.
+     *
+     * @param srcTarget     draw target used as src of the draw state.
+     */
+    void copyDrawState(const GrDrawTarget& srcTarget);
+
+    /**
+     * The format of vertices is represented as a bitfield of flags.
+     * Flags that indicate the layout of vertex data. Vertices always contain
+     * positions and may also contain up to kMaxTexCoords sets of 2D texture
+     * coordinates, per-vertex colors, and per-vertex coverage. Each stage can 
+     * use any of the texture coordinates as its input texture coordinates or it
+     * may use the positions as texture coordinates.
+     *
+     * If no texture coordinates are specified for a stage then the stage is
+     * disabled.
+     *
+     * Only one type of texture coord can be specified per stage. For
+     * example StageTexCoordVertexLayoutBit(0, 2) and
+     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
+     *
+     * The order in memory is always (position, texture coord 0, ..., color, 
+     * coverage) with any unused fields omitted. Note that this means that if 
+     * only texture coordinates 1 is referenced then there is no texture
+     * coordinates 0 and the order would be (position, texture coordinate 1
+     * [, color][, coverage]).
+     */
+
+    /**
+     * Generates a bit indicating that a texture stage uses texture coordinates
+     *
+     * @param stage       the stage that will use texture coordinates.
+     * @param texCoordIdx the index of the texture coordinates to use
+     *
+     * @return the bit to add to a GrVertexLayout bitfield.
+     */
+    static int StageTexCoordVertexLayoutBit(int stage, int texCoordIdx) {
+        GrAssert(stage < kNumStages);
+        GrAssert(texCoordIdx < kMaxTexCoords);
+        return 1 << (stage + (texCoordIdx * kNumStages));
+    }
+
 private:
     static const int TEX_COORD_BIT_CNT = kNumStages*kMaxTexCoords;
 public:
@@ -658,6 +667,7 @@
         GrAssert(stage < kNumStages);
         return (1 << (TEX_COORD_BIT_CNT + stage));
     }
+
 private:
     static const int STAGE_BIT_CNT = TEX_COORD_BIT_CNT + kNumStages;
 
@@ -667,14 +677,15 @@
      * Additional Bits that can be specified in GrVertexLayout.
      */
     enum VertexLayoutBits {
-        /* vertices have colors */
+        /* vertices have colors (GrColor) */
         kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
-                                                
+        /* vertices have coverage (GrColor where only the alpha chan is used */
+        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
         /* Use text vertices. (Pos and tex coords may be a different type for
            text [GrGpuTextVertex vs GrPoint].) */
-        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 1),
+        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
 
-        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 2),
+        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
         // for below assert
         kDummyVertexLayoutBit,
         kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
@@ -1121,6 +1132,13 @@
      */
     static int VertexColorOffset(GrVertexLayout vertexLayout);
 
+    /**
+     * Helper function to compute the offset of the coverage in a vertex
+     * @return offset of coverage in vertex layout or -1 if the
+     *         layout has no coverage.
+     */
+    static int VertexCoverageOffset(GrVertexLayout vertexLayout);
+
      /**
       * Helper function to compute the offset of the edge pts in a vertex
       * @return offset of edge in vertex layout or -1 if the
@@ -1162,12 +1180,24 @@
      * @param vertexLayout          the layout to query
      * @param texCoordOffsetsByIdx  after return it is the offset of each
      *                              tex coord index in the vertex or -1 if
-     *                              index isn't used.
+     *                              index isn't used. (optional)
+     * @param colorOffset           after return it is the offset of the
+     *                              color field in each vertex, or -1 if
+     *                              there aren't per-vertex colors. (optional)
+     * @param coverageOffset        after return it is the offset of the
+     *                              coverage field in each vertex, or -1 if
+     *                              there aren't per-vertex coeverages.
+     *                              (optional)
+     * @param edgeOffset            after return it is the offset of the
+     *                              edge eq field in each vertex, or -1 if
+     *                              there aren't per-vertex edge equations.
+     *                              (optional)
      * @return size of a single vertex
      */
     static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
                                          int texCoordOffsetsByIdx[kMaxTexCoords],
                                          int *colorOffset,
+                                         int *coverageOffset,
                                          int* edgeOffset);
 
     /**
@@ -1180,12 +1210,25 @@
      * @param vertexLayout              the layout to query
      * @param texCoordOffsetsByStage    after return it is the offset of each
      *                                  tex coord index in the vertex or -1 if
-     *                                  index isn't used.
+     *                                  index isn't used. (optional)
+     * @param colorOffset               after return it is the offset of the
+     *                                  color field in each vertex, or -1 if
+     *                                  there aren't per-vertex colors.
+     *                                  (optional)
+     * @param coverageOffset            after return it is the offset of the
+     *                                  coverage field in each vertex, or -1 if
+     *                                  there aren't per-vertex coeverages.
+     *                                  (optional)
+     * @param edgeOffset                after return it is the offset of the
+     *                                  edge eq field in each vertex, or -1 if
+     *                                  there aren't per-vertex edge equations.
+     *                                  (optional)
      * @return size of a single vertex
      */
     static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
                                            int texCoordOffsetsByStage[kNumStages],
                                            int *colorOffset,
+                                           int *coverageOffset,
                                            int* edgeOffset);
 
     /**
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index cbe2f42..4b97fab 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -81,6 +81,7 @@
 
 #define POS_ATTR_NAME "aPosition"
 #define COL_ATTR_NAME "aColor"
+#define COV_ATTR_NAME "aCoverage"
 #define EDGE_ATTR_NAME "aEdge"
 #define COL_UNI_NAME "uColor"
 #define EDGES_UNI_NAME "uEdges"
@@ -751,6 +752,23 @@
         // get edge AA coverage and use it as inCoverage to first coverage stage
         this->genEdgeCoverage(gl, layout, programData, &inCoverage, &segments);
 
+        // include explicit per-vertex coverage if we have it
+        if (GrDrawTarget::kCoverage_VertexLayoutBit & layout) {
+            segments.fVSAttrs.push_back().set(GrGLShaderVar::kFloat_Type,
+                                              COV_ATTR_NAME);
+            const char *vsName, *fsName;
+            append_varying(GrGLShaderVar::kFloat_Type, "Coverage", 
+                           &segments, &vsName, &fsName);
+            segments.fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
+            if (inCoverage.size()) {
+                segments.fFSCode.appendf("\tfloat edgeAndAttrCov = %s * %s;\n",
+                                         fsName, inCoverage.c_str());
+                inCoverage = "edgeAndAttrCov";
+            } else {
+                inCoverage = fsName;
+            }
+        }
+
         GrStringBuilder outCoverage;
         const int& startStage = fProgramDesc.fFirstCoverageStage;
         for (int s = startStage; s < GrDrawTarget::kNumStages; ++s) {
@@ -1100,6 +1118,8 @@
 
     GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
                                       COL_ATTR_NAME));
+    GR_GL_CALL(gl, BindAttribLocation(progID, CoverageAttributeIdx(),
+                                      COV_ATTR_NAME));
     GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(),
                                       EDGE_ATTR_NAME));
 
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index a79f877..197da5d 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -66,7 +66,10 @@
     static int PositionAttributeIdx() { return 0; }
     static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
     static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
-    static int EdgeAttributeIdx() { return 2 + GrDrawTarget::kMaxTexCoords; }
+    static int CoverageAttributeIdx() {
+        return 2 + GrDrawTarget::kMaxTexCoords;
+    }
+    static int EdgeAttributeIdx() { return 3 + GrDrawTarget::kMaxTexCoords; }
 
     static int ViewMatrixAttributeIdx() {
         return 2 + GrDrawTarget::kMaxTexCoords;
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index 195ca32..c9d5844 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -272,23 +272,29 @@
                                  int indexCount) {
 
     int newColorOffset;
+    int newCoverageOffset;
     int newTexCoordOffsets[kNumStages];
     int newEdgeOffset;
 
     GrGLsizei newStride = VertexSizeAndOffsetsByStage(this->getGeomSrc().fVertexLayout,
                                                       newTexCoordOffsets,
                                                       &newColorOffset,
+                                                      &newCoverageOffset,
                                                       &newEdgeOffset);
     GrAssert(-1 == newEdgeOffset); // not supported by fixed pipe
+    GrAssert(-1 == newCoverageOffset); // not supported by fixed pipe
 
     int oldColorOffset;
+    int oldCoverageOffset;
     int oldTexCoordOffsets[kNumStages];
     int oldEdgeOffset;
     GrGLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout,
                                                       oldTexCoordOffsets,
                                                       &oldColorOffset,
+                                                      &oldCoverageOffset,
                                                       &oldEdgeOffset);
     GrAssert(-1 == oldEdgeOffset);
+    GrAssert(-1 == oldCoverageOffset);
 
     bool indexed = NULL != startIndex;
 
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 80f0268..3ca4346 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -219,6 +219,10 @@
         idx = (int)(random.nextF() * (kNumStages+1));
         pdesc.fFirstCoverageStage = idx;
 
+        pdesc.fVertexLayout |= (random.nextF() > .5f) ?
+                                    GrDrawTarget::kCoverage_VertexLayoutBit :
+                                    0;
+
 #if GR_GL_EXPERIMENTAL_GS
         pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
                                 random.nextF() > .5f;
@@ -238,8 +242,8 @@
                 }
                 pdesc.fEdgeAANumEdges = 0;
             } else {
-                pdesc.fEdgeAANumEdges =  SkToS8(1 + random.nextF() *
-                                                this->getMaxEdges());
+                pdesc.fEdgeAANumEdges =  static_cast<int>(1 + random.nextF() *
+                                                          this->getMaxEdges());
                 pdesc.fEdgeAAConcave = random.nextF() > .5f;
             }
         } else {
@@ -312,6 +316,7 @@
 
     // Enable supported shader-releated caps
     fCaps.fShaderSupport = true;
+    fCaps.fSupportPerVertexCoverage = true;
     if (kDesktop_GrGLBinding == this->glBinding()) {
         fCaps.fDualSourceBlendingSupport =
                             this->glVersion() >= GR_GL_VER(3,3) ||
@@ -700,6 +705,7 @@
                                     int indexCount) {
 
     int newColorOffset;
+    int newCoverageOffset;
     int newTexCoordOffsets[kMaxTexCoords];
     int newEdgeOffset;
 
@@ -707,8 +713,10 @@
                                             this->getGeomSrc().fVertexLayout,
                                             newTexCoordOffsets,
                                             &newColorOffset,
+                                            &newCoverageOffset,
                                             &newEdgeOffset);
     int oldColorOffset;
+    int oldCoverageOffset;
     int oldTexCoordOffsets[kMaxTexCoords];
     int oldEdgeOffset;
 
@@ -716,6 +724,7 @@
                                             fHWGeometryState.fVertexLayout,
                                             oldTexCoordOffsets,
                                             &oldColorOffset,
+                                            &oldCoverageOffset,
                                             &oldEdgeOffset);
     bool indexed = NULL != startIndex;
 
@@ -792,6 +801,23 @@
         GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
     }
 
+    if (newCoverageOffset > 0) {
+        // bind just alpha channel.
+        GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset +
+                                             GrColor_INDEX_A);
+        int idx = GrGLProgram::CoverageAttributeIdx();
+        if (oldCoverageOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 1, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) {
+            GL_CALL(VertexAttribPointer(idx, 1, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        }
+    } else if (oldCoverageOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx()));
+    }
+
     if (newEdgeOffset > 0) {
         GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
         int idx = GrGLProgram::EdgeAttributeIdx();
@@ -954,13 +980,30 @@
     desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport;
 #endif
 
-    // use canonical value when coverage/color distinction won't affect
-    // generated code to prevent duplicate programs.
+    // we want to avoid generating programs with different "first cov stage"
+    // values when they would compute the same result.
+    // We set field in the desc to kNumStages when either there are no 
+    // coverage stages or the distinction between coverage and color is
+    // immaterial.
+    int firstCoverageStage = kNumStages;
     desc.fFirstCoverageStage = kNumStages;
-    if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
+    bool hasCoverage = fCurrDrawState.fFirstCoverageStage <= lastEnabledStage;
+    if (hasCoverage) {
+        firstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+    }
+
+    // other coverage inputs
+    if (!hasCoverage) {
+        hasCoverage =
+               desc.fEdgeAANumEdges ||
+               (desc.fVertexLayout & GrDrawTarget::kCoverage_VertexLayoutBit) ||
+               (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
+    }
+
+    if (hasCoverage) {
         // color filter is applied between color/coverage computation
         if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
-            desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            desc.fFirstCoverageStage = firstCoverageStage;
         }
 
         // We could consider cases where the final color is solid (0xff alpha)
@@ -972,17 +1015,17 @@
             if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
                 // write the coverage value to second color
                 desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
-                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+                desc.fFirstCoverageStage = firstCoverageStage;
             } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
                 // cover
                 desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
-                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+                desc.fFirstCoverageStage = firstCoverageStage;
             } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
                 // cover
                 desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
-                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+                desc.fFirstCoverageStage = firstCoverageStage;
             }
         }
     }
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index dfd1e4c..a3ffd65 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -35,6 +35,7 @@
         '../samplecode/OverView.cpp',
         '../samplecode/Sample2PtRadial.cpp',
         '../samplecode/SampleAARects.cpp',
+        '../samplecode/SampleAARectModes.cpp',
         '../samplecode/SampleAll.cpp',
         '../samplecode/SampleAnimator.cpp',
         '../samplecode/SampleApp.cpp',
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index fc43221..575cb8d 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -9,6 +9,7 @@
       'target_name': 'gm',
       'type': 'executable',
       'sources': [
+        '../gm/aarectmodes.cpp',
         '../gm/bitmapfilters.cpp',
         '../gm/bitmapscroll.cpp',
         '../gm/blurs.cpp',
diff --git a/samplecode/SampleAARectModes.cpp b/samplecode/SampleAARectModes.cpp
new file mode 100644
index 0000000..7b9e7d5
--- /dev/null
+++ b/samplecode/SampleAARectModes.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+static SkCanvas* create_canvas(int w, int h) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bm.allocPixels();
+    bm.eraseColor(0);
+    return new SkCanvas(bm);
+}
+
+static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
+    return canvas->getDevice()->accessBitmap(false);
+}
+
+static const struct {
+    SkXfermode::Mode  fMode;
+    const char*         fLabel;
+} gModes[] = {
+    { SkXfermode::kClear_Mode,    "Clear"     },
+    { SkXfermode::kSrc_Mode,      "Src"       },
+    { SkXfermode::kDst_Mode,      "Dst"       },
+    { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+    { SkXfermode::kDstOver_Mode,  "DstOver"   },
+    { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+    { SkXfermode::kDstIn_Mode,    "DstIn"     },
+    { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+    { SkXfermode::kDstOut_Mode,   "DstOut"    },
+    { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+    { SkXfermode::kDstATop_Mode,  "DstATop"   },
+    { SkXfermode::kXor_Mode,      "Xor"       },
+};
+
+const int gWidth = 64;
+const int gHeight = 64;
+const SkScalar W = SkIntToScalar(gWidth);
+const SkScalar H = SkIntToScalar(gHeight);
+
+static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode,
+                         SkAlpha a0, SkAlpha a1) {
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkRect r = SkRect::MakeWH(W, H);
+    r.inset(W/10, H/10);
+
+    paint.setColor(SK_ColorBLUE);
+    paint.setAlpha(a0);
+    canvas->drawOval(r, paint);
+
+    paint.setColor(SK_ColorRED);
+    paint.setAlpha(a1);
+    paint.setXfermode(mode);
+
+    SkScalar offset = SK_Scalar1 / 3;
+    SkRect rect = SkRect::MakeXYWH(W / 4 + offset,
+                                   H / 4 + offset,
+                                   W / 2, H / 2);
+    canvas->drawRect(rect, paint);
+    
+    return H;
+}
+
+static SkShader* make_bg_shader() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bm.allocPixels();
+    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
+    *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC,
+                                                             0xCC, 0xCC);
+
+    SkShader* s = SkShader::CreateBitmapShader(bm,
+                                               SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+    
+    SkMatrix m;
+    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+    s->setLocalMatrix(m);
+    return s;
+}
+
+class AARectsModesView : public SampleView {
+    SkPaint fBGPaint;
+public:
+    AARectsModesView () {
+        fBGPaint.setShader(make_bg_shader())->unref();
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "AARectsModes");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        const SkRect bounds = SkRect::MakeWH(W, H);
+        static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
+
+        canvas->translate(SkIntToScalar(4), SkIntToScalar(4));
+
+        for (int alpha = 0; alpha < 4; ++alpha) {
+            canvas->save();
+            canvas->save();
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) {
+                if (6 == i) {
+                    canvas->restore();
+                    canvas->translate(W * 5, 0);
+                    canvas->save();
+                }
+                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+                
+                canvas->drawRect(bounds, fBGPaint);
+                canvas->saveLayer(&bounds, NULL);
+                SkScalar dy = drawCell(canvas, mode,
+                                       gAlphaValue[alpha & 1],
+                                       gAlphaValue[alpha & 2]);
+                canvas->restore();
+
+                canvas->translate(0, dy * 5 / 4);
+                SkSafeUnref(mode);
+            }
+            canvas->restore();
+            canvas->restore();
+            canvas->translate(W * 5 / 4, 0);
+        }
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AARectsModesView; }
+static SkViewRegister reg(MyFactory);
+