[skottie] SkBlender-based blend modes

Refactor the blend modes impl to always use SkBlenders.

This is in preparation of non-standard blend mode support.

No functional changes.

Change-Id: Id4ed8a2e2f90261978f48c74bc6d40c91ea4a35e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/559097
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
Florin Malita 2022-07-15 11:41:41 -04:00 committed by SkCQ
parent 7d1636010a
commit b67b9c6158
9 changed files with 102 additions and 69 deletions

View File

@ -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",

View File

@ -8,6 +8,7 @@ filegroup(
name = "srcs",
srcs = [
"Adapter.h",
"BlendModes.cpp",
"Camera.cpp",
"Camera.h",
"Composition.cpp",

View File

@ -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<SkBlender> 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<size_t>(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<sksg::RenderNode> AnimationBuilder::attachBlendMode(const skjson::ObjectValue& jobject,
sk_sp<sksg::RenderNode> 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

View File

@ -92,37 +92,6 @@ private:
using INHERITED = DiscardableAdapterBase<OpacityAdapter, sksg::OpacityEffect>;
};
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<size_t>(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<sksg::RenderNode> AnimationBuilder::attachOpacity(const skjson::ObjectValue& jobject,
@ -146,17 +115,6 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachOpacity(const skjson::ObjectValu
return adapter->node();
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachBlendMode(const skjson::ObjectValue& jobject,
sk_sp<sksg::RenderNode> 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<ResourceProvider> rp, sk_sp<SkFontMgr> fontmgr,
sk_sp<PropertyObserver> pobserver, sk_sp<Logger> logger,
sk_sp<MarkerObserver> mobserver, sk_sp<PrecompInterceptor> pi,

View File

@ -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;)

View File

@ -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<BlendModeEffect> Make(sk_sp<RenderNode> child,
SkBlendMode = SkBlendMode::kSrcOver);
static sk_sp<BlenderEffect> Make(sk_sp<RenderNode> child, sk_sp<SkBlender> = nullptr);
SG_ATTRIBUTE(Mode, SkBlendMode, fMode)
SG_ATTRIBUTE(Blender, sk_sp<SkBlender>, fBlender)
protected:
void onRender(SkCanvas*, const RenderContext*) const override;
const RenderNode* onNodeAt(const SkPoint&) const override;
private:
BlendModeEffect(sk_sp<RenderNode>, SkBlendMode);
BlenderEffect(sk_sp<RenderNode>, sk_sp<SkBlender>);
SkBlendMode fMode;
sk_sp<SkBlender> fBlender;
using INHERITED = EffectNode;
};

View File

@ -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<SkColorFilter> fColorFilter;
sk_sp<SkShader> fShader;
sk_sp<SkShader> fMaskShader;
sk_sp<SkBlender> 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<SkColorFilter>);
ScopedRenderContext&& modulateShader(sk_sp<SkShader>, const SkMatrix& shader_ctm);
ScopedRenderContext&& modulateMaskShader(sk_sp<SkShader>, const SkMatrix& ms_ctm);
ScopedRenderContext&& modulateBlendMode(SkBlendMode);
ScopedRenderContext&& modulateBlender(sk_sp<SkBlender>);
// Force content isolation for a node sub-DAG by applying the RenderContext
// overrides via a layer.

View File

@ -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<SkImageFilter> BlurImageFilter::onRevalidateFilter() {
return SkImageFilters::Blur(fSigma.x(), fSigma.y(), fTileMode, this->refInput(0));
}
sk_sp<BlendModeEffect> BlendModeEffect::Make(sk_sp<RenderNode> child, SkBlendMode mode) {
return child ? sk_sp<BlendModeEffect>(new BlendModeEffect(std::move(child), mode))
sk_sp<BlenderEffect> BlenderEffect::Make(sk_sp<RenderNode> child, sk_sp<SkBlender> blender) {
return child ? sk_sp<BlenderEffect>(new BlenderEffect(std::move(child), std::move(blender)))
: nullptr;
}
BlendModeEffect::BlendModeEffect(sk_sp<RenderNode> child, SkBlendMode mode)
BlenderEffect::BlenderEffect(sk_sp<RenderNode> child, sk_sp<SkBlender> 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);
}

View File

@ -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<SkShader> ms, const Sk
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateBlendMode(SkBlendMode mode) {
fCtx.fBlendMode = mode;
RenderNode::ScopedRenderContext::modulateBlender(sk_sp<SkBlender> 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);