/src/skia/modules/skottie/src/Transform.cpp
Line | Count | Source (jump to first uncovered line) |
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/Transform.h" |
9 | | |
10 | | #include "include/core/SkRefCnt.h" |
11 | | #include "include/core/SkScalar.h" |
12 | | #include "include/private/base/SkAssert.h" |
13 | | #include "include/private/base/SkTPin.h" |
14 | | #include "modules/skottie/src/SkottiePriv.h" |
15 | | #include "modules/sksg/include/SkSGTransform.h" |
16 | | #include "src/utils/SkJSON.h" |
17 | | |
18 | | #include <cmath> |
19 | | |
20 | | namespace skottie { |
21 | | namespace internal { |
22 | | |
23 | | TransformAdapter2D::TransformAdapter2D(const AnimationBuilder& abuilder, |
24 | | const skjson::ObjectValue* janchor_point, |
25 | | const skjson::ObjectValue* jposition, |
26 | | const skjson::ObjectValue* jscale, |
27 | | const skjson::ObjectValue* jrotation, |
28 | | const skjson::ObjectValue* jskew, |
29 | | const skjson::ObjectValue* jskew_axis, |
30 | | bool auto_orient) |
31 | 100k | : INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) { |
32 | | |
33 | 100k | this->bind(abuilder, janchor_point, fAnchorPoint); |
34 | 100k | this->bind(abuilder, jscale , fScale); |
35 | 100k | this->bind(abuilder, jrotation , fRotation); |
36 | 100k | this->bind(abuilder, jskew , fSkew); |
37 | 100k | this->bind(abuilder, jskew_axis , fSkewAxis); |
38 | | |
39 | 100k | this->bindAutoOrientable(abuilder, jposition, &fPosition, auto_orient ? &fOrientation |
40 | 100k | : nullptr); |
41 | 100k | } |
42 | | |
43 | 100k | TransformAdapter2D::~TransformAdapter2D() {} |
44 | | |
45 | 53.9k | void TransformAdapter2D::onSync() { |
46 | 53.9k | this->node()->setMatrix(this->totalMatrix()); |
47 | 53.9k | } |
48 | | |
49 | 141k | SkMatrix TransformAdapter2D::totalMatrix() const { |
50 | 141k | auto skew_matrix = [](float sk, float sa) { |
51 | 141k | if (!sk) return SkMatrix::I(); |
52 | | |
53 | | // AE control limit. |
54 | 576 | static constexpr float kMaxSkewAngle = 85; |
55 | 576 | sk = -SkDegreesToRadians(SkTPin(sk, -kMaxSkewAngle, kMaxSkewAngle)); |
56 | 576 | sa = SkDegreesToRadians(sa); |
57 | | |
58 | | // Similar to CSS/SVG SkewX [1] with an explicit rotation. |
59 | | // [1] https://www.w3.org/TR/css-transforms-1/#SkewXDefined |
60 | 576 | return SkMatrix::RotateRad(sa) |
61 | 576 | * SkMatrix::Skew(std::tan(sk), 0) |
62 | 576 | * SkMatrix::RotateRad(-sa); |
63 | 141k | }; |
64 | | |
65 | 141k | return SkMatrix::Translate(fPosition.x, fPosition.y) |
66 | 141k | * SkMatrix::RotateDeg(fRotation + fOrientation) |
67 | 141k | * skew_matrix (fSkew, fSkewAxis) |
68 | 141k | * SkMatrix::Scale (fScale.x / 100, fScale.y / 100) // 100% based |
69 | 141k | * SkMatrix::Translate(-fAnchorPoint.x, -fAnchorPoint.y); |
70 | 141k | } |
71 | | |
72 | 0 | SkPoint TransformAdapter2D::getAnchorPoint() const { |
73 | 0 | return { fAnchorPoint.x, fAnchorPoint.y }; |
74 | 0 | } |
75 | | |
76 | 0 | void TransformAdapter2D::setAnchorPoint(const SkPoint& ap) { |
77 | 0 | fAnchorPoint = { ap.x(), ap.y() }; |
78 | 0 | this->onSync(); |
79 | 0 | } |
80 | | |
81 | 0 | SkPoint TransformAdapter2D::getPosition() const { |
82 | 0 | return { fPosition.x, fPosition.y }; |
83 | 0 | } |
84 | | |
85 | 0 | void TransformAdapter2D::setPosition(const SkPoint& p) { |
86 | 0 | fPosition = { p.x(), p.y() }; |
87 | 0 | this->onSync(); |
88 | 0 | } |
89 | | |
90 | 0 | SkVector TransformAdapter2D::getScale() const { |
91 | 0 | return { fScale.x, fScale.y }; |
92 | 0 | } |
93 | | |
94 | 0 | void TransformAdapter2D::setScale(const SkVector& s) { |
95 | 0 | fScale = { s.x(), s.y() }; |
96 | 0 | this->onSync(); |
97 | 0 | } |
98 | | |
99 | 0 | void TransformAdapter2D::setRotation(float r) { |
100 | 0 | fRotation = r; |
101 | 0 | this->onSync(); |
102 | 0 | } |
103 | | |
104 | 0 | void TransformAdapter2D::setSkew(float sk) { |
105 | 0 | fSkew = sk; |
106 | 0 | this->onSync(); |
107 | 0 | } |
108 | | |
109 | 0 | void TransformAdapter2D::setSkewAxis(float sa) { |
110 | 0 | fSkewAxis = sa; |
111 | 0 | this->onSync(); |
112 | 0 | } |
113 | | |
114 | | sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& jtransform, |
115 | | sk_sp<sksg::Transform> parent, |
116 | 100k | bool auto_orient) const { |
117 | 100k | const auto* jrotation = &jtransform["r"]; |
118 | 100k | if (jrotation->is<skjson::NullValue>()) { |
119 | | // Some 2D rotations are disguised as 3D... |
120 | 33.8k | jrotation = &jtransform["rz"]; |
121 | 33.8k | } |
122 | | |
123 | 100k | auto adapter = TransformAdapter2D::Make(*this, |
124 | 100k | jtransform["a"], |
125 | 100k | jtransform["p"], |
126 | 100k | jtransform["s"], |
127 | 100k | *jrotation, |
128 | 100k | jtransform["sk"], |
129 | 100k | jtransform["sa"], |
130 | 100k | auto_orient); |
131 | 100k | SkASSERT(adapter); |
132 | | |
133 | 100k | const auto dispatched = this->dispatchTransformProperty(adapter); |
134 | | |
135 | 100k | if (adapter->isStatic()) { |
136 | 87.9k | if (!dispatched && adapter->totalMatrix().isIdentity()) { |
137 | | // The transform has no observable effects - we can discard. |
138 | 40.4k | return parent; |
139 | 40.4k | } |
140 | 47.4k | adapter->seek(0); |
141 | 47.4k | } else { |
142 | 12.9k | fCurrentAnimatorScope->push_back(adapter); |
143 | 12.9k | } |
144 | | |
145 | 60.3k | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
146 | 100k | } |
147 | | |
148 | | TransformAdapter3D::TransformAdapter3D(const skjson::ObjectValue& jtransform, |
149 | | const AnimationBuilder& abuilder) |
150 | 12.7k | : INHERITED(sksg::Matrix<SkM44>::Make(SkM44())) { |
151 | | |
152 | 12.7k | this->bind(abuilder, jtransform["a"], fAnchorPoint); |
153 | 12.7k | this->bind(abuilder, jtransform["p"], fPosition); |
154 | 12.7k | this->bind(abuilder, jtransform["s"], fScale); |
155 | | |
156 | | // Axis-wise rotation and orientation are mapped to the same rotation property (3D rotation). |
157 | | // The difference is in how they get interpolated (scalar/decomposed vs. vector). |
158 | 12.7k | this->bind(abuilder, jtransform["rx"], fRx); |
159 | 12.7k | this->bind(abuilder, jtransform["ry"], fRy); |
160 | 12.7k | this->bind(abuilder, jtransform["rz"], fRz); |
161 | 12.7k | this->bind(abuilder, jtransform["or"], fOrientation); |
162 | 12.7k | } |
163 | | |
164 | 12.7k | TransformAdapter3D::~TransformAdapter3D() = default; |
165 | | |
166 | 8.39k | void TransformAdapter3D::onSync() { |
167 | 8.39k | this->node()->setMatrix(this->totalMatrix()); |
168 | 8.39k | } |
169 | | |
170 | 19.8k | SkV3 TransformAdapter3D::anchor_point() const { |
171 | 19.8k | return fAnchorPoint; |
172 | 19.8k | } |
173 | | |
174 | 19.8k | SkV3 TransformAdapter3D::position() const { |
175 | 19.8k | return fPosition; |
176 | 19.8k | } |
177 | | |
178 | 19.8k | SkV3 TransformAdapter3D::rotation() const { |
179 | | // orientation and axis-wise rotation map onto the same property. |
180 | 19.8k | return static_cast<SkV3>(fOrientation) + SkV3{ fRx, fRy, fRz }; |
181 | 19.8k | } |
182 | | |
183 | 19.8k | SkM44 TransformAdapter3D::totalMatrix() const { |
184 | 19.8k | const auto anchor_point = this->anchor_point(), |
185 | 19.8k | position = this->position(), |
186 | 19.8k | scale = static_cast<SkV3>(fScale), |
187 | 19.8k | rotation = this->rotation(); |
188 | | |
189 | 19.8k | return SkM44::Translate(position.x, position.y, position.z) |
190 | 19.8k | * SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(rotation.x)) |
191 | 19.8k | * SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(rotation.y)) |
192 | 19.8k | * SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(rotation.z)) |
193 | 19.8k | * SkM44::Scale(scale.x / 100, scale.y / 100, scale.z / 100) |
194 | 19.8k | * SkM44::Translate(-anchor_point.x, -anchor_point.y, -anchor_point.z); |
195 | 19.8k | } |
196 | | |
197 | | sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform, |
198 | | sk_sp<sksg::Transform> parent, |
199 | 12.7k | bool /*TODO: auto_orient*/) const { |
200 | 12.7k | auto adapter = TransformAdapter3D::Make(jtransform, *this); |
201 | 12.7k | SkASSERT(adapter); |
202 | | |
203 | 12.7k | if (adapter->isStatic()) { |
204 | | // TODO: SkM44::isIdentity? |
205 | 11.4k | if (adapter->totalMatrix() == SkM44()) { |
206 | | // The transform has no observable effects - we can discard. |
207 | 4.31k | return parent; |
208 | 4.31k | } |
209 | 7.10k | adapter->seek(0); |
210 | 7.10k | } else { |
211 | 1.35k | fCurrentAnimatorScope->push_back(adapter); |
212 | 1.35k | } |
213 | | |
214 | 8.46k | return sksg::Transform::MakeConcat(std::move(parent), adapter->node()); |
215 | 12.7k | } |
216 | | |
217 | | } // namespace internal |
218 | | } // namespace skottie |