1 | /* |
2 | * Copyright 2020 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "modules/skottie/src/Adapter.h" |
9 | #include "modules/skottie/src/SkottieJson.h" |
10 | #include "modules/skottie/src/SkottiePriv.h" |
11 | #include "modules/skottie/src/SkottieValue.h" |
12 | #include "modules/skottie/src/layers/shapelayer/ShapeLayer.h" |
13 | #include "modules/sksg/include/SkSGDashEffect.h" |
14 | #include "modules/sksg/include/SkSGPaint.h" |
15 | |
16 | namespace skottie { |
17 | namespace internal { |
18 | |
19 | namespace { |
20 | |
21 | class FillStrokeAdapter final : public DiscardableAdapterBase<FillStrokeAdapter, sksg::PaintNode> { |
22 | public: |
23 | enum class Type { kFill, kStroke }; |
24 | |
25 | FillStrokeAdapter(const skjson::ObjectValue& jpaint, |
26 | const AnimationBuilder& abuilder, |
27 | sk_sp<sksg::PaintNode> paint_node, |
28 | sk_sp<AnimatablePropertyContainer> gradient_adapter, |
29 | Type type) |
30 | : INHERITED(std::move(paint_node)) |
31 | , fShaderType(gradient_adapter ? ShaderType::kGradient : ShaderType::kColor) { |
32 | |
33 | this->attachDiscardableAdapter(std::move(gradient_adapter)); |
34 | |
35 | this->bind(abuilder, jpaint["o" ], fOpacity); |
36 | |
37 | this->node()->setAntiAlias(true); |
38 | |
39 | if (type == Type::kStroke) { |
40 | this->bind(abuilder, jpaint["w" ], fStrokeWidth); |
41 | |
42 | this->node()->setStyle(SkPaint::kStroke_Style); |
43 | this->node()->setStrokeMiter(ParseDefault<SkScalar>(jpaint["ml" ], 4.0f)); |
44 | |
45 | static constexpr SkPaint::Join gJoins[] = { |
46 | SkPaint::kMiter_Join, |
47 | SkPaint::kRound_Join, |
48 | SkPaint::kBevel_Join, |
49 | }; |
50 | this->node()->setStrokeJoin( |
51 | gJoins[std::min<size_t>(ParseDefault<size_t>(jpaint["lj" ], 1) - 1, |
52 | SK_ARRAY_COUNT(gJoins) - 1)]); |
53 | |
54 | static constexpr SkPaint::Cap gCaps[] = { |
55 | SkPaint::kButt_Cap, |
56 | SkPaint::kRound_Cap, |
57 | SkPaint::kSquare_Cap, |
58 | }; |
59 | this->node()->setStrokeCap( |
60 | gCaps[std::min<size_t>(ParseDefault<size_t>(jpaint["lc" ], 1) - 1, |
61 | SK_ARRAY_COUNT(gCaps) - 1)]); |
62 | } |
63 | |
64 | if (fShaderType == ShaderType::kColor) { |
65 | this->bind(abuilder, jpaint["c" ], fColor); |
66 | } |
67 | } |
68 | |
69 | private: |
70 | void onSync() override { |
71 | this->node()->setOpacity(fOpacity * 0.01f); |
72 | this->node()->setStrokeWidth(fStrokeWidth); |
73 | |
74 | if (fShaderType == ShaderType::kColor) { |
75 | auto* color_node = static_cast<sksg::Color*>(this->node().get()); |
76 | color_node->setColor(fColor); |
77 | } |
78 | } |
79 | |
80 | enum class ShaderType { kColor, kGradient }; |
81 | |
82 | const ShaderType fShaderType; |
83 | |
84 | VectorValue fColor; |
85 | ScalarValue fOpacity = 100, |
86 | fStrokeWidth = 1; |
87 | |
88 | using INHERITED = DiscardableAdapterBase<FillStrokeAdapter, sksg::PaintNode>; |
89 | }; |
90 | |
91 | class DashAdapter final : public DiscardableAdapterBase<DashAdapter, sksg::DashEffect> { |
92 | public: |
93 | DashAdapter(const skjson::ArrayValue& jdash, |
94 | const AnimationBuilder& abuilder, |
95 | sk_sp<sksg::GeometryNode> geo) |
96 | : INHERITED(sksg::DashEffect::Make(std::move(geo))) { |
97 | SkASSERT(jdash.size() > 1); |
98 | |
99 | // The dash is encoded as an arbitrary number of intervals (alternating dash/gap), |
100 | // plus a single trailing offset. Each value can be animated independently. |
101 | const auto interval_count = jdash.size() - 1; |
102 | fIntervals.resize(interval_count, 0); |
103 | |
104 | for (size_t i = 0; i < jdash.size(); ++i) { |
105 | if (const skjson::ObjectValue* jint = jdash[i]) { |
106 | auto* target = i < interval_count |
107 | ? &fIntervals[i] |
108 | : &fOffset; |
109 | this->bind(abuilder, (*jint)["v" ], target); |
110 | } |
111 | } |
112 | } |
113 | |
114 | private: |
115 | void onSync() override { |
116 | this->node()->setPhase(fOffset); |
117 | this->node()->setIntervals(fIntervals); |
118 | } |
119 | |
120 | std::vector<ScalarValue> fIntervals; |
121 | ScalarValue fOffset = 0; |
122 | |
123 | using INHERITED = DiscardableAdapterBase<DashAdapter, sksg::DashEffect>; |
124 | }; |
125 | |
126 | } // namespace |
127 | |
128 | sk_sp<sksg::PaintNode> ShapeBuilder::AttachFill(const skjson::ObjectValue& jpaint, |
129 | const AnimationBuilder* abuilder, |
130 | sk_sp<sksg::PaintNode> paint_node, |
131 | sk_sp<AnimatablePropertyContainer> gradient) { |
132 | return abuilder->attachDiscardableAdapter<FillStrokeAdapter> |
133 | (jpaint, |
134 | *abuilder, |
135 | std::move(paint_node), |
136 | std::move(gradient), |
137 | FillStrokeAdapter::Type::kFill); |
138 | } |
139 | |
140 | sk_sp<sksg::PaintNode> ShapeBuilder::AttachStroke(const skjson::ObjectValue& jpaint, |
141 | const AnimationBuilder* abuilder, |
142 | sk_sp<sksg::PaintNode> paint_node, |
143 | sk_sp<AnimatablePropertyContainer> gradient) { |
144 | return abuilder->attachDiscardableAdapter<FillStrokeAdapter> |
145 | (jpaint, |
146 | *abuilder, |
147 | std::move(paint_node), |
148 | std::move(gradient), |
149 | FillStrokeAdapter::Type::kStroke); |
150 | } |
151 | |
152 | sk_sp<sksg::PaintNode> ShapeBuilder::AttachColorFill(const skjson::ObjectValue& jpaint, |
153 | const AnimationBuilder* abuilder) { |
154 | auto color_node = sksg::Color::Make(SK_ColorBLACK); |
155 | abuilder->dispatchColorProperty(color_node); |
156 | |
157 | return AttachFill(jpaint, abuilder, std::move(color_node)); |
158 | } |
159 | |
160 | sk_sp<sksg::PaintNode> ShapeBuilder::AttachColorStroke(const skjson::ObjectValue& jpaint, |
161 | const AnimationBuilder* abuilder) { |
162 | auto color_node = sksg::Color::Make(SK_ColorBLACK); |
163 | abuilder->dispatchColorProperty(color_node); |
164 | |
165 | return AttachStroke(jpaint, abuilder, std::move(color_node)); |
166 | } |
167 | |
168 | std::vector<sk_sp<sksg::GeometryNode>> ShapeBuilder::AdjustStrokeGeometry( |
169 | const skjson::ObjectValue& jstroke, |
170 | const AnimationBuilder* abuilder, |
171 | std::vector<sk_sp<sksg::GeometryNode>>&& geos) { |
172 | |
173 | const skjson::ArrayValue* jdash = jstroke["d" ]; |
174 | if (jdash && jdash->size() > 1) { |
175 | for (size_t i = 0; i < geos.size(); ++i) { |
176 | geos[i] = abuilder->attachDiscardableAdapter<DashAdapter>( |
177 | *jdash, *abuilder, std::move(geos[i])); |
178 | } |
179 | } |
180 | |
181 | return std::move(geos); |
182 | } |
183 | |
184 | } // namespace internal |
185 | } // namespace skottie |
186 | |