diff options
author | Florin Malita <fmalita@chromium.org> | 2017-12-19 12:21:02 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-12-21 20:56:32 +0000 |
commit | 4aa4441186b06565a597ec4a9baac5a972fddb51 (patch) | |
tree | 8ae7cb5e988f3b89dfea886e118931e78013ff63 | |
parent | f059e7ca181ad20fa94edea65c8bfd839eceab46 (diff) |
Initial scene graph (SkSG)
Sketching a thin (as in close-to-skia-semantics) scene graph API, focused on
external animation, inval tracking and minimal repaint.
Only a few concrete classes/features so far:
* Rect/Color/Transform/Group
* basic inval tracking
* a trivial animated sample with inval visualization
Pretty much everything (especially naming) is volatile, so treat accordingly.
The interesting bits to review are likely in Node.{h,cpp} for inval and
SampleSGInval.cpp for usage.
Initial class hierarchy:
* Node: invalidation/ancestors tracking
|
-- * RenderNode: onRender(SkCanvas)
| |
| -- * Draw (concrete): rendering a [geometry, paint] tuple
| |
| -- * Group (concrete): grouping multiple RenderNodes
| |
| -- * EffectNode: single-descendant effect wrapper
| |
| -- * Transform (concrete): transform effect
|
-- * PaintNode: onMakePaint()
| |
| -- * Color (concrete): SkColor paint wrapper
|
-- * GeometryNode: onComputeBounds(), onDraw(SkCanvas, SkPaint)
|
-- * Rect (concrete): SkRect wrapper
TBR=
Change-Id: Iacf9b773c181a7582ecd31ee968562f179d1aa1b
Reviewed-on: https://skia-review.googlesource.com/85502
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
25 files changed, 1060 insertions, 0 deletions
@@ -1278,6 +1278,7 @@ if (skia_enable_tools) { public_include_dirs = [ "gm" ] sources = gm_sources deps = [ + ":experimental_sksg", ":flags", ":gpu_tool_utils", ":skia", @@ -1293,6 +1294,7 @@ if (skia_enable_tools) { sources -= [ "//tests/FontMgrAndroidParserTest.cpp" ] } deps = [ + ":experimental_sksg", ":experimental_svg_model", ":flags", ":skia", @@ -1350,6 +1352,31 @@ if (skia_enable_tools) { ] } + test_lib("experimental_sksg") { + public_include_dirs = [ + "experimental/sksg", + "experimental/sksg/effects", + "experimental/sksg/geometry", + "experimental/sksg/paint", + ] + sources = [ + "experimental/sksg/SkSGDraw.cpp", + "experimental/sksg/SkSGEffectNode.cpp", + "experimental/sksg/SkSGGeometryNode.cpp", + "experimental/sksg/SkSGGroup.cpp", + "experimental/sksg/SkSGInvalidationController.cpp", + "experimental/sksg/SkSGNode.cpp", + "experimental/sksg/SkSGPaintNode.cpp", + "experimental/sksg/SkSGRenderNode.cpp", + "experimental/sksg/effects/SkSGTransform.cpp", + "experimental/sksg/geometry/SkSGRect.cpp", + "experimental/sksg/paint/SkSGColor.cpp", + ] + deps = [ + ":skia", + ] + } + if (target_cpu != "wasm") { test_lib("views") { public_include_dirs = [ "include/views" ] @@ -1432,6 +1459,7 @@ if (skia_enable_tools) { "src/core/SkThreadedBMPDevice.h", ] deps = [ + ":experimental_sksg", ":experimental_svg_model", ":flags", ":gm", @@ -1459,6 +1487,7 @@ if (skia_enable_tools) { include_dirs = [ "tests" ] deps = [ ":common_flags", + ":experimental_sksg", ":experimental_svg_model", ":flags", ":gm", @@ -1497,6 +1526,7 @@ if (skia_enable_tools) { deps = [ ":bench", ":common_flags", + ":experimental_sksg", ":experimental_svg_model", ":flags", ":gm", diff --git a/experimental/sksg/SkSGDraw.cpp b/experimental/sksg/SkSGDraw.cpp new file mode 100644 index 0000000000..7319c635fe --- /dev/null +++ b/experimental/sksg/SkSGDraw.cpp @@ -0,0 +1,38 @@ +/* + * 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 "SkSGPaintNode.h" + +namespace sksg { + +Draw::Draw(sk_sp<GeometryNode> geometry, sk_sp<PaintNode> paint) + : fGeometry(std::move(geometry)) + , fPaint(std::move(paint)) { + fGeometry->addInvalReceiver(this); + fPaint->addInvalReceiver(this); +} + +Draw::~Draw() { + fGeometry->removeInvalReceiver(this); + fPaint->removeInvalReceiver(this); +} + +void Draw::onRender(SkCanvas* canvas) const { + fGeometry->draw(canvas, fPaint->makePaint()); +} + +void Draw::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + SkASSERT(this->isInvalidated()); + + fGeometry->revalidate(ic, ctm); + fPaint->revalidate(ic, ctm); +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGDraw.h b/experimental/sksg/SkSGDraw.h new file mode 100644 index 0000000000..f5146dee28 --- /dev/null +++ b/experimental/sksg/SkSGDraw.h @@ -0,0 +1,48 @@ +/* + * 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 SkSGDraw_DEFINED +#define SkSGDraw_DEFINED + +#include "SkSGRenderNode.h" + +namespace sksg { + +class GeometryNode; +class PaintNode; + +/** + * Concrete rendering node. + * + * Wraps and draws a [geometry, paint] tuple. + * + * Think Skia SkCanvas::drawFoo(foo, paint) calls. + */ +class Draw : public RenderNode { +public: + static sk_sp<Draw> Make(sk_sp<GeometryNode> geo, sk_sp<PaintNode> paint) { + return (geo && paint) ? sk_sp<Draw>(new Draw(std::move(geo), std::move(paint))) : nullptr; + } + +protected: + Draw(sk_sp<GeometryNode>, sk_sp<PaintNode> paint); + ~Draw() override; + + void onRender(SkCanvas*) const override; + + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + sk_sp<GeometryNode> fGeometry; + sk_sp<PaintNode> fPaint; + + typedef RenderNode INHERITED; +}; + +} // namespace sksg + +#endif // SkSGDraw_DEFINED diff --git a/experimental/sksg/SkSGEffectNode.cpp b/experimental/sksg/SkSGEffectNode.cpp new file mode 100644 index 0000000000..c09bcf0955 --- /dev/null +++ b/experimental/sksg/SkSGEffectNode.cpp @@ -0,0 +1,29 @@ +/* + * 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)) { + fChild->addInvalReceiver(this); +} + +EffectNode::~EffectNode() { + fChild->removeInvalReceiver(this); +} + +void EffectNode::onRender(SkCanvas* canvas) const { + fChild->render(canvas); +} + +void EffectNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + fChild->revalidate(ic, ctm); +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGEffectNode.h b/experimental/sksg/SkSGEffectNode.h new file mode 100644 index 0000000000..b9a44a6cad --- /dev/null +++ b/experimental/sksg/SkSGEffectNode.h @@ -0,0 +1,38 @@ +/* + * 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 SkSGEffectNode_DEFINED +#define SkSGEffectNode_DEFINED + +#include "SkSGRenderNode.h" + +namespace sksg { + +/** + * Base class for nodes which apply some transformation when rendering + * their descendants. + * + * This includes transforms, clipping, filters, etc. + */ +class EffectNode : public RenderNode { +protected: + explicit EffectNode(sk_sp<RenderNode>); + ~EffectNode() override; + + void onRender(SkCanvas*) const override; + + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + sk_sp<RenderNode> fChild; + + typedef RenderNode INHERITED; +}; + +} // namespace sksg + +#endif // SkSGEffectNode_DEFINED diff --git a/experimental/sksg/SkSGGeometryNode.cpp b/experimental/sksg/SkSGGeometryNode.cpp new file mode 100644 index 0000000000..ea14ec0255 --- /dev/null +++ b/experimental/sksg/SkSGGeometryNode.cpp @@ -0,0 +1,48 @@ +/* + * 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 "SkMatrix.h" +#include "SkSGInvalidationController.h" + +namespace sksg { + +GeometryNode::GeometryNode() + : fBounds(SkRect::MakeLTRB(SK_ScalarMin, SK_ScalarMin, SK_ScalarMax, SK_ScalarMax)) {} + +void GeometryNode::draw(SkCanvas* canvas, const SkPaint& paint) const { + SkASSERT(!this->isInvalidated()); + this->onDraw(canvas, paint); +} + +static void inval_rect(const SkRect& r, const SkMatrix& ctm, InvalidationController* ic) { + if (ctm.isIdentity()) { + ic->inval(r); + return; + } + + SkRect mappedRect; + if (!ctm.mapRect(&mappedRect, r)) { + mappedRect = SkRect::MakeLTRB(SK_ScalarMin, SK_ScalarMin, SK_ScalarMax, SK_ScalarMax); + } + ic->inval(mappedRect); +} + +void GeometryNode::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + SkASSERT(this->isInvalidated()); + + const auto oldBounds = fBounds; + fBounds = this->onComputeBounds(); + + inval_rect(oldBounds, ctm, ic); + if (fBounds != oldBounds) { + inval_rect(fBounds, ctm, ic); + } +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGGeometryNode.h b/experimental/sksg/SkSGGeometryNode.h new file mode 100644 index 0000000000..52b2df0367 --- /dev/null +++ b/experimental/sksg/SkSGGeometryNode.h @@ -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. + */ + +#ifndef SkSGGeometryNode_DEFINED +#define SkSGGeometryNode_DEFINED + +#include "SkSGNode.h" + +#include "SkRect.h" + +class SkCanvas; +class SkPaint; + +namespace sksg { + +/** + * Base class for nodes which provide 'geometry' (as opposed to paint) + * for drawing. + * + * Think SkRect, SkPath, etc. + */ +class GeometryNode : public Node { +public: + void draw(SkCanvas*, const SkPaint&) const; + + // SkPath asPath() const; // unused for now + +protected: + GeometryNode(); + + virtual void onDraw(SkCanvas*, const SkPaint&) const = 0; + + virtual SkRect onComputeBounds() const = 0; + + // virtual SkPath onAsPath() const = 0; // unused for now + + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + SkRect fBounds; + + typedef Node INHERITED; +}; + +} // namespace sksg + +#endif // SkSGGeometryNode_DEFINED diff --git a/experimental/sksg/SkSGGroup.cpp b/experimental/sksg/SkSGGroup.cpp new file mode 100644 index 0000000000..b8e28f7286 --- /dev/null +++ b/experimental/sksg/SkSGGroup.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 "SkSGGroup.h" + +namespace sksg { + +Group::Group() {} + +Group::~Group() { + for (const auto& child : fChildren) { + child->removeInvalReceiver(this); + } +} + +void Group::addChild(sk_sp<RenderNode> node) { + // should we allow duplicates? + for (const auto& child : fChildren) { + if (child == node) { + return; + } + } + + node->addInvalReceiver(this); + fChildren.push_back(std::move(node)); +} + +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); + node->removeInvalReceiver(this); + break; + } + } + SkASSERT(fChildren.count() == origCount - 1); +} + +void Group::onRender(SkCanvas* canvas) const { + for (const auto& child : fChildren) { + child->render(canvas); + } +} + +void Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + for (const auto& child : fChildren) { + child->revalidate(ic, ctm); + } +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGGroup.h b/experimental/sksg/SkSGGroup.h new file mode 100644 index 0000000000..2ed9d0ecb5 --- /dev/null +++ b/experimental/sksg/SkSGGroup.h @@ -0,0 +1,44 @@ +/* + * 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 SkSGGroup_DEFINED +#define SkSGGroup_DEFINED + +#include "SkSGRenderNode.h" + +#include "SkTArray.h" + +namespace sksg { + +/** + * Concrete node, grouping together multiple descendants. + */ +class Group : public RenderNode { +public: + static sk_sp<Group> Make() { + return sk_sp<Group>(new Group()); + } + + void addChild(sk_sp<RenderNode>); + void removeChild(const sk_sp<RenderNode>&); + +protected: + Group(); + ~Group() override; + + void onRender(SkCanvas*) const override; + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + SkTArray<sk_sp<RenderNode>, true> fChildren; + + typedef RenderNode INHERITED; +}; + +} // namespace sksg + +#endif // SkSGGroup_DEFINED diff --git a/experimental/sksg/SkSGInvalidationController.cpp b/experimental/sksg/SkSGInvalidationController.cpp new file mode 100644 index 0000000000..9693f1e754 --- /dev/null +++ b/experimental/sksg/SkSGInvalidationController.cpp @@ -0,0 +1,20 @@ +/* + * 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" + +namespace sksg { + +InvalidationController::InvalidationController() {} + +void InvalidationController::inval(const SkRect& r) { + fRects.push(r); +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGInvalidationController.h b/experimental/sksg/SkSGInvalidationController.h new file mode 100644 index 0000000000..e00ece52fc --- /dev/null +++ b/experimental/sksg/SkSGInvalidationController.h @@ -0,0 +1,40 @@ +/* + * 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 SkSGInvalidationController_DEFINED +#define SkSGInvalidationController_DEFINED + +#include "SkTDArray.h" +#include "SkTypes.h" + +struct SkRect; + +namespace sksg { + +/** + * Receiver for invalidation events. + * + * Tracks dirty regions for repaint. + */ +class InvalidationController : public SkNoncopyable { +public: + InvalidationController(); + + void inval(const SkRect&); + + const SkRect* begin() const { return fRects.begin(); } + const SkRect* end() const { return fRects.end(); } + +private: + SkTDArray<SkRect> fRects; + + typedef SkNoncopyable INHERITED; +}; + +} // namespace sksg + +#endif // SkSGInvalidationController_DEFINED diff --git a/experimental/sksg/SkSGNode.cpp b/experimental/sksg/SkSGNode.cpp new file mode 100644 index 0000000000..3b404edd50 --- /dev/null +++ b/experimental/sksg/SkSGNode.cpp @@ -0,0 +1,117 @@ +/* + * 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 "SkSGNode.h" + +namespace sksg { + +class Node::ScopedFlag { +public: + ScopedFlag(Node* node, uint32_t flag) + : fNode(node) + , fFlag(flag) { + SkASSERT(!(fNode->fFlags & fFlag)); + fNode->fFlags |= fFlag; + } + ~ScopedFlag() { + fNode->fFlags &= ~fFlag;; + } + +private: + Node* fNode; + uint32_t fFlag; +}; + +#define TRAVERSAL_GUARD \ + if (this->fFlags & kInTraversal_Flag) { \ + return; \ + } \ + ScopedFlag traversal_guard(this, kInTraversal_Flag); + +Node::Node() + : fInvalReceiver(nullptr) + , fFlags(kInvalidated_Flag) {} + +Node::~Node() { + if (fFlags & kReceiverArray_Flag) { + SkASSERT(fInvalReceiverArray->isEmpty()); + delete fInvalReceiverArray; + } else { + SkASSERT(!fInvalReceiver); + } +} + +void Node::addInvalReceiver(Node* receiver) { + if (!(fFlags & kReceiverArray_Flag)) { + if (!fInvalReceiver) { + fInvalReceiver = receiver; + return; + } + + auto receivers = new SkTDArray<Node*>(); + receivers->setReserve(2); + receivers->push(fInvalReceiver); + + fInvalReceiverArray = receivers; + fFlags |= kReceiverArray_Flag; + } + + // No duplicate receivers. + SkASSERT(fInvalReceiverArray->find(receiver) < 0); + + fInvalReceiverArray->push(receiver); +} + +void Node::removeInvalReceiver(Node* receiver) { + if (!(fFlags & kReceiverArray_Flag)) { + SkASSERT(fInvalReceiver == receiver); + fInvalReceiver = nullptr; + return; + } + + const auto idx = fInvalReceiverArray->find(receiver); + SkASSERT(idx >= 0); + fInvalReceiverArray->remove(idx); +} + +template <typename Func> +void Node::forEachInvalReceiver(Func&& func) const { + if (fFlags & kReceiverArray_Flag) { + for (const auto& parent : *fInvalReceiverArray) { + func(parent); + } + return; + } + + if (fInvalReceiver) { + func(fInvalReceiver); + } +} + +void Node::invalidate() { + TRAVERSAL_GUARD + + if (this->isInvalidated()) { + return; + } + + fFlags |= kInvalidated_Flag; + forEachInvalReceiver([&](Node* receiver) { + receiver->invalidate(); + }); +} + +void Node::revalidate(InvalidationController* ic, const SkMatrix& ctm) { + TRAVERSAL_GUARD + + if (this->isInvalidated()) { + this->onRevalidate(ic, ctm); + fFlags &= ~kInvalidated_Flag; + } +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGNode.h b/experimental/sksg/SkSGNode.h new file mode 100644 index 0000000000..08ee784a1e --- /dev/null +++ b/experimental/sksg/SkSGNode.h @@ -0,0 +1,86 @@ +/* + * 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 SkSGNode_DEFINED +#define SkSGNode_DEFINED + +#include "SkRefCnt.h" +#include "SkTDArray.h" + +class SkCanvas; +class SkMatrix; + +namespace sksg { + +class InvalidationController; + +/** + * Base class for all scene graph nodes. + * + * Handles ingress edge management for the DAG (i.e. node -> "parent" node mapping), + * and invalidation. + * + * Note: egress edges are only implemented/supported in container subclasses + * (e.g. Group, Effect, Draw). + */ +class Node : public SkRefCnt { +public: + // Traverse the DAG and revalidate any connected/invalidated nodes. + void revalidate(InvalidationController*, const SkMatrix&); + +protected: + Node(); + ~Node() override; + + // Mark this node and (transitively) any invalidation receivers for revalidation. + void invalidate(); + + bool isInvalidated() const { return fFlags & kInvalidated_Flag; } + + // Dispatched on revalidation. Subclasses are expected to recompute their geometry + // and push dirty rects to the InvalidationController. + virtual void onRevalidate(InvalidationController*, const SkMatrix& ctm) = 0; + +private: + void addInvalReceiver(Node*); + void removeInvalReceiver(Node*); + friend class Draw; + friend class EffectNode; + friend class Group; + + template <typename Func> + void forEachInvalReceiver(Func&&) const; + + enum Flags { + kInvalidated_Flag = 1 << 0, // the node requires revalidation + kReceiverArray_Flag = 1 << 1, // the node has more than one inval receiver + kInTraversal_Flag = 1 << 2, // the node is part of a traversal (cycle detection) + }; + + class ScopedFlag; + + union { + Node* fInvalReceiver; + SkTDArray<Node*>* fInvalReceiverArray; + }; + uint32_t fFlags; + + typedef SkRefCnt INHERITED; +}; + +// Helper for defining attribute getters/setters in subclasses. +#define SG_ATTRIBUTE(attr_name, attr_type, attr_container) \ + attr_type get##attr_name() const { return attr_container; } \ + void set##attr_name(attr_type v) { \ + if (attr_container == v) return; \ + attr_container = v; \ + this->invalidate(); \ + } + +} // namespace sksg + +#endif // SkSGNode_DEFINED diff --git a/experimental/sksg/SkSGPaintNode.cpp b/experimental/sksg/SkSGPaintNode.cpp new file mode 100644 index 0000000000..cd0bc81df5 --- /dev/null +++ b/experimental/sksg/SkSGPaintNode.cpp @@ -0,0 +1,26 @@ +/* + * 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 { + +PaintNode::PaintNode() {} + +const SkPaint& PaintNode::makePaint() { + SkASSERT(!this->isInvalidated()); + + return fPaint; +} + +void PaintNode::onRevalidate(InvalidationController*, const SkMatrix&) { + SkASSERT(this->isInvalidated()); + + fPaint = this->onMakePaint(); +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGPaintNode.h b/experimental/sksg/SkSGPaintNode.h new file mode 100644 index 0000000000..d0a7787bc8 --- /dev/null +++ b/experimental/sksg/SkSGPaintNode.h @@ -0,0 +1,43 @@ +/* + * 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 SkSGPaintNode_DEFINED +#define SkSGPaintNode_DEFINED + +#include "SkSGNode.h" + +#include "SkPaint.h" + +namespace sksg { + +/** + * Base class for nodes which provide a 'paint' (as opposed to geometry) for + * drawing (e.g. colors, gradients, patterns). + * + * Roughly equivalent to Skia's SkPaint. + */ +class PaintNode : public Node { +public: + + const SkPaint& makePaint(); + +protected: + PaintNode(); + + virtual SkPaint onMakePaint() const = 0; + + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + SkPaint fPaint; + + typedef Node INHERITED; +}; + +} // namespace sksg + +#endif // SkSGGeometryNode_DEFINED diff --git a/experimental/sksg/SkSGRenderNode.cpp b/experimental/sksg/SkSGRenderNode.cpp new file mode 100644 index 0000000000..1609bac724 --- /dev/null +++ b/experimental/sksg/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() {} + +void RenderNode::render(SkCanvas* canvas) const { + SkASSERT(!this->isInvalidated()); + this->onRender(canvas); +} + +} // namespace sksg diff --git a/experimental/sksg/SkSGRenderNode.h b/experimental/sksg/SkSGRenderNode.h new file mode 100644 index 0000000000..4ca1aec616 --- /dev/null +++ b/experimental/sksg/SkSGRenderNode.h @@ -0,0 +1,36 @@ +/* + * 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 SkSGRenderNode_DEFINED +#define SkSGRenderNode_DEFINED + +#include "SkSGNode.h" + +class SkCanvas; + +namespace sksg { + +/** + * Base class for nodes which can render to a canvas. + */ +class RenderNode : public Node { +public: + // Render the node and its descendants to the canvas. + void render(SkCanvas*) const; + +protected: + RenderNode(); + + virtual void onRender(SkCanvas*) const = 0; + +private: + typedef Node INHERITED; +}; + +} // namespace sksg + +#endif // SkSGRenderNode_DEFINED diff --git a/experimental/sksg/effects/SkSGTransform.cpp b/experimental/sksg/effects/SkSGTransform.cpp new file mode 100644 index 0000000000..ce83599871 --- /dev/null +++ b/experimental/sksg/effects/SkSGTransform.cpp @@ -0,0 +1,29 @@ +/* + * 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 { + +Transform::Transform(sk_sp<RenderNode> child, const SkMatrix& matrix) + : INHERITED(std::move(child)) + , fMatrix(matrix) {} + +void Transform::onRender(SkCanvas* canvas) const { + SkAutoCanvasRestore acr(canvas, !fMatrix.isIdentity()); + canvas->concat(fMatrix); + this->INHERITED::onRender(canvas); +} + +void Transform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + const auto localCTM = SkMatrix::Concat(ctm, fMatrix); + this->INHERITED::onRevalidate(ic, localCTM); +} + +} // namespace sksg diff --git a/experimental/sksg/effects/SkSGTransform.h b/experimental/sksg/effects/SkSGTransform.h new file mode 100644 index 0000000000..d7bc5d4fc8 --- /dev/null +++ b/experimental/sksg/effects/SkSGTransform.h @@ -0,0 +1,43 @@ +/* + * 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 SkSGTransform_DEFINED +#define SkSGTransform_DEFINED + +#include "SkSGEffectNode.h" + +#include "SkMatrix.h" + +namespace sksg { + +/** + * Concrete Effect node, wrapping an SkMatrix. + */ +class Transform : public EffectNode { +public: + static sk_sp<Transform> Make(sk_sp<RenderNode> child, const SkMatrix& matrix) { + return sk_sp<Transform>(new Transform(std::move(child), matrix)); + } + + SG_ATTRIBUTE(Matrix, SkMatrix, fMatrix) + +protected: + Transform(sk_sp<RenderNode>, const SkMatrix&); + + void onRender(SkCanvas*) const override; + + void onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + SkMatrix fMatrix; + + typedef EffectNode INHERITED; +}; + +} // namespace sksg + +#endif // SkSGTransform_DEFINED diff --git a/experimental/sksg/geometry/SkSGRect.cpp b/experimental/sksg/geometry/SkSGRect.cpp new file mode 100644 index 0000000000..69bd94681b --- /dev/null +++ b/experimental/sksg/geometry/SkSGRect.cpp @@ -0,0 +1,25 @@ +/* + * 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" + +namespace sksg { + +Rect::Rect(const SkRect& rect) : fRect(rect) {} + +void Rect::onDraw(SkCanvas* canvas, const SkPaint& paint) const { + canvas->drawRect(fRect, paint); +} + +SkRect Rect::onComputeBounds() const { + return fRect; +} + +} // namespace sksg diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h new file mode 100644 index 0000000000..a0e5ec62e8 --- /dev/null +++ b/experimental/sksg/geometry/SkSGRect.h @@ -0,0 +1,46 @@ +/* + * 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 SkSGRect_DEFINED +#define SkSGRect_DEFINED + +#include "SkSGGeometryNode.h" + +#include "SkRect.h" + +class SkCanvas; +class SkPaint; + +namespace sksg { + +/** + * Concrete Geometry node, wrapping an SkRect. + */ +class Rect : public GeometryNode { +public: + static sk_sp<Rect> Make() { return sk_sp<Rect>(new Rect(SkRect::MakeEmpty())); } + static sk_sp<Rect> Make(const SkRect& r) { return sk_sp<Rect>(new Rect(r)); } + + SG_ATTRIBUTE(L, SkScalar, fRect.fLeft ) + SG_ATTRIBUTE(T, SkScalar, fRect.fTop ) + SG_ATTRIBUTE(R, SkScalar, fRect.fRight ) + SG_ATTRIBUTE(B, SkScalar, fRect.fBottom) + +protected: + void onDraw(SkCanvas*, const SkPaint&) const override; + + SkRect onComputeBounds() const override; + +private: + explicit Rect(const SkRect&); + + SkRect fRect; +}; + +} // namespace sksg + +#endif // SkSGRect_DEFINED diff --git a/experimental/sksg/paint/SkSGColor.cpp b/experimental/sksg/paint/SkSGColor.cpp new file mode 100644 index 0000000000..826bc4bc7c --- /dev/null +++ b/experimental/sksg/paint/SkSGColor.cpp @@ -0,0 +1,20 @@ +/* + * 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) {} + +SkPaint Color::onMakePaint() const { + SkPaint paint; + paint.setColor(fColor); + return paint; +} + +} // namespace sksg diff --git a/experimental/sksg/paint/SkSGColor.h b/experimental/sksg/paint/SkSGColor.h new file mode 100644 index 0000000000..a4e0862892 --- /dev/null +++ b/experimental/sksg/paint/SkSGColor.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 SkSGColor_DEFINED +#define SkSGColor_DEFINED + +#include "SkSGPaintNode.h" + +#include "SkColor.h" + +namespace sksg { + +/** + * Concrete Paint node, wrapping an SkColor. + */ +class Color : public PaintNode { +public: + static sk_sp<Color> Make(SkColor c) { return sk_sp<Color>(new Color(c)); } + + SG_ATTRIBUTE(Color, SkColor, fColor) + +protected: + SkPaint onMakePaint() const override; + +private: + explicit Color(SkColor); + + SkColor fColor; +}; + +} // namespace sksg + +#endif // SkSGColor_DEFINED diff --git a/gn/samples.gni b/gn/samples.gni index 6bef4af8fb..fe2ab4eba7 100644 --- a/gn/samples.gni +++ b/gn/samples.gni @@ -87,6 +87,7 @@ samples_sources = [ "$_samplecode/SampleStrokePath.cpp", "$_samplecode/SampleStrokeRect.cpp", "$_samplecode/SampleSubpixelTranslate.cpp", + "$_samplecode/SampleSGInval.cpp", "$_samplecode/SampleSVGFile.cpp", "$_samplecode/SampleSVGPong.cpp", "$_samplecode/SampleText.cpp", diff --git a/samplecode/SampleSGInval.cpp b/samplecode/SampleSGInval.cpp new file mode 100644 index 0000000000..08bfd64377 --- /dev/null +++ b/samplecode/SampleSGInval.cpp @@ -0,0 +1,90 @@ +/* + * 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 "SampleCode.h" +#include "SkCanvas.h" +#include "SkSGColor.h" +#include "SkSGDraw.h" +#include "SkSGGroup.h" +#include "SkSGInvalidationController.h" +#include "SkSGRect.h" +#include "SkSGTransform.h" +#include "SkAnimTimer.h" + +#include <cmath> + +class SGInvalView final : public SampleView { +public: + SGInvalView() {} + +protected: + void onOnceBeforeDraw() override { + fRect1 = sksg::Rect::Make(SkRect::MakeLTRB(100, 100, 100, 100)); + fRect2 = sksg::Rect::Make(SkRect::MakeLTRB(300, 200, 300, 200)); + fColor1 = sksg::Color::Make(0); + fColor2 = sksg::Color::Make(0); + + fRoot = sksg::Group::Make(); + fRoot->addChild(sksg::Draw::Make(fRect1, fColor1)); + fRoot->addChild(sksg::Transform::Make(sksg::Draw::Make(fRect2, fColor2), + SkMatrix::MakeScale(1.5f, 1.5f))); + } + + bool onQuery(SkEvent* evt) override { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "SGInval"); + return true; + } + + return this->INHERITED::onQuery(evt); + } + + void onDrawContent(SkCanvas* canvas) override { + sksg::InvalidationController ic; + fRoot->revalidate(&ic, SkMatrix::I()); + + // TODO: clip/cull + fRoot->render(canvas); + + SkPaint p; + p.setColor(0xffff0000); + p.setStyle(SkPaint::kStroke_Style); + p.setAntiAlias(true); + p.setStrokeWidth(0); + + for (const auto& r : ic) { + canvas->drawRect(r, p); + } + } + + bool onAnimate(const SkAnimTimer& timer) override { + static constexpr SkScalar kSize = 50; + static constexpr SkScalar kRate = 1.0f / 500; + const auto t = timer.msec() * kRate; + + fRect1->setR(fRect1->getL() + kSize * (1 + std::sin(t))); + fRect1->setB(fRect1->getT() + kSize * (1 + std::cos(t))); + fRect2->setR(fRect2->getL() + kSize * (1 + std::cos(SK_ScalarPI / 2 + t))); + fRect2->setB(fRect2->getT() + kSize * (1 + std::sin(SK_ScalarPI / 2 + t))); + + fColor1->setColor(SkColorSetARGB(128 * (1 + std::sin(t)), 0, 0x80, 0)); + fColor2->setColor(SkColorSetARGB(128 * (1 + std::cos(t)), 0, 0, 0x80)); + return true; + } + +private: + typedef SampleView INHERITED; + + sk_sp<sksg::Rect> fRect1, + fRect2; + sk_sp<sksg::Color> fColor1, + fColor2; + sk_sp<sksg::Group> fRoot; +}; + +static SkView* SGInvalFactory() { return new SGInvalView; } +static SkViewRegister reg(SGInvalFactory); |