[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;