From 6ec66b9d3863eb9b3d78246eb29119abe7775e41 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Fri, 3 Jul 2020 14:01:25 -0400 Subject: [PATCH] [skottie] Add shape layer Offset Paths support Based on SkPathOps for now. Change-Id: Id27c8a235cbd4ab5083735b67cf5d2635ee16cfc Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300497 Commit-Queue: Florin Malita Reviewed-by: Mike Reed --- modules/skottie/skottie.gni | 1 + .../src/layers/shapelayer/OffsetPaths.cpp | 67 +++++++++++++++++++ .../src/layers/shapelayer/ShapeLayer.cpp | 2 + .../src/layers/shapelayer/ShapeLayer.h | 3 + modules/sksg/include/SkSGGeometryEffect.h | 26 +++++++ modules/sksg/src/SkSGGeometryEffect.cpp | 29 ++++++++ .../skottie/skottie-offsetpaths-effect.json | 1 + 7 files changed, 129 insertions(+) create mode 100644 modules/skottie/src/layers/shapelayer/OffsetPaths.cpp create mode 100644 resources/skottie/skottie-offsetpaths-effect.json diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni index 3b64e1dac6..4fa1f3d60c 100644 --- a/modules/skottie/skottie.gni +++ b/modules/skottie/skottie.gni @@ -72,6 +72,7 @@ skia_skottie_sources = [ "$_src/layers/shapelayer/FillStroke.cpp", "$_src/layers/shapelayer/Gradient.cpp", "$_src/layers/shapelayer/MergePaths.cpp", + "$_src/layers/shapelayer/OffsetPaths.cpp", "$_src/layers/shapelayer/Polystar.cpp", "$_src/layers/shapelayer/Rectangle.cpp", "$_src/layers/shapelayer/Repeater.cpp", diff --git a/modules/skottie/src/layers/shapelayer/OffsetPaths.cpp b/modules/skottie/src/layers/shapelayer/OffsetPaths.cpp new file mode 100644 index 0000000000..527473ac59 --- /dev/null +++ b/modules/skottie/src/layers/shapelayer/OffsetPaths.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "modules/skottie/src/Adapter.h" +#include "modules/skottie/src/SkottieJson.h" +#include "modules/skottie/src/SkottiePriv.h" +#include "modules/skottie/src/SkottieValue.h" +#include "modules/skottie/src/layers/shapelayer/ShapeLayer.h" +#include "modules/sksg/include/SkSGGeometryEffect.h" + +namespace skottie::internal { + +namespace { + +class OffsetPathsAdapter final : public DiscardableAdapterBase { +public: + OffsetPathsAdapter(const skjson::ObjectValue& joffset, + const AnimationBuilder& abuilder, + sk_sp child) + : INHERITED(sksg::OffsetEffect::Make(std::move(child))) { + static constexpr SkPaint::Join gJoinMap[] = { + SkPaint::kMiter_Join, // 'lj': 1 + SkPaint::kRound_Join, // 'lj': 2 + SkPaint::kBevel_Join, // 'lj': 3 + }; + + const auto join = ParseDefault(joffset["lj"], 1) - 1; + this->node()->setJoin(gJoinMap[SkTPin(join, 0, SK_ARRAY_COUNT(gJoinMap) - 1)]); + + this->bind(abuilder, joffset["a" ], fAmount); + this->bind(abuilder, joffset["ml"], fMiterLimit); + } + +private: + void onSync() override { + this->node()->setOffset(fAmount); + this->node()->setMiterLimit(fMiterLimit); + } + + ScalarValue fAmount = 0, + fMiterLimit = 0; + + using INHERITED = DiscardableAdapterBase; +}; + +} // namespace + +std::vector> ShapeBuilder::AttachOffsetGeometryEffect( + const skjson::ObjectValue& jround, const AnimationBuilder* abuilder, + std::vector>&& geos) { + std::vector> offsetted; + offsetted.reserve(geos.size()); + + for (auto& g : geos) { + offsetted.push_back(abuilder->attachDiscardableAdapter + (jround, *abuilder, std::move(g))); + } + + return offsetted; +} + +} // namespace skottie::internal diff --git a/modules/skottie/src/layers/shapelayer/ShapeLayer.cpp b/modules/skottie/src/layers/shapelayer/ShapeLayer.cpp index e9993a1d1d..de892d3d39 100644 --- a/modules/skottie/src/layers/shapelayer/ShapeLayer.cpp +++ b/modules/skottie/src/layers/shapelayer/ShapeLayer.cpp @@ -47,6 +47,7 @@ static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { ShapeBuilder::AttachMergeGeometryEffect, ShapeBuilder::AttachTrimGeometryEffect, ShapeBuilder::AttachRoundGeometryEffect, + ShapeBuilder::AttachOffsetGeometryEffect, }; using PaintAttacherT = sk_sp (*)(const skjson::ObjectValue&, @@ -99,6 +100,7 @@ const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) { { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect + { "op", ShapeType::kGeometryEffect, 3 }, // offset -> AttachOffsetGeometryEffect { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect { "rp", ShapeType::kDrawEffect , 0 }, // repeater -> AttachRepeaterDrawEffect diff --git a/modules/skottie/src/layers/shapelayer/ShapeLayer.h b/modules/skottie/src/layers/shapelayer/ShapeLayer.h index 9ca498122a..f7a3b20863 100644 --- a/modules/skottie/src/layers/shapelayer/ShapeLayer.h +++ b/modules/skottie/src/layers/shapelayer/ShapeLayer.h @@ -67,6 +67,9 @@ public: static std::vector> AttachRoundGeometryEffect( const skjson::ObjectValue&, const AnimationBuilder*, std::vector>&&); + static std::vector> AttachOffsetGeometryEffect( + const skjson::ObjectValue&, const AnimationBuilder*, + std::vector>&&); static std::vector> AdjustStrokeGeometry( const skjson::ObjectValue&, const AnimationBuilder*, std::vector>&&); diff --git a/modules/sksg/include/SkSGGeometryEffect.h b/modules/sksg/include/SkSGGeometryEffect.h index 01b501c6fa..030695b927 100644 --- a/modules/sksg/include/SkSGGeometryEffect.h +++ b/modules/sksg/include/SkSGGeometryEffect.h @@ -10,6 +10,7 @@ #include "modules/sksg/include/SkSGGeometryNode.h" +#include "include/core/SkPaint.h" #include "include/core/SkPath.h" #include "include/effects/SkTrimPathEffect.h" #include "modules/sksg/include/SkSGTransform.h" @@ -139,6 +140,31 @@ private: using INHERITED = GeometryEffect; }; +/** + * Apply an offset effect to the child geometry. + */ +class OffsetEffect final : public GeometryEffect { +public: + static sk_sp Make(sk_sp child) { + return child ? sk_sp(new OffsetEffect(std::move(child))) : nullptr; + } + + SG_ATTRIBUTE(Offset , SkScalar , fOffset ) + SG_ATTRIBUTE(MiterLimit , SkScalar , fMiterLimit) + SG_ATTRIBUTE(Join , SkPaint::Join, fJoin ) + +private: + explicit OffsetEffect(sk_sp child) : INHERITED(std::move(child)) {} + + SkPath onRevalidateEffect(const sk_sp&) override; + + SkScalar fOffset = 0, + fMiterLimit = 4; + SkPaint::Join fJoin = SkPaint::kMiter_Join; + + using INHERITED = GeometryEffect; +}; + } // namespace sksg #endif // SkSGGeometryEffect_DEFINED diff --git a/modules/sksg/src/SkSGGeometryEffect.cpp b/modules/sksg/src/SkSGGeometryEffect.cpp index 935c917485..3941dac733 100644 --- a/modules/sksg/src/SkSGGeometryEffect.cpp +++ b/modules/sksg/src/SkSGGeometryEffect.cpp @@ -12,8 +12,11 @@ #include "include/effects/SkCornerPathEffect.h" #include "include/effects/SkDashPathEffect.h" #include "include/effects/SkTrimPathEffect.h" +#include "include/pathops/SkPathOps.h" #include "modules/sksg/src/SkSGTransformPriv.h" +#include + namespace sksg { GeometryEffect::GeometryEffect(sk_sp child) @@ -133,4 +136,30 @@ SkPath RoundEffect::onRevalidateEffect(const sk_sp& child) { return path; } +SkPath OffsetEffect::onRevalidateEffect(const sk_sp& child) { + SkPath path = child->asPath(); + + if (!SkScalarNearlyZero(fOffset)) { + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(std::abs(fOffset) * 2); + paint.setStrokeMiter(fMiterLimit); + paint.setStrokeJoin(fJoin); + + SkPath fill_path; + paint.getFillPath(path, &fill_path, nullptr); + + if (fOffset > 0) { + Op(path, fill_path, kUnion_SkPathOp, &path); + } else { + Op(path, fill_path, kDifference_SkPathOp, &path); + } + + // TODO: this seems to break path combining (winding mismatch?) + // Simplify(path, &path); + } + + return path; +} + } // namesapce sksg diff --git a/resources/skottie/skottie-offsetpaths-effect.json b/resources/skottie/skottie-offsetpaths-effect.json new file mode 100644 index 0000000000..900062f68f --- /dev/null +++ b/resources/skottie/skottie-offsetpaths-effect.json @@ -0,0 +1 @@ +{"v":"5.7.0","fr":60,"ip":0,"op":301,"w":500,"h":500,"nm":"offset paths","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"bevel","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[338,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[86,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.279090076685,0.751332700253,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":37,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":100,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":210,"s":[-30]},{"t":300,"s":[0]}],"ix":1},"lj":3,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.037944238633,0.091421566904,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"round","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116,367,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[86,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.279090076685,0.751332700253,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":37,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":100,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":210,"s":[-30]},{"t":300,"s":[0]}],"ix":1},"lj":2,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.037944238633,0.091421566904,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"miter","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116,129,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[86,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.279090076685,0.751332700253,1,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":37,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":100,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"op","nm":"Offset Paths 1","a":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":90,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":210,"s":[-30]},{"t":300,"s":[0]}],"ix":1},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.037944238633,0.091421566904,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":1,"nm":"Light Gray Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#c8c8c8","ip":0,"op":301,"st":0,"bm":0}],"markers":[]} \ No newline at end of file