/* * 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 "include/effects/SkShaderMaskFilter.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; fMaskFilter = nullptr; } else { static constexpr float kFeatherToSigma = 0.3f; // close enough to AE fMaskSigma = std::max(fFeather, 0.0f) * kFeatherToSigma; // The gradient is inverted between non-blurred and blurred (latter requires dstOut). const SkColor c0 = fMaskSigma > 0 ? 0xffffffff : 0x00000000, c1 = 0xffffffff - c0; auto t = fCompletion * 0.01f; const SkColor grad_colors[] = { c0, c1 }; const SkScalar grad_pos[] = { t, t }; SkMatrix lm; lm.setRotate(fStartAngle - 90 + t * this->wipeAlignment(), fWipeCenter.x(), fWipeCenter.y()); fMaskFilter = SkShaderMaskFilter::Make( SkGradientShader::MakeSweep(fWipeCenter.x(), fWipeCenter.y(), grad_colors, grad_pos, SK_ARRAY_COUNT(grad_colors), 0, &lm)); // Edge feather requires a real blur. if (fMaskSigma > 0) { fMaskFilter = SkMaskFilter::MakeCompose(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, fMaskSigma), std::move(fMaskFilter)); } } return content_bounds; } void onRender(SkCanvas* canvas, const RenderContext* ctx) const override { if (fCompletion >= 100) { // Fully masked out. return; } if (!fMaskSigma) { // No mask filter, or a shader-only mask filter: we can draw the content directly. const auto local_ctx = ScopedRenderContext(canvas, ctx) .modulateMaskFilter(fMaskFilter, canvas->getTotalMatrix()); this->children()[0]->render(canvas, local_ctx); return; } // Blurred mask filters require a separate layer. SkAutoCanvasRestore acr(canvas, false); canvas->saveLayer(this->bounds(), nullptr); this->children()[0]->render(canvas, ctx); // Outset the mask to clip-out any edge blur. const auto mask_bounds = this->bounds().makeOutset(fMaskSigma * 3, fMaskSigma * 3); SkPaint mask_paint; mask_paint.setBlendMode(SkBlendMode::kDstOut); mask_paint.setMaskFilter(fMaskFilter); canvas->drawRect(mask_bounds, mask_paint); } 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 fMaskFilter; float fMaskSigma; // edge feather/blur using INHERITED = sksg::CustomRenderNode; }; } // namespace sk_sp EffectBuilder::attachRadialWipeEffect(const skjson::ArrayValue& jprops, sk_sp layer) const { enum : size_t { kCompletion_Index = 0, kStartAngle_Index = 1, kWipeCenter_Index = 2, kWipe_Index = 3, kFeather_Index = 4, }; auto wiper = sk_make_sp(std::move(layer)); fBuilder->bindProperty(GetPropValue(jprops, kCompletion_Index), [wiper](const ScalarValue& c) { wiper->setCompletion(c); }); fBuilder->bindProperty(GetPropValue(jprops, kStartAngle_Index), [wiper](const ScalarValue& sa) { wiper->setStartAngle(sa); }); fBuilder->bindProperty(GetPropValue(jprops, kWipeCenter_Index), [wiper](const VectorValue& c) { wiper->setWipeCenter(ValueTraits::As(c)); }); fBuilder->bindProperty(GetPropValue(jprops, kWipe_Index), [wiper](const ScalarValue& w) { wiper->setWipe(w); }); fBuilder->bindProperty(GetPropValue(jprops, kFeather_Index), [wiper](const ScalarValue& f) { wiper->setFeather(f); }); return wiper; } } // namespace internal } // namespace skottie