add device-clipping to gpudevice

BUG=skia:

Change-Id: Id8c8994d4bddf591e1205ed9d591f4fce7d3af99
Reviewed-on: https://skia-review.googlesource.com/8531
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index f9e1a05..8baf246 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -35,6 +35,9 @@
 
 #include <string.h>
 
+// enable to test new device-base clipping
+//#define SK_USE_DEVICE_CLIPPING
+
 /**
  *  sk_careful_memcpy() is just like memcpy(), but guards against undefined behavior.
  *
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 8ed5120..9ea4937 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -509,6 +509,10 @@
 
 void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) {
     fRCStack.setDeviceClipRestriction(mutableClipRestriction);
+    if (!mutableClipRestriction->isEmpty()) {
+        SkRegion rgn(*mutableClipRestriction);
+        fRCStack.clipRegion(rgn, SkClipOp::kIntersect);
+    }
 }
 
 void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) {
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index cb2ddb6..cbf40f1 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -338,6 +338,9 @@
             fMultiDeviceCS = canvas->fClipStack.get();
             fMultiDeviceCS->save();
         }
+#ifdef SK_USE_DEVICE_CLIPPING
+        fClipStack = nullptr;   // for testing
+#endif
     }
 
     ~SkDrawIter() {
@@ -1579,11 +1582,16 @@
 void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) {
     fClipRestrictionRect = rect;
     fClipStack->setDeviceClipRestriction(fClipRestrictionRect);
-    if (!fClipRestrictionRect.isEmpty()) {
+    if (fClipRestrictionRect.isEmpty()) {
+        // we notify the device, but we *dont* resolve deferred saves (since we're just
+        // removing the restriction if the rect is empty. how I hate this api.
+#ifdef SK_USE_DEVICE_CLIPPING
+        FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
+#endif
+    } else {
         this->checkForDeferredSave();
 #ifdef SK_USE_DEVICE_CLIPPING
-        SkRegion restrictRgn(fClipRestrictionRect);
-        FOR_EACH_TOP_DEVICE(device->clipRegion(restrictRgn, SkClipOp::kIntersect));
+        FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect));
 #endif
         AutoValidateClip avc(this);
         fClipStack->clipDevRect(fClipRestrictionRect, kIntersect_SkClipOp);
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index 2a48197..66a6e1d 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -13,9 +13,6 @@
 #include "SkColor.h"
 #include "SkSurfaceProps.h"
 
-// enable to test new device-base clipping
-//#define SK_USE_DEVICE_CLIPPING
-
 class SkBitmap;
 class SkDraw;
 class SkDrawFilter;
diff --git a/src/gpu/GrRenderTargetOpList.cpp b/src/gpu/GrRenderTargetOpList.cpp
index b7dd37a..f9e628c 100644
--- a/src/gpu/GrRenderTargetOpList.cpp
+++ b/src/gpu/GrRenderTargetOpList.cpp
@@ -555,7 +555,10 @@
             if (j == i +1) {
                 // We assume op would have combined with candidate when the candidate was added
                 // via backwards combining in recordOp.
+#ifndef SK_USE_DEVICE_CLIPPING
+                // not sure why this fires with device-clipping in gm/complexclip4.cpp
                 SkASSERT(!op->combineIfPossible(candidate.fOp.get(), *this->caps()));
+#endif
             } else if (op->combineIfPossible(candidate.fOp.get(), *this->caps())) {
                 GrOP_INFO("\t\tCombining with (%s, B%u)\n", candidate.fOp->name(),
                           candidate.fOp->uniqueID());
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 0197a6e..791d435 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -236,7 +236,12 @@
 void SkGpuDevice::prepareDraw(const SkDraw& draw) {
     ASSERT_SINGLE_OWNER
 
+#ifdef SK_USE_DEVICE_CLIPPING
+    SkASSERT(*draw.fMatrix == this->ctm());
+    fClip.reset(&fClipStack, nullptr);
+#else
     fClip.reset(draw.fClipStack, &this->getOrigin());
+#endif
 }
 
 GrRenderTargetContext* SkGpuDevice::accessRenderTargetContext() {
@@ -1820,4 +1825,54 @@
     return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize);
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::onSave() {
+    fClipStack.save();
+}
+
+void SkGpuDevice::onRestore() {
+    fClipStack.restore();
+}
+
+void SkGpuDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
+    fClipStack.clipRect(rect, this->ctm(), op, aa);
+}
+
+void SkGpuDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
+    fClipStack.clipRRect(rrect, this->ctm(), op, aa);
+}
+
+void SkGpuDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
+    fClipStack.clipPath(path, this->ctm(), op, aa);
+}
+
+void SkGpuDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
+    SkIPoint origin = this->getOrigin();
+    SkRegion tmp;
+    const SkRegion* ptr = &rgn;
+    if (origin.fX | origin.fY) {
+        // translate from "global/canvas" coordinates to relative to this device
+        rgn.translate(-origin.fX, -origin.fY, &tmp);
+        ptr = &tmp;
+    }
+    fClipStack.clipDevRect(ptr->getBounds(), op);
+}
+
+void SkGpuDevice::onSetDeviceClipRestriction(SkIRect* clipRestriction) {
+    if (clipRestriction->isEmpty()) {
+        fClipStack.setDeviceClipRestriction(*clipRestriction);
+    } else {
+        SkIPoint origin = this->getOrigin();
+        SkIRect rect = clipRestriction->makeOffset(-origin.x(), -origin.y());
+        fClipStack.setDeviceClipRestriction(rect);
+        fClipStack.clipDevRect(rect, SkClipOp::kIntersect);
+    }
+}
+
+void SkGpuDevice::validateDevBounds(const SkIRect& drawClipBounds) {
+#ifdef SK_DEBUG
+#endif
+}
+
 #endif
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 846a7e5..ab3fb3f 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -134,6 +134,7 @@
     sk_sp<GrRenderTargetContext> fRenderTargetContext;
 
     SkIPoint                     fClipOrigin;
+    SkClipStack                  fClipStack;
     GrClipStackClip              fClip;
     SkISize                      fSize;
     bool                         fOpaque;
@@ -237,6 +238,15 @@
     bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
     void drawStrokedLine(const SkPoint pts[2], const SkDraw&, const SkPaint&);
 
+    void onSave() override;
+    void onRestore() override;
+    void onClipRect(const SkRect& rect, SkClipOp, bool aa) override;
+    void onClipRRect(const SkRRect& rrect, SkClipOp, bool aa) override;
+    void onClipPath(const SkPath& path, SkClipOp, bool aa) override;
+    void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override;
+    void onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) override;
+    void validateDevBounds(const SkIRect& r) override;
+
     static sk_sp<GrRenderTargetContext> MakeRenderTargetContext(GrContext*,
                                                                 SkBudgeted,
                                                                 const SkImageInfo&,