1 /*
2 * Copyright 2018 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/SkottieAdapter.h"
9
10 #include "include/core/SkFont.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkMatrix44.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkRRect.h"
15 #include "include/private/SkTo.h"
16 #include "include/utils/Sk3D.h"
17 #include "modules/skottie/src/SkottieValue.h"
18 #include "modules/sksg/include/SkSGDraw.h"
19 #include "modules/sksg/include/SkSGGradient.h"
20 #include "modules/sksg/include/SkSGGroup.h"
21 #include "modules/sksg/include/SkSGPaint.h"
22 #include "modules/sksg/include/SkSGPath.h"
23 #include "modules/sksg/include/SkSGRect.h"
24 #include "modules/sksg/include/SkSGTransform.h"
25 #include "modules/sksg/include/SkSGTrimEffect.h"
26
27 #include <cmath>
28 #include <utility>
29
30 namespace skottie {
31
32 namespace internal {
33
34 DiscardableAdaptorBase::DiscardableAdaptorBase() = default;
35
setAnimators(sksg::AnimatorList && animators)36 void DiscardableAdaptorBase::setAnimators(sksg::AnimatorList&& animators) {
37 fAnimators = std::move(animators);
38 }
39
onTick(float t)40 void DiscardableAdaptorBase::onTick(float t) {
41 for (auto& animator : fAnimators) {
42 animator->tick(t);
43 }
44
45 this->onSync();
46 }
47
48 } // namespace internal
49
RRectAdapter(sk_sp<sksg::RRect> wrapped_node)50 RRectAdapter::RRectAdapter(sk_sp<sksg::RRect> wrapped_node)
51 : fRRectNode(std::move(wrapped_node)) {}
52
53 RRectAdapter::~RRectAdapter() = default;
54
apply()55 void RRectAdapter::apply() {
56 // BM "position" == "center position"
57 auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2,
58 fPosition.y() - fSize.height() / 2,
59 fSize.width(), fSize.height()),
60 fRadius.width(),
61 fRadius.height());
62 fRRectNode->setRRect(rr);
63 }
64
TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>> matrix)65 TransformAdapter2D::TransformAdapter2D(sk_sp<sksg::Matrix<SkMatrix>> matrix)
66 : fMatrixNode(std::move(matrix)) {}
67
68 TransformAdapter2D::~TransformAdapter2D() = default;
69
totalMatrix() const70 SkMatrix TransformAdapter2D::totalMatrix() const {
71 SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
72
73 t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
74 t.postRotate(fRotation);
75 t.postTranslate(fPosition.x(), fPosition.y());
76 // TODO: skew
77
78 return t;
79 }
80
apply()81 void TransformAdapter2D::apply() {
82 fMatrixNode->setMatrix(this->totalMatrix());
83 }
84
Vec3(const VectorValue & v)85 TransformAdapter3D::Vec3::Vec3(const VectorValue& v) {
86 fX = v.size() > 0 ? v[0] : 0;
87 fY = v.size() > 1 ? v[1] : 0;
88 fZ = v.size() > 2 ? v[2] : 0;
89 }
90
TransformAdapter3D()91 TransformAdapter3D::TransformAdapter3D()
92 : fMatrixNode(sksg::Matrix<SkMatrix44>::Make(SkMatrix::I())) {}
93
94 TransformAdapter3D::~TransformAdapter3D() = default;
95
refTransform() const96 sk_sp<sksg::Transform> TransformAdapter3D::refTransform() const {
97 return fMatrixNode;
98 }
99
totalMatrix() const100 SkMatrix44 TransformAdapter3D::totalMatrix() const {
101 SkMatrix44 t;
102
103 t.setTranslate(-fAnchorPoint.fX, -fAnchorPoint.fY, -fAnchorPoint.fZ);
104 t.postScale(fScale.fX / 100, fScale.fY / 100, fScale.fZ / 100);
105
106 SkMatrix44 r;
107 r.setRotateDegreesAbout(0, 0, 1, fRotation.fZ);
108 t.postConcat(r);
109 r.setRotateDegreesAbout(0, 1, 0, fRotation.fY);
110 t.postConcat(r);
111 r.setRotateDegreesAbout(1, 0, 0, fRotation.fX);
112 t.postConcat(r);
113
114 t.postTranslate(fPosition.fX, fPosition.fY, fPosition.fZ);
115
116 return t;
117 }
118
apply()119 void TransformAdapter3D::apply() {
120 fMatrixNode->setMatrix(this->totalMatrix());
121 }
122
CameraAdapter(const SkSize & viewport_size)123 CameraAdapter:: CameraAdapter(const SkSize& viewport_size)
124 : fViewportSize(viewport_size) {}
125
126 CameraAdapter::~CameraAdapter() = default;
127
totalMatrix() const128 SkMatrix44 CameraAdapter::totalMatrix() const {
129 // Camera parameters:
130 //
131 // * location -> position attribute
132 // * point of interest -> anchor point attribute
133 // * orientation -> rotation attribute
134 //
135 SkPoint3 pos = { this->getPosition().fX,
136 this->getPosition().fY,
137 -this->getPosition().fZ },
138 poi = { this->getAnchorPoint().fX,
139 this->getAnchorPoint().fY,
140 -this->getAnchorPoint().fZ },
141 up = { 0, 1, 0 };
142
143 // Initial camera vector.
144 SkMatrix44 cam_t;
145 Sk3LookAt(&cam_t, pos, poi, up);
146
147 // Rotation origin is camera position.
148 {
149 SkMatrix44 rot;
150 rot.setRotateDegreesAbout(1, 0, 0, this->getRotation().fX);
151 cam_t.postConcat(rot);
152 rot.setRotateDegreesAbout(0, 1, 0, this->getRotation().fY);
153 cam_t.postConcat(rot);
154 rot.setRotateDegreesAbout(0, 0, 1, -this->getRotation().fZ);
155 cam_t.postConcat(rot);
156 }
157
158 // Flip world Z, as it is opposite of what Sk3D expects.
159 cam_t.preScale(1, 1, -1);
160
161 // View parameters:
162 //
163 // * size -> composition size (TODO: AE seems to base it on width only?)
164 // * distance -> "zoom" camera attribute
165 //
166 const auto view_size = SkTMax(fViewportSize.width(), fViewportSize.height()),
167 view_distance = this->getZoom(),
168 view_angle = std::atan(sk_ieee_float_divide(view_size * 0.5f, view_distance));
169
170 SkMatrix44 persp_t;
171 Sk3Perspective(&persp_t, 0, view_distance, 2 * view_angle);
172 persp_t.postScale(view_size * 0.5f, view_size * 0.5f, 1);
173
174 SkMatrix44 t;
175 t.setTranslate(fViewportSize.width() * 0.5f, fViewportSize.height() * 0.5f, 0);
176 t.preConcat(persp_t);
177 t.preConcat(cam_t);
178
179 return t;
180 }
181
RepeaterAdapter(sk_sp<sksg::RenderNode> repeater_node,Composite composite)182 RepeaterAdapter::RepeaterAdapter(sk_sp<sksg::RenderNode> repeater_node, Composite composite)
183 : fRepeaterNode(repeater_node)
184 , fComposite(composite)
185 , fRoot(sksg::Group::Make()) {}
186
187 RepeaterAdapter::~RepeaterAdapter() = default;
188
apply()189 void RepeaterAdapter::apply() {
190 static constexpr SkScalar kMaxCount = 512;
191 const auto count = static_cast<size_t>(SkTPin(fCount, 0.0f, kMaxCount) + 0.5f);
192
193 const auto& compute_transform = [this] (size_t index) {
194 const auto t = fOffset + index;
195
196 // Position, scale & rotation are "scaled" by index/offset.
197 SkMatrix m = SkMatrix::MakeTrans(-fAnchorPoint.x(),
198 -fAnchorPoint.y());
199 m.postScale(std::pow(fScale.x() * .01f, fOffset),
200 std::pow(fScale.y() * .01f, fOffset));
201 m.postRotate(t * fRotation);
202 m.postTranslate(t * fPosition.x() + fAnchorPoint.x(),
203 t * fPosition.y() + fAnchorPoint.y());
204
205 return m;
206 };
207
208 // TODO: start/end opacity support.
209
210 // TODO: we can avoid rebuilding all the fragments in most cases.
211 fRoot->clear();
212 for (size_t i = 0; i < count; ++i) {
213 const auto insert_index = (fComposite == Composite::kAbove) ? i : count - i - 1;
214 fRoot->addChild(sksg::TransformEffect::Make(fRepeaterNode,
215 compute_transform(insert_index)));
216 }
217 }
218
PolyStarAdapter(sk_sp<sksg::Path> wrapped_node,Type t)219 PolyStarAdapter::PolyStarAdapter(sk_sp<sksg::Path> wrapped_node, Type t)
220 : fPathNode(std::move(wrapped_node))
221 , fType(t) {}
222
223 PolyStarAdapter::~PolyStarAdapter() = default;
224
apply()225 void PolyStarAdapter::apply() {
226 static constexpr int kMaxPointCount = 100000;
227 const auto count = SkToUInt(SkTPin(SkScalarRoundToInt(fPointCount), 0, kMaxPointCount));
228 const auto arc = sk_ieee_float_divide(SK_ScalarPI * 2, count);
229
230 const auto pt_on_circle = [](const SkPoint& c, SkScalar r, SkScalar a) {
231 return SkPoint::Make(c.x() + r * std::cos(a),
232 c.y() + r * std::sin(a));
233 };
234
235 // TODO: inner/outer "roundness"?
236
237 SkPath poly;
238
239 auto angle = SkDegreesToRadians(fRotation - 90);
240 poly.moveTo(pt_on_circle(fPosition, fOuterRadius, angle));
241 poly.incReserve(fType == Type::kStar ? count * 2 : count);
242
243 for (unsigned i = 0; i < count; ++i) {
244 if (fType == Type::kStar) {
245 poly.lineTo(pt_on_circle(fPosition, fInnerRadius, angle + arc * 0.5f));
246 }
247 angle += arc;
248 poly.lineTo(pt_on_circle(fPosition, fOuterRadius, angle));
249 }
250
251 poly.close();
252 fPathNode->setPath(poly);
253 }
254
GradientAdapter(sk_sp<sksg::Gradient> grad,size_t stopCount)255 GradientAdapter::GradientAdapter(sk_sp<sksg::Gradient> grad, size_t stopCount)
256 : fGradient(std::move(grad))
257 , fStopCount(stopCount) {}
258
apply()259 void GradientAdapter::apply() {
260 this->onApply();
261
262 // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ]
263
264 if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) {
265 // apply() may get called before the stops are set, so only log when we have some stops.
266 if (!fColorStops.empty()) {
267 SkDebugf("!! Invalid gradient stop array size: %zu\n", fColorStops.size());
268 }
269 return;
270 }
271
272 std::vector<sksg::Gradient::ColorStop> stops;
273
274 // TODO: merge/lerp opacity stops
275 const auto csEnd = fColorStops.cbegin() + fStopCount * 4;
276 for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) {
277 const auto pos = cs[0];
278 const VectorValue rgb({ cs[1], cs[2], cs[3] });
279
280 stops.push_back({ pos, ValueTraits<VectorValue>::As<SkColor>(rgb) });
281 }
282
283 fGradient->setColorStops(std::move(stops));
284 }
285
LinearGradientAdapter(sk_sp<sksg::LinearGradient> grad,size_t stopCount)286 LinearGradientAdapter::LinearGradientAdapter(sk_sp<sksg::LinearGradient> grad, size_t stopCount)
287 : INHERITED(std::move(grad), stopCount) {}
288
onApply()289 void LinearGradientAdapter::onApply() {
290 auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
291 grad->setStartPoint(this->startPoint());
292 grad->setEndPoint(this->endPoint());
293 }
294
RadialGradientAdapter(sk_sp<sksg::RadialGradient> grad,size_t stopCount)295 RadialGradientAdapter::RadialGradientAdapter(sk_sp<sksg::RadialGradient> grad, size_t stopCount)
296 : INHERITED(std::move(grad), stopCount) {}
297
onApply()298 void RadialGradientAdapter::onApply() {
299 auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
300 grad->setStartCenter(this->startPoint());
301 grad->setEndCenter(this->startPoint());
302 grad->setStartRadius(0);
303 grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint()));
304 }
305
TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)306 TrimEffectAdapter::TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)
307 : fTrimEffect(std::move(trimEffect)) {
308 SkASSERT(fTrimEffect);
309 }
310
311 TrimEffectAdapter::~TrimEffectAdapter() = default;
312
apply()313 void TrimEffectAdapter::apply() {
314 // BM semantics: start/end are percentages, offset is "degrees" (?!).
315 const auto start = fStart / 100,
316 end = fEnd / 100,
317 offset = fOffset / 360;
318
319 auto startT = SkTMin(start, end) + offset,
320 stopT = SkTMax(start, end) + offset;
321 auto mode = SkTrimPathEffect::Mode::kNormal;
322
323 if (stopT - startT < 1) {
324 startT -= SkScalarFloorToScalar(startT);
325 stopT -= SkScalarFloorToScalar(stopT);
326
327 if (startT > stopT) {
328 using std::swap;
329 swap(startT, stopT);
330 mode = SkTrimPathEffect::Mode::kInverted;
331 }
332 } else {
333 startT = 0;
334 stopT = 1;
335 }
336
337 fTrimEffect->setStart(startT);
338 fTrimEffect->setStop(stopT);
339 fTrimEffect->setMode(mode);
340 }
341
342 } // namespace skottie
343