Simplify SkRasterClip now that it's only intersect/diff

Bug: skia:10205
Change-Id: Id29a63783bd38e5977e94bf8e8d7fbb4fe16cb51
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/442279
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/experimental/xform/XContext.cpp b/experimental/xform/XContext.cpp
index d2a9077..f16867b 100644
--- a/experimental/xform/XContext.cpp
+++ b/experimental/xform/XContext.cpp
@@ -42,16 +42,16 @@
     }
 
     void clipRect(const SkRect& r, SkClipOp op) override {
-        fRC.op(r, fCTM, fBounds, (SkRegion::Op)op, false);
+        fRC.op(r, fCTM, op, false);
         fCache.reset(nullptr);
     }
 
     void clipRRect(const SkRRect& rr, SkClipOp op) override {
-        fRC.op(rr, fCTM, fBounds, (SkRegion::Op)op, false);
+        fRC.op(rr, fCTM, op, false);
         fCache.reset(nullptr);
     }
     void clipPath(const SkPath& p, SkClipOp op) override {
-        fRC.op(p, fCTM, fBounds, (SkRegion::Op)op, false);
+        fRC.op(p, fCTM, op, false);
         fCache.reset(nullptr);
     }
 
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 475fcb2..9eaf54e 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -169,7 +169,7 @@
                                                          SkIntToScalar(-fOrigin.y()));
         fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC);
         fTileRC.op(SkIRect::MakeWH(fDraw.fDst.width(), fDraw.fDst.height()),
-                   SkRegion::kIntersect_Op);
+                   SkClipOp::kIntersect);
     }
 };
 
@@ -708,7 +708,7 @@
     const SkRasterClip& rc = fRCStack.rc();
     if (rc.isEmpty()) {
         return ClipType::kEmpty;
-    } else if (rc.isRect()) {
+    } else if (rc.isRect() && !SkToBool(rc.clipShader())) {
         return ClipType::kRect;
     } else {
         return ClipType::kComplex;
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index b436032..8604bd3 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -1058,7 +1058,7 @@
 }
 
 static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
-    return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
+    return clip.isBW() || clip.quickContains(SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height()));
 }
 
 void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
diff --git a/src/core/SkRasterClip.cpp b/src/core/SkRasterClip.cpp
index bfb67b0..4193d2c 100644
--- a/src/core/SkRasterClip.cpp
+++ b/src/core/SkRasterClip.cpp
@@ -64,6 +64,20 @@
     SkDEBUGCODE(this->validate();)
 }
 
+SkRasterClip::SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA) {
+    SkRegion clip(bounds);
+    if (doAA) {
+        fIsBW = false;
+        fAA.setPath(path, &clip, true);
+    } else {
+        fIsBW = true;
+        fBW.setPath(path, clip);
+    }
+    fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
+    fIsRect = this->computeIsRect();
+    SkDEBUGCODE(this->validate();)
+}
+
 SkRasterClip::~SkRasterClip() {
     SkDEBUGCODE(this->validate();)
 }
@@ -82,14 +96,6 @@
     return isEqual;
 }
 
-bool SkRasterClip::isComplex() const {
-    return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
-}
-
-const SkIRect& SkRasterClip::getBounds() const {
-    return fIsBW ? fBW.getBounds() : fAA.getBounds();
-}
-
 bool SkRasterClip::setEmpty() {
     AUTO_RASTERCLIP_VALIDATE(*this);
 
@@ -113,51 +119,82 @@
 
 /////////////////////////////////////////////////////////////////////////////////////
 
-bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
-    SkRegion::Op op;
-    if (isInverse) {
-        op = SkRegion::kDifference_Op;
+bool SkRasterClip::op(const SkIRect& rect, SkClipOp op) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+
+    if (fIsBW) {
+        fBW.op(rect, (SkRegion::Op) op);
     } else {
-        op = SkRegion::kIntersect_Op;
+        fAA.op(rect, (SkRegion::Op) op);
     }
-    fBW.setRect(clipR);
-    fBW.op(r.roundOut(), op);
     return this->updateCacheAndReturnNonEmpty();
 }
 
-/////////////////////////////////////////////////////////////////////////////////////
-
-bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
+bool SkRasterClip::op(const SkRegion& rgn, SkClipOp op) {
     AUTO_RASTERCLIP_VALIDATE(*this);
 
-    if (this->isBW() && !doAA) {
-        (void)fBW.setPath(path, clip);
+    if (fIsBW) {
+        (void)fBW.op(rgn, (SkRegion::Op) op);
     } else {
-        // TODO: since we are going to over-write fAA completely (aren't we?)
-        // we should just clear our BW data (if any) and set fIsAA=true
-        if (this->isBW()) {
+        SkAAClip tmp;
+        tmp.setRegion(rgn);
+        (void)fAA.op(tmp, (SkRegion::Op) op);
+    }
+    return this->updateCacheAndReturnNonEmpty();
+}
+
+/**
+ *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
+ *  axis. Thus we can treat an axis coordinate as an integer if it differs
+ *  from its nearest int by < half of that value (1/8 in this case).
+ */
+static bool nearly_integral(SkScalar x) {
+    static const SkScalar domain = SK_Scalar1 / 4;
+    static const SkScalar halfDomain = domain / 2;
+
+    x += halfDomain;
+    return x - SkScalarFloorToScalar(x) < domain;
+}
+
+bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+
+    const bool isScaleTrans = matrix.isScaleTranslate();
+    if (!isScaleTrans) {
+        SkPath path = SkPath::Rect(localRect);
+        path.setIsVolatile(true);
+        return this->op(path, matrix, op, doAA);
+    }
+
+    SkRect devRect = matrix.mapRect(localRect);
+    if (fIsBW && doAA) {
+        // check that the rect really needs aa, or is it close enought to
+        // integer boundaries that we can just treat it as a BW rect?
+        if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
+            nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
+            doAA = false;
+        }
+    }
+
+    if (fIsBW && !doAA) {
+        (void)fBW.op(devRect.round(), (SkRegion::Op) op);
+    } else {
+        if (fIsBW) {
             this->convertToAA();
         }
-        (void)fAA.setPath(path, &clip, doAA);
+        (void)fAA.op(devRect, (SkRegion::Op) op, doAA);
     }
     return this->updateCacheAndReturnNonEmpty();
 }
 
-bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
-                      SkRegion::Op op, bool doAA) {
-    SkIRect bounds(devBounds);
-    return this->op(SkPath::RRect(rrect), matrix, bounds, op, doAA);
+bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
+    return this->op(SkPath::RRect(rrect), matrix, op, doAA);
 }
 
-bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
-                      SkRegion::Op op, bool doAA) {
+bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, bool doAA) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    SkIRect bounds(devBounds);
 
-    // base is used to limit the size (and therefore memory allocation) of the
-    // region that results from scan converting devPath.
-    SkRegion base;
-
+    // Transform into device space
     SkPath devPath;
     if (matrix.isIdentity()) {
         devPath = path;
@@ -165,82 +202,11 @@
         path.transform(matrix, &devPath);
         devPath.setIsVolatile(true);
     }
-    if (SkRegion::kIntersect_Op == op) {
-        // 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 (this->isRect()) {
-            // FIXME: we should also be able to do this when this->isBW(),
-            // but relaxing the test above triggers GM asserts in
-            // SkRgnBuilder::blitH(). We need to investigate what's going on.
-            return this->setPath(devPath, this->bwRgn(), doAA);
-        } else {
-            base.setRect(this->getBounds());
-            SkRasterClip clip;
-            clip.setPath(devPath, base, doAA);
-            return this->op(clip, op);
-        }
-    } else {
-        base.setRect(bounds);
 
-        if (SkRegion::kReplace_Op == op) {
-            return this->setPath(devPath, base, doAA);
-        } else {
-            SkRasterClip clip;
-            clip.setPath(devPath, base, doAA);
-            return this->op(clip, op);
-        }
-    }
-}
-
-bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
-    SkRegion tmp;
-    tmp.setRect(clip);
-    return this->setPath(path, tmp, doAA);
-}
-
-bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
-    AUTO_RASTERCLIP_VALIDATE(*this);
-
-    fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
-    return this->updateCacheAndReturnNonEmpty();
-}
-
-bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
-    AUTO_RASTERCLIP_VALIDATE(*this);
-
-    if (fIsBW) {
-        (void)fBW.op(rgn, op);
-    } else {
-        SkAAClip tmp;
-        tmp.setRegion(rgn);
-        (void)fAA.op(tmp, op);
-    }
-    return this->updateCacheAndReturnNonEmpty();
-}
-
-bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
-    AUTO_RASTERCLIP_VALIDATE(*this);
-    clip.validate();
-
-    if (this->isBW() && clip.isBW()) {
-        (void)fBW.op(clip.fBW, op);
-    } else {
-        SkAAClip tmp;
-        const SkAAClip* other;
-
-        if (this->isBW()) {
-            this->convertToAA();
-        }
-        if (clip.isBW()) {
-            tmp.setRegion(clip.bwRgn());
-            other = &tmp;
-        } else {
-            other = &clip.aaRgn();
-        }
-        (void)fAA.op(*other, op);
-    }
-    return this->updateCacheAndReturnNonEmpty();
+    // Since op is either intersect or difference, the clip is always shrinking; that means we can
+    // always use our current bounds as the limiting factor for region/aaclip operations
+    SkRasterClip clip(devPath, this->getBounds(), doAA);
+    return this->op(clip, op);
 }
 
 bool SkRasterClip::op(sk_sp<SkShader> sh) {
@@ -254,52 +220,26 @@
     return !this->isEmpty();
 }
 
-/**
- *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
- *  axis. Thus we can treat an axis coordinate as an integer if it differs
- *  from its nearest int by < half of that value (1.8 in this case).
- */
-static bool nearly_integral(SkScalar x) {
-    static const SkScalar domain = SK_Scalar1 / 4;
-    static const SkScalar halfDomain = domain / 2;
-
-    x += halfDomain;
-    return x - SkScalarFloorToScalar(x) < domain;
-}
-
-bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
-                      SkRegion::Op op, bool doAA) {
+bool SkRasterClip::op(const SkRasterClip& clip, SkClipOp op) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    SkRect devRect;
+    clip.validate();
 
-    const bool isScaleTrans = matrix.isScaleTranslate();
-    if (!isScaleTrans) {
-        SkPath path;
-        path.addRect(localRect);
-        path.setIsVolatile(true);
-        return this->op(path, matrix, devBounds, op, doAA);
-    }
-
-    matrix.mapRect(&devRect, localRect);
-
-    if (fIsBW && doAA) {
-        // check that the rect really needs aa, or is it close enought to
-        // integer boundaries that we can just treat it as a BW rect?
-        if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
-            nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
-            doAA = false;
-        }
-    }
-
-    if (fIsBW && !doAA) {
-        SkIRect ir;
-        devRect.round(&ir);
-        (void)fBW.op(ir, op);
+    if (this->isBW() && clip.isBW()) {
+        (void)fBW.op(clip.fBW, (SkRegion::Op) op);
     } else {
-        if (fIsBW) {
+        SkAAClip tmp;
+        const SkAAClip* other;
+
+        if (this->isBW()) {
             this->convertToAA();
         }
-        (void)fAA.op(devRect, op, doAA);
+        if (clip.isBW()) {
+            tmp.setRegion(clip.bwRgn());
+            other = &tmp;
+        } else {
+            other = &clip.aaRgn();
+        }
+        (void)fAA.op(*other, (SkRegion::Op) op);
     }
     return this->updateCacheAndReturnNonEmpty();
 }
@@ -331,21 +271,6 @@
     dst->updateCacheAndReturnNonEmpty();
 }
 
-bool SkRasterClip::quickContains(const SkIRect& ir) const {
-    return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-const SkRegion& SkRasterClip::forceGetBW() {
-    AUTO_RASTERCLIP_VALIDATE(*this);
-
-    if (!fIsBW) {
-        fBW.setRect(fAA.getBounds());
-    }
-    return fBW;
-}
-
 void SkRasterClip::convertToAA() {
     AUTO_RASTERCLIP_VALIDATE(*this);
 
diff --git a/src/core/SkRasterClip.h b/src/core/SkRasterClip.h
index ccc2f9d..1dd1397 100644
--- a/src/core/SkRasterClip.h
+++ b/src/core/SkRasterClip.h
@@ -8,6 +8,7 @@
 #ifndef SkRasterClip_DEFINED
 #define SkRasterClip_DEFINED
 
+#include "include/core/SkClipOp.h"
 #include "include/core/SkRegion.h"
 #include "include/core/SkShader.h"
 #include "include/private/SkMacros.h"
@@ -18,23 +19,19 @@
 /**
  *  Wraps a SkRegion and SkAAClip, so we have a single object that can represent either our
  *  BW or antialiased clips.
- *
- *  This class is optimized for the raster backend of canvas, but can be expense to keep up2date,
- *  so it supports a runtime option (force-conservative-rects) to turn it into a super-fast
- *  rect-only tracker. The gpu backend uses this since it does not need the result (it uses
- *  SkClipStack instead).
  */
 class SkRasterClip {
 public:
     SkRasterClip();
-    SkRasterClip(const SkIRect&);
-    SkRasterClip(const SkRegion&);
-    SkRasterClip(const SkRasterClip&);
-    SkRasterClip& operator=(const SkRasterClip&);
+    explicit SkRasterClip(const SkIRect&);
+    explicit SkRasterClip(const SkRegion&);
+    explicit SkRasterClip(const SkRasterClip&);
+    SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA);
+
     ~SkRasterClip();
 
-    // Only compares the current state. Does not compare isForceConservativeRects(), so that field
-    // could be different but this could still return true.
+    SkRasterClip& operator=(const SkRasterClip&);
+
     bool operator==(const SkRasterClip&) const;
     bool operator!=(const SkRasterClip& other) const {
         return !(*this == other);
@@ -55,27 +52,27 @@
         return fIsRect;
     }
 
-    bool isComplex() const;
-    const SkIRect& getBounds() const;
+    bool isComplex() const {
+        return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
+    }
+    const SkIRect& getBounds() const {
+        return fIsBW ? fBW.getBounds() : fAA.getBounds();
+    }
 
     bool setEmpty();
     bool setRect(const SkIRect&);
 
-    bool op(const SkIRect&, SkRegion::Op);
-    bool op(const SkRegion&, SkRegion::Op);
-    bool op(const SkRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
-    bool op(const SkRRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
-    bool op(const SkPath&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
+    bool op(const SkIRect&, SkClipOp);
+    bool op(const SkRegion&, SkClipOp);
+    bool op(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
+    bool op(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
+    bool op(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
     bool op(sk_sp<SkShader>);
 
     void translate(int dx, int dy, SkRasterClip* dst) const;
-    void translate(int dx, int dy) {
-        this->translate(dx, dy, this);
-    }
 
-    bool quickContains(const SkIRect& rect) const;
-    bool quickContains(int left, int top, int right, int bottom) const {
-        return quickContains(SkIRect::MakeLTRB(left, top, right, bottom));
+    bool quickContains(const SkIRect& rect) const {
+        return fIsBW ? fBW.quickContains(rect) : fAA.quickContains(rect);
     }
 
     /**
@@ -87,9 +84,6 @@
         return !SkIRect::Intersects(this->getBounds(), rect);
     }
 
-    // hack for SkCanvas::getTotalClip
-    const SkRegion& forceGetBW();
-
 #ifdef SK_DEBUG
     void validate() const;
 #else
@@ -132,10 +126,7 @@
 
     void convertToAA();
 
-    bool setPath(const SkPath& path, const SkRegion& clip, bool doAA);
-    bool setPath(const SkPath& path, const SkIRect& clip, bool doAA);
-    bool op(const SkRasterClip&, SkRegion::Op);
-    bool setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse);
+    bool op(const SkRasterClip&, SkClipOp);
 };
 
 class SkAutoRasterClipValidate : SkNoncopyable {
diff --git a/src/core/SkRasterClipStack.h b/src/core/SkRasterClipStack.h
index a642c5f..3f991d5 100644
--- a/src/core/SkRasterClipStack.h
+++ b/src/core/SkRasterClipStack.h
@@ -9,68 +9,16 @@
 #define SkRasterClipStack_DEFINED
 
 #include "include/core/SkClipOp.h"
-#include "include/private/SkDeque.h"
 #include "src/core/SkRasterClip.h"
 #include "src/core/SkScan.h"
-#include <new>
-
-template <typename T> class SkTStack {
-public:
-    SkTStack(void* storage, size_t size) : fDeque(sizeof(T), storage, size), fTop(nullptr) {}
-    ~SkTStack() {
-        while (!fDeque.empty()) {
-            ((T*)fDeque.back())->~T();
-            fDeque.pop_back();
-        }
-    }
-
-    bool empty() const { return fDeque.empty(); }
-
-    int count() const { return fDeque.count(); }
-
-    const T& top() const {
-        SkASSERT(fTop);
-        return *fTop;
-    }
-
-    T& top() {
-        SkASSERT(fTop);
-        return *fTop;
-    }
-
-    T* push_raw() { return (T*)fDeque.push_back(); }
-    T& push() {
-        fTop = this->push_raw();
-        new (fTop) T();
-        return *fTop;
-    }
-    T& push(const T& src) {
-        fTop = this->push_raw();
-        new (fTop) T(src);
-        return *fTop;
-    }
-
-    void pop() {
-        fTop->~T();
-        fDeque.pop_back();
-        fTop = fDeque.empty() ? nullptr : (T*)fDeque.back();
-    }
-
-private:
-    SkDeque fDeque;
-    T*      fTop;
-};
+#include "src/core/SkTBlockList.h"
 
 class SkRasterClipStack : SkNoncopyable {
-    int fCounter = 0;
 public:
     SkRasterClipStack(int width, int height)
-        : fStack(fStorage, sizeof(fStorage))
-        , fRootBounds(SkIRect::MakeWH(width, height))
-        , fDisableAA(SkScan::DowngradeClipAA(fRootBounds)) {
-        Rec& rec = fStack.push();
-        rec.fRC.setRect(fRootBounds);
-        rec.fDeferredCount = 0;
+            : fRootBounds(SkIRect::MakeWH(width, height))
+            , fDisableAA(SkScan::DowngradeClipAA(fRootBounds)) {
+        fStack.emplace_back(SkRasterClip(fRootBounds));
         SkASSERT(fStack.count() == 1);
     }
 
@@ -78,43 +26,42 @@
         fRootBounds.setXYWH(0, 0, w, h);
 
         SkASSERT(fStack.count() == 1);
-        Rec& rec = fStack.top();
+        Rec& rec = fStack.back();
         SkASSERT(rec.fDeferredCount == 0);
         rec.fRC.setRect(fRootBounds);
     }
 
-    const SkRasterClip& rc() const { return fStack.top().fRC; }
+    const SkRasterClip& rc() const { return fStack.back().fRC; }
 
     void save() {
-        fCounter += 1;
-        SkASSERT(fStack.top().fDeferredCount >= 0);
-        fStack.top().fDeferredCount += 1;
+        SkDEBUGCODE(fCounter += 1);
+        SkASSERT(fStack.back().fDeferredCount >= 0);
+        fStack.back().fDeferredCount += 1;
     }
 
     void restore() {
-        fCounter -= 1; SkASSERT(fCounter >= 0);
-        if (--fStack.top().fDeferredCount < 0) {
-            SkASSERT(fStack.top().fDeferredCount == -1);
+        SkDEBUGCODE(fCounter -= 1);
+        SkASSERT(fCounter >= 0);
+
+        if (--fStack.back().fDeferredCount < 0) {
+            SkASSERT(fStack.back().fDeferredCount == -1);
             SkASSERT(fStack.count() > 1);
-            fStack.pop();
+            fStack.pop_back();
         }
     }
 
     void clipRect(const SkMatrix& ctm, const SkRect& rect, SkClipOp op, bool aa) {
-        this->writable_rc().op(rect, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
-        this->trimIfExpanding(op);
+        this->writable_rc().op(rect, ctm, op, this->finalAA(aa));
         this->validate();
     }
 
     void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, SkClipOp op, bool aa) {
-        this->writable_rc().op(rrect, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
-        this->trimIfExpanding(op);
+        this->writable_rc().op(rrect, ctm, op, this->finalAA(aa));
         this->validate();
     }
 
     void clipPath(const SkMatrix& ctm, const SkPath& path, SkClipOp op, bool aa) {
-        this->writable_rc().op(path, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
-        this->trimIfExpanding(op);
+        this->writable_rc().op(path, ctm, op, this->finalAA(aa));
         this->validate();
     }
 
@@ -124,8 +71,7 @@
     }
 
     void clipRegion(const SkRegion& rgn, SkClipOp op) {
-        this->writable_rc().op(rgn, (SkRegion::Op)op);
-        this->trimIfExpanding(op);
+        this->writable_rc().op(rgn, op);
         this->validate();
     }
 
@@ -151,38 +97,27 @@
 
 private:
     struct Rec {
-        SkRasterClip    fRC;
-        int             fDeferredCount; // 0 for a "normal" entry
+        SkRasterClip fRC;
+        int          fDeferredCount; // 0 for a "normal" entry
+
+        Rec(const SkRasterClip& rc) : fRC(rc), fDeferredCount(0) {}
     };
 
-    enum {
-        ELEM_COUNT = 16,
-        PTR_COUNT = ELEM_COUNT * sizeof(Rec) / sizeof(void*)
-    };
-    void*           fStorage[PTR_COUNT];
-    SkTStack<Rec>   fStack;
-    SkIRect         fRootBounds;
-    bool            fDisableAA;
+    SkTBlockList<Rec, 16> fStack;
+    SkIRect fRootBounds;
+    bool fDisableAA;
+    SkDEBUGCODE(int fCounter = 0);
 
     SkRasterClip& writable_rc() {
-        SkASSERT(fStack.top().fDeferredCount >= 0);
-        if (fStack.top().fDeferredCount > 0) {
-            fStack.top().fDeferredCount -= 1;
-            fStack.push(fStack.top());
-            fStack.top().fDeferredCount = 0;
+        SkASSERT(fStack.back().fDeferredCount >= 0);
+        if (fStack.back().fDeferredCount > 0) {
+            fStack.back().fDeferredCount -= 1;
+            fStack.emplace_back(fStack.back().fRC);
         }
-        return fStack.top().fRC;
+        return fStack.back().fRC;
     }
 
     bool finalAA(bool aa) const { return aa && !fDisableAA; }
-
-    void trimIfExpanding(SkClipOp op) {
-        if ((int)op > (int)SkClipOp::kIntersect) {
-            Rec& rec = fStack.top();
-            SkASSERT(rec.fDeferredCount == 0);
-            rec.fRC.op(fRootBounds, SkRegion::kIntersect_Op);
-        }
-    }
 };
 
 #endif
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp
index 88fbf03..cdd710e 100644
--- a/tests/AAClipTest.cpp
+++ b/tests/AAClipTest.cpp
@@ -356,7 +356,6 @@
 
 static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
                           size_t count, bool changed) {
-    const SkIRect baseBounds = SkIRect::MakeXYWH(0, 0, 10, 10);
     SkIRect ir = { 0, 0, 10, 10 };
 
     for (size_t i = 0; i < count; ++i) {
@@ -367,11 +366,11 @@
         SkRasterClip rc1(ir);
         SkRasterClip rc2(ir);
 
-        rc0.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, false);
+        rc0.op(r, SkMatrix::I(), SkClipOp::kIntersect, false);
         r.offset(dx[i], 0);
-        rc1.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
+        rc1.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
         r.offset(-2*dx[i], 0);
-        rc2.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
+        rc2.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
 
         REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
         REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
@@ -417,7 +416,7 @@
     SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000));
     SkPath path;
     path.addCircle(50, 50, 50);
-    rc.op(path, SkMatrix::I(), rc.getBounds(), SkRegion::kIntersect_Op, true);
+    rc.op(path, SkMatrix::I(), SkClipOp::kIntersect, true);
 }
 
 static void test_huge(skiatest::Reporter* reporter) {