Flutter Engine
The Flutter Engine
ShapeKeyframeAnimator.cpp
Go to the documentation of this file.
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
17#include "src/utils/SkJSON.h"
18
19#include <cstddef>
20#include <vector>
21
22namespace skottie::internal {
23class AnimationBuilder;
24}
25
26namespace skottie {
27
28// Shapes (paths) are encoded as a vector of floats. For each vertex, we store 6 floats:
29//
30// - vertex point (2 floats)
31// - in-tangent point (2 floats)
32// - out-tangent point (2 floats)
33//
34// Additionally, we store one trailing "closed shape" flag - e.g.
35//
36// [ v0.x, v0.y, v0_in.x, v0_in.y, v0_out.x, v0_out.y, ... , closed_flag ]
37//
38enum ShapeEncodingInfo : size_t {
45
47};
48
49static size_t shape_encoding_len(size_t vertex_count) {
50 return vertex_count * kFloatsPerVertex + 1;
51}
52
53// Some versions wrap shape values as single-element arrays.
55 if (const skjson::ArrayValue* av = jv) {
56 if (av->size() == 1) {
57 return (*av)[0];
58 }
59 }
60
61 return jv;
62}
63
64static bool parse_encoding_len(const skjson::Value& jv, size_t* len) {
65 if (const auto* jshape = shape_root(jv)) {
66 if (const skjson::ArrayValue* jvs = (*jshape)["v"]) {
67 *len = shape_encoding_len(jvs->size());
68 return true;
69 }
70 }
71 return false;
72}
73
74static bool parse_encoding_data(const skjson::Value& jv, size_t data_len, float data[]) {
75 const auto* jshape = shape_root(jv);
76 if (!jshape) {
77 return false;
78 }
79
80 // vertices are required, in/out tangents are optional
81 const skjson::ArrayValue* jvs = (*jshape)["v"]; // vertex points
82 const skjson::ArrayValue* jis = (*jshape)["i"]; // in-tangent points
83 const skjson::ArrayValue* jos = (*jshape)["o"]; // out-tangent points
84
85 if (!jvs || data_len != shape_encoding_len(jvs->size())) {
86 return false;
87 }
88
89 auto parse_point = [](const skjson::ArrayValue* ja, size_t i, float* x, float* y) {
90 SkASSERT(ja);
91 const skjson::ArrayValue* jpt = (*ja)[i];
92
93 if (!jpt || jpt->size() != 2ul) {
94 return false;
95 }
96
97 return Parse((*jpt)[0], x) && Parse((*jpt)[1], y);
98 };
99
100 auto parse_optional_point = [&parse_point](const skjson::ArrayValue* ja, size_t i,
101 float* x, float* y) {
102 if (!ja || i >= ja->size()) {
103 // default control point
104 *x = *y = 0;
105 return true;
106 }
107
108 return parse_point(*ja, i, x, y);
109 };
110
111 for (size_t i = 0; i < jvs->size(); ++i) {
112 float* dst = data + i * kFloatsPerVertex;
113 SkASSERT(dst + kFloatsPerVertex <= data + data_len);
114
115 if (!parse_point (jvs, i, dst + kX_Index, dst + kY_Index) ||
116 !parse_optional_point(jis, i, dst + kInX_Index, dst + kInY_Index) ||
117 !parse_optional_point(jos, i, dst + kOutX_Index, dst + kOutY_Index)) {
118 return false;
119 }
120 }
121
122 // "closed" flag
123 data[data_len - 1] = ParseDefault<bool>((*jshape)["c"], false);
124
125 return true;
126}
127
128ShapeValue::operator SkPath() const {
129 const auto vertex_count = this->size() / kFloatsPerVertex;
130
132
133 if (vertex_count) {
134 // conservatively assume all cubics
135 path.incReserve(1 + SkToInt(vertex_count * 3));
136
137 // Move to first vertex.
138 path.moveTo((*this)[kX_Index], (*this)[kY_Index]);
139 }
140
141 auto addCubic = [&](size_t from_vertex, size_t to_vertex) {
142 const auto from_index = kFloatsPerVertex * from_vertex,
143 to_index = kFloatsPerVertex * to_vertex;
144
145 const SkPoint p0 = SkPoint{ (*this)[from_index + kX_Index],
146 (*this)[from_index + kY_Index] },
147 p1 = SkPoint{ (*this)[ to_index + kX_Index],
148 (*this)[ to_index + kY_Index] },
149 c0 = SkPoint{ (*this)[from_index + kOutX_Index],
150 (*this)[from_index + kOutY_Index] } + p0,
151 c1 = SkPoint{ (*this)[ to_index + kInX_Index],
152 (*this)[ to_index + kInY_Index] } + p1;
153
154 if (c0 == p0 && c1 == p1) {
155 // If the control points are coincident, we can power-reduce to a straight line.
156 // TODO: we could also do that when the controls are on the same line as the
157 // vertices, but it's unclear how common that case is.
158 path.lineTo(p1);
159 } else {
160 path.cubicTo(c0, c1, p1);
161 }
162 };
163
164 for (size_t i = 1; i < vertex_count; ++i) {
165 addCubic(i - 1, i);
166 }
167
168 // Close the path with an extra cubic, if needed.
169 if (vertex_count && this->back() != 0) {
170 addCubic(vertex_count - 1, 0);
171 path.close();
172 }
173
174 return path.detach();
175}
176
177namespace internal {
178
179template <>
180bool AnimatablePropertyContainer::bind<ShapeValue>(const AnimationBuilder& abuilder,
181 const skjson::ObjectValue* jprop,
182 ShapeValue* v) {
184
185 return this->bindImpl(abuilder, jprop, builder);
186}
187
188} // namespace internal
189
190} // namespace skottie
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr int SkToInt(S x)
Definition: SkTo.h:29
Definition: SkPath.h:59
size_t size() const
Definition: SkJSON.h:262
static const char * ja
Definition: fontmgr.cpp:72
double y
double x
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
static bool parse_encoding_data(const skjson::Value &jv, size_t data_len, float data[])
static const skjson::ObjectValue * shape_root(const skjson::Value &jv)
static size_t shape_encoding_len(size_t vertex_count)
static bool parse_encoding_len(const skjson::Value &jv, size_t *len)
bool Parse(const skjson::Value &, T *)
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63