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