Our region blitter (invoked by region::setPath()) must have its scanlines
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 3 Mar 2011 14:32:51 +0000 (14:32 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 3 Mar 2011 14:32:51 +0000 (14:32 +0000)
fed to it in scanline order (Y sorted, then X sorted).

If, however, we call rgn->setPath(path, otherRgn) with path in inverse mode,
then our scan converter code looks like this:

if (inverse)
    blit_top
scan_convert_path
if (inverse)
    blit_bottom

This is fine, unless otherRgn is complex. If it is, then blit_top will want to
efficiently "blit" otherRgn (the part above the path), and that means calling
blitRect in an iterator. That can result in chunks being passed to our blitter
(which is really accumulating scanlines) out of scanline order.

The change is to detect that otherRgn is complex, and if it is, just perform
the op in two steps:

1. setPath on a tmp region, limited by the bounds of otherRgn (simple)
2. intersect this tmp region with the currRgn.

This is effectively what we do for non-intersect ops. Intersect was different
only because we could (sometimes) avoid the step of create a tmp region.

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

src/core/SkCanvas.cpp

index e636d2b..664426c 100644 (file)
@@ -954,10 +954,23 @@ bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
 
 static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
                            const SkPath& devPath, SkRegion::Op op) {
+    // base is used to limit the size (and therefore memory allocation) of the
+    // region that results from scan converting devPath.
+    SkRegion base;
+
     if (SkRegion::kIntersect_Op == op) {
-        return currRgn->setPath(devPath, *currRgn);
+        // since we are intersect, we can do better (tighter) with currRgn's
+        // bounds, than just using the device. However, if currRgn is complex,
+        // our region blitter may hork, so we do that case in two steps.
+        if (currRgn->isRect()) {
+            return currRgn->setPath(devPath, *currRgn);
+        } else {
+            base.setRect(currRgn->getBounds());
+            SkRegion rgn;
+            rgn.setPath(devPath, base);
+            return currRgn->op(rgn, op);
+        }
     } else {
-        SkRegion base;
         const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
         base.setRect(0, 0, bm.width(), bm.height());