Move alpha-ramp AA to GrContext, detect cases when AA is applied via other methods (smooth lines, MSAA) or rect falls on integer coords and skip the alpha ramp path. Use pre-fab index buffer for alpha-ramped fill rects and stroke rects.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@1169 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index f68564a..7cf0cca 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -58,29 +58,38 @@
 
 GrContext::~GrContext() {
     this->flush();
-    fGpu->unref();
     delete fTextureCache;
     delete fFontCache;
     delete fDrawBuffer;
     delete fDrawBufferVBAllocPool;
     delete fDrawBufferIBAllocPool;
     GrSafeUnref(fCustomPathRenderer);
+    GrSafeUnref(fAAFillRectIndexBuffer);
+    GrSafeUnref(fAAStrokeRectIndexBuffer);
+    fGpu->unref();
 }
 
 void GrContext::contextLost() {
+    // abandon first to so destructors
+    // don't try to free the resources in the API.
+    fGpu->abandonResources();
+
     delete fDrawBuffer;
     fDrawBuffer = NULL;
+
     delete fDrawBufferVBAllocPool;
     fDrawBufferVBAllocPool = NULL;
+
     delete fDrawBufferIBAllocPool;
     fDrawBufferIBAllocPool = NULL;
 
+    GrSafeSetNull(fAAFillRectIndexBuffer);
+    GrSafeSetNull(fAAStrokeRectIndexBuffer);
+
     fTextureCache->removeAll();
     fFontCache->freeAll();
     fGpu->markContextDirty();
 
-    fGpu->abandonResources();
-
     this->setupDrawBuffer();
 }
 
@@ -348,14 +357,17 @@
     this->drawRect(paint, r);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 /*  create a triangle strip that strokes the specified triangle. There are 8
  unique vertices, but we repreat the last 2 to close up. Alternatively we
  could use an indices array, and then only send 8 verts, but not sure that
  would be faster.
  */
-static void setStrokeRectStrip(GrPoint verts[10], const GrRect& rect,
+static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
                                GrScalar width) {
     const GrScalar rad = GrScalarHalf(width);
+    rect.sort();
 
     verts[0].set(rect.fLeft + rad, rect.fTop + rad);
     verts[1].set(rect.fLeft - rad, rect.fTop - rad);
@@ -369,6 +381,235 @@
     verts[9] = verts[1];
 }
 
+static GrColor getColorForMesh(const GrPaint& paint) {
+    if (NULL == paint.getTexture()) {
+        return paint.fColor;
+    } else {
+        unsigned a = GrColorUnpackA(paint.fColor);
+        return GrColorPackRGBA(a, a, a, a);
+    }
+}
+
+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);
+}
+
+static const uint16_t gFillAARectIdx[] = {
+    0, 1, 5, 5, 4, 0,
+    1, 2, 6, 6, 5, 1,
+    2, 3, 7, 7, 6, 2,
+    3, 0, 4, 4, 7, 3,
+    4, 5, 6, 6, 7, 4,
+};
+
+int GrContext::aaFillRectIndexCount() const {
+    return GR_ARRAY_COUNT(gFillAARectIdx);
+}
+
+GrIndexBuffer* GrContext::aaFillRectIndexBuffer() {
+    if (NULL == fAAFillRectIndexBuffer) {
+        fAAFillRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gFillAARectIdx),
+                                                         false);
+        GrAssert(NULL != fAAFillRectIndexBuffer);
+#if GR_DEBUG
+        bool updated =
+#endif
+        fAAFillRectIndexBuffer->updateData(gFillAARectIdx,
+                                           sizeof(gFillAARectIdx));
+        GR_DEBUGASSERT(updated);
+    }
+    return fAAFillRectIndexBuffer;
+}
+
+static const uint16_t gStrokeAARectIdx[] = {
+    0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
+    1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
+    2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
+    3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
+
+    0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
+    1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
+    2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
+    3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
+
+    0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
+    1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
+    2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
+    3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
+};
+
+int GrContext::aaStrokeRectIndexCount() const {
+    return GR_ARRAY_COUNT(gStrokeAARectIdx);
+}
+
+GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() {
+    if (NULL == fAAStrokeRectIndexBuffer) {
+        fAAStrokeRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gStrokeAARectIdx),
+                                                           false);
+        GrAssert(NULL != fAAStrokeRectIndexBuffer);
+#if GR_DEBUG
+        bool updated =
+#endif
+        fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
+                                             sizeof(gStrokeAARectIdx));
+        GR_DEBUGASSERT(updated);
+    }
+    return fAAStrokeRectIndexBuffer;
+}
+
+void GrContext::fillAARect(GrDrawTarget* target,
+                           const GrPaint& paint,
+                           const GrRect& devRect) {
+
+    GrVertexLayout layout = GrDrawTarget::kColor_VertexLayoutBit;
+    if (NULL != paint.getTexture()) {
+        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+    }
+
+    size_t vsize = GrDrawTarget::VertexSize(layout);
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
+
+    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+
+    setInsetFan(fan0Pos, vsize, devRect, -GR_ScalarHalf, -GR_ScalarHalf);
+    setInsetFan(fan1Pos, vsize, devRect,  GR_ScalarHalf,  GR_ScalarHalf);
+
+    verts += sizeof(GrPoint);
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    GrColor innerColor = getColorForMesh(paint);
+    verts += 4 * vsize;
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+    }
+
+    target->setIndexSourceToBuffer(this->aaFillRectIndexBuffer());
+
+    target->drawIndexed(kTriangles_PrimitiveType, 0,
+                         0, 8, this->aaFillRectIndexCount());
+}
+
+void GrContext::strokeAARect(GrDrawTarget* target, const GrPaint& paint,
+                             const GrRect& devRect, const GrVec& devStrokeSize) {
+    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 = GrDrawTarget::kColor_VertexLayoutBit;
+
+    if (NULL != paint.getTexture()) {
+        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
+    }
+
+    GrScalar spare;
+    {
+        GrScalar w = devRect.width() - dx;
+        GrScalar h = devRect.height() - dy;
+        spare = GrMin(w, h);
+    }
+
+    if (spare <= 0) {
+        GrRect r(devRect);
+        r.inset(-rx, -ry);
+        fillAARect(target, paint, r);
+        return;
+    }
+
+    size_t vsize = GrDrawTarget::VertexSize(layout);
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
+
+    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+    GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
+    GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
+
+    setInsetFan(fan0Pos, vsize, devRect, -rx - GR_ScalarHalf, -ry - GR_ScalarHalf);
+    setInsetFan(fan1Pos, vsize, devRect, -rx + GR_ScalarHalf, -ry + GR_ScalarHalf);
+    setInsetFan(fan2Pos, vsize, devRect,  rx - GR_ScalarHalf,  ry - GR_ScalarHalf);
+    setInsetFan(fan3Pos, vsize, devRect,  rx + GR_ScalarHalf,  ry + GR_ScalarHalf);
+
+    verts += sizeof(GrPoint);
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    GrColor innerColor = getColorForMesh(paint);
+    verts += 4 * vsize;
+    for (int i = 0; i < 8; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+    }
+
+    verts += 8 * vsize;
+    for (int i = 0; i < 8; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    target->setIndexSourceToBuffer(aaStrokeRectIndexBuffer());
+    target->drawIndexed(kTriangles_PrimitiveType,
+                        0, 0, 16, aaStrokeRectIndexCount());
+}
+
+static bool apply_aa_to_rect(GrDrawTarget* target,
+                             GrGpu* gpu,
+                             const GrPaint& paint,
+                             const GrRect& rect,
+                             GrScalar width, 
+                             const GrMatrix* matrix,
+                             GrMatrix* combinedMatrix,
+                             GrRect* devRect) {
+    // 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
+    // multisampled, and the rect won't land on integer coords.
+
+    if (!paint.fAntiAlias) {
+        return false;
+    }
+
+    if (target->getRenderTarget()->isMultisampled()) {
+        return false;
+    }
+
+    if (0 == width && gpu->supportsAALines()) {
+        return false;
+    }
+
+    if (!target->getViewMatrix().preservesAxisAlignment()) {
+        return false;
+    }
+
+    if (NULL != matrix && 
+        !matrix->preservesAxisAlignment()) {
+        return false;
+    }
+
+    *combinedMatrix = target->getViewMatrix();
+    if (NULL != matrix) {
+        combinedMatrix->preConcat(*matrix);
+        GrAssert(combinedMatrix->preservesAxisAlignment());
+    }
+    
+    combinedMatrix->mapRect(devRect, rect);
+    devRect->sort();
+
+    if (width < 0) {
+        return !devRect->isIRect();
+    } else {
+        return true;
+    }
+}
+
 void GrContext::drawRect(const GrPaint& paint,
                          const GrRect& rect,
                          GrScalar width,
@@ -378,13 +619,43 @@
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
 
+    GrRect devRect = rect;
+    GrMatrix combinedMatrix;
+    bool doAA = apply_aa_to_rect(target, fGpu, paint, rect, width, matrix, 
+                                 &combinedMatrix, &devRect);
+
+    if (doAA) {
+        GrDrawTarget::AutoViewMatrixRestore avm(target);
+        if (textured) {
+            GrMatrix inv;
+            if (combinedMatrix.invert(&inv)) {
+                target->preConcatSamplerMatrix(0, inv);
+            }
+        }
+        target->setViewMatrix(GrMatrix::I());
+        if (width >= 0) {
+            GrVec strokeSize;;
+            if (width > 0) {
+                strokeSize.set(width, width);
+                combinedMatrix.mapVec(&strokeSize);
+                strokeSize.setAbs(strokeSize);
+            } else {
+                strokeSize.set(GR_Scalar1, GR_Scalar1);
+            }
+            strokeAARect(target, paint, devRect, strokeSize);
+        } else {
+            fillAARect(target, paint, devRect);
+        }
+        return;
+    }
+
     if (width >= 0) {
         // 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 = textured ?
+                            GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
+                            0;
         static const int worstCaseVertCount = 10;
         GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
 
@@ -415,7 +686,9 @@
         if (NULL != matrix) {
             avmr.set(target);
             target->preConcatViewMatrix(*matrix);
-            target->preConcatSamplerMatrix(0, *matrix);
+            if (textured) {
+                target->preConcatSamplerMatrix(0, *matrix);
+            }
         }
 
         target->drawNonIndexed(primType, 0, vertCount);
@@ -429,8 +702,8 @@
             GrDrawTarget::AutoViewMatrixRestore avmr(target);
             GrMatrix m;
             m.setAll(rect.width(), 0,             rect.fLeft,
-                     0,            rect.height(), rect.fTop,
-                     0,            0,             GrMatrix::I()[8]);
+                        0,            rect.height(), rect.fTop,
+                        0,            0,             GrMatrix::I()[8]);
 
             if (NULL != matrix) {
                 m.postConcat(*matrix);
@@ -819,6 +1092,9 @@
     fDrawBufferVBAllocPool = NULL;
     fDrawBufferIBAllocPool = NULL;
 
+    fAAFillRectIndexBuffer = NULL;
+    fAAStrokeRectIndexBuffer = NULL;
+
     this->setupDrawBuffer();
 }