Add code path to SW rasterize paths and upload as a mask texture

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



git-svn-id: http://skia.googlecode.com/svn/trunk@3031 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index 9fcded1..454204d 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -144,6 +144,7 @@
         '../include/core',
         '../include/config',
         '../include/gpu',
+        '../src/core', # SkRasterClip.h
       ],
       'dependencies': [
         'libtess.gyp:libtess',
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index a301254..518698e 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -1455,6 +1455,173 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+#include "SkDraw.h"
+#include "SkRasterClip.h"
+
+namespace {
+
+SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
+    switch (fill) {
+        case kWinding_PathFill:
+            return SkPath::kWinding_FillType;
+        case kEvenOdd_PathFill:
+            return SkPath::kEvenOdd_FillType;
+        case kInverseWinding_PathFill:
+            return SkPath::kInverseWinding_FillType;
+        case kInverseEvenOdd_PathFill:
+            return SkPath::kInverseEvenOdd_FillType;
+        default:
+            GrCrash("Unexpected fill.");
+            return SkPath::kWinding_FillType;
+    }
+}
+
+// gets device coord bounds of path (not considering the fill) and clip. The
+// path bounds will be a subset of the clip bounds. returns false if path bounds
+// would be empty.
+bool get_path_and_clip_bounds(const GrDrawTarget* target,
+                              const GrPath& path,
+                              const GrVec* translate,
+                              GrIRect* pathBounds,
+                              GrIRect* clipBounds) {
+    // compute bounds as intersection of rt size, clip, and path
+    const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
+    if (NULL == rt) {
+        return false;
+    }
+    *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
+    const GrClip& clip = target->getClip();
+    if (clip.hasConservativeBounds()) {
+        clip.getConservativeBounds().roundOut(clipBounds);
+        if (!pathBounds->intersect(*clipBounds)) {
+            return false;
+        }
+    } else {
+        // pathBounds is currently the rt extent, set clip bounds to that rect.
+        *clipBounds = *pathBounds;
+    }
+    GrRect pathSBounds = path.getBounds();
+    if (!pathSBounds.isEmpty()) {
+        if (NULL != translate) {
+            pathSBounds.offset(*translate);
+        }
+        target->getDrawState().getViewMatrix().mapRect(&pathSBounds,
+                                                        pathSBounds);
+        GrIRect pathIBounds;
+        pathSBounds.roundOut(&pathIBounds);
+        if (!pathBounds->intersect(pathIBounds)) {
+            return false;
+        }
+    } else {
+        return false;
+    }
+    return true;
+}
+
+/**
+ * sw rasterizes path to A8 mask using the context's matrix and uploads to a 
+ * scratch texture.
+ */
+
+bool sw_draw_path_to_mask_texture(const GrPath& clientPath,
+                                  const GrIRect& pathDevBounds,
+                                  GrPathFill fill,
+                                  GrContext* context,
+                                  const GrPoint* translate,
+                                  GrAutoScratchTexture* tex) {
+    SkPaint paint;
+    SkPath tmpPath;
+    const SkPath* pathToDraw = &clientPath;
+    if (kHairLine_PathFill == fill) {
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SK_Scalar1);
+    } else {
+        paint.setStyle(SkPaint::kFill_Style);
+        SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
+        if (skfill != pathToDraw->getFillType()) {
+            tmpPath = *pathToDraw;
+            tmpPath.setFillType(skfill);
+            pathToDraw = &tmpPath;
+        }
+    }
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorWHITE);
+
+    GrMatrix matrix = context->getMatrix();
+    if (NULL != translate) {
+        matrix.postTranslate(translate->fX, translate->fY);
+    }
+
+    matrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
+                         -pathDevBounds.fTop * SK_Scalar1);
+    GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
+                                     pathDevBounds.height());
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
+    if (!bm.allocPixels()) {
+        return false;
+    }
+    sk_bzero(bm.getPixels(), bm.getSafeSize());
+
+    SkDraw  draw;
+    sk_bzero(&draw, sizeof(draw));
+    SkRasterClip rc(bounds);
+    draw.fRC    = &rc;
+    draw.fClip  = &rc.bwRgn();
+    draw.fMatrix = &matrix;
+    draw.fBitmap = &bm;
+    draw.drawPath(*pathToDraw, paint);
+
+    const GrTextureDesc desc = {
+        kNone_GrTextureFlags,
+        kNone_GrAALevel,
+        bounds.fRight,
+        bounds.fBottom,
+        kAlpha_8_GrPixelConfig
+    };
+
+    tex->set(context, desc);
+    GrTexture* texture = tex->texture();
+
+    if (NULL == texture) {
+        return false;
+    }
+    SkAutoLockPixels alp(bm);
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                         bm.getPixels(), bm.rowBytes());
+    return true;
+}
+
+void draw_around_inv_path(GrDrawTarget* target,
+                          GrDrawState::StageMask stageMask,
+                          const GrIRect& clipBounds,
+                          const GrIRect& pathBounds) {
+    GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
+    GrRect rect;
+    if (clipBounds.fTop < pathBounds.fTop) {
+        rect.iset(clipBounds.fLeft, clipBounds.fTop, 
+                    clipBounds.fRight, pathBounds.fTop);
+        target->drawSimpleRect(rect, NULL, stageMask);
+    }
+    if (clipBounds.fLeft < pathBounds.fLeft) {
+        rect.iset(clipBounds.fLeft, pathBounds.fTop, 
+                    pathBounds.fLeft, pathBounds.fBottom);
+        target->drawSimpleRect(rect, NULL, stageMask);
+    }
+    if (clipBounds.fRight > pathBounds.fRight) {
+        rect.iset(pathBounds.fRight, pathBounds.fTop, 
+                    clipBounds.fRight, pathBounds.fBottom);
+        target->drawSimpleRect(rect, NULL, stageMask);
+    }
+    if (clipBounds.fBottom > pathBounds.fBottom) {
+        rect.iset(clipBounds.fLeft, pathBounds.fBottom, 
+                    clipBounds.fRight, clipBounds.fBottom);
+        target->drawSimpleRect(rect, NULL, stageMask);
+    }
+}
+
+}
 
 void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
                          GrPathFill fill, const GrPoint* translate) {
@@ -1467,6 +1634,7 @@
     }
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    GrDrawState::StageMask stageMask = paint.getActiveStageMask();
 
     bool prAA = paint.fAntiAlias && !this->getRenderTarget()->isMultisampled();
 
@@ -1486,9 +1654,47 @@
     if (prAA) {
         pr = this->getPathRenderer(path, fill, true);
         if (NULL == pr) {
+            GrAutoScratchTexture ast;
+            GrIRect pathBounds, clipBounds;
+            if (!get_path_and_clip_bounds(target, path, translate,
+                                          &pathBounds, &clipBounds)) {
+                return;
+            }
             prAA = false;
-            doOSAA = this->doOffscreenAA(target, kHairLine_PathFill == fill);
-            pr = this->getPathRenderer(path, fill, false);
+            if (this->doOffscreenAA(target, kHairLine_PathFill == fill)) {
+                pr = this->getPathRenderer(path, fill, false);
+                doOSAA = true;
+            }
+            if (NULL == pr && sw_draw_path_to_mask_texture(path, pathBounds,
+                                                           fill, this,
+                                                           translate, &ast)) {
+                GrTexture* texture = ast.texture();
+                GrAssert(NULL != texture);
+                GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
+                enum {
+                    kPathMaskStage = GrPaint::kTotalStages,
+                };
+                target->drawState()->setTexture(kPathMaskStage, texture);
+                target->drawState()->sampler(kPathMaskStage)->reset();
+                GrScalar w = GrIntToScalar(pathBounds.width());
+                GrScalar h = GrIntToScalar(pathBounds.height());
+                GrRect maskRect = GrRect::MakeWH(w / texture->width(),
+                                                 h / texture->height());
+                const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
+                srcRects[kPathMaskStage] = &maskRect;
+                stageMask |= 1 << kPathMaskStage;
+                GrRect dstRect = GrRect::MakeLTRB(pathBounds.fLeft,
+                                                  pathBounds.fTop,
+                                                  pathBounds.fRight,
+                                                  pathBounds.fBottom);
+                target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
+                target->drawState()->setTexture(kPathMaskStage, NULL);
+                if (GrIsFillInverted(fill)) {
+                    draw_around_inv_path(target, stageMask,
+                                         clipBounds, pathBounds);
+                }
+                return;
+            }
         }
     } else {
         pr = this->getPathRenderer(path, fill, false);
@@ -1502,67 +1708,30 @@
     }
 
     GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, prAA, translate);
-    GrDrawState::StageMask stageMask = paint.getActiveStageMask();
 
     if (doOSAA) {
         bool needsStencil = pr->requiresStencilPass(target, path, fill);
-        const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
-        // compute bounds as intersection of rt size, clip, and path
-        GrIRect bound = SkIRect::MakeWH(rt->width(), rt->height());
-        GrIRect clipIBounds;
-        if (target->getClip().hasConservativeBounds()) {
-            target->getClip().getConservativeBounds().roundOut(&clipIBounds);
-            if (!bound.intersect(clipIBounds)) {
-                return;
-            }
-        }
-        GrRect pathBounds = path.getBounds();
-        if (!pathBounds.isEmpty()) {
-            if (NULL != translate) {
-                pathBounds.offset(*translate);
-            }
-            target->getDrawState().getViewMatrix().mapRect(&pathBounds,
-                                                           pathBounds);
-            GrIRect pathIBounds;
-            pathBounds.roundOut(&pathIBounds);
-            if (!bound.intersect(pathIBounds)) {
-                return;
-            }
+        GrIRect pathBounds;
+        GrIRect clipBounds;
+        if (!get_path_and_clip_bounds(target, path, translate,
+                                      &pathBounds, &clipBounds)) {
+            return;
         }
         OffscreenRecord record;
-        if (this->prepareForOffscreenAA(target, needsStencil, bound,
+        if (this->prepareForOffscreenAA(target, needsStencil, pathBounds,
                                         pr, &record)) {
             for (int tx = 0; tx < record.fTileCountX; ++tx) {
                 for (int ty = 0; ty < record.fTileCountY; ++ty) {
-                    this->setupOffscreenAAPass1(target, bound, tx, ty, &record);
+                    this->setupOffscreenAAPass1(target, pathBounds,
+                                                tx, ty, &record);
                     pr->drawPath(0);
-                    this->doOffscreenAAPass2(target, paint, bound, tx, ty, &record);
+                    this->doOffscreenAAPass2(target, paint, pathBounds,
+                                             tx, ty, &record);
                 }
             }
             this->cleanupOffscreenAA(target, pr, &record);
-            if (GrIsFillInverted(fill) && bound != clipIBounds) {
-                GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
-                GrRect rect;
-                if (clipIBounds.fTop < bound.fTop) {
-                    rect.iset(clipIBounds.fLeft, clipIBounds.fTop, 
-                              clipIBounds.fRight, bound.fTop);
-                    target->drawSimpleRect(rect, NULL, stageMask);
-                }
-                if (clipIBounds.fLeft < bound.fLeft) {
-                    rect.iset(clipIBounds.fLeft, bound.fTop, 
-                              bound.fLeft, bound.fBottom);
-                    target->drawSimpleRect(rect, NULL, stageMask);
-                }
-                if (clipIBounds.fRight > bound.fRight) {
-                    rect.iset(bound.fRight, bound.fTop, 
-                              clipIBounds.fRight, bound.fBottom);
-                    target->drawSimpleRect(rect, NULL, stageMask);
-                }
-                if (clipIBounds.fBottom > bound.fBottom) {
-                    rect.iset(clipIBounds.fLeft, bound.fBottom, 
-                              clipIBounds.fRight, clipIBounds.fBottom);
-                    target->drawSimpleRect(rect, NULL, stageMask);
-                }
+            if (GrIsFillInverted(fill)) {
+                draw_around_inv_path(target, stageMask, clipBounds, pathBounds);
             }
             return;
         }