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