[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:
parent
7d1636010a
commit
b67b9c6158
@ -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",
|
||||
|
@ -8,6 +8,7 @@ filegroup(
|
||||
name = "srcs",
|
||||
srcs = [
|
||||
"Adapter.h",
|
||||
"BlendModes.cpp",
|
||||
"Camera.cpp",
|
||||
"Camera.h",
|
||||
"Composition.cpp",
|
||||
|
72
modules/skottie/src/BlendModes.cpp
Normal file
72
modules/skottie/src/BlendModes.cpp
Normal 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
|
@ -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,
|
||||
|
@ -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;)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user