diff options
author | Florin Malita <fmalita@chromium.org> | 2018-05-25 12:43:51 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-05-25 17:11:52 +0000 |
commit | 3b526b05d652ad6c310d9c636187b20b51c7648c (patch) | |
tree | 6c6fa99f2e80db81e9c3f593fe5883aabdaa442b /modules/sksg/src | |
parent | 59da548b0c4d4239e0ec1855d3f7f77a2bff4b93 (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')
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 |