aboutsummaryrefslogtreecommitdiffhomepage
path: root/modules/sksg/src
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2018-05-25 12:43:51 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-25 17:11:52 +0000
commit3b526b05d652ad6c310d9c636187b20b51c7648c (patch)
tree6c6fa99f2e80db81e9c3f593fe5883aabdaa442b /modules/sksg/src
parent59da548b0c4d4239e0ec1855d3f7f77a2bff4b93 (diff)
"Modularize" SkSG
* relocate all SkSG-related files under modules/sksg/ * fix various tidbits to make non-sksg builds possible * drop obsolete SampleSGInval.cpp Change-Id: I54e6c5bb1a09f45030fa8d607b3eb3f7cba78957 Reviewed-on: https://skia-review.googlesource.com/130025 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Mike Klein <mtklein@google.com>
Diffstat (limited to 'modules/sksg/src')
-rw-r--r--modules/sksg/src/SkSGClipEffect.cpp50
-rw-r--r--modules/sksg/src/SkSGColor.cpp18
-rw-r--r--modules/sksg/src/SkSGDraw.cpp50
-rw-r--r--modules/sksg/src/SkSGEffectNode.cpp31
-rw-r--r--modules/sksg/src/SkSGGeometryNode.cpp32
-rw-r--r--modules/sksg/src/SkSGGeometryTransform.cpp53
-rw-r--r--modules/sksg/src/SkSGGradient.cpp60
-rw-r--r--modules/sksg/src/SkSGGroup.cpp66
-rw-r--r--modules/sksg/src/SkSGImage.cpp29
-rw-r--r--modules/sksg/src/SkSGInvalidationController.cpp32
-rw-r--r--modules/sksg/src/SkSGMaskEffect.cpp54
-rw-r--r--modules/sksg/src/SkSGMerge.cpp84
-rw-r--r--modules/sksg/src/SkSGNode.cpp152
-rw-r--r--modules/sksg/src/SkSGOpacityEffect.cpp42
-rw-r--r--modules/sksg/src/SkSGPaintNode.cpp41
-rw-r--r--modules/sksg/src/SkSGPath.cpp41
-rw-r--r--modules/sksg/src/SkSGPlane.cpp36
-rw-r--r--modules/sksg/src/SkSGRect.cpp60
-rw-r--r--modules/sksg/src/SkSGRenderNode.cpp19
-rw-r--r--modules/sksg/src/SkSGRoundEffect.cpp56
-rw-r--r--modules/sksg/src/SkSGScene.cpp70
-rw-r--r--modules/sksg/src/SkSGText.cpp77
-rw-r--r--modules/sksg/src/SkSGTransform.cpp70
-rw-r--r--modules/sksg/src/SkSGTrimEffect.cpp56
24 files changed, 1279 insertions, 0 deletions
diff --git a/modules/sksg/src/SkSGClipEffect.cpp b/modules/sksg/src/SkSGClipEffect.cpp
new file mode 100644
index 0000000000..b2d68fc8cf
--- /dev/null
+++ b/modules/sksg/src/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/modules/sksg/src/SkSGColor.cpp b/modules/sksg/src/SkSGColor.cpp
new file mode 100644
index 0000000000..d5d4d1ce62
--- /dev/null
+++ b/modules/sksg/src/SkSGColor.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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 "SkSGColor.h"
+
+namespace sksg {
+
+Color::Color(SkColor c) : fColor(c) {}
+
+void Color::onApplyToPaint(SkPaint* paint) const {
+ paint->setColor(fColor);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGDraw.cpp b/modules/sksg/src/SkSGDraw.cpp
new file mode 100644
index 0000000000..b73bf3b577
--- /dev/null
+++ b/modules/sksg/src/SkSGDraw.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "SkSGDraw.h"
+
+#include "SkSGGeometryNode.h"
+#include "SkSGInvalidationController.h"
+#include "SkSGPaintNode.h"
+
+namespace sksg {
+
+Draw::Draw(sk_sp<GeometryNode> geometry, sk_sp<PaintNode> paint)
+ : fGeometry(std::move(geometry))
+ , fPaint(std::move(paint)) {
+ this->observeInval(fGeometry);
+ this->observeInval(fPaint);
+}
+
+Draw::~Draw() {
+ this->unobserveInval(fGeometry);
+ this->unobserveInval(fPaint);
+}
+
+void Draw::onRender(SkCanvas* canvas) const {
+ const auto& paint = fPaint->makePaint();
+ const auto skipDraw = paint.nothingToDraw() ||
+ (paint.getStyle() == SkPaint::kStroke_Style && paint.getStrokeWidth() <= 0);
+
+ if (!skipDraw) {
+ fGeometry->draw(canvas, paint);
+ }
+}
+
+SkRect Draw::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ auto bounds = fGeometry->revalidate(ic, ctm);
+ fPaint->revalidate(ic, ctm);
+
+ const auto& paint = fPaint->makePaint();
+ SkASSERT(paint.canComputeFastBounds());
+
+ return paint.computeFastBounds(bounds, &bounds);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGEffectNode.cpp b/modules/sksg/src/SkSGEffectNode.cpp
new file mode 100644
index 0000000000..70050ccb70
--- /dev/null
+++ b/modules/sksg/src/SkSGEffectNode.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "SkSGEffectNode.h"
+
+namespace sksg {
+
+EffectNode::EffectNode(sk_sp<RenderNode> child)
+ : fChild(std::move(child)) {
+ this->observeInval(fChild);
+}
+
+EffectNode::~EffectNode() {
+ this->unobserveInval(fChild);
+}
+
+void EffectNode::onRender(SkCanvas* canvas) const {
+ fChild->render(canvas);
+}
+
+SkRect EffectNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ return fChild->revalidate(ic, ctm);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGGeometryNode.cpp b/modules/sksg/src/SkSGGeometryNode.cpp
new file mode 100644
index 0000000000..6b78c488b7
--- /dev/null
+++ b/modules/sksg/src/SkSGGeometryNode.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "SkSGGeometryNode.h"
+
+#include "SkPath.h"
+
+namespace sksg {
+
+// 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);
+}
+
+SkPath GeometryNode::asPath() const {
+ SkASSERT(!this->hasInval());
+ return this->onAsPath();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGGeometryTransform.cpp b/modules/sksg/src/SkSGGeometryTransform.cpp
new file mode 100644
index 0000000000..5b366b9620
--- /dev/null
+++ b/modules/sksg/src/SkSGGeometryTransform.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "SkSGGeometryTransform.h"
+
+#include "SkCanvas.h"
+
+namespace sksg {
+
+GeometryTransform::GeometryTransform(sk_sp<GeometryNode> child, sk_sp<Matrix> matrix)
+ : fChild(std::move(child))
+ , fMatrix(std::move(matrix)) {
+ this->observeInval(fChild);
+ this->observeInval(fMatrix);
+}
+
+GeometryTransform::~GeometryTransform() {
+ this->unobserveInval(fChild);
+ 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());
+
+ // We don't care about matrix reval results.
+ fMatrix->revalidate(ic, ctm);
+ const auto& m = fMatrix->getMatrix();
+
+ auto bounds = fChild->revalidate(ic, ctm);
+ fTransformed = fChild->asPath();
+ fTransformed.transform(m);
+
+ m.mapRect(&bounds);
+ return bounds;
+}
+
+SkPath GeometryTransform::onAsPath() const {
+ return fTransformed;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGGradient.cpp b/modules/sksg/src/SkSGGradient.cpp
new file mode 100644
index 0000000000..98e7f395f8
--- /dev/null
+++ b/modules/sksg/src/SkSGGradient.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "SkSGGradient.h"
+
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+
+namespace sksg {
+
+void Gradient::onApplyToPaint(SkPaint* paint) const {
+ if (fColorStops.empty()) {
+ paint->setShader(nullptr);
+ return;
+ }
+
+ std::vector<SkColor> colors;
+ std::vector<SkScalar> positions;
+ colors.reserve(fColorStops.size());
+ positions.reserve(fColorStops.size());
+
+ SkScalar position = 0;
+ for (const auto& stop : fColorStops) {
+ colors.push_back(stop.fColor);
+ position = SkTPin(stop.fPosition, position, 1.0f);
+ positions.push_back(position);
+ }
+
+ // TODO: detect even stop distributions, pass null for positions.
+ paint->setShader(this->onMakeShader(colors, positions));
+}
+
+sk_sp<SkShader> LinearGradient::onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& positions) const {
+ SkASSERT(colors.size() == positions.size());
+
+ const SkPoint pts[] = { fStartPoint, fEndPoint };
+ return SkGradientShader::MakeLinear(pts, colors.data(), positions.data(), colors.size(),
+ this->getTileMode());
+}
+
+sk_sp<SkShader> RadialGradient::onMakeShader(const std::vector<SkColor>& colors,
+ const std::vector<SkScalar>& positions) const {
+ SkASSERT(colors.size() == positions.size());
+
+ return (fStartRadius <= 0 && fStartCenter == fEndCenter)
+ ? SkGradientShader::MakeRadial(fEndCenter, fEndRadius,
+ colors.data(), positions.data(), colors.size(),
+ this->getTileMode())
+ : SkGradientShader::MakeTwoPointConical(fStartCenter, fStartRadius,
+ fEndCenter, fEndRadius,
+ colors.data(), positions.data(), colors.size(),
+ this->getTileMode());
+}
+
+} //namespace sksg
diff --git a/modules/sksg/src/SkSGGroup.cpp b/modules/sksg/src/SkSGGroup.cpp
new file mode 100644
index 0000000000..aeccf233f7
--- /dev/null
+++ b/modules/sksg/src/SkSGGroup.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "SkSGGroup.h"
+
+namespace sksg {
+
+Group::Group() {}
+
+Group::~Group() {
+ for (const auto& child : fChildren) {
+ this->unobserveInval(child);
+ }
+}
+
+void Group::addChild(sk_sp<RenderNode> node) {
+ // should we allow duplicates?
+ for (const auto& child : fChildren) {
+ if (child == node) {
+ return;
+ }
+ }
+
+ this->observeInval(node);
+ fChildren.push_back(std::move(node));
+
+ this->invalidate();
+}
+
+void Group::removeChild(const sk_sp<RenderNode>& node) {
+ int origCount = fChildren.count();
+ for (int i = 0; i < origCount; ++i) {
+ if (fChildren[i] == node) {
+ fChildren.removeShuffle(i);
+ this->unobserveInval(node);
+ break;
+ }
+ }
+ SkASSERT(fChildren.count() == origCount - 1);
+
+ this->invalidate();
+}
+
+void Group::onRender(SkCanvas* canvas) const {
+ for (const auto& child : fChildren) {
+ child->render(canvas);
+ }
+}
+
+SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ SkRect bounds = SkRect::MakeEmpty();
+
+ for (const auto& child : fChildren) {
+ bounds.join(child->revalidate(ic, ctm));
+ }
+
+ return bounds;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGImage.cpp b/modules/sksg/src/SkSGImage.cpp
new file mode 100644
index 0000000000..a0c3a759dc
--- /dev/null
+++ b/modules/sksg/src/SkSGImage.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "SkSGImage.h"
+
+#include "SkCanvas.h"
+#include "SkImage.h"
+
+namespace sksg {
+
+Image::Image(sk_sp<SkImage> image) : fImage(std::move(image)) {}
+
+void Image::onRender(SkCanvas* canvas) const {
+ SkPaint paint;
+ paint.setAntiAlias(fAntiAlias);
+ paint.setFilterQuality(fQuality);
+
+ canvas->drawImage(fImage, 0, 0);
+}
+
+SkRect Image::onRevalidate(InvalidationController*, const SkMatrix& ctm) {
+ return SkRect::Make(fImage->bounds());
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGInvalidationController.cpp b/modules/sksg/src/SkSGInvalidationController.cpp
new file mode 100644
index 0000000000..81a3376bf6
--- /dev/null
+++ b/modules/sksg/src/SkSGInvalidationController.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "SkSGInvalidationController.h"
+
+#include "SkRect.h"
+#include "SkTLazy.h"
+
+namespace sksg {
+
+InvalidationController::InvalidationController() : fBounds(SkRect::MakeEmpty()) {}
+
+void InvalidationController::inval(const SkRect& r, const SkMatrix& ctm) {
+ if (r.isEmpty()) {
+ return;
+ }
+
+ SkTCopyOnFirstWrite<SkRect> rect(r);
+
+ if (!ctm.isIdentity()) {
+ ctm.mapRect(rect.writable());
+ }
+
+ fRects.push(*rect);
+ fBounds.join(*rect);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGMaskEffect.cpp b/modules/sksg/src/SkSGMaskEffect.cpp
new file mode 100644
index 0000000000..16e4c0dd8d
--- /dev/null
+++ b/modules/sksg/src/SkSGMaskEffect.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "SkSGMaskEffect.h"
+
+#include "SkCanvas.h"
+
+namespace sksg {
+
+MaskEffect::MaskEffect(sk_sp<RenderNode> child, sk_sp<RenderNode> mask, Mode mode)
+ : INHERITED(std::move(child))
+ , fMaskNode(std::move(mask))
+ , fMaskMode(mode) {
+ this->observeInval(fMaskNode);
+}
+
+MaskEffect::~MaskEffect() {
+ this->unobserveInval(fMaskNode);
+}
+
+void MaskEffect::onRender(SkCanvas* canvas) const {
+ if (this->bounds().isEmpty())
+ return;
+
+ SkAutoCanvasRestore acr(canvas, false);
+
+ canvas->saveLayer(this->bounds(), nullptr);
+ fMaskNode->render(canvas);
+
+
+ SkPaint p;
+ p.setBlendMode(fMaskMode == Mode::kNormal ? SkBlendMode::kSrcIn : SkBlendMode::kSrcOut);
+ canvas->saveLayer(this->bounds(), &p);
+
+ this->INHERITED::onRender(canvas);
+}
+
+
+SkRect MaskEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ const auto maskBounds = fMaskNode->revalidate(ic, ctm);
+ auto childBounds = this->INHERITED::onRevalidate(ic, ctm);
+
+ return (fMaskMode == Mode::kInvert || childBounds.intersect(maskBounds))
+ ? childBounds
+ : SkRect::MakeEmpty();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGMerge.cpp b/modules/sksg/src/SkSGMerge.cpp
new file mode 100644
index 0000000000..be1ff4123a
--- /dev/null
+++ b/modules/sksg/src/SkSGMerge.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "SkSGMerge.h"
+
+#include "SkCanvas.h"
+#include "SkPathOps.h"
+
+namespace sksg {
+
+Merge::Merge(std::vector<sk_sp<GeometryNode>>&& geos, Mode mode)
+ : fGeos(std::move(geos))
+ , fMode(mode) {
+ for (const auto& geo : fGeos) {
+ this->observeInval(geo);
+ }
+}
+
+Merge::~Merge() {
+ for (const auto& geo : fGeos) {
+ this->unobserveInval(geo);
+ }
+}
+
+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);
+}
+
+SkPath Merge::onAsPath() const {
+ return fMerged;
+}
+
+static SkPathOp mode_to_op(Merge::Mode mode) {
+ switch (mode) {
+ case Merge::Mode::kUnion:
+ return kUnion_SkPathOp;
+ case Merge::Mode::kIntersect:
+ return kIntersect_SkPathOp;
+ case Merge::Mode::kDifference:
+ return kDifference_SkPathOp;
+ case Merge::Mode::kReverseDifference:
+ return kReverseDifference_SkPathOp;
+ case Merge::Mode::kXOR:
+ return kXOR_SkPathOp;
+ default:
+ break;
+ }
+
+ return kUnion_SkPathOp;
+}
+
+SkRect Merge::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ const auto op = mode_to_op(fMode);
+ SkOpBuilder builder;
+
+ fMerged.reset();
+
+ for (const auto& geo : fGeos) {
+ geo->revalidate(ic, ctm);
+ if (fMode == Mode::kMerge) {
+ fMerged.addPath(geo->asPath());
+ } else {
+ builder.add(geo->asPath(), geo == fGeos.front() ? kUnion_SkPathOp : op);
+ }
+ }
+
+ if (fMode != Mode::kMerge) {
+ builder.resolve(&fMerged);
+ }
+
+ return fMerged.computeTightBounds();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGNode.cpp b/modules/sksg/src/SkSGNode.cpp
new file mode 100644
index 0000000000..35b2640dbb
--- /dev/null
+++ b/modules/sksg/src/SkSGNode.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "SkRectPriv.h"
+#include "SkSGNode.h"
+#include "SkSGInvalidationController.h"
+
+namespace sksg {
+
+class Node::ScopedFlag {
+public:
+ ScopedFlag(Node* node, uint32_t flag)
+ : fNode(node)
+ , fFlag(flag)
+ , fWasSet(node->fFlags & flag) {
+ node->fFlags |= flag;
+ }
+ ~ScopedFlag() {
+ if (!fWasSet) {
+ fNode->fFlags &= ~fFlag;;
+ }
+ }
+
+ bool wasSet() const { return fWasSet; }
+
+private:
+ Node* fNode;
+ uint32_t fFlag;
+ bool fWasSet;
+};
+
+#define TRAVERSAL_GUARD \
+ ScopedFlag traversal_guard(this, kInTraversal_Flag); \
+ if (traversal_guard.wasSet()) \
+ return
+
+Node::Node(uint32_t invalTraits)
+ : fInvalObserver(nullptr)
+ , fBounds(SkRectPriv::MakeLargeS32())
+ , fInvalTraits(invalTraits)
+ , fFlags(kInvalidated_Flag) {}
+
+Node::~Node() {
+ if (fFlags & kObserverArray_Flag) {
+ SkASSERT(fInvalObserverArray->isEmpty());
+ delete fInvalObserverArray;
+ } else {
+ SkASSERT(!fInvalObserver);
+ }
+}
+
+void Node::observeInval(const sk_sp<Node>& node) {
+ SkASSERT(node);
+ if (!(node->fFlags & kObserverArray_Flag)) {
+ if (!node->fInvalObserver) {
+ node->fInvalObserver = this;
+ return;
+ }
+
+ auto observers = new SkTDArray<Node*>();
+ observers->setReserve(2);
+ observers->push(node->fInvalObserver);
+
+ node->fInvalObserverArray = observers;
+ node->fFlags |= kObserverArray_Flag;
+ }
+
+ // No duplicate observers.
+ SkASSERT(node->fInvalObserverArray->find(this) < 0);
+
+ node->fInvalObserverArray->push(this);
+}
+
+void Node::unobserveInval(const sk_sp<Node>& node) {
+ SkASSERT(node);
+ if (!(node->fFlags & kObserverArray_Flag)) {
+ SkASSERT(node->fInvalObserver == this);
+ node->fInvalObserver = nullptr;
+ return;
+ }
+
+ const auto idx = node->fInvalObserverArray->find(this);
+ SkASSERT(idx >= 0);
+ node->fInvalObserverArray->remove(idx);
+}
+
+template <typename Func>
+void Node::forEachInvalObserver(Func&& func) const {
+ if (fFlags & kObserverArray_Flag) {
+ for (const auto& parent : *fInvalObserverArray) {
+ func(parent);
+ }
+ return;
+ }
+
+ if (fInvalObserver) {
+ func(fInvalObserver);
+ }
+}
+
+void Node::invalidate(bool damageBubbling) {
+ TRAVERSAL_GUARD;
+
+ if (this->hasInval() && (!damageBubbling || (fFlags & kDamage_Flag))) {
+ // All done.
+ return;
+ }
+
+ if (damageBubbling && !(fInvalTraits & kBubbleDamage_Trait)) {
+ // Found a damage observer.
+ fFlags |= kDamage_Flag;
+ damageBubbling = false;
+ }
+
+ fFlags |= kInvalidated_Flag;
+
+ forEachInvalObserver([&](Node* observer) {
+ observer->invalidate(damageBubbling);
+ });
+}
+
+const SkRect& Node::revalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ TRAVERSAL_GUARD fBounds;
+
+ if (!this->hasInval()) {
+ return fBounds;
+ }
+
+ SkRect prevBounds;
+ if (fFlags & kDamage_Flag) {
+ prevBounds = fBounds;
+ }
+
+ fBounds = this->onRevalidate(ic, ctm);
+
+ if (fFlags & kDamage_Flag) {
+ ic->inval(prevBounds, ctm);
+ if (fBounds != prevBounds) {
+ ic->inval(fBounds, ctm);
+ }
+ }
+
+ fFlags &= ~(kInvalidated_Flag | kDamage_Flag);
+
+ return fBounds;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGOpacityEffect.cpp b/modules/sksg/src/SkSGOpacityEffect.cpp
new file mode 100644
index 0000000000..b1ff10d217
--- /dev/null
+++ b/modules/sksg/src/SkSGOpacityEffect.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "SkSGOpacityEffect.h"
+
+#include "SkCanvas.h"
+
+#include <math.h>
+
+namespace sksg {
+
+OpacityEffect::OpacityEffect(sk_sp<RenderNode> child, float opacity)
+ : INHERITED(std::move(child))
+ , fOpacity(opacity) {}
+
+void OpacityEffect::onRender(SkCanvas* canvas) const {
+ // opacity <= 0 disables rendering
+ if (fOpacity <= 0)
+ return;
+
+ // TODO: we could avoid savelayer if there is no more than one drawing primitive
+ // in the sub-DAG.
+ SkAutoCanvasRestore acr(canvas, false);
+ if (fOpacity < 1) {
+ canvas->saveLayerAlpha(&this->bounds(), roundf(fOpacity * 255));
+ }
+
+ this->INHERITED::onRender(canvas);
+}
+
+SkRect OpacityEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ // opacity <= 0 disables rendering AND revalidation for the sub-DAG
+ return fOpacity > 0 ? this->INHERITED::onRevalidate(ic, ctm) : SkRect::MakeEmpty();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGPaintNode.cpp b/modules/sksg/src/SkSGPaintNode.cpp
new file mode 100644
index 0000000000..9220b0f0af
--- /dev/null
+++ b/modules/sksg/src/SkSGPaintNode.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "SkSGPaintNode.h"
+
+namespace sksg {
+
+// Paint nodes don't generate damage on their own, but via their aggregation ancestor Draw nodes.
+PaintNode::PaintNode() : INHERITED(kBubbleDamage_Trait) {}
+
+const SkPaint& PaintNode::makePaint() {
+ SkASSERT(!this->hasInval());
+
+ return fPaint;
+}
+
+SkRect PaintNode::onRevalidate(InvalidationController*, const SkMatrix&) {
+ SkASSERT(this->hasInval());
+
+ fPaint.reset();
+ fPaint.setAntiAlias(fAntiAlias);
+ fPaint.setBlendMode(fBlendMode);
+ fPaint.setStyle(fStyle);
+ fPaint.setStrokeWidth(fStrokeWidth);
+ fPaint.setStrokeMiter(fStrokeMiter);
+ fPaint.setStrokeJoin(fStrokeJoin);
+ fPaint.setStrokeCap(fStrokeCap);
+
+ this->onApplyToPaint(&fPaint);
+
+ // Compose opacity on top of the subclass value.
+ fPaint.setAlpha(SkScalarRoundToInt(fPaint.getAlpha() * SkTPin<SkScalar>(fOpacity, 0, 1)));
+
+ return SkRect::MakeEmpty();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGPath.cpp b/modules/sksg/src/SkSGPath.cpp
new file mode 100644
index 0000000000..230442d409
--- /dev/null
+++ b/modules/sksg/src/SkSGPath.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "SkSGPath.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRectPriv.h"
+
+namespace sksg {
+
+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);
+}
+
+SkRect Path::onRevalidate(InvalidationController*, const SkMatrix&) {
+ SkASSERT(this->hasInval());
+
+ const auto ft = fPath.getFillType();
+ return (ft == SkPath::kWinding_FillType || ft == SkPath::kEvenOdd_FillType)
+ // "Containing" fills have finite bounds.
+ ? fPath.computeTightBounds()
+ // Inverse fills are "infinite".
+ : SkRectPriv::MakeLargeS32();
+}
+
+SkPath Path::onAsPath() const {
+ return fPath;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGPlane.cpp b/modules/sksg/src/SkSGPlane.cpp
new file mode 100644
index 0000000000..806fcc7d29
--- /dev/null
+++ b/modules/sksg/src/SkSGPlane.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "SkSGPlane.h"
+
+#include "SkCanvas.h"
+#include "SkPath.h"
+
+namespace sksg {
+
+Plane::Plane() = default;
+
+void Plane::onClip(SkCanvas*, bool) const {}
+
+void Plane::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+ canvas->drawPaint(paint);
+}
+
+SkRect Plane::onRevalidate(InvalidationController*, const SkMatrix&) {
+ SkASSERT(this->hasInval());
+
+ return SkRect::MakeLTRB(SK_ScalarMin, SK_ScalarMin, SK_ScalarMax, SK_ScalarMax);
+}
+
+SkPath Plane::onAsPath() const {
+ SkPath path;
+ path.setFillType(SkPath::kInverseWinding_FillType);
+
+ return path;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGRect.cpp b/modules/sksg/src/SkSGRect.cpp
new file mode 100644
index 0000000000..16f0a6f1e1
--- /dev/null
+++ b/modules/sksg/src/SkSGRect.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "SkSGRect.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+
+namespace sksg {
+
+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);
+}
+
+SkRect Rect::onRevalidate(InvalidationController*, const SkMatrix&) {
+ SkASSERT(this->hasInval());
+
+ return fRect;
+}
+
+SkPath Rect::onAsPath() const {
+ SkPath path;
+ path.addRect(fRect);
+ return path;
+}
+
+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);
+}
+
+SkRect RRect::onRevalidate(InvalidationController*, const SkMatrix&) {
+ SkASSERT(this->hasInval());
+
+ return fRRect.getBounds();
+}
+
+SkPath RRect::onAsPath() const {
+ SkPath path;
+ path.addRRect(fRRect);
+ return path;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGRenderNode.cpp b/modules/sksg/src/SkSGRenderNode.cpp
new file mode 100644
index 0000000000..e952c69f20
--- /dev/null
+++ b/modules/sksg/src/SkSGRenderNode.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 "SkSGRenderNode.h"
+
+namespace sksg {
+
+RenderNode::RenderNode() : INHERITED(0) {}
+
+void RenderNode::render(SkCanvas* canvas) const {
+ SkASSERT(!this->hasInval());
+ this->onRender(canvas);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGRoundEffect.cpp b/modules/sksg/src/SkSGRoundEffect.cpp
new file mode 100644
index 0000000000..8cf9068f65
--- /dev/null
+++ b/modules/sksg/src/SkSGRoundEffect.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "SkSGRoundEffect.h"
+
+#include "SkCanvas.h"
+#include "SkCornerPathEffect.h"
+#include "SkStrokeRec.h"
+
+namespace sksg {
+
+RoundEffect::RoundEffect(sk_sp<GeometryNode> child)
+ : fChild(std::move(child)) {
+ this->observeInval(fChild);
+}
+
+RoundEffect::~RoundEffect() {
+ this->unobserveInval(fChild);
+}
+
+void RoundEffect::onClip(SkCanvas* canvas, bool antiAlias) const {
+ canvas->clipPath(fRoundedPath, SkClipOp::kIntersect, antiAlias);
+}
+
+void RoundEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+ SkASSERT(!paint.getPathEffect());
+
+ canvas->drawPath(fRoundedPath, paint);
+}
+
+SkPath RoundEffect::onAsPath() const {
+ return fRoundedPath;
+}
+
+SkRect RoundEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ const auto childbounds = fChild->revalidate(ic, ctm);
+ const auto path = fChild->asPath();
+
+ if (auto round = SkCornerPathEffect::Make(fRadius)) {
+ fRoundedPath.reset();
+ SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
+ SkAssertResult(round->filterPath(&fRoundedPath, path, &rec, &childbounds));
+ } else {
+ fRoundedPath = path;
+ }
+
+ return fRoundedPath.computeTightBounds();
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGScene.cpp b/modules/sksg/src/SkSGScene.cpp
new file mode 100644
index 0000000000..8d7e0b369b
--- /dev/null
+++ b/modules/sksg/src/SkSGScene.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 "SkSGScene.h"
+
+#include "SkCanvas.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkSGInvalidationController.h"
+#include "SkSGRenderNode.h"
+
+namespace sksg {
+
+Animator::Animator() = default;
+Animator::~Animator() = default;
+
+void Animator::tick(float t) {
+ this->onTick(t);
+}
+
+GroupAnimator::GroupAnimator(AnimatorList&& animators)
+ : fAnimators(std::move(animators)) {}
+
+void GroupAnimator::onTick(float t) {
+ for (const auto& a : fAnimators) {
+ a->tick(t);
+ }
+}
+
+std::unique_ptr<Scene> Scene::Make(sk_sp<RenderNode> root, AnimatorList&& anims) {
+ return root ? std::unique_ptr<Scene>(new Scene(std::move(root), std::move(anims))) : nullptr;
+}
+
+Scene::Scene(sk_sp<RenderNode> root, AnimatorList&& animators)
+ : fRoot(std::move(root))
+ , fAnimators(std::move(animators)) {}
+
+Scene::~Scene() = default;
+
+void Scene::render(SkCanvas* canvas) const {
+ InvalidationController ic;
+ fRoot->revalidate(&ic, SkMatrix::I());
+ fRoot->render(canvas);
+
+ if (fShowInval) {
+ SkPaint fill, stroke;
+ fill.setAntiAlias(true);
+ fill.setColor(0x40ff0000);
+ stroke.setAntiAlias(true);
+ stroke.setColor(0xffff0000);
+ stroke.setStyle(SkPaint::kStroke_Style);
+
+ for (const auto& r : ic) {
+ canvas->drawRect(r, fill);
+ canvas->drawRect(r, stroke);
+ }
+ }
+}
+
+void Scene::animate(float t) {
+ for (const auto& anim : fAnimators) {
+ anim->tick(t);
+ }
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGText.cpp b/modules/sksg/src/SkSGText.cpp
new file mode 100644
index 0000000000..c149390023
--- /dev/null
+++ b/modules/sksg/src/SkSGText.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "SkSGText.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkTArray.h"
+#include "SkTextBlob.h"
+#include "SkTypeface.h"
+
+namespace sksg {
+
+sk_sp<Text> Text::Make(sk_sp<SkTypeface> tf, const SkString& text) {
+ return sk_sp<Text>(new Text(std::move(tf), text));
+}
+
+Text::Text(sk_sp<SkTypeface> tf, const SkString& text)
+ : fTypeface(std::move(tf))
+ , fText(text) {}
+
+Text::~Text() = default;
+
+SkRect Text::onRevalidate(InvalidationController*, const SkMatrix&) {
+ // TODO: we could potentially track invals which don't require rebuilding the blob.
+
+ SkPaint font;
+ font.setFlags(fFlags);
+ font.setTypeface(fTypeface);
+ font.setTextSize(fSize);
+ font.setTextScaleX(fScaleX);
+ font.setTextSkewX(fSkewX);
+ font.setTextAlign(fAlign);
+ font.setHinting(fHinting);
+
+ // First, convert to glyphIDs.
+ font.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+ SkSTArray<256, SkGlyphID, true> glyphs;
+ glyphs.reset(font.textToGlyphs(fText.c_str(), fText.size(), nullptr));
+ SkAssertResult(font.textToGlyphs(fText.c_str(), fText.size(), glyphs.begin()) == glyphs.count());
+ font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ // Next, build the cached blob.
+ SkTextBlobBuilder builder;
+ const auto& buf = builder.allocRun(font, glyphs.count(), 0, 0, nullptr);
+ if (!buf.glyphs) {
+ fBlob.reset();
+ return SkRect::MakeEmpty();
+ }
+
+ memcpy(buf.glyphs, glyphs.begin(), glyphs.count() * sizeof(SkGlyphID));
+
+ fBlob = builder.make();
+ return fBlob
+ ? fBlob->bounds().makeOffset(fPosition.x(), fPosition.y())
+ : SkRect::MakeEmpty();
+}
+
+void Text::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+ canvas->drawTextBlob(fBlob, fPosition.x(), fPosition.y(), paint);
+}
+
+SkPath Text::onAsPath() const {
+ // TODO
+ return SkPath();
+}
+
+void Text::onClip(SkCanvas* canvas, bool antiAlias) const {
+ canvas->clipPath(this->asPath(), antiAlias);
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGTransform.cpp b/modules/sksg/src/SkSGTransform.cpp
new file mode 100644
index 0000000000..6a985a971e
--- /dev/null
+++ b/modules/sksg/src/SkSGTransform.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 "SkSGTransform.h"
+
+#include "SkCanvas.h"
+
+namespace sksg {
+// Matrix nodes don't generate damage on their own, but via aggregation ancestor Transform nodes.
+Matrix::Matrix(const SkMatrix& m, sk_sp<Matrix> parent)
+ : INHERITED(kBubbleDamage_Trait)
+ , fParent(std::move(parent))
+ , fLocalMatrix(m) {
+ if (fParent) {
+ this->observeInval(fParent);
+ }
+}
+
+Matrix::~Matrix() {
+ if (fParent) {
+ this->unobserveInval(fParent);
+ }
+}
+
+SkRect Matrix::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ fTotalMatrix = fLocalMatrix;
+
+ if (fParent) {
+ fParent->revalidate(ic, ctm);
+ fTotalMatrix.postConcat(fParent->getTotalMatrix());
+ }
+
+ return SkRect::MakeEmpty();
+}
+
+Transform::Transform(sk_sp<RenderNode> child, sk_sp<Matrix> matrix)
+ : INHERITED(std::move(child))
+ , fMatrix(std::move(matrix)) {
+ this->observeInval(fMatrix);
+}
+
+Transform::~Transform() {
+ this->unobserveInval(fMatrix);
+}
+
+void Transform::onRender(SkCanvas* canvas) const {
+ const auto& m = fMatrix->getTotalMatrix();
+ SkAutoCanvasRestore acr(canvas, !m.isIdentity());
+ canvas->concat(m);
+ this->INHERITED::onRender(canvas);
+}
+
+SkRect Transform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ // We don't care about matrix reval results.
+ fMatrix->revalidate(ic, ctm);
+
+ const auto& m = fMatrix->getTotalMatrix();
+ auto bounds = this->INHERITED::onRevalidate(ic, SkMatrix::Concat(ctm, m));
+ m.mapRect(&bounds);
+
+ return bounds;
+}
+
+} // namespace sksg
diff --git a/modules/sksg/src/SkSGTrimEffect.cpp b/modules/sksg/src/SkSGTrimEffect.cpp
new file mode 100644
index 0000000000..b8c59bcfe8
--- /dev/null
+++ b/modules/sksg/src/SkSGTrimEffect.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "SkSGTrimEffect.h"
+
+#include "SkCanvas.h"
+#include "SkStrokeRec.h"
+#include "SkTrimPathEffect.h"
+
+namespace sksg {
+
+TrimEffect::TrimEffect(sk_sp<GeometryNode> child)
+ : fChild(std::move(child)) {
+ this->observeInval(fChild);
+}
+
+TrimEffect::~TrimEffect() {
+ this->unobserveInval(fChild);
+}
+
+void TrimEffect::onClip(SkCanvas* canvas, bool antiAlias) const {
+ canvas->clipPath(fTrimmedPath, SkClipOp::kIntersect, antiAlias);
+}
+
+void TrimEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+ SkASSERT(!paint.getPathEffect());
+
+ canvas->drawPath(fTrimmedPath, paint);
+}
+
+SkPath TrimEffect::onAsPath() const {
+ return fTrimmedPath;
+}
+
+SkRect TrimEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+ SkASSERT(this->hasInval());
+
+ const auto childbounds = fChild->revalidate(ic, ctm);
+ const auto path = fChild->asPath();
+
+ if (auto trim = SkTrimPathEffect::Make(fStart, fStop, fMode)) {
+ fTrimmedPath.reset();
+ SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
+ SkAssertResult(trim->filterPath(&fTrimmedPath, path, &rec, &childbounds));
+ } else {
+ fTrimmedPath = path;
+ }
+
+ return fTrimmedPath.computeTightBounds();
+}
+
+} // namespace sksg