xref: /aosp_15_r20/external/skia/modules/skottie/src/Transform.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
TransformAdapter2D(const AnimationBuilder & abuilder,const skjson::ObjectValue * janchor_point,const skjson::ObjectValue * jposition,const skjson::ObjectValue * jscale,const skjson::ObjectValue * jrotation,const skjson::ObjectValue * jskew,const skjson::ObjectValue * jskew_axis,bool auto_orient)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     : INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) {
32 
33     this->bind(abuilder, janchor_point, fAnchorPoint);
34     this->bind(abuilder, jscale       , fScale);
35     this->bind(abuilder, jrotation    , fRotation);
36     this->bind(abuilder, jskew        , fSkew);
37     this->bind(abuilder, jskew_axis   , fSkewAxis);
38 
39     this->bindAutoOrientable(abuilder, jposition, &fPosition, auto_orient ? &fOrientation
40                                                                           : nullptr);
41 }
42 
~TransformAdapter2D()43 TransformAdapter2D::~TransformAdapter2D() {}
44 
onSync()45 void TransformAdapter2D::onSync() {
46     this->node()->setMatrix(this->totalMatrix());
47 }
48 
totalMatrix() const49 SkMatrix TransformAdapter2D::totalMatrix() const {
50     auto skew_matrix = [](float sk, float sa) {
51         if (!sk) return SkMatrix::I();
52 
53         // AE control limit.
54         static constexpr float kMaxSkewAngle = 85;
55         sk = -SkDegreesToRadians(SkTPin(sk, -kMaxSkewAngle, kMaxSkewAngle));
56         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         return SkMatrix::RotateRad(sa)
61              * SkMatrix::Skew(std::tan(sk), 0)
62              * SkMatrix::RotateRad(-sa);
63     };
64 
65     return SkMatrix::Translate(fPosition.x, fPosition.y)
66          * SkMatrix::RotateDeg(fRotation + fOrientation)
67          * skew_matrix        (fSkew, fSkewAxis)
68          * SkMatrix::Scale    (fScale.x / 100, fScale.y / 100) // 100% based
69          * SkMatrix::Translate(-fAnchorPoint.x, -fAnchorPoint.y);
70 }
71 
getAnchorPoint() const72 SkPoint TransformAdapter2D::getAnchorPoint() const {
73     return { fAnchorPoint.x, fAnchorPoint.y };
74 }
75 
setAnchorPoint(const SkPoint & ap)76 void TransformAdapter2D::setAnchorPoint(const SkPoint& ap) {
77     fAnchorPoint = { ap.x(), ap.y() };
78     this->onSync();
79 }
80 
getPosition() const81 SkPoint TransformAdapter2D::getPosition() const {
82     return { fPosition.x, fPosition.y };
83 }
84 
setPosition(const SkPoint & p)85 void TransformAdapter2D::setPosition(const SkPoint& p) {
86     fPosition = { p.x(), p.y() };
87     this->onSync();
88 }
89 
getScale() const90 SkVector TransformAdapter2D::getScale() const {
91     return { fScale.x, fScale.y };
92 }
93 
setScale(const SkVector & s)94 void TransformAdapter2D::setScale(const SkVector& s) {
95     fScale = { s.x(), s.y() };
96     this->onSync();
97 }
98 
setRotation(float r)99 void TransformAdapter2D::setRotation(float r) {
100     fRotation = r;
101     this->onSync();
102 }
103 
setSkew(float sk)104 void TransformAdapter2D::setSkew(float sk) {
105     fSkew = sk;
106     this->onSync();
107 }
108 
setSkewAxis(float sa)109 void TransformAdapter2D::setSkewAxis(float sa) {
110     fSkewAxis = sa;
111     this->onSync();
112 }
113 
attachMatrix2D(const skjson::ObjectValue & jtransform,sk_sp<sksg::Transform> parent,bool auto_orient) const114 sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& jtransform,
115                                                         sk_sp<sksg::Transform> parent,
116                                                         bool auto_orient) const {
117     const auto* jrotation = &jtransform["r"];
118     if (jrotation->is<skjson::NullValue>()) {
119         // Some 2D rotations are disguised as 3D...
120         jrotation = &jtransform["rz"];
121     }
122 
123     auto adapter = TransformAdapter2D::Make(*this,
124                                             jtransform["a"],
125                                             jtransform["p"],
126                                             jtransform["s"],
127                                             *jrotation,
128                                             jtransform["sk"],
129                                             jtransform["sa"],
130                                             auto_orient);
131     SkASSERT(adapter);
132 
133     const auto dispatched = this->dispatchTransformProperty(adapter);
134 
135     if (adapter->isStatic()) {
136         if (!dispatched && adapter->totalMatrix().isIdentity()) {
137             // The transform has no observable effects - we can discard.
138             return parent;
139         }
140         adapter->seek(0);
141     } else {
142         fCurrentAnimatorScope->push_back(adapter);
143     }
144 
145     return sksg::Transform::MakeConcat(std::move(parent), adapter->node());
146 }
147 
TransformAdapter3D(const skjson::ObjectValue & jtransform,const AnimationBuilder & abuilder)148 TransformAdapter3D::TransformAdapter3D(const skjson::ObjectValue& jtransform,
149                                        const AnimationBuilder& abuilder)
150     : INHERITED(sksg::Matrix<SkM44>::Make(SkM44())) {
151 
152     this->bind(abuilder, jtransform["a"], fAnchorPoint);
153     this->bind(abuilder, jtransform["p"], fPosition);
154     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     this->bind(abuilder, jtransform["rx"], fRx);
159     this->bind(abuilder, jtransform["ry"], fRy);
160     this->bind(abuilder, jtransform["rz"], fRz);
161     this->bind(abuilder, jtransform["or"], fOrientation);
162 }
163 
164 TransformAdapter3D::~TransformAdapter3D() = default;
165 
onSync()166 void TransformAdapter3D::onSync() {
167     this->node()->setMatrix(this->totalMatrix());
168 }
169 
anchor_point() const170 SkV3 TransformAdapter3D::anchor_point() const {
171     return fAnchorPoint;
172 }
173 
position() const174 SkV3 TransformAdapter3D::position() const {
175     return fPosition;
176 }
177 
rotation() const178 SkV3 TransformAdapter3D::rotation() const {
179     // orientation and axis-wise rotation map onto the same property.
180     return static_cast<SkV3>(fOrientation) + SkV3{ fRx, fRy, fRz };
181 }
182 
totalMatrix() const183 SkM44 TransformAdapter3D::totalMatrix() const {
184     const auto anchor_point = this->anchor_point(),
185                position     = this->position(),
186                scale        = static_cast<SkV3>(fScale),
187                rotation     = this->rotation();
188 
189     return SkM44::Translate(position.x, position.y, position.z)
190          * SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(rotation.x))
191          * SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(rotation.y))
192          * SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(rotation.z))
193          * SkM44::Scale(scale.x / 100, scale.y / 100, scale.z / 100)
194          * SkM44::Translate(-anchor_point.x, -anchor_point.y, -anchor_point.z);
195 }
196 
attachMatrix3D(const skjson::ObjectValue & jtransform,sk_sp<sksg::Transform> parent,bool) const197 sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform,
198                                                         sk_sp<sksg::Transform> parent,
199                                                         bool /*TODO: auto_orient*/) const {
200     auto adapter = TransformAdapter3D::Make(jtransform, *this);
201     SkASSERT(adapter);
202 
203     if (adapter->isStatic()) {
204         // TODO: SkM44::isIdentity?
205         if (adapter->totalMatrix() == SkM44()) {
206             // The transform has no observable effects - we can discard.
207             return parent;
208         }
209         adapter->seek(0);
210     } else {
211         fCurrentAnimatorScope->push_back(adapter);
212     }
213 
214     return sksg::Transform::MakeConcat(std::move(parent), adapter->node());
215 }
216 
217 } // namespace internal
218 } // namespace skottie
219