use common intermediate device class for clipstack management

BUG=skia:6214

Change-Id: I64b849ad7c8dafe423e24e6fccfb3f0c1d096ab0
Reviewed-on: https://skia-review.googlesource.com/8669
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/gn/core.gni b/gn/core.gni
index bc6035b..7097983 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -69,6 +69,8 @@
   "$_src/core/SkChunkAlloc.cpp",
   "$_src/core/SkClipStack.cpp",
   "$_src/core/SkClipStack.h",
+  "$_src/core/SkClipStackDevice.cpp",
+  "$_src/core/SkClipStackDevice.h",
   "$_src/core/SkColor.cpp",
   "$_src/core/SkColorFilter.cpp",
   "$_src/core/SkColorFilterShader.cpp",
diff --git a/src/core/SkClipStackDevice.cpp b/src/core/SkClipStackDevice.cpp
new file mode 100644
index 0000000..e1014e9
--- /dev/null
+++ b/src/core/SkClipStackDevice.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkClipStackDevice.h"
+
+void SkClipStackDevice::onSave() {
+    fClipStack.save();
+}
+
+void SkClipStackDevice::onRestore() {
+    fClipStack.restore();
+}
+
+void SkClipStackDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
+    fClipStack.clipRect(rect, this->ctm(), op, aa);
+}
+
+void SkClipStackDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
+    fClipStack.clipRRect(rrect, this->ctm(), op, aa);
+}
+
+void SkClipStackDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
+    fClipStack.clipPath(path, this->ctm(), op, aa);
+}
+
+void SkClipStackDevice::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 SkClipStackDevice::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);
+    }
+}
diff --git a/src/core/SkClipStackDevice.h b/src/core/SkClipStackDevice.h
new file mode 100644
index 0000000..ef18a00
--- /dev/null
+++ b/src/core/SkClipStackDevice.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkClipStackDevice_DEFINED
+#define SkClipStackDevice_DEFINED
+
+#include "SkClipStack.h"
+#include "SkDevice.h"
+
+class SK_API SkClipStackDevice : public SkBaseDevice {
+public:
+    SkClipStackDevice(const SkImageInfo& info, const SkSurfaceProps& props)
+        : SkBaseDevice(info, props)
+    {}
+
+    const SkClipStack& cs() const { return fClipStack; }
+
+protected:
+    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;
+
+private:
+    SkClipStack fClipStack;
+
+    typedef SkBaseDevice INHERITED;
+};
+
+#endif
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index ad9df5b..cd3bf50 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -35,8 +35,6 @@
     fCTM.reset();
 }
 
-SkBaseDevice::~SkBaseDevice() {}
-
 void SkBaseDevice::setOrigin(const SkMatrix& globalCTM, int x, int y) {
     fOrigin.set(x, y);
     fCTM = globalCTM;
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index 66a6e1d..cd0e1d0 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -26,11 +26,7 @@
 
 class SK_API SkBaseDevice : public SkRefCnt {
 public:
-    /**
-     *  Construct a new device.
-    */
-    explicit SkBaseDevice(const SkImageInfo&, const SkSurfaceProps&);
-    virtual ~SkBaseDevice();
+    SkBaseDevice(const SkImageInfo&, const SkSurfaceProps&);
 
     /**
      *  Return ImageInfo for this device. If the canvas is not backed by pixels
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 791d435..6ab2d60 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -238,7 +238,7 @@
 
 #ifdef SK_USE_DEVICE_CLIPPING
     SkASSERT(*draw.fMatrix == this->ctm());
-    fClip.reset(&fClipStack, nullptr);
+    fClip.reset(&this->cs(), nullptr);
 #else
     fClip.reset(draw.fClipStack, &this->getOrigin());
 #endif
@@ -1825,54 +1825,4 @@
     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 ab3fb3f..31ade05 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -11,7 +11,7 @@
 #include "SkGr.h"
 #include "SkGrPriv.h"
 #include "SkBitmap.h"
-#include "SkDevice.h"
+#include "SkClipStackDevice.h"
 #include "SkPicture.h"
 #include "SkRegion.h"
 #include "SkSurface.h"
@@ -31,7 +31,7 @@
  *  Subclass of SkBaseDevice, which directs all drawing to the GrGpu owned by the
  *  canvas.
  */
-class SK_API SkGpuDevice : public SkBaseDevice {
+class SK_API SkGpuDevice : public SkClipStackDevice {
 public:
     enum InitContents {
         kClear_InitContents,
@@ -134,7 +134,6 @@
     sk_sp<GrRenderTargetContext> fRenderTargetContext;
 
     SkIPoint                     fClipOrigin;
-    SkClipStack                  fClipStack;
     GrClipStackClip              fClip;
     SkISize                      fSize;
     bool                         fOpaque;
@@ -238,15 +237,6 @@
     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&,
@@ -256,7 +246,7 @@
 
     friend class GrAtlasTextContext;
     friend class SkSurface_Gpu;      // for access to surfaceProps
-    typedef SkBaseDevice INHERITED;
+    typedef SkClipStackDevice INHERITED;
 };
 
 #endif
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 06679da..261ad22 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -274,6 +274,21 @@
     uint32_t fImageCount;
 };
 
+struct SkSVGDevice::MxCp {
+    const SkMatrix* fMatrix;
+    const SkClipStack*  fClipStack;
+
+    MxCp(SkSVGDevice* device, const SkDraw& draw) {
+#ifdef SK_USE_DEVICE_CLIPPING
+        fMatrix = &device->ctm();
+        fClipStack = &device->cs();
+#else
+        fMatrix = draw.fMatrix;
+        fClipStack = draw.fClipStack;
+#endif
+    }
+};
+
 class SkSVGDevice::AutoElement : ::SkNoncopyable {
 public:
     AutoElement(const char name[], SkXMLWriter* writer)
@@ -283,11 +298,11 @@
     }
 
     AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket,
-                const SkDraw& draw, const SkPaint& paint)
+                const MxCp& mc, const SkPaint& paint)
         : fWriter(writer)
         , fResourceBucket(bucket) {
 
-        Resources res = this->addResources(draw, paint);
+        Resources res = this->addResources(mc, paint);
         if (!res.fClip.isEmpty()) {
             // The clip is in device space. Apply it via a <g> wrapper to avoid local transform
             // interference.
@@ -299,8 +314,8 @@
 
         this->addPaint(paint, res);
 
-        if (!draw.fMatrix->isIdentity()) {
-            this->addAttribute("transform", svg_transform(*draw.fMatrix));
+        if (!mc.fMatrix->isIdentity()) {
+            this->addAttribute("transform", svg_transform(*mc.fMatrix));
         }
     }
 
@@ -333,8 +348,8 @@
     void addTextAttributes(const SkPaint&);
 
 private:
-    Resources addResources(const SkDraw& draw, const SkPaint& paint);
-    void addClipResources(const SkDraw& draw, Resources* resources);
+    Resources addResources(const MxCp&, const SkPaint& paint);
+    void addClipResources(const MxCp&, Resources* resources);
     void addShaderResources(const SkPaint& paint, Resources* resources);
 
     void addPaint(const SkPaint& paint, const Resources& resources);
@@ -391,18 +406,18 @@
     }
 }
 
-Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) {
+Resources SkSVGDevice::AutoElement::addResources(const MxCp& mc, const SkPaint& paint) {
     Resources resources(paint);
 
     // FIXME: this is a weak heuristic and we end up with LOTS of redundant clips.
-    bool hasClip   = !draw.fClipStack->isWideOpen();
+    bool hasClip   = !mc.fClipStack->isWideOpen();
     bool hasShader = SkToBool(paint.getShader());
 
     if (hasClip || hasShader) {
         AutoElement defs("defs", fWriter);
 
         if (hasClip) {
-            this->addClipResources(draw, &resources);
+            this->addClipResources(mc, &resources);
         }
 
         if (hasShader) {
@@ -438,11 +453,11 @@
     resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str());
 }
 
-void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* resources) {
-    SkASSERT(!draw.fClipStack->isWideOpen());
+void SkSVGDevice::AutoElement::addClipResources(const MxCp& mc, Resources* resources) {
+    SkASSERT(!mc.fClipStack->isWideOpen());
 
     SkPath clipPath;
-    (void) draw.fClipStack->asPath(&clipPath);
+    (void) mc.fClipStack->asPath(&clipPath);
 
     SkString clipID = fResourceBucket->addClip();
     const char* clipRule = clipPath.getFillType() == SkPath::kEvenOdd_FillType ?
@@ -592,7 +607,7 @@
 }
 
 void SkSVGDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
-    AutoElement rect("rect", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement rect("rect", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()),
                                           SkIntToScalar(this->height())));
 }
@@ -612,7 +627,7 @@
                 path.rewind();
                 path.moveTo(pts[i]);
                 path.lineTo(pts[i+1]);
-                AutoElement elem("path", fWriter, fResourceBucket.get(), draw, paint);
+                AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
                 elem.addPathAttributes(path);
             }
             break;
@@ -620,7 +635,7 @@
             if (count > 1) {
                 path.addPoly(pts, SkToInt(count), false);
                 path.moveTo(pts[0]);
-                AutoElement elem("path", fWriter, fResourceBucket.get(), draw, paint);
+                AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
                 elem.addPathAttributes(path);
             }
             break;
@@ -628,12 +643,12 @@
 }
 
 void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
-    AutoElement rect("rect", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement rect("rect", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     rect.addRectAttributes(r);
 }
 
 void SkSVGDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
-    AutoElement ellipse("ellipse", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement ellipse("ellipse", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     ellipse.addAttribute("cx", oval.centerX());
     ellipse.addAttribute("cy", oval.centerY());
     ellipse.addAttribute("rx", oval.width() / 2);
@@ -644,13 +659,13 @@
     SkPath path;
     path.addRRect(rr);
 
-    AutoElement elem("path", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     elem.addPathAttributes(path);
 }
 
 void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint,
                            const SkMatrix* prePathMatrix, bool pathIsMutable) {
-    AutoElement elem("path", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     elem.addPathAttributes(path);
 
     // TODO: inverse fill types?
@@ -664,8 +679,7 @@
     return SkEncodeImage(&buf, src, SkEncodedImageFormat::kPNG, 80) ? buf.detachAsData() : nullptr;
 }
 
-void SkSVGDevice::drawBitmapCommon(const SkDraw& draw, const SkBitmap& bm,
-                                   const SkPaint& paint) {
+void SkSVGDevice::drawBitmapCommon(const MxCp& mc, const SkBitmap& bm, const SkPaint& paint) {
     sk_sp<SkData> pngData = encode(bm);
     if (!pngData) {
         return;
@@ -691,57 +705,57 @@
     }
 
     {
-        AutoElement imageUse("use", fWriter, fResourceBucket.get(), draw, paint);
+        AutoElement imageUse("use", fWriter, fResourceBucket.get(), mc, paint);
         imageUse.addAttribute("xlink:href", SkStringPrintf("#%s", imageID.c_str()));
     }
 }
 
 void SkSVGDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
                              const SkMatrix& matrix, const SkPaint& paint) {
-    SkMatrix adjustedMatrix = *draw.fMatrix;
+    MxCp mc(this, draw);
+    SkMatrix adjustedMatrix = *mc.fMatrix;
     adjustedMatrix.preConcat(matrix);
-    SkDraw adjustedDraw(draw);
-    adjustedDraw.fMatrix = &adjustedMatrix;
+    mc.fMatrix = &adjustedMatrix;
 
-    drawBitmapCommon(adjustedDraw, bitmap, paint);
+    drawBitmapCommon(mc, bitmap, paint);
 }
 
 void SkSVGDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                              int x, int y, const SkPaint& paint) {
-    SkMatrix adjustedMatrix = *draw.fMatrix;
+    MxCp mc(this, draw);
+    SkMatrix adjustedMatrix = *mc.fMatrix;
     adjustedMatrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y));
-    SkDraw adjustedDraw(draw);
-    adjustedDraw.fMatrix = &adjustedMatrix;
+    mc.fMatrix = &adjustedMatrix;
 
-    drawBitmapCommon(adjustedDraw, bitmap, paint);
+    drawBitmapCommon(mc, bitmap, paint);
 }
 
 void SkSVGDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bm, const SkRect* srcOrNull,
                                  const SkRect& dst, const SkPaint& paint,
                                  SkCanvas::SrcRectConstraint) {
+    MxCp mc(this, draw);
+
+    SkClipStack adjustedClipStack;
+    if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) {
+        adjustedClipStack = *mc.fClipStack;
+        adjustedClipStack.clipRect(dst, *mc.fMatrix, kIntersect_SkClipOp,
+                                   paint.isAntiAlias());
+        mc.fClipStack = &adjustedClipStack;
+    }
+
     SkMatrix adjustedMatrix;
     adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds()),
                                  dst,
                                  SkMatrix::kFill_ScaleToFit);
-    adjustedMatrix.postConcat(*draw.fMatrix);
+    adjustedMatrix.postConcat(*mc.fMatrix);
+    mc.fMatrix = &adjustedMatrix;
 
-    SkDraw adjustedDraw(draw);
-    adjustedDraw.fMatrix = &adjustedMatrix;
-
-    SkClipStack adjustedClipStack;
-    if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) {
-        adjustedClipStack = *draw.fClipStack;
-        adjustedClipStack.clipRect(dst, *draw.fMatrix, kIntersect_SkClipOp,
-                                   paint.isAntiAlias());
-        adjustedDraw.fClipStack = &adjustedClipStack;
-    }
-
-    drawBitmapCommon(adjustedDraw, bm, paint);
+    drawBitmapCommon(mc, bm, paint);
 }
 
 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
                            SkScalar x, SkScalar y, const SkPaint& paint) {
-    AutoElement elem("text", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement elem("text", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     elem.addTextAttributes(paint);
 
     SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0);
@@ -755,7 +769,7 @@
                               const SkPaint& paint) {
     SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
 
-    AutoElement elem("text", fWriter, fResourceBucket.get(), draw, paint);
+    AutoElement elem("text", fWriter, fResourceBucket.get(), MxCp(this, draw), paint);
     elem.addTextAttributes(paint);
 
     SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos);
diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h
index 5de7735..106537e 100644
--- a/src/svg/SkSVGDevice.h
+++ b/src/svg/SkSVGDevice.h
@@ -8,12 +8,12 @@
 #ifndef SkSVGDevice_DEFINED
 #define SkSVGDevice_DEFINED
 
-#include "SkDevice.h"
+#include "SkClipStackDevice.h"
 #include "SkTemplates.h"
 
 class SkXMLWriter;
 
-class SkSVGDevice : public SkBaseDevice {
+class SkSVGDevice : public SkClipStackDevice {
 public:
     static SkBaseDevice* Create(const SkISize& size, SkXMLWriter* writer);
 
@@ -58,7 +58,8 @@
     SkSVGDevice(const SkISize& size, SkXMLWriter* writer);
     virtual ~SkSVGDevice();
 
-    void drawBitmapCommon(const SkDraw& draw, const SkBitmap& bm, const SkPaint& paint);
+    struct MxCp;
+    void drawBitmapCommon(const MxCp&, const SkBitmap& bm, const SkPaint& paint);
 
     class AutoElement;
     class ResourceBucket;
@@ -67,7 +68,7 @@
     std::unique_ptr<AutoElement>    fRootElement;
     std::unique_ptr<ResourceBucket> fResourceBucket;
 
-    typedef SkBaseDevice INHERITED;
+    typedef SkClipStackDevice INHERITED;
 };
 
 #endif // SkSVGDevice_DEFINED