First functioning version of SW-only clip mask creator

http://codereview.appspot.com/6208072/



git-svn-id: http://skia.googlecode.com/svn/trunk@3984 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index ca7ecdc..3da4af2 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -430,19 +430,26 @@
 
 }
 
-bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
-                                         GrPathFill fill,
-                                         const GrDrawTarget* target,
-                                         bool antiAlias) const {
+bool GrAAConvexPathRenderer::staticCanDrawPath(bool pathIsConvex,
+                                               GrPathFill fill,
+                                               const GrDrawTarget* target,
+                                               bool antiAlias) {
     if (!target->getCaps().fShaderDerivativeSupport || !antiAlias ||
         kHairLine_PathFill == fill || GrIsFillInverted(fill) ||
-        !path.isConvex()) {
+        !pathIsConvex) {
         return false;
     }  else {
         return true;
     }
 }
 
+bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
+                                         GrPathFill fill,
+                                         const GrDrawTarget* target,
+                                         bool antiAlias) const {
+    return staticCanDrawPath(path.isConvex(), fill, target, antiAlias);
+}
+
 bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
                                         GrPathFill fill,
                                         const GrVec* translate,
diff --git a/src/gpu/GrAAConvexPathRenderer.h b/src/gpu/GrAAConvexPathRenderer.h
index df0c001..875560c 100644
--- a/src/gpu/GrAAConvexPathRenderer.h
+++ b/src/gpu/GrAAConvexPathRenderer.h
@@ -17,6 +17,12 @@
                              GrPathFill fill,
                              const GrDrawTarget* target,
                              bool antiAlias) const SK_OVERRIDE;
+
+    static bool staticCanDrawPath(bool pathIsConvex,
+                                  GrPathFill fill,
+                                  const GrDrawTarget* target,
+                                  bool antiAlias);
+
 protected:
     virtual bool onDrawPath(const SkPath& path,
                             GrPathFill fill,
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 701bf3a..94d94a3 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -574,10 +574,10 @@
     return true;
 }
 
-bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
-                                           GrPathFill fill,
-                                           const GrDrawTarget* target,
-                                           bool antiAlias) const {
+bool GrAAHairLinePathRenderer::staticCanDrawPath(const SkPath& path,
+                                                 GrPathFill fill,
+                                                 const GrDrawTarget* target,
+                                                 bool antiAlias) {
     if (fill != kHairLine_PathFill || !antiAlias) {
         return false;
     }
@@ -591,6 +591,13 @@
     return true;
 }
 
+bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
+                                           GrPathFill fill,
+                                           const GrDrawTarget* target,
+                                           bool antiAlias) const {
+    return staticCanDrawPath(path, fill, target, antiAlias);
+}
+
 bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
                                           GrPathFill fill,
                                           const GrVec* translate,
diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h
index 6dd9ea9..e45fa35 100644
--- a/src/gpu/GrAAHairLinePathRenderer.h
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -21,6 +21,12 @@
                             GrPathFill fill,
                             const GrDrawTarget* target,
                             bool antiAlias) const SK_OVERRIDE;
+
+    static bool staticCanDrawPath(const SkPath& path,
+                                  GrPathFill fill,
+                                  const GrDrawTarget* target,
+                                  bool antiAlias);
+
 protected:
     virtual bool onDrawPath(const SkPath& path,
                             GrPathFill fill,
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index f8e81a3..e618bf1 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -13,6 +13,11 @@
 #include "GrPathRenderer.h"
 #include "GrPaint.h"
 #include "SkRasterClip.h"
+#include "GrAAConvexPathRenderer.h"
+#include "GrAAHairLinePathRenderer.h"
+
+// TODO: move GrSWMaskHelper out of GrSoftwarePathRender.h & remove this include
+#include "GrSoftwarePathRenderer.h"
 
 //#define GR_AA_CLIP 1
 //#define GR_SW_CLIP 1
@@ -55,12 +60,80 @@
                 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
 }
 
-bool create_mask_in_sw() {
-    return false;
 }
 
+/*
+ * This method traverses the clip stack to see if the GrSoftwarePathRenderer
+ * will be used on any element. If so, it returns true to indicate that the
+ * entire clip should be rendered in SW and then uploaded en masse to the gpu.
+ */
+bool GrClipMaskManager::useSWOnlyPath(GrGpu* gpu, const GrClip& clipIn) {
+    // TODO: this check is correct for the createAlphaClipMask path.
+    // The createStencilClipMask path does a lot more flip flopping of fill,
+    // etc - so this isn't quite correct in that case
+
+    // TODO: generalize this test so that when
+    // a clip gets complex enough it can just be done in SW regardless
+    // of whether it would invoke the GrSoftwarePathRenderer.
+    bool useSW = false;
+
+    for (int i = 0; i < clipIn.getElementCount(); ++i) {
+
+        if (SkRegion::kReplace_Op == clipIn.getOp(i)) {
+            // Everything before a replace op can be ignored so start
+            // afresh w.r.t. determining if any element uses the SW path
+            useSW = false;
+        }
+
+        if (!clipIn.getDoAA(i)) {
+            // non-anti-aliased rects and paths can always be drawn either
+            // directly or by the GrDefaultPathRenderer
+            continue;
+        }
+
+        if (kRect_ClipType == clipIn.getElementType(i)) {
+            // Antialiased rects are converted to paths and then drawn with
+            // kEvenOdd_PathFill. 
+            if (!GrAAConvexPathRenderer::staticCanDrawPath(
+                                                    true, // always convex
+                                                    kEvenOdd_PathFill,
+                                                    gpu, true)) {
+                // if the GrAAConvexPathRenderer can't render this rect (due
+                // to lack of derivative support in the shaders) then 
+                // the GrSoftwarePathRenderer will be used
+                useSW = true;
+            }
+
+            continue;
+        }
+
+        // only paths need to be considered in the rest of the loop body
+
+        if (GrAAHairLinePathRenderer::staticCanDrawPath(clipIn.getPath(i),
+                                                        clipIn.getPathFill(i),
+                                                        gpu,
+                                                        clipIn.getDoAA(i))) {
+            // the hair line path renderer can handle this one
+            continue;
+        }
+
+        if (GrAAConvexPathRenderer::staticCanDrawPath(
+                                                clipIn.getPath(i).isConvex(),
+                                                clipIn.getPathFill(i),
+                                                gpu,
+                                                clipIn.getDoAA(i))) {
+            // the convex path renderer can handle this one
+            continue;
+        }
+
+        // otherwise the GrSoftwarePathRenderer is going to be invoked
+        useSW = true;
+    }
+
+    return useSW;
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////
 // sort out what kind of clip mask needs to be created: alpha, stencil,
 // scissor, or entirely software
@@ -85,7 +158,7 @@
     GrAssert(NULL != rt);
 
 #if GR_SW_CLIP
-    if (create_mask_in_sw()) {
+    if (useSWOnlyPath(gpu, clipIn)) {
         // The clip geometry is complex enough that it will be more
         // efficient to create it entirely in software
         GrTexture* result = NULL;
@@ -133,7 +206,7 @@
     GrRect bounds;
     GrRect rtRect;
     rtRect.setLTRB(0, 0,
-                    GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
+                   GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
     if (clipIn.hasConservativeBounds()) {
         bounds = clipIn.getConservativeBounds();
         if (!bounds.intersect(rtRect)) {
@@ -786,6 +859,27 @@
     return true;
 }
 
+namespace {
+
+GrPathFill invert_fill(GrPathFill fill) {
+    static const GrPathFill gInvertedFillTable[] = {
+        kInverseWinding_PathFill, // kWinding_PathFill
+        kInverseEvenOdd_PathFill, // kEvenOdd_PathFill
+        kWinding_PathFill,        // kInverseWinding_PathFill
+        kEvenOdd_PathFill,        // kInverseEvenOdd_PathFill
+        kHairLine_PathFill,       // kHairLine_PathFill
+    };
+    GR_STATIC_ASSERT(0 == kWinding_PathFill);
+    GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
+    GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
+    GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
+    GR_STATIC_ASSERT(4 == kHairLine_PathFill);
+    GR_STATIC_ASSERT(5 == kPathFillCount);
+    return gInvertedFillTable[fill];
+}
+
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu,
                                                const GrClip& clipIn,
@@ -803,30 +897,98 @@
         return false;
     }
 
-#if 0
-    SkRasterClip rasterClip;
+    GrSWMaskHelper helper(fAACache.getContext());
 
-    // TODO: refactor GrClip out of existance and use SkCanvas's ClipVisitor
-    //      - may have to move it to SkClipStack
-    for (int i = 0; i < clipIn.getElementCount(); ++i) {
+    helper.init(*resultBounds, NULL, false);
+
+    int count = clipIn.getElementCount();
+
+    bool clearToInside;
+    SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
+    int start = process_initial_clip_elements(clipIn,
+                                              *resultBounds,
+                                              &clearToInside,
+                                              &startOp);
+
+    helper.clear(clearToInside ? SK_ColorWHITE : 0x00000000);
+
+    for (int i = start; i < count; ++i) {
+
+        SkRegion::Op op = (i == start) ? startOp : clipIn.getOp(i);
+
+        if (SkRegion::kIntersect_Op == op ||
+            SkRegion::kReverseDifference_Op == op) {
+            // Intersect and reverse difference require modifying pixels
+            // outside of the geometry that is being "drawn". In both cases
+            // we erase all the pixels outside of the geometry but
+            // leave the pixels inside the geometry alone. For reverse
+            // difference we invert all the pixels before clearing the ones
+            // outside the geometry.
+            if (SkRegion::kReverseDifference_Op == op) {
+                SkRect temp = SkRect::MakeLTRB(
+                                       SkIntToScalar(resultBounds->left()),
+                                       SkIntToScalar(resultBounds->top()),
+                                       SkIntToScalar(resultBounds->right()),
+                                       SkIntToScalar(resultBounds->bottom()));
+
+                // invert the entire scene
+                helper.draw(temp, SkRegion::kXOR_Op, false, SK_ColorWHITE);
+            }
+
+            if (kRect_ClipType == clipIn.getElementType(i)) {
+
+                // convert the rect to a path so we can invert the fill
+                SkPath temp;
+                temp.addRect(clipIn.getRect(i));
+
+                helper.draw(temp, SkRegion::kReplace_Op, 
+                            kInverseEvenOdd_PathFill, clipIn.getDoAA(i),
+                            0x00000000);
+            } else {
+                GrAssert(kPath_ClipType == clipIn.getElementType(i));
+
+                helper.draw(clipIn.getPath(i),
+                            SkRegion::kReplace_Op,
+                            invert_fill(clipIn.getPathFill(i)),
+                            clipIn.getDoAA(i),
+                            0x00000000);
+            }
+
+            continue;
+        }
+
+        // The other ops (union, xor, diff) only affect pixels inside
+        // the geometry so they can just be drawn normally
         if (kRect_ClipType == clipIn.getElementType(i)) {
-            rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i));
+
+            helper.draw(clipIn.getRect(i),
+                        op,
+                        clipIn.getDoAA(i), SK_ColorWHITE);
+
         } else {
             GrAssert(kPath_ClipType == clipIn.getElementType(i));
 
-            SkIPoint deviceSize = SkIPoint::Make(resultBounds->width(), 
-                                                 resultBounds->height());
-
-            SkRasterClip::clipPathHelper(&rasterClip, 
-                                         clipIn.getPath(i),
-                                         clipIn.getOp(i),
-                                         clipIn.getDoAA(i),
-                                         deviceSize);
+            helper.draw(clipIn.getPath(i), 
+                        op,
+                        clipIn.getPathFill(i), 
+                        clipIn.getDoAA(i), SK_ColorWHITE);
         }
     }
 
-    // TODO: need to get pixels out of SkRasterClip & into the texture!
-#endif
+    // Because we are using the scratch texture cache, "accum" may be
+    // larger than expected and have some cruft in the areas we aren't using.
+    // Clear it out.
+
+    // TODO: need a simpler way to clear the texture - can we combine
+    // the clear and the writePixels (inside toTexture)
+    GrDrawState* drawState = gpu->drawState();
+    GrAssert(NULL != drawState);
+    GrRenderTarget* temp = drawState->getRenderTarget();
+    clear(gpu, accum, 0x00000000);
+    // can't leave the accum bound as a rendertarget
+    drawState->setRenderTarget(temp);
+
+    helper.toTexture(accum);
 
     *result = accum;
 
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 02fc875..d5fcc83 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -332,6 +332,8 @@
                           GrTexture** result,
                           GrIRect *resultBounds);
 
+    bool useSWOnlyPath(GrGpu* gpu, const GrClip& clipIn);
+
     bool drawPath(GrGpu* gpu,
                   const SkPath& path,
                   GrPathFill fill,
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 410223c..817f55a 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -101,7 +101,7 @@
 SkXfermode::Mode op_to_mode(SkRegion::Op op) {
 
     static const SkXfermode::Mode modeMap[] = {
-        SkXfermode::kSrcOut_Mode,   // kDifference_Op
+        SkXfermode::kDstOut_Mode,   // kDifference_Op
         SkXfermode::kMultiply_Mode, // kIntersect_Op
         SkXfermode::kSrcOver_Mode,  // kUnion_Op
         SkXfermode::kXor_Mode,      // kXOR_Op
@@ -118,14 +118,14 @@
  * Draw a single rect element of the clip stack into the accumulation bitmap
  */
 void GrSWMaskHelper::draw(const GrRect& clientRect, SkRegion::Op op, 
-                          bool antiAlias) {
+                          bool antiAlias, GrColor color) {
     SkPaint paint;
 
     SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
 
     paint.setXfermode(mode);
     paint.setAntiAlias(antiAlias);
-    paint.setColor(SK_ColorWHITE);
+    paint.setColor(color);
 
     fDraw.drawRect(clientRect, paint);
 
@@ -136,7 +136,7 @@
  * Draw a single path element of the clip stack into the accumulation bitmap
  */
 void GrSWMaskHelper::draw(const SkPath& clientPath, SkRegion::Op op,
-                          GrPathFill fill, bool antiAlias) {
+                          GrPathFill fill, bool antiAlias, GrColor color) {
 
     SkPaint paint;
     SkPath tmpPath;
@@ -157,23 +157,30 @@
 
     paint.setXfermode(mode);
     paint.setAntiAlias(antiAlias);
-    paint.setColor(SK_ColorWHITE);
+    paint.setColor(color);
 
     fDraw.drawPath(*pathToDraw, paint);
 
     SkSafeUnref(mode);
 }
 
-bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, const GrPoint* translate) {
-    fMatrix = fContext->getMatrix();
+bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, 
+                          const GrPoint* translate,
+                          bool useMatrix) {
+    if (useMatrix) {    
+        fMatrix = fContext->getMatrix();
+    } else {
+        fMatrix.setIdentity();
+    }
+
     if (NULL != translate) {
         fMatrix.postTranslate(translate->fX, translate->fY);
     }
 
     fMatrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
-                            -pathDevBounds.fTop * SK_Scalar1);
+                          -pathDevBounds.fTop * SK_Scalar1);
     GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
-                                        pathDevBounds.height());
+                                     pathDevBounds.height());
 
     fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
     if (!fBM.allocPixels()) {
@@ -237,11 +244,12 @@
                                   bool antiAlias) {
     GrSWMaskHelper helper(context);
 
-    if (!helper.init(pathDevBounds, translate)) {
+    if (!helper.init(pathDevBounds, translate, true)) {
         return false;
     }
 
-    helper.draw(clientPath, SkRegion::kReplace_Op, fill, antiAlias);
+    helper.draw(clientPath, SkRegion::kReplace_Op, 
+                fill, antiAlias, SK_ColorWHITE);
 
     if (!helper.getTexture(tex)) {
         return false;
@@ -339,4 +347,3 @@
 
     return false;
 }
-
diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h
index 4b75d03..74715f6 100644
--- a/src/gpu/GrSoftwarePathRenderer.h
+++ b/src/gpu/GrSoftwarePathRenderer.h
@@ -28,17 +28,24 @@
 
     }
 
-    void draw(const GrRect& clientRect, SkRegion::Op op, bool antiAlias);
+    void draw(const GrRect& clientRect, SkRegion::Op op, 
+              bool antiAlias, GrColor color);
 
     void draw(const SkPath& clientPath, SkRegion::Op op, 
-              GrPathFill fill, bool antiAlias);
+              GrPathFill fill, bool antiAlias, GrColor color);
 
-    bool init(const GrIRect& pathDevBounds, const GrPoint* translate);
+    bool init(const GrIRect& pathDevBounds, 
+              const GrPoint* translate,
+              bool useMatrix);
 
     bool getTexture(GrAutoScratchTexture* tex);
 
     void toTexture(GrTexture* texture);
 
+    void clear(GrColor color) {
+        fBM.eraseColor(color);
+    }
+
 protected:
 private:
     GrContext*      fContext;