Improve handling of inverse clip paths in GrClipMaskManager.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 7 Dec 2012 20:43:52 +0000 (20:43 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 7 Dec 2012 20:43:52 +0000 (20:43 +0000)
Will require rebaselining of complexclip_aa and complexclip_aa_layer on GPU.

R=robertphillips@google.com
Review URL: https://codereview.appspot.com/6907052

git-svn-id: http://skia.googlecode.com/svn/trunk@6712 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkClipStack.h
src/core/SkClipStack.cpp
src/core/SkTLList.h
src/gpu/GrClipMaskManager.cpp
src/gpu/GrReducedClip.cpp

index 6af60a2..52b1b22 100644 (file)
@@ -98,6 +98,10 @@ public:
             when it is rasterized. */
         bool isAA() const { return fDoAA; }
 
+        //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
+        void invertShapeFillType();
+
+        //!< Sets the set operation represented by the element.
         void setOp(SkRegion::Op op) { fOp = op; }
 
         /** The GenID can be used by clip stack clients to cache representations of the clip. The
index 107c20f..29d008a 100644 (file)
 static const int32_t kFirstUnreservedGenID = 3;
 int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
 
+void SkClipStack::Element::invertShapeFillType() {
+    switch (fType) {
+        case kRect_Type:
+            fPath.reset();
+            fPath.addRect(fRect);
+            fPath.setFillType(SkPath::kInverseWinding_FillType);
+            fType = kPath_Type;
+            break;
+        case kPath_Type:
+            fPath.toggleInverseFillType();
+        case kEmpty_Type:
+            break;
+    }
+}
+
 void SkClipStack::Element::checkEmpty() const {
     SkASSERT(fFiniteBound.isEmpty());
     SkASSERT(kNormal_BoundsType == fFiniteBoundType);
index bc85daa..298ce51 100644 (file)
@@ -66,32 +66,34 @@ public:
         }
     }
 
-    void addToHead(const T& t) {
+    T* addToHead(const T& t) {
         this->validate();
         Node* node = this->createNode();
         fList.addToHead(node);
         SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
         this->validate();
+        return reinterpret_cast<T*>(node->fObj);
     }
 
-    void addToTail(const T& t) {
+    T* addToTail(const T& t) {
         this->validate();
         Node* node = this->createNode();
         fList.addToTail(node);
         SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
         this->validate();
+        return reinterpret_cast<T*>(node->fObj);
     }
 
     /** Adds a new element to the list before the location indicated by the iterator. If the
         iterator refers to a NULL location then the new element is added at the tail */
-    void addBefore(const T& t, const Iter& location) {
-        SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
+    T* addBefore(const T& t, const Iter& location) {
+        return SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
     }
 
     /** Adds a new element to the list after the location indicated by the iterator. If the
         iterator refers to a NULL location then the new element is added at the head */
-    void addAfter(const T& t, const Iter& location) {
-        SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
+    T* addAfter(const T& t, const Iter& location) {
+        return SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
     }
 
     /** Convenience methods for getting an iterator initialized to the head/tail of the list. */
index 15dc7e3..c400486 100644 (file)
@@ -20,6 +20,8 @@
 #include "GrSWMaskHelper.h"
 #include "GrCacheID.h"
 
+#include "SkTLazy.h"
+
 GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain)
 
 #define GR_AA_CLIP 1
@@ -59,11 +61,16 @@ void setup_drawstate_aaclip(GrGpu* gpu,
 
 bool path_needs_SW_renderer(GrContext* context,
                             GrGpu* gpu,
-                            const SkPath& path,
+                            const SkPath& origPath,
                             const SkStroke& stroke,
                             bool doAA) {
+    // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
+    SkTCopyOnFirstWrite<SkPath> path(origPath);
+    if (path->isInverseFillType()) {
+        path.writable()->toggleInverseFillType();
+    }
     // last (false) parameter disallows use of the SW path renderer
-    return NULL == context->getPathRenderer(path, stroke, gpu, doAA, false);
+    return NULL == context->getPathRenderer(*path, stroke, gpu, doAA, false);
 }
 
 }
@@ -300,9 +307,13 @@ bool GrClipMaskManager::drawClipShape(GrTexture* target, const SkClipStack::Elem
             }
             return true;
         case Element::kPath_Type: {
+            SkTCopyOnFirstWrite<SkPath> path(element->getPath());
+            if (path->isInverseFillType()) {
+                path.writable()->toggleInverseFillType();
+            }
             SkStroke stroke;
             stroke.setDoFill(true);
-            GrPathRenderer* pr = this->getContext()->getPathRenderer(element->getPath(),
+            GrPathRenderer* pr = this->getContext()->getPathRenderer(*path,
                                                                      stroke,
                                                                      fGpu,
                                                                      element->isAA(), false);
@@ -437,16 +448,11 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
     GrAutoScratchTexture temp;
     // walk through each clip element and perform its set op
     for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
-
         const Element* element = iter.get();
         SkRegion::Op op = element->getOp();
 
-        if (SkRegion::kReplace_Op == op) {
-            setup_boolean_blendcoeffs(drawState, op);
-            this->drawClipShape(result, element);
-
-        } else if (SkRegion::kReverseDifference_Op == op ||
-                   SkRegion::kIntersect_Op == op) {
+        if (SkRegion::kReverseDifference_Op == op || SkRegion::kIntersect_Op == op ||
+            element->isInverseFilled()) {
             this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
             if (NULL == temp.texture()) {
                 fAACache.reset();
@@ -466,21 +472,27 @@ GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
                 elementBounds.roundOut(&maskSpaceElementIBounds);
             }
 
-            // clear the temp target & draw into it
-            fGpu->clear(&maskSpaceElementIBounds, 0x00000000, temp.texture()->asRenderTarget());
+            // determines whether we're drawing white-on-black or black-on-white
+            bool invert = element->isInverseFilled();
 
+            // clear the temp target & draw into it
+            fGpu->clear(&maskSpaceElementIBounds,
+                        invert ? 0xffffffff : 0x00000000,
+                        temp.texture()->asRenderTarget());
+            drawState->setAlpha(invert ? 0x00 : 0xff);
             setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
+
             if (!this->drawClipShape(temp.texture(), element)) {
                 fAACache.reset();
                 return NULL;
             }
 
-            // Now draw into the accumulator using the real operation
-            // and the temp buffer as a texture
+            // Now draw into the accumulator using the real operation and the temp buffer as a
+            // texture
             this->mergeMask(result, temp.texture(), op, maskSpaceIBounds, maskSpaceElementIBounds);
         } else {
-            // all the remaining ops can just be directly draw into
-            // the accumulation buffer
+            // all the remaining ops can just be directly draw into the accumulation buffer
+            drawState->setAlpha(0xff);
             setup_boolean_blendcoeffs(drawState, op);
             this->drawClipShape(result, element);
         }
index baa8533..5287858 100644 (file)
@@ -21,9 +21,7 @@ void reduced_stack_walker(const SkClipStack& stack,
                           bool* requiresAA);
 
 /*
-There are plenty of optimizations that could be added here. For example we could consider
-checking for cases where an inverse path can be changed to a regular fill with a different op.
-(e.g. [kIntersect, inverse path] -> [kDifference, path]). Maybe flips could be folded into
+There are plenty of optimizations that could be added here. Maybe flips could be folded into
 earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
 for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
 based on later intersect operations, and perhaps remove intersect-rects. We could optionally
@@ -322,10 +320,23 @@ void reduced_stack_walker(const SkClipStack& stack,
                                            Element,
                                            (queryBounds, SkRegion::kReverseDifference_Op, false));
             } else {
-                result->addToHead(*element);
-                if (element->isAA()) {
+                Element* newElement = result->addToHead(*element);
+                if (newElement->isAA()) {
                     ++numAAElements;
                 }
+                // Intersecting an inverse shape is the same as differencing the non-inverse shape.
+                // Replacing with a inverse shape the same as setting initialState=kAllIn and
+                // differencing the non-inverse shape.
+                bool isReplace = SkRegion::kReplace_Op == newElement->getOp();
+                if (newElement->isInverseFilled() &&
+                    (SkRegion::kIntersect_Op == newElement->getOp() || isReplace)) {
+                    newElement->invertShapeFillType();
+                    newElement->setOp(SkRegion::kDifference_Op);
+                    if (isReplace) {
+                        SkASSERT(kAllOut_InitialState == *initialState);
+                        *initialState = kAllIn_InitialState;
+                    }
+                }
             }
         }
     }