Flutter Engine
The Flutter Engine
Layer.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 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"
14#include "include/core/SkRect.h"
40#include "src/utils/SkJSON.h"
41
42#include <utility>
43#include <vector>
44
45struct SkSize;
46
47using namespace skia_private;
48
49namespace skottie {
50namespace internal {
51
52namespace {
53
54struct MaskInfo {
55 SkBlendMode fBlendMode; // used when masking with layers/blending
56 sksg::Merge::Mode fMergeMode; // used when clipping
58};
59
60const MaskInfo* GetMaskInfo(char mode) {
61 static constexpr MaskInfo k_add_info =
63 static constexpr MaskInfo k_int_info =
65 static constexpr MaskInfo k_sub_info =
67 static constexpr MaskInfo k_dif_info =
69
70 switch (mode) {
71 case 'a': return &k_add_info;
72 case 'f': return &k_dif_info;
73 case 'i': return &k_int_info;
74 case 's': return &k_sub_info;
75 default: break;
76 }
77
78 return nullptr;
79}
80
81class MaskAdapter final : public AnimatablePropertyContainer {
82public:
83 MaskAdapter(const skjson::ObjectValue& jmask, const AnimationBuilder& abuilder, SkBlendMode bm)
84 : fMaskPaint(sksg::Color::Make(SK_ColorBLACK))
85 , fBlendMode(bm)
86 {
87 fMaskPaint->setAntiAlias(true);
88 if (!this->requires_isolation()) {
89 // We can mask at draw time.
90 fMaskPaint->setBlendMode(bm);
91 }
92
93 this->bind(abuilder, jmask["o"], fOpacity);
94
95 if (this->bind(abuilder, jmask["f"], fFeather)) {
96 fMaskFilter = sksg::BlurImageFilter::Make();
97 // Mask feathers don't repeat edge pixels.
98 fMaskFilter->setTileMode(SkTileMode::kDecal);
99 }
100 }
101
102 bool hasEffect() const {
103 return !this->isStatic()
104 || fOpacity < 100
105 || fFeather != SkV2{0,0};
106 }
107
108 sk_sp<sksg::RenderNode> makeMask(sk_sp<sksg::Path> mask_path) const {
109 sk_sp<sksg::RenderNode> mask = sksg::Draw::Make(std::move(mask_path), fMaskPaint);
110
111 // Optional mask blur (feather).
112 mask = sksg::ImageFilterEffect::Make(std::move(mask), fMaskFilter);
113
114 if (this->requires_isolation()) {
115 mask = sksg::LayerEffect::Make(std::move(mask), fBlendMode);
116 }
117
118 return mask;
119 }
120
121private:
122 void onSync() override {
123 fMaskPaint->setOpacity(fOpacity * 0.01f);
124 if (fMaskFilter) {
125 // Close enough to AE.
126 static constexpr SkScalar kFeatherToSigma = 0.38f;
127 fMaskFilter->setSigma({fFeather.x * kFeatherToSigma,
128 fFeather.y * kFeatherToSigma});
129 }
130 }
131
132 bool requires_isolation() const {
138
139 // Some mask modes touch pixels outside the immediate draw geometry.
140 // These require a layer.
141 switch (fBlendMode) {
142 case (SkBlendMode::kSrcIn): return true;
143 default : return false;
144 }
146 }
147
148 const sk_sp<sksg::PaintNode> fMaskPaint;
150 sk_sp<sksg::BlurImageFilter> fMaskFilter; // optional "feather"
151
152 Vec2Value fFeather = {0,0};
153 ScalarValue fOpacity = 100;
154};
155
156sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
157 const AnimationBuilder* abuilder,
158 sk_sp<sksg::RenderNode> childNode) {
159 if (!jmask) return childNode;
160
161 struct MaskRecord {
162 sk_sp<sksg::Path> mask_path; // for clipping and masking
163 sk_sp<MaskAdapter> mask_adapter; // for masking
164 sksg::Merge::Mode merge_mode; // for clipping
165 };
166
168 bool has_effect = false;
169
170 for (const skjson::ObjectValue* m : *jmask) {
171 if (!m) continue;
172
173 const skjson::StringValue* jmode = (*m)["mode"];
174 if (!jmode || jmode->size() != 1) {
175 abuilder->log(Logger::Level::kError, &(*m)["mode"], "Invalid mask mode.");
176 continue;
177 }
178
179 const auto mode = *jmode->begin();
180 if (mode == 'n') {
181 // "None" masks have no effect.
182 continue;
183 }
184
185 const auto* mask_info = GetMaskInfo(mode);
186 if (!mask_info) {
187 abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported mask mode: '%c'.", mode);
188 continue;
189 }
190
191 auto mask_path = abuilder->attachPath((*m)["pt"]);
192 if (!mask_path) {
193 abuilder->log(Logger::Level::kError, m, "Could not parse mask path.");
194 continue;
195 }
196
197 auto mask_blend_mode = mask_info->fBlendMode;
198 auto mask_merge_mode = mask_info->fMergeMode;
199 auto mask_inverted = ParseDefault<bool>((*m)["inv"], false);
200
201 if (mask_stack.empty()) {
202 // First mask adjustments:
203 // - always draw in source mode
204 // - invert geometry if needed
205 mask_blend_mode = SkBlendMode::kSrc;
206 mask_merge_mode = sksg::Merge::Mode::kMerge;
207 mask_inverted = mask_inverted != mask_info->fInvertGeometry;
208 }
209
210 mask_path->setFillType(mask_inverted ? SkPathFillType::kInverseWinding
212
213 auto mask_adapter = sk_make_sp<MaskAdapter>(*m, *abuilder, mask_blend_mode);
214 abuilder->attachDiscardableAdapter(mask_adapter);
215
216 has_effect |= mask_adapter->hasEffect();
217
218 mask_stack.push_back({ std::move(mask_path),
219 std::move(mask_adapter),
220 mask_merge_mode });
221 }
222
223
224 if (mask_stack.empty())
225 return childNode;
226
227 // If the masks are fully opaque, we can clip.
228 if (!has_effect) {
230
231 if (mask_stack.size() == 1) {
232 // Single path -> just clip.
233 clip_node = std::move(mask_stack.front().mask_path);
234 } else {
235 // Multiple clip paths -> merge.
236 std::vector<sksg::Merge::Rec> merge_recs;
237 merge_recs.reserve(SkToSizeT(mask_stack.size()));
238
239 for (auto& mask : mask_stack) {
240 merge_recs.push_back({std::move(mask.mask_path), mask.merge_mode });
241 }
242 clip_node = sksg::Merge::Make(std::move(merge_recs));
243 }
244
245 return sksg::ClipEffect::Make(std::move(childNode), std::move(clip_node), true);
246 }
247
248 // Complex masks (non-opaque or blurred) turn into a mask node stack.
250 if (mask_stack.size() == 1) {
251 // no group needed for single mask
252 const auto rec = mask_stack.front();
253 maskNode = rec.mask_adapter->makeMask(std::move(rec.mask_path));
254 } else {
255 std::vector<sk_sp<sksg::RenderNode>> masks;
256 masks.reserve(SkToSizeT(mask_stack.size()));
257 for (auto& rec : mask_stack) {
258 masks.push_back(rec.mask_adapter->makeMask(std::move(rec.mask_path)));
259 }
260
261 maskNode = sksg::Group::Make(std::move(masks));
262 }
263
264 return sksg::MaskEffect::Make(std::move(childNode), std::move(maskNode));
265}
266
267class LayerController final : public Animator {
268public:
269 LayerController(AnimatorScope&& layer_animators,
271 size_t tanim_count, float in, float out)
272 : fLayerAnimators(std::move(layer_animators))
273 , fLayerNode(std::move(layer))
274 , fTransformAnimatorsCount(tanim_count)
275 , fIn(in)
276 , fOut(out) {}
277
278protected:
279 StateChanged onSeek(float t) override {
280 // in/out may be inverted for time-reversed layers
281 const auto active = (t >= fIn && t < fOut) || (t > fOut && t <= fIn);
282
283 bool changed = false;
284 if (fLayerNode) {
285 changed |= (fLayerNode->isVisible() != active);
286 fLayerNode->setVisible(active);
287 }
288
289 // When active, dispatch ticks to all layer animators.
290 // When inactive, we must still dispatch ticks to the layer transform animators
291 // (active child layers depend on transforms being updated).
292 const auto dispatch_count = active ? fLayerAnimators.size()
293 : fTransformAnimatorsCount;
294 for (size_t i = 0; i < dispatch_count; ++i) {
295 changed |= fLayerAnimators[i]->seek(t);
296 }
297
298 return changed;
299 }
300
301private:
302 const AnimatorScope fLayerAnimators;
303 const sk_sp<sksg::RenderNode> fLayerNode;
304 const size_t fTransformAnimatorsCount;
305 const float fIn,
306 fOut;
307};
308
309class MotionBlurController final : public Animator {
310public:
311 explicit MotionBlurController(sk_sp<MotionBlurEffect> mbe)
312 : fMotionBlurEffect(std::move(mbe)) {}
313
314protected:
315 // When motion blur is present, time ticks are not passed to layer animators
316 // but to the motion blur effect. The effect then drives the animators/scene-graph
317 // during reval and render phases.
318 StateChanged onSeek(float t) override {
319 fMotionBlurEffect->setT(t);
320 return true;
321 }
322
323private:
324 const sk_sp<MotionBlurEffect> fMotionBlurEffect;
325};
326
327} // namespace
328
330 : fJlayer(jlayer)
331 , fIndex (ParseDefault<int>(jlayer["ind" ], -1))
332 , fParentIndex(ParseDefault<int>(jlayer["parent"], -1))
333 , fType (ParseDefault<int>(jlayer["ty" ], -1))
334 , fAutoOrient (ParseDefault<int>(jlayer["ao" ], 0))
335 , fInfo{comp_size,
336 ParseDefault<float>(jlayer["ip"], 0.0f),
337 ParseDefault<float>(jlayer["op"], 0.0f)}
338{
339
340 if (this->isCamera() || ParseDefault<int>(jlayer["ddd"], 0)) {
341 fFlags |= Flags::kIs3D;
342 }
343}
344
346
348 static constexpr int kCameraLayerType = 13;
349
350 return fType == kCameraLayerType;
351}
352
354 CompositionBuilder* cbuilder) {
355 // Depending on the leaf node type, we treat the whole transform chain as either 2D or 3D.
356 const auto transform_chain_type = this->is3D() ? TransformType::k3D
357 : TransformType::k2D;
358 fLayerTransform = this->getTransform(abuilder, cbuilder, transform_chain_type);
359
360 return fLayerTransform;
361}
362
363sk_sp<sksg::Transform> LayerBuilder::getTransform(const AnimationBuilder& abuilder,
364 CompositionBuilder* cbuilder,
365 TransformType ttype) {
366 const auto cache_valid_mask = (1ul << ttype);
367 if (!(fFlags & cache_valid_mask)) {
368 // Set valid flag upfront to break cycles.
369 fFlags |= cache_valid_mask;
370
372 AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
373 fTransformCache[ttype] = this->doAttachTransform(abuilder, cbuilder, ttype);
374 fLayerScope = ascope.release();
375 fTransformAnimatorCount = fLayerScope.size();
376 }
377
378 return fTransformCache[ttype];
379}
380
381sk_sp<sksg::Transform> LayerBuilder::getParentTransform(const AnimationBuilder& abuilder,
382 CompositionBuilder* cbuilder,
383 TransformType ttype) {
384 if (auto* parent_builder = cbuilder->layerBuilder(fParentIndex)) {
385 // Explicit parent layer.
386 return parent_builder->getTransform(abuilder, cbuilder, ttype);
387 }
388
389 // Camera layers have no implicit parent transform,
390 // while regular 3D transform chains are implicitly rooted onto the camera.
391 if (ttype == TransformType::k3D && !this->isCamera()) {
392 return cbuilder->getCameraTransform();
393 }
394
395 return nullptr;
396}
397
398sk_sp<sksg::Transform> LayerBuilder::doAttachTransform(const AnimationBuilder& abuilder,
399 CompositionBuilder* cbuilder,
400 TransformType ttype) {
401 const skjson::ObjectValue* jtransform = fJlayer["ks"];
402 if (!jtransform) {
403 return nullptr;
404 }
405
406 auto parent_transform = this->getParentTransform(abuilder, cbuilder, ttype);
407
408 if (this->isCamera()) {
409 // parent_transform applies to the camera itself => it pre-composes inverted to the
410 // camera/view/adapter transform.
411 //
412 // T_camera' = T_camera x Inv(parent_transform)
413 //
414 return abuilder.attachCamera(fJlayer,
415 *jtransform,
416 sksg::Transform::MakeInverse(std::move(parent_transform)),
417 cbuilder->fSize);
418 }
419
420 return this->is3D()
421 ? abuilder.attachMatrix3D(*jtransform, std::move(parent_transform), fAutoOrient)
422 : abuilder.attachMatrix2D(*jtransform, std::move(parent_transform), fAutoOrient);
423}
424
425bool LayerBuilder::hasMotionBlur(const CompositionBuilder* cbuilder) const {
426 return cbuilder->fMotionBlurSamples > 1
427 && cbuilder->fMotionBlurAngle > 0
428 && ParseDefault(fJlayer["mb"], false);
429}
430
432 CompositionBuilder* cbuilder,
433 const LayerBuilder* prev_layer) {
435
436 using LayerBuilder =
438 AnimationBuilder::LayerInfo*) const;
439
440 // AE is annoyingly inconsistent in how effects interact with layer transforms: depending on
441 // the layer type, effects are applied before or after the content is transformed.
442 //
443 // Empirically, pre-rendered layers (for some loose meaning of "pre-rendered") are in the
444 // former category (effects are subject to transformation), while the remaining types are in
445 // the latter.
446 enum : uint32_t {
447 kTransformEffects = 0x01, // The layer transform also applies to its effects.
448 kForceSeek = 0x02, // Dispatch all seek() events even when the layer is inactive.
449 };
450
451 static constexpr struct {
452 LayerBuilder fBuilder;
453 uint32_t fFlags;
454 } gLayerBuildInfo[] = {
455 { &AnimationBuilder::attachPrecompLayer, kTransformEffects }, // 'ty': 0 -> precomp
456 { &AnimationBuilder::attachSolidLayer , kTransformEffects }, // 'ty': 1 -> solid
457 { &AnimationBuilder::attachFootageLayer, kTransformEffects }, // 'ty': 2 -> image
458 { &AnimationBuilder::attachNullLayer , 0 }, // 'ty': 3 -> null
459 { &AnimationBuilder::attachShapeLayer , 0 }, // 'ty': 4 -> shape
460 { &AnimationBuilder::attachTextLayer , 0 }, // 'ty': 5 -> text
461 { &AnimationBuilder::attachAudioLayer , kForceSeek }, // 'ty': 6 -> audio
462 { nullptr , 0 }, // 'ty': 7 -> pholderVideo
463 { nullptr , 0 }, // 'ty': 8 -> imageSeq
464 { &AnimationBuilder::attachFootageLayer, kTransformEffects }, // 'ty': 9 -> video
465 { nullptr , 0 }, // 'ty': 10 -> pholderStill
466 { nullptr , 0 }, // 'ty': 11 -> guide
467 { nullptr , 0 }, // 'ty': 12 -> adjustment
468 { &AnimationBuilder::attachNullLayer , 0 }, // 'ty': 13 -> camera
469 { nullptr , 0 }, // 'ty': 14 -> light
470 };
471
472 if (fType < 0 || static_cast<size_t>(fType) >= std::size(gLayerBuildInfo)) {
473 return nullptr;
474 }
475
476 const auto& build_info = gLayerBuildInfo[fType];
477
478 // Switch to the layer animator scope (which at this point holds transform-only animators).
479 AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
480
481 // Potentially null.
483
484 // Build the layer content fragment.
485 if (build_info.fBuilder) {
486 layer = (abuilder.*(build_info.fBuilder))(fJlayer, &fInfo);
487 }
488
489 // Clip layers with explicit dimensions.
490 float w = 0, h = 0;
491 if (::skottie::Parse<float>(fJlayer["w"], &w) && ::skottie::Parse<float>(fJlayer["h"], &h)) {
492 layer = sksg::ClipEffect::Make(std::move(layer),
494#ifdef SK_LEGACY_SKOTTIE_CLIPPING
495 /*aa=*/true, /*force_clip=*/false);
496#else
497 /*aa=*/true, /*force_clip=*/true);
498#endif
499 }
500
501 // Optional layer mask.
502 layer = AttachMask(fJlayer["masksProperties"], &abuilder, std::move(layer));
503
504 // Does the transform apply to effects also?
505 // (AE quirk: it doesn't - except for solid layers)
506 const auto transform_effects = (build_info.fFlags & kTransformEffects);
507
508 // Attach the transform before effects, when needed.
509 if (fLayerTransform && !transform_effects) {
510 layer = sksg::TransformEffect::Make(std::move(layer), fLayerTransform);
511 }
512
513 // Optional layer effects.
514 if (const skjson::ArrayValue* jeffects = fJlayer["ef"]) {
515 layer = EffectBuilder(&abuilder, fInfo.fSize, cbuilder)
516 .attachEffects(*jeffects, std::move(layer));
517 }
518
519 // Attach the transform after effects, when needed.
520 if (fLayerTransform && transform_effects) {
521 layer = sksg::TransformEffect::Make(std::move(layer), std::move(fLayerTransform));
522 }
523
524 // Optional layer styles.
525 if (const skjson::ArrayValue* jstyles = fJlayer["sy"]) {
526 layer = EffectBuilder(&abuilder, fInfo.fSize, cbuilder)
527 .attachStyles(*jstyles, std::move(layer));
528 }
529
530 // Optional layer opacity.
531 // TODO: de-dupe this "ks" lookup with matrix above.
532 if (const skjson::ObjectValue* jtransform = fJlayer["ks"]) {
533 layer = abuilder.attachOpacity(*jtransform, std::move(layer));
534 }
535
536 // Stash the content tree in case it is needed for later mattes.
537 fContentTree = layer;
538 if (ParseDefault<bool>(fJlayer["hd"], false)) {
539 layer = nullptr;
540 }
541
542 const auto has_animators = !abuilder.fCurrentAnimatorScope->empty();
543 const auto force_seek_count = build_info.fFlags & kForceSeek
544 ? abuilder.fCurrentAnimatorScope->size()
545 : fTransformAnimatorCount;
546
547 sk_sp<Animator> controller = sk_make_sp<LayerController>(ascope.release(),
548 layer,
549 force_seek_count,
550 fInfo.fInPoint,
551 fInfo.fOutPoint);
552
553 // Optional motion blur.
554 if (layer && has_animators && this->hasMotionBlur(cbuilder)) {
555 // Wrap both the layer node and the controller.
556 auto motion_blur = MotionBlurEffect::Make(std::move(controller), std::move(layer),
557 cbuilder->fMotionBlurSamples,
558 cbuilder->fMotionBlurAngle,
559 cbuilder->fMotionBlurPhase);
560 controller = sk_make_sp<MotionBlurController>(motion_blur);
561 layer = std::move(motion_blur);
562 }
563
564 abuilder.fCurrentAnimatorScope->push_back(std::move(controller));
565
566 if (ParseDefault<bool>(fJlayer["td"], false)) {
567 // |layer| is a track matte. We apply it as a mask to the next layer.
568 return nullptr;
569 }
570
571 // Optional matte.
572 const auto matte_mode = prev_layer
573 ? ParseDefault<size_t>(fJlayer["tt"], 0)
574 : 0;
575 if (matte_mode > 0) {
576 static constexpr sksg::MaskEffect::Mode gMatteModes[] = {
581 };
582
583 if (matte_mode <= std::size(gMatteModes)) {
584 // The current layer is masked with the previous layer *content*.
585 layer = sksg::MaskEffect::Make(std::move(layer),
586 prev_layer->fContentTree,
587 gMatteModes[matte_mode - 1]);
588 } else {
589 abuilder.log(Logger::Level::kError, nullptr,
590 "Unknown track matte mode: %zu\n", matte_mode);
591 }
592 }
593
594 // Finally, attach an optional blend mode.
595 // NB: blend modes are never applied to matte sources (layer content only).
596 return abuilder.attachBlendMode(fJlayer, std::move(layer));
597}
598
599} // namespace internal
600} // namespace skottie
bool fInvertGeometry
Definition: Layer.cpp:57
sksg::Merge::Mode fMergeMode
Definition: Layer.cpp:56
SkBlendMode fBlendMode
Definition: Layer.cpp:55
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlendMode
Definition: SkBlendMode.h:38
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kDstOut
r = d * (1-sa)
@ kSrcIn
r = s * da
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
bool empty() const
Definition: SkTArray.h:199
int size() const
Definition: SkTArray.h:421
const char * begin() const
Definition: SkJSON.h:315
size_t size() const
Definition: SkJSON.h:300
sk_sp< sksg::RenderNode > attachOpacity(const skjson::ObjectValue &, sk_sp< sksg::RenderNode >) const
Definition: Skottie.cpp:118
void log(Logger::Level, const skjson::Value *, const char fmt[],...) const SK_PRINTF_LIKE(4
Definition: Skottie.cpp:71
sk_sp< sksg::RenderNode > attachEffects(const skjson::ArrayValue &, sk_sp< sksg::RenderNode >) const
Definition: Effects.cpp:112
sk_sp< sksg::RenderNode > attachStyles(const skjson::ArrayValue &, sk_sp< sksg::RenderNode >) const
Definition: Effects.cpp:141
LayerBuilder(const skjson::ObjectValue &jlayer, const SkSize &comp_size)
Definition: Layer.cpp:329
sk_sp< sksg::RenderNode > buildRenderTree(const AnimationBuilder &, CompositionBuilder *, const LayerBuilder *prev_layer)
Definition: Layer.cpp:431
sk_sp< sksg::Transform > buildTransform(const AnimationBuilder &, CompositionBuilder *)
Definition: Layer.cpp:353
static sk_sp< MotionBlurEffect > Make(sk_sp< Animator > animator, sk_sp< sksg::RenderNode > child, size_t samples_per_frame, float shutter_angle, float shutter_phase)
static sk_sp< BlurImageFilter > Make()
static sk_sp< ClipEffect > Make(sk_sp< RenderNode > child, sk_sp< GeometryNode > clip, bool aa=false, bool force_clip=false)
static sk_sp< Draw > Make(sk_sp< GeometryNode > geo, sk_sp< PaintNode > paint)
Definition: SkSGDraw.h:35
static sk_sp< Group > Make()
Definition: SkSGGroup.h:31
static sk_sp< RenderNode > Make(sk_sp< RenderNode > child, sk_sp< ImageFilter > filter)
static sk_sp< LayerEffect > Make(sk_sp< RenderNode > child, SkBlendMode mode=SkBlendMode::kSrcOver)
static sk_sp< MaskEffect > Make(sk_sp< RenderNode > child, sk_sp< RenderNode > mask, Mode mode=Mode::kAlphaNormal)
static sk_sp< Merge > Make(std::vector< Rec > &&recs)
Definition: SkSGMerge.h:49
static sk_sp< Rect > Make()
Definition: SkSGRect.h:36
bool isVisible() const
void setVisible(bool)
static sk_sp< TransformEffect > Make(sk_sp< RenderNode > child, sk_sp< Transform > transform)
Definition: SkSGTransform.h:97
static sk_sp< Transform > MakeInverse(sk_sp< Transform > t)
float SkScalar
Definition: extension.cpp:12
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
SK_API sk_sp< SkShader > Color(SkColor)
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 mode
Definition: switches.h:228
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
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By default
Definition: switches.h:183
std::vector< sk_sp< Animator > > AnimatorScope
Definition: SkottiePriv.h:55
SkScalar ScalarValue
Definition: SkottieValue.h:22
SkV2 Vec2Value
Definition: SkottieValue.h:23
T ParseDefault(const skjson::Value &v, const T &defaultValue)
Definition: SkottieJson.h:23
Definition: Skottie.h:32
Definition: ref_ptr.h:256
SkScalar w
SkScalar h
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
Definition: SkSize.h:52
Definition: SkM44.h:19