/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "modules/skottie/src/effects/Effects.h" #include "include/core/SkCanvas.h" #include "include/effects/SkGradientShader.h" #include "modules/skottie/src/Adapter.h" #include "modules/skottie/src/SkottieValue.h" #include "modules/sksg/include/SkSGRenderNode.h" #include "src/utils/SkJSON.h" #include namespace skottie { namespace internal { namespace { class RWipeRenderNode final : public sksg::CustomRenderNode { public: explicit RWipeRenderNode(sk_sp layer) : INHERITED({std::move(layer)}) {} SG_ATTRIBUTE(Completion, float , fCompletion) SG_ATTRIBUTE(StartAngle, float , fStartAngle) SG_ATTRIBUTE(WipeCenter, SkPoint, fWipeCenter) SG_ATTRIBUTE(Wipe , float , fWipe ) SG_ATTRIBUTE(Feather , float , fFeather ) protected: const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override { SkASSERT(this->children().size() == 1ul); const auto content_bounds = this->children()[0]->revalidate(ic, ctm); if (fCompletion >= 100) { return SkRect::MakeEmpty(); } if (fCompletion <= 0) { fMaskSigma = 0; fMaskShader = nullptr; } else { fMaskSigma = std::max(fFeather, 0.0f) * kBlurSizeToSigma; const auto t = fCompletion * 0.01f; // Note: this could be simplified as a one-hard-stop gradient + local matrix // (to apply rotation). Alas, local matrices are no longer supported in SkSG. SkColor c0 = 0x00000000, c1 = 0xffffffff; auto sanitize_angle = [](float a) { a = std::fmod(a, 360); if (a < 0) { a += 360; } return a; }; auto a0 = sanitize_angle(fStartAngle - 90 + t * this->wipeAlignment()), a1 = sanitize_angle(a0 + t * 360); if (a0 > a1) { std::swap(a0, a1); std::swap(c0, c1); } const SkColor grad_colors[] = { c1, c0, c0, c1 }; const SkScalar grad_pos[] = { 0, 0, 1, 1 }; fMaskShader = SkGradientShader::MakeSweep(fWipeCenter.x(), fWipeCenter.y(), grad_colors, grad_pos, SK_ARRAY_COUNT(grad_colors), SkTileMode::kClamp, a0, a1, 0, nullptr); // Edge feather requires a real blur. if (fMaskSigma > 0) { // TODO: this feature is disabled ATM. } } return content_bounds; } void onRender(SkCanvas* canvas, const RenderContext* ctx) const override { if (fCompletion >= 100) { // Fully masked out. return; } const auto local_ctx = ScopedRenderContext(canvas, ctx) .modulateMaskShader(fMaskShader, canvas->getTotalMatrix()); this->children()[0]->render(canvas, local_ctx); } private: float wipeAlignment() const { switch (SkScalarRoundToInt(fWipe)) { case 1: return 0.0f; // Clockwise case 2: return -360.0f; // Counterclockwise case 3: return -180.0f; // Both/center default: break; } return 0.0f; } SkPoint fWipeCenter = { 0, 0 }; float fCompletion = 0, fStartAngle = 0, fWipe = 0, fFeather = 0; // Cached during revalidation. sk_sp fMaskShader; float fMaskSigma; // edge feather/blur using INHERITED = sksg::CustomRenderNode; }; class RadialWipeAdapter final : public DiscardableAdapterBase { public: RadialWipeAdapter(const skjson::ArrayValue& jprops, sk_sp layer, const AnimationBuilder& abuilder) : INHERITED(sk_make_sp(std::move(layer))) { enum : size_t { kCompletion_Index = 0, kStartAngle_Index = 1, kWipeCenter_Index = 2, kWipe_Index = 3, kFeather_Index = 4, }; EffectBinder(jprops, abuilder, this) .bind(kCompletion_Index, fCompletion) .bind(kStartAngle_Index, fStartAngle) .bind(kWipeCenter_Index, fWipeCenter) .bind( kWipe_Index, fWipe ) .bind( kFeather_Index, fFeather ); } private: void onSync() override { const auto& wiper = this->node(); wiper->setCompletion(fCompletion); wiper->setStartAngle(fStartAngle); wiper->setWipeCenter({fWipeCenter.x, fWipeCenter.y}); wiper->setWipe(fWipe); wiper->setFeather(fFeather); } Vec2Value fWipeCenter = {0,0}; ScalarValue fCompletion = 0, fStartAngle = 0, fWipe = 0, fFeather = 0; using INHERITED = DiscardableAdapterBase; }; } // namespace sk_sp EffectBuilder::attachRadialWipeEffect(const skjson::ArrayValue& jprops, sk_sp layer) const { return fBuilder->attachDiscardableAdapter(jprops, std::move(layer), *fBuilder); } } // namespace internal } // namespace skottie