• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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/SkottieValue.h"
9 
10 #include "include/core/SkColor.h"
11 #include "include/core/SkM44.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkSize.h"
14 #include "include/private/SkNx.h"
15 #include "modules/skottie/src/SkottieJson.h"
16 #include "modules/skottie/src/SkottiePriv.h"
17 
18 namespace  skottie {
19 
20 template <>
FromJSON(const skjson::Value & jv,const internal::AnimationBuilder *,ScalarValue * v)21 bool ValueTraits<ScalarValue>::FromJSON(const skjson::Value& jv, const internal::AnimationBuilder*,
22                                         ScalarValue* v) {
23     return Parse(jv, v);
24 }
25 
26 template <>
CanLerp(const ScalarValue &,const ScalarValue &)27 bool ValueTraits<ScalarValue>::CanLerp(const ScalarValue&, const ScalarValue&) {
28     return true;
29 }
30 
31 template <>
Lerp(const ScalarValue & v0,const ScalarValue & v1,float t,ScalarValue * result)32 void ValueTraits<ScalarValue>::Lerp(const ScalarValue& v0, const ScalarValue& v1, float t,
33                                     ScalarValue* result) {
34     *result = v0 + (v1 - v0) * t;
35 }
36 
37 template <>
38 template <>
As(const ScalarValue & v)39 SkScalar ValueTraits<ScalarValue>::As<SkScalar>(const ScalarValue& v) {
40     return v;
41 }
42 
43 template <>
FromJSON(const skjson::Value & jv,const internal::AnimationBuilder *,VectorValue * v)44 bool ValueTraits<VectorValue>::FromJSON(const skjson::Value& jv, const internal::AnimationBuilder*,
45                                         VectorValue* v) {
46     return Parse(jv, v);
47 }
48 
49 template <>
CanLerp(const VectorValue & v1,const VectorValue & v2)50 bool ValueTraits<VectorValue>::CanLerp(const VectorValue& v1, const VectorValue& v2) {
51     return v1.size() == v2.size();
52 }
53 
54 template <>
Lerp(const VectorValue & v0,const VectorValue & v1,float t,VectorValue * result)55 void ValueTraits<VectorValue>::Lerp(const VectorValue& v0, const VectorValue& v1, float t,
56                                     VectorValue* result) {
57     SkASSERT(v0.size() == v1.size());
58 
59     result->resize(v0.size());
60 
61     for (size_t i = 0; i < v0.size(); ++i) {
62         ValueTraits<ScalarValue>::Lerp(v0[i], v1[i], t, &(*result)[i]);
63     }
64 }
65 
66 // DEPRECATED: remove after converting everything to SkColor4f
67 template <>
68 template <>
As(const VectorValue & v)69 SkColor ValueTraits<VectorValue>::As<SkColor>(const VectorValue& v) {
70     // best effort to turn this into a color
71     const auto r = v.size() > 0 ? v[0] : 0,
72                g = v.size() > 1 ? v[1] : 0,
73                b = v.size() > 2 ? v[2] : 0,
74                a = v.size() > 3 ? v[3] : 1;
75 
76     return SkColorSetARGB(SkScalarRoundToInt(SkTPin(a, 0.0f, 1.0f) * 255),
77                           SkScalarRoundToInt(SkTPin(r, 0.0f, 1.0f) * 255),
78                           SkScalarRoundToInt(SkTPin(g, 0.0f, 1.0f) * 255),
79                           SkScalarRoundToInt(SkTPin(b, 0.0f, 1.0f) * 255));
80 }
81 
82 template <>
83 template <>
As(const VectorValue & v)84 SkColor4f ValueTraits<VectorValue>::As<SkColor4f>(const VectorValue& v) {
85     // best effort to turn a vector into a color
86     const auto r = v.size() > 0 ? SkTPin(v[0], 0.0f, 1.0f) : 0,
87                g = v.size() > 1 ? SkTPin(v[1], 0.0f, 1.0f) : 0,
88                b = v.size() > 2 ? SkTPin(v[2], 0.0f, 1.0f) : 0,
89                a = v.size() > 3 ? SkTPin(v[3], 0.0f, 1.0f) : 1;
90 
91     return { r, g, b, a };
92 }
93 
94 template <>
95 template <>
As(const VectorValue & vec)96 SkPoint ValueTraits<VectorValue>::As<SkPoint>(const VectorValue& vec) {
97     // best effort to turn this into a 2D point
98     return SkPoint {
99         vec.size() > 0 ? vec[0] : 0,
100         vec.size() > 1 ? vec[1] : 0,
101     };
102 }
103 
104 template <>
105 template <>
As(const VectorValue & vec)106 SkV3 ValueTraits<VectorValue>::As<SkV3>(const VectorValue& vec) {
107     // best effort to turn this into a 3D point
108     return SkV3 {
109         vec.size() > 0 ? vec[0] : 0,
110         vec.size() > 1 ? vec[1] : 0,
111         vec.size() > 2 ? vec[2] : 0,
112     };
113 }
114 
115 template <>
116 template <>
As(const VectorValue & vec)117 SkSize ValueTraits<VectorValue>::As<SkSize>(const VectorValue& vec) {
118     const auto pt = ValueTraits::As<SkPoint>(vec);
119     return SkSize::Make(pt.x(), pt.y());
120 }
121 
122 namespace {
123 
ParsePointVec(const skjson::Value & jv,std::vector<SkPoint> * pts)124 bool ParsePointVec(const skjson::Value& jv, std::vector<SkPoint>* pts) {
125     if (!jv.is<skjson::ArrayValue>())
126         return false;
127     const auto& av = jv.as<skjson::ArrayValue>();
128 
129     pts->clear();
130     pts->reserve(av.size());
131 
132     std::vector<float> vec;
133     for (size_t i = 0; i < av.size(); ++i) {
134         if (!Parse(av[i], &vec) || vec.size() != 2)
135             return false;
136         pts->push_back(SkPoint::Make(vec[0], vec[1]));
137     }
138 
139     return true;
140 }
141 
142 } // namespace
143 
144 template <>
FromJSON(const skjson::Value & jv,const internal::AnimationBuilder * abuilder,ShapeValue * v)145 bool ValueTraits<ShapeValue>::FromJSON(const skjson::Value& jv,
146                                        const internal::AnimationBuilder* abuilder,
147                                        ShapeValue* v) {
148     SkASSERT(v->fVertices.empty());
149 
150     // Some versions wrap values as single-element arrays.
151     if (const skjson::ArrayValue* av = jv) {
152         if (av->size() == 1) {
153             return FromJSON((*av)[0], abuilder, v);
154         }
155     }
156 
157     if (!jv.is<skjson::ObjectValue>())
158         return false;
159     const auto& ov = jv.as<skjson::ObjectValue>();
160 
161     std::vector<SkPoint> verts,  // Cubic Bezier vertices.
162                          inPts,  // Cubic Bezier "in" control points, relative to vertices.
163                          outPts; // Cubic Bezier "out" control points, relative to vertices.
164 
165     if (!ParsePointVec(ov["v"], &verts)) {
166         // Vertices are required.
167         return false;
168     }
169 
170     // In/out points are optional.
171     ParsePointVec(ov["i"], &inPts);
172     if (!inPts.empty() && inPts.size() != verts.size()) {
173         return false;
174     }
175     inPts.resize(verts.size(), { 0, 0 });
176 
177     ParsePointVec(ov["o"], &outPts);
178     if (!outPts.empty() && outPts.size() != verts.size()) {
179         return false;
180     }
181     outPts.resize(verts.size(), { 0, 0 });
182 
183     v->fVertices.reserve(inPts.size());
184     for (size_t i = 0; i < inPts.size(); ++i) {
185         v->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]}));
186     }
187     v->fClosed = ParseDefault<bool>(ov["c"], false);
188 
189     return true;
190 }
191 
192 template <>
CanLerp(const ShapeValue & v1,const ShapeValue & v2)193 bool ValueTraits<ShapeValue>::CanLerp(const ShapeValue& v1, const ShapeValue& v2) {
194     return v1.fVertices.size() == v2.fVertices.size()
195         && v1.fClosed == v2.fClosed;
196 }
197 
lerp_point(const SkPoint & v0,const SkPoint & v1,const Sk2f & t)198 static SkPoint lerp_point(const SkPoint& v0, const SkPoint& v1, const Sk2f& t) {
199     const auto v2f0 = Sk2f::Load(&v0),
200                v2f1 = Sk2f::Load(&v1);
201 
202     SkPoint v;
203     (v2f0 + (v2f1 - v2f0) * t).store(&v);
204 
205     return v;
206 }
207 
208 template <>
Lerp(const ShapeValue & v0,const ShapeValue & v1,float t,ShapeValue * result)209 void ValueTraits<ShapeValue>::Lerp(const ShapeValue& v0, const ShapeValue& v1, float t,
210                                    ShapeValue* result) {
211     SkASSERT(v0.fVertices.size() == v1.fVertices.size());
212     SkASSERT(v0.fClosed == v1.fClosed);
213 
214     result->fClosed = v0.fClosed;
215     result->fVolatile = true; // interpolated values are volatile
216 
217     const auto t2f = Sk2f(t);
218     result->fVertices.resize(v0.fVertices.size());
219 
220     for (size_t i = 0; i < v0.fVertices.size(); ++i) {
221         result->fVertices[i] = BezierVertex({
222             lerp_point(v0.fVertices[i].fInPoint , v1.fVertices[i].fInPoint , t2f),
223             lerp_point(v0.fVertices[i].fOutPoint, v1.fVertices[i].fOutPoint, t2f),
224             lerp_point(v0.fVertices[i].fVertex  , v1.fVertices[i].fVertex  , t2f)
225         });
226     }
227 }
228 
229 template <>
230 template <>
As(const ShapeValue & shape)231 SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& shape) {
232     SkPath path;
233 
234     if (!shape.fVertices.empty()) {
235         // conservatively assume all cubics
236         path.incReserve(1 + SkToU32(shape.fVertices.size() * 3));
237 
238         path.moveTo(shape.fVertices.front().fVertex);
239     }
240 
241     const auto& addCubic = [&](size_t from, size_t to) {
242         const auto c0 = shape.fVertices[from].fVertex + shape.fVertices[from].fOutPoint,
243                    c1 = shape.fVertices[to].fVertex   + shape.fVertices[to].fInPoint;
244 
245         if (c0 == shape.fVertices[from].fVertex &&
246             c1 == shape.fVertices[to].fVertex) {
247             // If the control points are coincident, we can power-reduce to a straight line.
248             // TODO: we could also do that when the controls are on the same line as the
249             //       vertices, but it's unclear how common that case is.
250             path.lineTo(shape.fVertices[to].fVertex);
251         } else {
252             path.cubicTo(c0, c1, shape.fVertices[to].fVertex);
253         }
254     };
255 
256     for (size_t i = 1; i < shape.fVertices.size(); ++i) {
257         addCubic(i - 1, i);
258     }
259 
260     if (!shape.fVertices.empty() && shape.fClosed) {
261         addCubic(shape.fVertices.size() - 1, 0);
262         path.close();
263     }
264 
265     path.setIsVolatile(shape.fVolatile);
266     path.shrinkToFit();
267 
268     return path;
269 }
270 
271 } // namespace skottie
272