| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkottiePriv.h" |
| |
| #include "SkJSON.h" |
| #include "SkottieAdapter.h" |
| #include "SkottieJson.h" |
| #include "SkottieValue.h" |
| #include "SkSGColor.h" |
| #include "SkSGRenderEffect.h" |
| #include "SkSGColorFilter.h" |
| |
| namespace skottie { |
| namespace internal { |
| |
| namespace { |
| |
| sk_sp<sksg::RenderNode> AttachTintLayerEffect(const skjson::ArrayValue& jprops, |
| const AnimationBuilder* abuilder, |
| AnimatorScope* ascope, |
| sk_sp<sksg::RenderNode> layer) { |
| enum : size_t { |
| kMapBlackTo_Index = 0, |
| kMapWhiteTo_Index = 1, |
| kAmount_Index = 2, |
| // kOpacity_Index = 3, // currently unused (not exported) |
| |
| kMax_Index = kAmount_Index, |
| }; |
| |
| if (jprops.size() <= kMax_Index) { |
| return nullptr; |
| } |
| |
| const skjson::ObjectValue* color0_prop = jprops[kMapBlackTo_Index]; |
| const skjson::ObjectValue* color1_prop = jprops[kMapWhiteTo_Index]; |
| const skjson::ObjectValue* amount_prop = jprops[ kAmount_Index]; |
| |
| if (!color0_prop || !color1_prop || !amount_prop) { |
| return nullptr; |
| } |
| |
| auto tint_node = |
| sksg::GradientColorFilter::Make(std::move(layer), |
| abuilder->attachColor(*color0_prop, ascope, "v"), |
| abuilder->attachColor(*color1_prop, ascope, "v")); |
| if (!tint_node) { |
| return nullptr; |
| } |
| |
| abuilder->bindProperty<ScalarValue>((*amount_prop)["v"], ascope, |
| [tint_node](const ScalarValue& w) { |
| tint_node->setWeight(w / 100); // 100-based |
| }); |
| |
| return std::move(tint_node); |
| } |
| |
| sk_sp<sksg::RenderNode> AttachTritoneLayerEffect(const skjson::ArrayValue& jprops, |
| const AnimationBuilder* abuilder, |
| AnimatorScope* ascope, |
| sk_sp<sksg::RenderNode> layer) { |
| enum : size_t { |
| kHiColor_Index = 0, |
| kMiColor_Index = 1, |
| kLoColor_Index = 2, |
| kBlendAmount_Index = 3, |
| |
| kMax_Index = kBlendAmount_Index, |
| }; |
| |
| if (jprops.size() <= kMax_Index) { |
| return nullptr; |
| } |
| |
| const skjson::ObjectValue* hicolor_prop = jprops[ kHiColor_Index]; |
| const skjson::ObjectValue* micolor_prop = jprops[ kMiColor_Index]; |
| const skjson::ObjectValue* locolor_prop = jprops[ kLoColor_Index]; |
| const skjson::ObjectValue* blend_prop = jprops[kBlendAmount_Index]; |
| |
| if (!hicolor_prop || !micolor_prop || !locolor_prop || !blend_prop) { |
| return nullptr; |
| } |
| |
| auto tritone_node = |
| sksg::GradientColorFilter::Make(std::move(layer), { |
| abuilder->attachColor(*locolor_prop, ascope, "v"), |
| abuilder->attachColor(*micolor_prop, ascope, "v"), |
| abuilder->attachColor(*hicolor_prop, ascope, "v") }); |
| if (!tritone_node) { |
| return nullptr; |
| } |
| |
| abuilder->bindProperty<ScalarValue>((*blend_prop)["v"], ascope, |
| [tritone_node](const ScalarValue& w) { |
| tritone_node->setWeight((100 - w) / 100); // 100-based, inverted (!?). |
| }); |
| |
| return std::move(tritone_node); |
| } |
| |
| sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue& jprops, |
| const AnimationBuilder* abuilder, |
| AnimatorScope* ascope, |
| sk_sp<sksg::RenderNode> layer) { |
| enum : size_t { |
| kFillMask_Index = 0, |
| kAllMasks_Index = 1, |
| kColor_Index = 2, |
| kInvert_Index = 3, |
| kHFeather_Index = 4, |
| kVFeather_Index = 5, |
| kOpacity_Index = 6, |
| |
| kMax_Index = kOpacity_Index, |
| }; |
| |
| if (jprops.size() <= kMax_Index) { |
| return nullptr; |
| } |
| |
| const skjson::ObjectValue* color_prop = jprops[ kColor_Index]; |
| const skjson::ObjectValue* opacity_prop = jprops[kOpacity_Index]; |
| if (!color_prop || !opacity_prop) { |
| return nullptr; |
| } |
| |
| sk_sp<sksg::Color> color_node = abuilder->attachColor(*color_prop, ascope, "v"); |
| if (!color_node) { |
| return nullptr; |
| } |
| |
| abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope, |
| [color_node](const ScalarValue& o) { |
| const auto c = color_node->getColor(); |
| const auto a = sk_float_round2int_no_saturate(SkTPin(o, 0.0f, 1.0f) * 255); |
| color_node->setColor(SkColorSetA(c, a)); |
| }); |
| |
| return sksg::ModeColorFilter::Make(std::move(layer), |
| std::move(color_node), |
| SkBlendMode::kSrcIn); |
| } |
| |
| sk_sp<sksg::RenderNode> AttachDropShadowLayerEffect(const skjson::ArrayValue& jprops, |
| const AnimationBuilder* abuilder, |
| AnimatorScope* ascope, |
| sk_sp<sksg::RenderNode> layer) { |
| enum : size_t { |
| kShadowColor_Index = 0, |
| kOpacity_Index = 1, |
| kDirection_Index = 2, |
| kDistance_Index = 3, |
| kSoftness_Index = 4, |
| kShadowOnly_Index = 5, |
| |
| kMax_Index = kShadowOnly_Index, |
| }; |
| |
| if (jprops.size() <= kMax_Index) { |
| return nullptr; |
| } |
| |
| const skjson::ObjectValue* color_prop = jprops[kShadowColor_Index]; |
| const skjson::ObjectValue* opacity_prop = jprops[ kOpacity_Index]; |
| const skjson::ObjectValue* direction_prop = jprops[ kDirection_Index]; |
| const skjson::ObjectValue* distance_prop = jprops[ kDistance_Index]; |
| const skjson::ObjectValue* softness_prop = jprops[ kSoftness_Index]; |
| const skjson::ObjectValue* shadow_only_prop = jprops[ kShadowOnly_Index]; |
| |
| if (!color_prop || |
| !opacity_prop || |
| !direction_prop || |
| !distance_prop || |
| !softness_prop || |
| !shadow_only_prop) { |
| return nullptr; |
| } |
| |
| auto shadow_effect = sksg::DropShadowImageFilter::Make(); |
| auto shadow_adapter = sk_make_sp<DropShadowEffectAdapter>(shadow_effect); |
| |
| abuilder->bindProperty<VectorValue>((*color_prop)["v"], ascope, |
| [shadow_adapter](const VectorValue& c) { |
| shadow_adapter->setColor(ValueTraits<VectorValue>::As<SkColor>(c)); |
| }); |
| abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope, |
| [shadow_adapter](const ScalarValue& o) { |
| shadow_adapter->setOpacity(o); |
| }); |
| abuilder->bindProperty<ScalarValue>((*direction_prop)["v"], ascope, |
| [shadow_adapter](const ScalarValue& d) { |
| shadow_adapter->setDirection(d); |
| }); |
| abuilder->bindProperty<ScalarValue>((*distance_prop)["v"], ascope, |
| [shadow_adapter](const ScalarValue& d) { |
| shadow_adapter->setDistance(d); |
| }); |
| abuilder->bindProperty<ScalarValue>((*softness_prop)["v"], ascope, |
| [shadow_adapter](const ScalarValue& s) { |
| shadow_adapter->setSoftness(s); |
| }); |
| abuilder->bindProperty<ScalarValue>((*shadow_only_prop)["v"], ascope, |
| [shadow_adapter](const ScalarValue& s) { |
| shadow_adapter->setShadowOnly(SkToBool(s)); |
| }); |
| |
| return sksg::ImageFilterEffect::Make(std::move(layer), std::move(shadow_effect)); |
| } |
| |
| } // namespace |
| |
| sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::ArrayValue& jeffects, |
| AnimatorScope* ascope, |
| sk_sp<sksg::RenderNode> layer) const { |
| if (!layer) { |
| return nullptr; |
| } |
| |
| enum : int32_t { |
| kTint_Effect = 20, |
| kFill_Effect = 21, |
| kTritone_Effect = 23, |
| kDropShadow_Effect = 25, |
| }; |
| |
| for (const skjson::ObjectValue* jeffect : jeffects) { |
| if (!jeffect) { |
| continue; |
| } |
| |
| const skjson::ArrayValue* jprops = (*jeffect)["ef"]; |
| if (!jprops) { |
| continue; |
| } |
| |
| switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) { |
| case kTint_Effect: |
| layer = AttachTintLayerEffect(*jprops, this, ascope, std::move(layer)); |
| break; |
| case kFill_Effect: |
| layer = AttachFillLayerEffect(*jprops, this, ascope, std::move(layer)); |
| break; |
| case kTritone_Effect: |
| layer = AttachTritoneLayerEffect(*jprops, this, ascope, std::move(layer)); |
| break; |
| case kDropShadow_Effect: |
| layer = AttachDropShadowLayerEffect(*jprops, this, ascope, std::move(layer)); |
| break; |
| default: |
| this->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty); |
| break; |
| } |
| |
| if (!layer) { |
| this->log(Logger::Level::kError, jeffect, "Invalid layer effect."); |
| return nullptr; |
| } |
| } |
| |
| return layer; |
| } |
| |
| } // namespace internal |
| } // namespace skottie |