From 51b8c89b1c2772cf4126ec5c9bfaf4b0dccbd2e3 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Sun, 7 Jan 2018 08:54:24 -0500 Subject: [skotty,sksg] Initial trim path effect TBR= Change-Id: I5b612c5aade23f727a3622daeff2534f68e6b66a Reviewed-on: https://skia-review.googlesource.com/91404 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/Skotty.cpp | 47 ++++++++++++++++++++- experimental/sksg/SkSGNode.h | 1 + experimental/sksg/geometry/SkSGTrimEffect.cpp | 61 +++++++++++++++++++++++++++ experimental/sksg/geometry/SkSGTrimEffect.h | 53 +++++++++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 experimental/sksg/geometry/SkSGTrimEffect.cpp create mode 100644 experimental/sksg/geometry/SkSGTrimEffect.h (limited to 'experimental') diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp index 8c48d1f871..a13a04c03c 100644 --- a/experimental/skotty/Skotty.cpp +++ b/experimental/skotty/Skotty.cpp @@ -24,6 +24,7 @@ #include "SkSGPath.h" #include "SkSGRect.h" #include "SkSGTransform.h" +#include "SkSGTrimEffect.h" #include "SkStream.h" #include "SkTArray.h" #include "SkTHash.h" @@ -308,7 +309,8 @@ std::vector> AttachMergeGeometryEffect( sksg::Merge::Mode::kXOR , // "mm": 5 }; - const auto mode = gModes[SkTPin(ParseInt(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes))]; + const auto mode = gModes[SkTPin(ParseInt(jmerge["mm"], 1) - 1, + 0, SK_ARRAY_COUNT(gModes) - 1)]; merged.push_back(sksg::Merge::Make(std::move(geos), mode)); LOG("** Attached merge path effect, mode: %d\n", mode); @@ -316,6 +318,47 @@ std::vector> AttachMergeGeometryEffect( return merged; } +std::vector> AttachTrimGeometryEffect( + const Json::Value& jtrim, AttachContext* ctx, std::vector>&& geos) { + + enum class Mode { + kMerged, // "m": 1 + kSeparate, // "m": 2 + } gModes[] = { Mode::kMerged, Mode::kSeparate }; + + const auto mode = gModes[SkTPin(ParseInt(jtrim["m"], 1) - 1, + 0, SK_ARRAY_COUNT(gModes) - 1)]; + + std::vector> inputs; + if (mode == Mode::kMerged) { + inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge)); + } else { + inputs = std::move(geos); + } + + std::vector> trimmed; + trimmed.reserve(inputs.size()); + for (const auto& i : inputs) { + const auto trim = sksg::TrimEffect::Make(i); + trimmed.push_back(trim); + AttachProperty(jtrim["s"], ctx, trim, + [](const sk_sp& node, const SkScalar& s) { + node->setStart(s * 0.01f); + }); + AttachProperty(jtrim["e"], ctx, trim, + [](const sk_sp& node, const SkScalar& e) { + node->setEnd(e * 0.01f); + }); + // TODO: "offset" doesn't currently work the same as BM - figure out what's going on. + AttachProperty(jtrim["o"], ctx, trim, + [](const sk_sp& node, const SkScalar& o) { + node->setOffset(o * 0.01f); + }); + } + + return trimmed; +} + using GeometryAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPathGeometry, @@ -341,6 +384,7 @@ using GeometryEffectAttacherT = std::vector>&&); static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { AttachMergeGeometryEffect, + AttachTrimGeometryEffect, }; enum class ShapeType { @@ -367,6 +411,7 @@ const ShapeInfo* FindShapeInfo(const Json::Value& shape) { { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint + { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect { "tr", ShapeType::kTransform , 0 }, // transform -> In-place handler }; diff --git a/experimental/sksg/SkSGNode.h b/experimental/sksg/SkSGNode.h index 769e2a4e68..4e1e7b0d2a 100644 --- a/experimental/sksg/SkSGNode.h +++ b/experimental/sksg/SkSGNode.h @@ -70,6 +70,7 @@ private: friend class Merge; friend class Stroke; friend class Transform; + friend class TrimEffect; template void forEachInvalReceiver(Func&&) const; diff --git a/experimental/sksg/geometry/SkSGTrimEffect.cpp b/experimental/sksg/geometry/SkSGTrimEffect.cpp new file mode 100644 index 0000000000..4c30389877 --- /dev/null +++ b/experimental/sksg/geometry/SkSGTrimEffect.cpp @@ -0,0 +1,61 @@ +/* + * 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 "SkDashPathEffect.h" +#include "SkPathMeasure.h" + +namespace sksg { + +TrimEffect::TrimEffect(sk_sp child) + : fChild(std::move(child)) { + fChild->addInvalReceiver(this); +} + +TrimEffect::~TrimEffect() { + fChild->removeInvalReceiver(this); +} + +// TODO +// This is a quick hack to get something on the screen. What we really want here is to apply +// the geometry transformation and cache the result on revalidation. Or an SkTrimPathEffect. +void TrimEffect::onDraw(SkCanvas* canvas, const SkPaint& paint) const { + SkASSERT(!this->hasInval()); + + SkASSERT(!paint.getPathEffect()); + + const auto path = fChild->asPath(); + SkScalar pathLen = 0; + SkPathMeasure measure(path, false); + do { + pathLen += measure.getLength(); + } while (measure.nextContour()); + + const auto start = SkScalarPin(fStart , 0, 1) * pathLen, + end = SkScalarPin(fEnd , 0, 1) * pathLen, + offset = SkScalarPin(fOffset, 0, 1) * pathLen, + len = SkTMax(end - start, 0); + + const SkScalar dashes[4] = { 0, start, len, pathLen - end }; + SkPaint dashedPaint(paint); + dashedPaint.setPathEffect(SkDashPathEffect::Make(dashes, 4, -offset)); + + canvas->drawPath(path, dashedPaint); +} + +SkPath TrimEffect::onAsPath() const { + return fChild->asPath(); +} + +SkRect TrimEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + SkASSERT(this->hasInval()); + return fChild->revalidate(ic, ctm); +} + +} // namespace sksg diff --git a/experimental/sksg/geometry/SkSGTrimEffect.h b/experimental/sksg/geometry/SkSGTrimEffect.h new file mode 100644 index 0000000000..77da3ab2d0 --- /dev/null +++ b/experimental/sksg/geometry/SkSGTrimEffect.h @@ -0,0 +1,53 @@ +/* + * 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 SkSGTrimEffect_DEFINED +#define SkSGTrimEffect_DEFINED + +#include "SkSGGeometryNode.h" + +#include "SkPath.h" + +class SkCanvas; +class SkPaint; + +namespace sksg { + +/** + * Concrete Geometry node, applying a trim effect to its child. + */ +class TrimEffect final : public GeometryNode { +public: + static sk_sp Make(sk_sp child) { + return child ? sk_sp(new TrimEffect(std::move(child))) : nullptr; + } + + ~TrimEffect() override; + + SG_ATTRIBUTE(Start , SkScalar, fStart ) + SG_ATTRIBUTE(End , SkScalar, fEnd ) + SG_ATTRIBUTE(Offset, SkScalar, fOffset) + +protected: + void onDraw(SkCanvas*, const SkPaint&) const override; + + SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; + +private: + explicit TrimEffect(sk_sp); + + const sk_sp fChild; + + SkScalar fStart = 0, // starting t + fEnd = 1, // ending t + fOffset = 0; // t offset +}; + +} // namespace sksg + +#endif // SkSGTrimEffect_DEFINED -- cgit v1.2.3