Flutter Engine
The Flutter Engine
VectorKeyframeAnimator.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
9
12#include "include/core/SkM44.h"
25#include "src/base/SkSafeMath.h"
26#include "src/base/SkVx.h"
27#include "src/utils/SkJSON.h"
28
29#include <algorithm>
30#include <cstdint>
31#include <cstring>
32#include <utility>
33
34namespace skottie {
35
36// Parses an array of exact size.
37static bool parse_array(const skjson::ArrayValue* ja, float* a, size_t count) {
38 if (!ja || ja->size() != count) {
39 return false;
40 }
41
42 for (size_t i = 0; i < count; ++i) {
43 if (!Parse((*ja)[i], a + i)) {
44 return false;
45 }
46 }
47
48 return true;
49}
50
51VectorValue::operator SkV3() const {
52 // best effort to turn this into a 3D point
53 return SkV3 {
54 this->size() > 0 ? (*this)[0] : 0,
55 this->size() > 1 ? (*this)[1] : 0,
56 this->size() > 2 ? (*this)[2] : 0,
57 };
58}
59
60ColorValue::operator SkColor() const {
61 return static_cast<SkColor4f>(*this).toSkColor();
62}
63
64ColorValue::operator SkColor4f() const {
65 // best effort to turn a vector into a color
66 const auto r = this->size() > 0 ? SkTPin((*this)[0], 0.0f, 1.0f) : 0,
67 g = this->size() > 1 ? SkTPin((*this)[1], 0.0f, 1.0f) : 0,
68 b = this->size() > 2 ? SkTPin((*this)[2], 0.0f, 1.0f) : 0,
69 a = this->size() > 3 ? SkTPin((*this)[3], 0.0f, 1.0f) : 1;
70
71 return { r, g, b, a };
72}
73
74namespace internal {
75namespace {
76
77// Vector specialization - stores float vector values (of same length) in consolidated/contiguous
78// storage. Keyframe records hold the storage offset for each value:
79//
80// fStorage: [ vec0 ][ vec1 ] ... [ vecN ]
81// <- vec_len -> <- vec_len -> <- vec_len ->
82//
83// ^ ^ ^
84// fKFs[]: .idx .idx ... .idx
85//
86class VectorKeyframeAnimator final : public KeyframeAnimator {
87public:
88 VectorKeyframeAnimator(std::vector<Keyframe> kfs,
89 std::vector<SkCubicMap> cms,
90 std::vector<float> storage,
91 size_t vec_len,
92 std::vector<float>* target_value)
93 : INHERITED(std::move(kfs), std::move(cms))
94 , fStorage(std::move(storage))
95 , fVecLen(vec_len)
96 , fTarget(target_value) {
97
98 // Resize the target value appropriately.
99 fTarget->resize(fVecLen);
100 }
101
102private:
103 StateChanged onSeek(float t) override {
104 const auto& lerp_info = this->getLERPInfo(t);
105
106 SkASSERT(lerp_info.vrec0.idx + fVecLen <= fStorage.size());
107 SkASSERT(lerp_info.vrec1.idx + fVecLen <= fStorage.size());
108 SkASSERT(fTarget->size() == fVecLen);
109
110 const auto* v0 = fStorage.data() + lerp_info.vrec0.idx;
111 const auto* v1 = fStorage.data() + lerp_info.vrec1.idx;
112 auto* dst = fTarget->data();
113
114 const auto is_constant = lerp_info.vrec0.equals(lerp_info.vrec1,
116 if (is_constant) {
117 if (0 != std::memcmp(dst, v0, fVecLen * sizeof(float))) {
118 std::copy(v0, v0 + fVecLen, dst);
119 return true;
120 }
121 return false;
122 }
123
124 size_t count = fVecLen;
125 bool changed = false;
126
127 while (count >= 4) {
128 const auto old_val = skvx::float4::Load(dst),
129 new_val = Lerp(skvx::float4::Load(v0),
131 lerp_info.weight);
132
133 changed |= any(new_val != old_val);
134 new_val.store(dst);
135
136 v0 += 4;
137 v1 += 4;
138 dst += 4;
139 count -= 4;
140 }
141
142 while (count-- > 0) {
143 const auto new_val = Lerp(*v0++, *v1++, lerp_info.weight);
144
145 changed |= (new_val != *dst);
146 *dst++ = new_val;
147 }
148
149 return changed;
150 }
151
152 const std::vector<float> fStorage;
153 const size_t fVecLen;
154
155 std::vector<float>* fTarget;
156
157 using INHERITED = KeyframeAnimator;
158};
159
160class VectorExpressionAnimator final : public Animator {
161public:
162 VectorExpressionAnimator(sk_sp<ExpressionEvaluator<std::vector<float>>> expression_evaluator,
163 std::vector<float>* target_value)
164 : fExpressionEvaluator(std::move(expression_evaluator))
165 , fTarget(target_value) {}
166
167private:
168
169 StateChanged onSeek(float t) override {
170 std::vector<float> result = fExpressionEvaluator->evaluate(t);
171 bool changed = false;
172 for (size_t i = 0; i < fTarget->size(); i++) {
173 // Use 0 as a default if the result is too small.
174 float val = i >= result.size() ? 0 : result[i];
175 if (!SkScalarNearlyEqual(val, (*fTarget)[i])) {
176 changed = true;
177 }
178 (*fTarget)[i] = val;
179 }
180
181 return changed;
182 }
183
185 std::vector<float>* fTarget;
186};
187} // namespace
188
190 VectorLenParser parse_len,
191 VectorDataParser parse_data)
193 , fParseLen(parse_len)
194 , fParseData(parse_data)
195 , fTarget(target) {}
196
198 const skjson::ArrayValue& jkfs) {
199 SkASSERT(jkfs.size() > 0);
200
201 // peek at the first keyframe value to find our vector length
202 const skjson::ObjectValue* jkf0 = jkfs[0];
203 if (!jkf0 || !fParseLen((*jkf0)["s"], &fVecLen)) {
204 return nullptr;
205 }
206
207 SkSafeMath safe;
208 // total elements: vector length x number vectors
209 const auto total_size = safe.mul(fVecLen, jkfs.size());
210
211 // we must be able to store all offsets in Keyframe::Value::idx (uint32_t)
212 if (!safe || !SkTFitsIn<uint32_t>(total_size)) {
213 return nullptr;
214 }
215 fStorage.resize(total_size);
216
217 if (!this->parseKeyframes(abuilder, jkfs)) {
218 return nullptr;
219 }
220
221 // parseKFValue() might have stored fewer vectors thanks to tail-deduping.
222 SkASSERT(fCurrentVec <= jkfs.size());
223 fStorage.resize(fCurrentVec * fVecLen);
224 fStorage.shrink_to_fit();
225
227 new VectorKeyframeAnimator(std::move(fKFs),
228 std::move(fCMs),
229 std::move(fStorage),
230 fVecLen,
231 fTarget));
232}
233
237 return sk_make_sp<VectorExpressionAnimator>(expression_evaluator, fTarget);
238}
239
240bool VectorAnimatorBuilder::parseValue(const AnimationBuilder&,
241 const skjson::Value& jv) const {
242 size_t vec_len;
243 if (!this->fParseLen(jv, &vec_len)) {
244 return false;
245 }
246
247 fTarget->resize(vec_len);
248 return fParseData(jv, vec_len, fTarget->data());
249}
250
251bool VectorAnimatorBuilder::parseKFValue(const AnimationBuilder&,
252 const skjson::ObjectValue&,
253 const skjson::Value& jv,
254 Keyframe::Value* kfv) {
255 auto offset = fCurrentVec * fVecLen;
256 SkASSERT(offset + fVecLen <= fStorage.size());
257
258 if (!fParseData(jv, fVecLen, fStorage.data() + offset)) {
259 return false;
260 }
261
262 SkASSERT(!fCurrentVec || offset >= fVecLen);
263 // compare with previous vector value
264 if (fCurrentVec > 0 && !memcmp(fStorage.data() + offset,
265 fStorage.data() + offset - fVecLen,
266 fVecLen * sizeof(float))) {
267 // repeating value -> use prev offset (dedupe)
268 offset -= fVecLen;
269 } else {
270 // new value -> advance the current index
271 fCurrentVec += 1;
272 }
273
274 // Keyframes record the storage-offset for a given vector value.
275 kfv->idx = SkToU32(offset);
276
277 return true;
278}
279
280template <>
281bool AnimatablePropertyContainer::bind<VectorValue>(const AnimationBuilder& abuilder,
282 const skjson::ObjectValue* jprop,
283 VectorValue* v) {
284 if (!jprop) {
285 return false;
286 }
287
288 if (!ParseDefault<bool>((*jprop)["s"], false)) {
289 // Regular (static or keyframed) vector value.
291 v,
292 // Len parser.
293 [](const skjson::Value& jv, size_t* len) -> bool {
294 if (const skjson::ArrayValue* ja = jv) {
295 *len = ja->size();
296 return true;
297 }
298 return false;
299 },
300 // Data parser.
301 [](const skjson::Value& jv, size_t len, float* data) {
302 return parse_array(jv, data, len);
303 });
304
305 return this->bindImpl(abuilder, jprop, builder);
306 }
307
308 // Separate-dimensions vector value: each component is animated independently.
309 *v = { 0, 0, 0 };
310 bool boundX = this->bind(abuilder, (*jprop)["x"], v->data() + 0);
311 bool boundY = this->bind(abuilder, (*jprop)["y"], v->data() + 1);
312 bool boundZ = this->bind(abuilder, (*jprop)["z"], v->data() + 2);
313 return boundX || boundY || boundZ;
314}
315
316template <>
317bool AnimatablePropertyContainer::bind<ColorValue>(const AnimationBuilder& abuilder,
318 const skjson::ObjectValue* jprop,
319 ColorValue* v) {
320 if (const auto* sid = ParseSlotID(jprop)) {
321 fHasSlotID = true;
322 abuilder.fSlotManager->trackColorValue(SkString(sid->begin()), v, sk_ref_sp(this));
323 }
324 return this->bind(abuilder, jprop, static_cast<VectorValue*>(v));
325}
326
327} // namespace internal
328} // namespace skottie
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
static size_t total_size(SkSBlockAllocator< N > &pool)
SkRGBA4f< kUnpremul_SkAlphaType > SkColor4f
Definition: SkColor.h:426
uint32_t SkColor
Definition: SkColor.h:37
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:31
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
constexpr uint32_t SkToU32(S x)
Definition: SkTo.h:26
size_t mul(size_t x, size_t y)
Definition: SkSafeMath.h:29
size_t size() const
Definition: SkJSON.h:262
virtual sk_sp< ExpressionEvaluator< std::vector< float > > > createArrayExpressionEvaluator(const char expression[])=0
bool bind(const AnimationBuilder &, const skjson::ObjectValue *, T *)
std::vector< SkCubicMap > fCMs
bool parseKeyframes(const AnimationBuilder &, const skjson::ArrayValue &)
bool(*)(const skjson::Value &, size_t, float *) VectorDataParser
sk_sp< Animator > makeFromExpression(ExpressionManager &, const char *) override
sk_sp< KeyframeAnimator > makeFromKeyframes(const AnimationBuilder &, const skjson::ArrayValue &) override
VectorAnimatorBuilder(std::vector< float > *, VectorLenParser, VectorDataParser)
bool(*)(const skjson::Value &, size_t *) VectorLenParser
static bool b
struct MyStruct a[10]
GAsyncResult * result
uint32_t * target
static const char * ja
Definition: fontmgr.cpp:72
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
T Lerp(const T &a, const T &b, float t)
static bool parse_array(const skjson::ArrayValue *ja, float *a, size_t count)
bool Parse(const skjson::Value &, T *)
const skjson::StringValue * ParseSlotID(const skjson::ObjectValue *jobj)
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530
Definition: ref_ptr.h:256
SeparatedVector2 offset
Definition: SkM44.h:56
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63