diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni index 596275f837..d4c6f36ee0 100644 --- a/modules/skottie/skottie.gni +++ b/modules/skottie/skottie.gni @@ -15,6 +15,7 @@ skia_skottie_public = [ skia_skottie_sources = [ "$_src/Adapter.h", + "$_src/BlendModes.cpp", "$_src/Camera.cpp", "$_src/Camera.h", "$_src/Composition.cpp", diff --git a/modules/skottie/src/BUILD.bazel b/modules/skottie/src/BUILD.bazel index 4138b9d77e..ef48add0d5 100644 --- a/modules/skottie/src/BUILD.bazel +++ b/modules/skottie/src/BUILD.bazel @@ -8,6 +8,7 @@ filegroup( name = "srcs", srcs = [ "Adapter.h", + "BlendModes.cpp", "Camera.cpp", "Camera.h", "Composition.cpp", diff --git a/modules/skottie/src/BlendModes.cpp b/modules/skottie/src/BlendModes.cpp new file mode 100644 index 0000000000..61c38d6a6c --- /dev/null +++ b/modules/skottie/src/BlendModes.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2022 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkBlendMode.h" +#include "include/core/SkBlender.h" +#include "modules/skottie/src/SkottieJson.h" +#include "modules/skottie/src/SkottiePriv.h" +#include "modules/sksg/include/SkSGRenderEffect.h" + +namespace skottie::internal { + +namespace { + +static sk_sp get_blender(const skjson::ObjectValue& jobject, + const AnimationBuilder* abuilder) { + static constexpr SkBlendMode kBlendModeMap[] = { + SkBlendMode::kSrcOver, // 0:'normal' + SkBlendMode::kMultiply, // 1:'multiply' + SkBlendMode::kScreen, // 2:'screen' + SkBlendMode::kOverlay, // 3:'overlay + SkBlendMode::kDarken, // 4:'darken' + SkBlendMode::kLighten, // 5:'lighten' + SkBlendMode::kColorDodge, // 6:'color-dodge' + SkBlendMode::kColorBurn, // 7:'color-burn' + SkBlendMode::kHardLight, // 8:'hard-light' + SkBlendMode::kSoftLight, // 9:'soft-light' + SkBlendMode::kDifference, // 10:'difference' + SkBlendMode::kExclusion, // 11:'exclusion' + SkBlendMode::kHue, // 12:'hue' + SkBlendMode::kSaturation, // 13:'saturation' + SkBlendMode::kColor, // 14:'color' + SkBlendMode::kLuminosity, // 15:'luminosity' + SkBlendMode::kPlus, // 16:'add' + }; + + const size_t mode = ParseDefault(jobject["bm"], 0); + + // Special handling of src-over, so we can detect the trivial/no-fancy-blending case + // (a null blender is equivalent to src-over). + if (!mode) { + return nullptr; + } + + // Modes that are expressible as SkBlendMode. + if (mode < std::size(kBlendModeMap)) { + return SkBlender::Mode(kBlendModeMap[mode]); + } + + // Modes that require custom blenders. + // TODO: add custom blenders + + abuilder->log(Logger::Level::kWarning, &jobject, "Unsupported blend mode %zu\n", mode); + return nullptr; +} + +} // namespace + +sk_sp AnimationBuilder::attachBlendMode(const skjson::ObjectValue& jobject, + sk_sp child) const { + if (auto blender = get_blender(jobject, this)) { + fHasNontrivialBlending = true; + child = sksg::BlenderEffect::Make(std::move(child), std::move(blender)); + } + + return child; +} + +} // namespace skottie::internal diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp index 6dcaf1bb59..1af63669bc 100644 --- a/modules/skottie/src/Skottie.cpp +++ b/modules/skottie/src/Skottie.cpp @@ -92,37 +92,6 @@ private: using INHERITED = DiscardableAdapterBase; }; -static SkBlendMode GetBlendMode(const skjson::ObjectValue& jobject, - const AnimationBuilder* abuilder) { - static constexpr SkBlendMode kBlendModeMap[] = { - SkBlendMode::kSrcOver, // 0:'normal' - SkBlendMode::kMultiply, // 1:'multiply' - SkBlendMode::kScreen, // 2:'screen' - SkBlendMode::kOverlay, // 3:'overlay - SkBlendMode::kDarken, // 4:'darken' - SkBlendMode::kLighten, // 5:'lighten' - SkBlendMode::kColorDodge, // 6:'color-dodge' - SkBlendMode::kColorBurn, // 7:'color-burn' - SkBlendMode::kHardLight, // 8:'hard-light' - SkBlendMode::kSoftLight, // 9:'soft-light' - SkBlendMode::kDifference, // 10:'difference' - SkBlendMode::kExclusion, // 11:'exclusion' - SkBlendMode::kHue, // 12:'hue' - SkBlendMode::kSaturation, // 13:'saturation' - SkBlendMode::kColor, // 14:'color' - SkBlendMode::kLuminosity, // 15:'luminosity' - SkBlendMode::kPlus, // 16:'add' - }; - - const size_t bm_index = ParseDefault(jobject["bm"], 0); - if (bm_index >= std::size(kBlendModeMap)) { - abuilder->log(Logger::Level::kWarning, &jobject, "Unsupported blend mode %zu\n", bm_index); - return SkBlendMode::kSrcOver; - } - - return kBlendModeMap[bm_index]; -} - } // namespace sk_sp AnimationBuilder::attachOpacity(const skjson::ObjectValue& jobject, @@ -146,17 +115,6 @@ sk_sp AnimationBuilder::attachOpacity(const skjson::ObjectValu return adapter->node(); } -sk_sp AnimationBuilder::attachBlendMode(const skjson::ObjectValue& jobject, - sk_sp child) const { - const auto bm = GetBlendMode(jobject, this); - if (bm != SkBlendMode::kSrcOver) { - fHasNontrivialBlending = true; - child = sksg::BlendModeEffect::Make(std::move(child), bm); - } - - return child; -} - AnimationBuilder::AnimationBuilder(sk_sp rp, sk_sp fontmgr, sk_sp pobserver, sk_sp logger, sk_sp mobserver, sk_sp pi, diff --git a/modules/skottie/src/effects/MotionBlurEffect.cpp b/modules/skottie/src/effects/MotionBlurEffect.cpp index 86aae532df..74579420ea 100644 --- a/modules/skottie/src/effects/MotionBlurEffect.cpp +++ b/modules/skottie/src/effects/MotionBlurEffect.cpp @@ -222,13 +222,13 @@ void MotionBlurEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) cons ScopedRenderContext frame_ctx(canvas, ctx); SkPaint frame_paint; - const bool isolate_frames = frame_ctx->fBlendMode != SkBlendMode::kSrcOver; + const bool isolate_frames = !!frame_ctx->fBlender; if (isolate_frames) { frame_paint.setAlphaf(frame_alpha); frame_paint.setBlendMode(SkBlendMode::kPlus); } else { frame_ctx = frame_ctx.modulateOpacity(frame_alpha) - .modulateBlendMode(SkBlendMode::kPlus); + .modulateBlender(SkBlender::Mode(SkBlendMode::kPlus)); } SkDEBUGCODE(size_t frames_rendered = 0;) diff --git a/modules/sksg/include/SkSGRenderEffect.h b/modules/sksg/include/SkSGRenderEffect.h index aa0d41310d..1ad21eadf8 100644 --- a/modules/sksg/include/SkSGRenderEffect.h +++ b/modules/sksg/include/SkSGRenderEffect.h @@ -19,6 +19,7 @@ // TODO: merge EffectNode.h with this header +class SkBlender; class SkImageFilter; class SkMaskFilter; class SkShader; @@ -225,25 +226,24 @@ private: }; /** - * Applies a SkBlendMode to descendant render nodes. + * Applies an SkBlender to descendant render nodes. */ -class BlendModeEffect final : public EffectNode { +class BlenderEffect final : public EffectNode { public: - ~BlendModeEffect() override; + ~BlenderEffect() override; - static sk_sp Make(sk_sp child, - SkBlendMode = SkBlendMode::kSrcOver); + static sk_sp Make(sk_sp child, sk_sp = nullptr); - SG_ATTRIBUTE(Mode, SkBlendMode, fMode) + SG_ATTRIBUTE(Blender, sk_sp, fBlender) protected: void onRender(SkCanvas*, const RenderContext*) const override; const RenderNode* onNodeAt(const SkPoint&) const override; private: - BlendModeEffect(sk_sp, SkBlendMode); + BlenderEffect(sk_sp, sk_sp); - SkBlendMode fMode; + sk_sp fBlender; using INHERITED = EffectNode; }; diff --git a/modules/sksg/include/SkSGRenderNode.h b/modules/sksg/include/SkSGRenderNode.h index 00b7ced0ae..a040722fbe 100644 --- a/modules/sksg/include/SkSGRenderNode.h +++ b/modules/sksg/include/SkSGRenderNode.h @@ -10,7 +10,7 @@ #include "modules/sksg/include/SkSGNode.h" -#include "include/core/SkBlendMode.h" +#include "include/core/SkBlender.h" #include "include/core/SkColorFilter.h" #include "include/core/SkShader.h" @@ -53,10 +53,10 @@ protected: sk_sp fColorFilter; sk_sp fShader; sk_sp fMaskShader; + sk_sp fBlender; SkMatrix fShaderCTM = SkMatrix::I(), fMaskCTM = SkMatrix::I(); float fOpacity = 1; - SkBlendMode fBlendMode = SkBlendMode::kSrcOver; // Returns true if the paint overrides require a layer when applied to non-atomic draws. bool requiresIsolation() const; @@ -91,7 +91,7 @@ protected: ScopedRenderContext&& modulateColorFilter(sk_sp); ScopedRenderContext&& modulateShader(sk_sp, const SkMatrix& shader_ctm); ScopedRenderContext&& modulateMaskShader(sk_sp, const SkMatrix& ms_ctm); - ScopedRenderContext&& modulateBlendMode(SkBlendMode); + ScopedRenderContext&& modulateBlender(sk_sp); // Force content isolation for a node sub-DAG by applying the RenderContext // overrides via a layer. diff --git a/modules/sksg/src/SkSGRenderEffect.cpp b/modules/sksg/src/SkSGRenderEffect.cpp index 8109b47749..4944ddbcfc 100644 --- a/modules/sksg/src/SkSGRenderEffect.cpp +++ b/modules/sksg/src/SkSGRenderEffect.cpp @@ -7,6 +7,7 @@ #include "modules/sksg/include/SkSGRenderEffect.h" +#include "include/core/SkBlender.h" #include "include/core/SkCanvas.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkShader.h" @@ -202,24 +203,24 @@ sk_sp BlurImageFilter::onRevalidateFilter() { return SkImageFilters::Blur(fSigma.x(), fSigma.y(), fTileMode, this->refInput(0)); } -sk_sp BlendModeEffect::Make(sk_sp child, SkBlendMode mode) { - return child ? sk_sp(new BlendModeEffect(std::move(child), mode)) +sk_sp BlenderEffect::Make(sk_sp child, sk_sp blender) { + return child ? sk_sp(new BlenderEffect(std::move(child), std::move(blender))) : nullptr; } -BlendModeEffect::BlendModeEffect(sk_sp child, SkBlendMode mode) +BlenderEffect::BlenderEffect(sk_sp child, sk_sp blender) : INHERITED(std::move(child)) - , fMode(mode) {} + , fBlender (std::move(blender)) {} -BlendModeEffect::~BlendModeEffect() = default; +BlenderEffect::~BlenderEffect() = default; -void BlendModeEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const { - const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateBlendMode(fMode); +void BlenderEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const { + const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateBlender(fBlender); this->INHERITED::onRender(canvas, local_ctx); } -const RenderNode* BlendModeEffect::onNodeAt(const SkPoint& p) const { +const RenderNode* BlenderEffect::onNodeAt(const SkPoint& p) const { // TODO: we likely need to do something more sophisticated than delegate to descendants here. return this->INHERITED::onNodeAt(p); } diff --git a/modules/sksg/src/SkSGRenderNode.cpp b/modules/sksg/src/SkSGRenderNode.cpp index 9a6ce32a63..1de209874e 100644 --- a/modules/sksg/src/SkSGRenderNode.cpp +++ b/modules/sksg/src/SkSGRenderNode.cpp @@ -88,7 +88,7 @@ bool RenderNode::RenderContext::requiresIsolation() const { return ScaleAlpha(SK_AlphaOPAQUE, fOpacity) != SK_AlphaOPAQUE || fColorFilter || fMaskShader - || fBlendMode != SkBlendMode::kSrcOver; + || fBlender; } void RenderNode::RenderContext::modulatePaint(const SkMatrix& ctm, SkPaint* paint, @@ -98,8 +98,8 @@ void RenderNode::RenderContext::modulatePaint(const SkMatrix& ctm, SkPaint* pain if (fShader) { paint->setShader(LocalShader(fShader, fShaderCTM, ctm)); } - if (fBlendMode != SkBlendMode::kSrcOver) { - paint->setBlendMode(fBlendMode); + if (fBlender) { + paint->setBlender(fBlender); } // Only apply the shader mask for regular paints. Isolation layers require @@ -177,8 +177,8 @@ RenderNode::ScopedRenderContext::modulateMaskShader(sk_sp ms, const Sk } RenderNode::ScopedRenderContext&& -RenderNode::ScopedRenderContext::modulateBlendMode(SkBlendMode mode) { - fCtx.fBlendMode = mode; +RenderNode::ScopedRenderContext::modulateBlender(sk_sp blender) { + fCtx.fBlender = std::move(blender); return std::move(*this); } @@ -198,8 +198,8 @@ RenderNode::ScopedRenderContext::setIsolation(const SkRect& bounds, const SkMatr // Reset only the props applied via isolation layers. fCtx.fColorFilter = nullptr; fCtx.fMaskShader = nullptr; + fCtx.fBlender = nullptr; fCtx.fOpacity = 1; - fCtx.fBlendMode = SkBlendMode::kSrcOver; } return std::move(*this);