Split createAlphaClipMask in two to allow reuse with SW-only path

http://codereview.appspot.com/6198065/



git-svn-id: http://skia.googlecode.com/svn/trunk@3910 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 7672f5a..2e9a4b9 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -12,8 +12,10 @@
 #include "GrStencilBuffer.h"
 #include "GrPathRenderer.h"
 #include "GrPaint.h"
+#include "SkRasterClip.h"
 
 //#define GR_AA_CLIP 1
+//#define GR_SW_CLIP 1
 
 ////////////////////////////////////////////////////////////////////////////////
 void ScissoringSettings::setupScissoring(GrGpu* gpu) {
@@ -28,7 +30,9 @@
 namespace {
 // set up the draw state to enable the aa clipping mask. Besides setting up the 
 // sampler matrix this also alters the vertex layout
-void setupDrawStateAAClip(GrGpu* gpu, GrTexture* result, const GrRect &bound) {
+void setup_drawstate_aaclip(GrGpu* gpu, 
+                            GrTexture* result, 
+                            const GrRect &bound) {
     GrDrawState* drawState = gpu->drawState();
     GrAssert(drawState);
 
@@ -51,11 +55,15 @@
                 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
 }
 
+bool create_mask_in_sw() {
+    return false;
+}
+
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// sort out what kind of clip mask needs to be created: alpha, stencil
-// or scissor
+// sort out what kind of clip mask needs to be created: alpha, stencil,
+// scissor, or entirely software
 bool GrClipMaskManager::createClipMask(GrGpu* gpu, 
                                        const GrClip& clipIn,
                                        ScissoringSettings* scissorSettings) {
@@ -76,6 +84,21 @@
     // GrDrawTarget should have filtered this for us
     GrAssert(NULL != rt);
 
+#if GR_SW_CLIP
+    if (create_mask_in_sw()) {
+        // The clip geometry is complex enough that it will be more
+        // efficient to create it entirely in software
+        GrTexture* result = NULL;
+        GrRect bound;
+        if (this->createSoftwareClipMask(gpu, clipIn, &result, &bound)) {
+            fClipMaskInAlpha = true;
+
+            setup_drawstate_aaclip(gpu, result, bound);
+            return true;
+        }
+    }
+#endif
+
 #if GR_AA_CLIP
     // If MSAA is enabled use the (faster) stencil path for AA clipping
     // otherwise the alpha clip mask is our only option
@@ -89,7 +112,7 @@
         if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) {
             fClipMaskInAlpha = true;
 
-            setupDrawStateAAClip(gpu, result, bound);
+            setup_drawstate_aaclip(gpu, result, bound);
             return true;
         }
 
@@ -239,7 +262,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // set up the OpenGL blend function to perform the specified 
 // boolean operation for alpha clip mask creation 
-void setUpBooleanBlendCoeffs(GrDrawState* drawState, SkRegion::Op op) {
+void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
 
     switch (op) {
         case SkRegion::kReplace_Op:
@@ -351,43 +374,63 @@
 
 // get a texture to act as a temporary buffer for AA clip boolean operations
 // TODO: given the expense of createTexture we may want to just cache this too
-void needTemp(GrGpu *gpu, const GrTextureDesc& desc, GrTexture** temp) {
+void get_temp(GrGpu *gpu, const GrRect& bounds, GrTexture** temp) {
     if (NULL != *temp) {
         // we've already allocated the temp texture
         return;
     }
 
-     *temp = gpu->createTexture(desc, NULL, 0);
+    const GrTextureDesc desc = {
+        kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
+        SkScalarCeilToInt(bounds.width()),
+        SkScalarCeilToInt(bounds.height()),
+        kAlpha_8_GrPixelConfig,
+        0           // samples
+    };
+
+    *temp = gpu->createTexture(desc, NULL, 0);
 }
 
 }
 
 void GrClipMaskManager::getAccum(GrGpu* gpu,
-                                 const GrTextureDesc& desc,
+                                 const GrRect& bounds,
                                  GrTexture** accum) {
     GrAssert(NULL == *accum);
 
     // since we are getting an accumulator we know our cache is shot. See
     // if we can reuse the texture stored in the cache
-    if (fAACache.getLastMaskWidth() >= desc.fWidth &&
-        fAACache.getLastMaskHeight() >= desc.fHeight) {
+    if (fAACache.getLastMaskWidth() >= bounds.width() &&
+        fAACache.getLastMaskHeight() >= bounds.height()) {
         // we can just reuse the existing texture
         *accum = fAACache.detachLastMask();
         fAACache.reset();
     } else {
+        const GrTextureDesc desc = {
+            kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
+            SkScalarCeilToInt(bounds.width()),
+            SkScalarCeilToInt(bounds.height()),
+            kAlpha_8_GrPixelConfig,
+            0           // samples
+        };
+
         *accum = gpu->createTexture(desc, NULL, 0);
     }
 
     GrAssert(1 == (*accum)->getRefCnt());
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Create a 8-bit clip mask in alpha
-bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
-                                            const GrClip& clipIn,
-                                            GrTexture** result,
-                                            GrRect *resultBounds) {
 
+////////////////////////////////////////////////////////////////////////////////
+// Shared preamble between gpu and SW-only AA clip mask creation paths.
+// Handles caching, determination of clip mask bound & allocation (if needed)
+// of the result texture
+// Returns true if there is no more work to be done (i.e., we got a cache hit)
+bool GrClipMaskManager::clipMaskPreamble(GrGpu* gpu,
+                                         const GrClip& clipIn,
+                                         GrTexture** result,
+                                         GrRect *resultBounds,
+                                         GrTexture** maskStorage) {
     GrDrawState* origDrawState = gpu->drawState();
     GrAssert(origDrawState->isClipState());
 
@@ -434,26 +477,31 @@
         return true;
     }
 
-    const GrTextureDesc desc = {
-        kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
-        SkScalarCeilToInt(bounds.width()),
-        SkScalarCeilToInt(bounds.height()),
-        kAlpha_8_GrPixelConfig,
-        0           // samples
-    };
+    this->getAccum(gpu, bounds, maskStorage);
+    *resultBounds = bounds;
+    return false;
+}
 
-    GrRect newRTBounds;
-    newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
+                                            const GrClip& clipIn,
+                                            GrTexture** result,
+                                            GrRect *resultBounds) {
 
-    GrTexture* accum = NULL, *temp = NULL;
-    
-    getAccum(gpu, desc, &accum);
+    GrTexture* accum = NULL;
+    if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds, &accum)) {
+        return true;
+    }
+
     if (NULL == accum) {
         fClipMaskInAlpha = false;
-        SkSafeUnref(accum);
         return false;
     }
 
+    GrRect newRTBounds;
+    newRTBounds.setLTRB(0, 0, resultBounds->width(), resultBounds->height());
+    
     GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
     GrDrawState* drawState = gpu->drawState();
 
@@ -461,12 +509,12 @@
 
     int count = clipIn.getElementCount();
 
-    if (0 != bounds.fTop || 0 != bounds.fLeft) {
+    if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
         // if we were able to trim down the size of the mask we need to 
         // offset the paths & rects that will be used to compute it
         GrMatrix m;
 
-        m.setTranslate(-bounds.fLeft, -bounds.fTop);
+        m.setTranslate(-resultBounds->fLeft, -resultBounds->fTop);
 
         drawState->setViewMatrix(m);
     }
@@ -474,12 +522,14 @@
     bool clearToInside;
     SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
     int start = process_initial_clip_elements(clipIn,
-                                              bounds,
+                                              *resultBounds,
                                               &clearToInside,
                                               &startOp);
 
     clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
 
+    GrTexture* temp = NULL;
+
     // walk through each clip element and perform its set op
     for (int c = start; c < count; ++c) {
 
@@ -493,7 +543,7 @@
             // clear the accumulator and draw the new object directly into it
             clear(gpu, accum, 0x00000000);
 
-            setUpBooleanBlendCoeffs(drawState, op);
+            setup_boolean_blendcoeffs(drawState, op);
             this->drawClipShape(gpu, accum, clipIn, c);
 
         } else if (SkRegion::kReverseDifference_Op == op ||
@@ -501,11 +551,11 @@
             // there is no point in intersecting a screen filling rectangle.
             if (SkRegion::kIntersect_Op == op &&
                 kRect_ClipType == clipIn.getElementType(c) &&
-                clipIn.getRect(c).contains(bounds)) {
+                clipIn.getRect(c).contains(*resultBounds)) {
                 continue;
             }
 
-            needTemp(gpu, desc, &temp);
+            get_temp(gpu, *resultBounds, &temp);
             if (NULL == temp) {
                 fClipMaskInAlpha = false;
                 SkSafeUnref(accum);
@@ -515,29 +565,29 @@
             // clear the temp target & draw into it
             clear(gpu, temp, 0x00000000);
 
-            setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
+            setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
             this->drawClipShape(gpu, temp, clipIn, c);
 
             // TODO: rather than adding these two translations here
             // compute the bounding box needed to render the texture
             // into temp
-            if (0 != bounds.fTop || 0 != bounds.fLeft) {
+            if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
                 GrMatrix m;
 
-                m.setTranslate(bounds.fLeft, bounds.fTop);
+                m.setTranslate(resultBounds->fLeft, resultBounds->fTop);
 
                 drawState->preConcatViewMatrix(m);
             }
 
             // Now draw into the accumulator using the real operation
             // and the temp buffer as a texture
-            setUpBooleanBlendCoeffs(drawState, op);
+            setup_boolean_blendcoeffs(drawState, op);
             this->drawTexture(gpu, accum, newRTBounds, temp);
 
-            if (0 != bounds.fTop || 0 != bounds.fLeft) {
+            if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
                 GrMatrix m;
 
-                m.setTranslate(-bounds.fLeft, -bounds.fTop);
+                m.setTranslate(-resultBounds->fLeft, -resultBounds->fTop);
 
                 drawState->preConcatViewMatrix(m);
             }
@@ -545,14 +595,13 @@
         } else {
             // all the remaining ops can just be directly draw into 
             // the accumulation buffer
-            setUpBooleanBlendCoeffs(drawState, op);
+            setup_boolean_blendcoeffs(drawState, op);
             this->drawClipShape(gpu, accum, clipIn, c);
         }
     }
 
-    fAACache.set(clipIn, accum, bounds);
+    fAACache.set(clipIn, accum, *resultBounds);
     *result = accum;
-    *resultBounds = bounds;
     SkSafeUnref(accum);     // fAACache still has a ref to accum
     SkSafeUnref(temp);
 
@@ -629,10 +678,10 @@
             drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
 
             bool canRenderDirectToStencil; // can the clip element be drawn
-                                            // directly to the stencil buffer
-                                            // with a non-inverted fill rule
-                                            // without extra passes to
-                                            // resolve in/out status.
+                                           // directly to the stencil buffer
+                                           // with a non-inverted fill rule
+                                           // without extra passes to
+                                           // resolve in/out status.
 
             SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
 
@@ -730,6 +779,55 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu,
+                                               const GrClip& clipIn,
+                                               GrTexture** result,
+                                               GrRect *resultBounds) {
+
+    GrTexture* accum = NULL;
+    if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds, &accum)) {
+        return true;
+    }
+
+    if (NULL == accum) {
+        fClipMaskInAlpha = false;
+        return false;
+    }
+
+#if 0
+    SkRasterClip rasterClip;
+
+    // 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) {
+        if (kRect_ClipType == clipIn.getElementType(i)) {
+            rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i));
+        } 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);
+        }
+    }
+
+    // TODO: need to get pixels out of SkRasterClip & into the texture!
+#endif
+
+    fAACache.set(clipIn, accum, *resultBounds);
+    *result = accum;
+    SkSafeUnref(accum);     // fAACache still has a ref to accum
+
+    return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
                                                        const SkPath& path,
                                                        GrPathFill fill,