[skottie] Layer clip support

TBR=

Change-Id: Ibf65efc69031f8f6e19f4f28cccab29c357e704d
Reviewed-on: https://skia-review.googlesource.com/101540
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 9eb4022..5514093 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1400,6 +1400,7 @@
       "experimental/sksg/SkSGPaintNode.cpp",
       "experimental/sksg/SkSGRenderNode.cpp",
       "experimental/sksg/SkSGScene.cpp",
+      "experimental/sksg/effects/SkSGClipEffect.cpp",
       "experimental/sksg/effects/SkSGMaskEffect.cpp",
       "experimental/sksg/effects/SkSGOpacityEffect.cpp",
       "experimental/sksg/effects/SkSGTransform.cpp",
diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp
index 532cc8d..1ac8f7d 100644
--- a/experimental/skottie/Skottie.cpp
+++ b/experimental/skottie/Skottie.cpp
@@ -19,6 +19,7 @@
 #include "SkPaint.h"
 #include "SkParse.h"
 #include "SkPoint.h"
+#include "SkSGClipEffect.h"
 #include "SkSGColor.h"
 #include "SkSGDraw.h"
 #include "SkSGGeometryTransform.h"
@@ -944,12 +945,23 @@
 
     // Layer content.
     auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale);
+
+    // Clip layers with explicit dimensions.
+    float w, h;
+    if (Parse(jlayer["w"], &w) && Parse(jlayer["h"], &h)) {
+        layer = sksg::ClipEffect::Make(std::move(layer),
+                                       sksg::Rect::Make(SkRect::MakeWH(w, h)),
+                                       true);
+    }
+
     // Optional layer mask.
     layer = AttachMask(jlayer["masksProperties"], &local_ctx, std::move(layer));
+
     // Optional layer transform.
     if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
         layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
     }
+
     // Optional layer opacity.
     layer = AttachOpacity(jlayer["ks"], &local_ctx, std::move(layer));
 
diff --git a/experimental/sksg/SkSGGeometryNode.cpp b/experimental/sksg/SkSGGeometryNode.cpp
index 98022ad..6b78c48 100644
--- a/experimental/sksg/SkSGGeometryNode.cpp
+++ b/experimental/sksg/SkSGGeometryNode.cpp
@@ -14,6 +14,11 @@
 // Geometry nodes don't generate damage on their own, but via their aggregation ancestor Draw nodes.
 GeometryNode::GeometryNode() : INHERITED(kBubbleDamage_Trait) {}
 
+void GeometryNode::clip(SkCanvas* canvas, bool aa) const {
+    SkASSERT(!this->hasInval());
+    this->onClip(canvas, aa);
+}
+
 void GeometryNode::draw(SkCanvas* canvas, const SkPaint& paint) const {
     SkASSERT(!this->hasInval());
     this->onDraw(canvas, paint);
diff --git a/experimental/sksg/SkSGGeometryNode.h b/experimental/sksg/SkSGGeometryNode.h
index b436d27..7ce3aa9 100644
--- a/experimental/sksg/SkSGGeometryNode.h
+++ b/experimental/sksg/SkSGGeometryNode.h
@@ -24,6 +24,7 @@
  */
 class GeometryNode : public Node {
 public:
+    void clip(SkCanvas*, bool antiAlias) const;
     void draw(SkCanvas*, const SkPaint&) const;
 
     SkPath asPath() const;
@@ -31,6 +32,8 @@
 protected:
     GeometryNode();
 
+    virtual void onClip(SkCanvas*, bool antiAlias) const = 0;
+
     virtual void onDraw(SkCanvas*, const SkPaint&) const = 0;
 
     virtual SkPath onAsPath() const = 0;
diff --git a/experimental/sksg/effects/SkSGClipEffect.cpp b/experimental/sksg/effects/SkSGClipEffect.cpp
new file mode 100644
index 0000000..b2d68fc
--- /dev/null
+++ b/experimental/sksg/effects/SkSGClipEffect.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSGClipEffect.h"
+
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkSGGeometryNode.h"
+
+namespace sksg {
+
+ClipEffect::ClipEffect(sk_sp<RenderNode> child, sk_sp<GeometryNode> clip, bool aa)
+    : INHERITED(std::move(child))
+    , fClipNode(std::move(clip))
+    , fAntiAlias(aa) {
+    this->observeInval(fClipNode);
+}
+
+ClipEffect::~ClipEffect() {
+    this->unobserveInval(fClipNode);
+}
+
+void ClipEffect::onRender(SkCanvas* canvas) const {
+    if (this->bounds().isEmpty())
+        return;
+
+    SkAutoCanvasRestore acr(canvas, !fNoop);
+    if (!fNoop) {
+        fClipNode->clip(canvas, fAntiAlias);
+    }
+
+    this->INHERITED::onRender(canvas);
+}
+
+SkRect ClipEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+    SkASSERT(this->hasInval());
+
+    const auto clipBounds = fClipNode->revalidate(ic, ctm);
+    auto childBounds = this->INHERITED::onRevalidate(ic, ctm);
+
+    fNoop = fClipNode->asPath().conservativelyContainsRect(childBounds);
+
+    return childBounds.intersect(clipBounds) ? childBounds : SkRect::MakeEmpty();
+}
+
+} // namespace sksg
diff --git a/experimental/sksg/effects/SkSGClipEffect.h b/experimental/sksg/effects/SkSGClipEffect.h
new file mode 100644
index 0000000..674edb2
--- /dev/null
+++ b/experimental/sksg/effects/SkSGClipEffect.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSGClipEffect_DEFINED
+#define SkSGClipEffect_DEFINED
+
+#include "SkSGEffectNode.h"
+
+namespace sksg {
+
+class GeometryNode;
+
+/**
+ * Concrete Effect node, applying a clip to its descendants.
+ *
+ */
+class ClipEffect final : public EffectNode {
+public:
+    static sk_sp<ClipEffect> Make(sk_sp<RenderNode> child, sk_sp<GeometryNode> clip,
+                                  bool aa = false) {
+        return (child && clip)
+            ? sk_sp<ClipEffect>(new ClipEffect(std::move(child), std::move(clip), aa))
+            : nullptr;
+    }
+
+    ~ClipEffect() override;
+
+protected:
+    ClipEffect(sk_sp<RenderNode>, sk_sp<GeometryNode>, bool aa);
+
+    void onRender(SkCanvas*) const override;
+
+    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+
+private:
+    const sk_sp<GeometryNode> fClipNode;
+    const bool                fAntiAlias;
+
+    bool                      fNoop = false;
+
+    typedef EffectNode INHERITED;
+};
+
+} // namespace sksg
+
+#endif // SkSGClipEffect_DEFINED
diff --git a/experimental/sksg/geometry/SkSGGeometryTransform.cpp b/experimental/sksg/geometry/SkSGGeometryTransform.cpp
index 91367d4..5b366b9 100644
--- a/experimental/sksg/geometry/SkSGGeometryTransform.cpp
+++ b/experimental/sksg/geometry/SkSGGeometryTransform.cpp
@@ -23,6 +23,14 @@
     this->unobserveInval(fMatrix);
 }
 
+void GeometryTransform::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipPath(fTransformed, SkClipOp::kIntersect, antiAlias);
+}
+
+void GeometryTransform::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+    canvas->drawPath(fTransformed, paint);
+}
+
 SkRect GeometryTransform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
     SkASSERT(this->hasInval());
 
@@ -42,8 +50,4 @@
     return fTransformed;
 }
 
-void GeometryTransform::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
-    canvas->drawPath(fTransformed, paint);
-}
-
 } // namespace sksg
diff --git a/experimental/sksg/geometry/SkSGGeometryTransform.h b/experimental/sksg/geometry/SkSGGeometryTransform.h
index 31a3371..ea990c6 100644
--- a/experimental/sksg/geometry/SkSGGeometryTransform.h
+++ b/experimental/sksg/geometry/SkSGGeometryTransform.h
@@ -37,9 +37,11 @@
     const sk_sp<Matrix>& getMatrix() const { return fMatrix; }
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
+    void onDraw(SkCanvas*, const SkPaint&) const override;
+
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
     SkPath onAsPath() const override;
-    void   onDraw(SkCanvas*, const SkPaint&) const override;
 
 private:
     GeometryTransform(sk_sp<GeometryNode>, sk_sp<Matrix>);
diff --git a/experimental/sksg/geometry/SkSGMerge.cpp b/experimental/sksg/geometry/SkSGMerge.cpp
index 49e7804..be1ff41 100644
--- a/experimental/sksg/geometry/SkSGMerge.cpp
+++ b/experimental/sksg/geometry/SkSGMerge.cpp
@@ -26,6 +26,10 @@
     }
 }
 
+void Merge::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipPath(fMerged, SkClipOp::kIntersect, antiAlias);
+}
+
 void Merge::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
     canvas->drawPath(fMerged, paint);
 }
diff --git a/experimental/sksg/geometry/SkSGMerge.h b/experimental/sksg/geometry/SkSGMerge.h
index b0cb40d..69f824e 100644
--- a/experimental/sksg/geometry/SkSGMerge.h
+++ b/experimental/sksg/geometry/SkSGMerge.h
@@ -43,6 +43,7 @@
     ~Merge() override;
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
diff --git a/experimental/sksg/geometry/SkSGPath.cpp b/experimental/sksg/geometry/SkSGPath.cpp
index 2b7dc94..8d5a253 100644
--- a/experimental/sksg/geometry/SkSGPath.cpp
+++ b/experimental/sksg/geometry/SkSGPath.cpp
@@ -14,6 +14,10 @@
 
 Path::Path(const SkPath& path) : fPath(path) {}
 
+void Path::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipPath(fPath, SkClipOp::kIntersect, antiAlias);
+}
+
 void Path::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
     canvas->drawPath(fPath, paint);
 }
diff --git a/experimental/sksg/geometry/SkSGPath.h b/experimental/sksg/geometry/SkSGPath.h
index 18caa10..6dd2593 100644
--- a/experimental/sksg/geometry/SkSGPath.h
+++ b/experimental/sksg/geometry/SkSGPath.h
@@ -28,6 +28,7 @@
     SG_ATTRIBUTE(Path, SkPath, fPath)
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
diff --git a/experimental/sksg/geometry/SkSGRect.cpp b/experimental/sksg/geometry/SkSGRect.cpp
index 532a604..16f0a6f 100644
--- a/experimental/sksg/geometry/SkSGRect.cpp
+++ b/experimental/sksg/geometry/SkSGRect.cpp
@@ -15,6 +15,10 @@
 
 Rect::Rect(const SkRect& rect) : fRect(rect) {}
 
+void Rect::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipRect(fRect, SkClipOp::kIntersect, antiAlias);
+}
+
 void Rect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
     canvas->drawRect(fRect, paint);
 }
@@ -33,6 +37,10 @@
 
 RRect::RRect(const SkRRect& rr) : fRRect(rr) {}
 
+void RRect::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipRRect(fRRect, SkClipOp::kIntersect, antiAlias);
+}
+
 void RRect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
     canvas->drawRRect(fRRect, paint);
 }
diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h
index ad27910..a99c76a 100644
--- a/experimental/sksg/geometry/SkSGRect.h
+++ b/experimental/sksg/geometry/SkSGRect.h
@@ -32,6 +32,7 @@
     SG_ATTRIBUTE(B, SkScalar, fRect.fBottom)
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
@@ -54,6 +55,7 @@
     SG_ATTRIBUTE(RRect, SkRRect, fRRect)
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
diff --git a/experimental/sksg/geometry/SkSGTrimEffect.cpp b/experimental/sksg/geometry/SkSGTrimEffect.cpp
index afcc7e4..0b664fd 100644
--- a/experimental/sksg/geometry/SkSGTrimEffect.cpp
+++ b/experimental/sksg/geometry/SkSGTrimEffect.cpp
@@ -22,6 +22,10 @@
     this->unobserveInval(fChild);
 }
 
+void TrimEffect::onClip(SkCanvas* canvas, bool antiAlias) const {
+    canvas->clipPath(fChild->asPath(), SkClipOp::kIntersect, antiAlias);
+}
+
 // TODO
 //   This is a quick hack to get something on the screen.  What we really want here is to apply
 //   the geometry transformation and cache the result on revalidation. Or an SkTrimPathEffect.
diff --git a/experimental/sksg/geometry/SkSGTrimEffect.h b/experimental/sksg/geometry/SkSGTrimEffect.h
index 77da3ab..860cfaf 100644
--- a/experimental/sksg/geometry/SkSGTrimEffect.h
+++ b/experimental/sksg/geometry/SkSGTrimEffect.h
@@ -33,6 +33,7 @@
     SG_ATTRIBUTE(Offset, SkScalar, fOffset)
 
 protected:
+    void onClip(SkCanvas*, bool antiAlias) const override;
     void onDraw(SkCanvas*, const SkPaint&) const override;
 
     SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;