[skottie] Tint effect support
Introduce a TintColorFilter scene graph node, and hook into Skottie. Change-Id: I350dec9ebcdc0bfc68d2874703112f1b20b51d75 Reviewed-on: https://skia-review.googlesource.com/c/193662 Commit-Queue: Florin Malita <fmalita@chromium.org> Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
parent
ddeed37bf8
commit
48cc589886
@ -20,6 +20,46 @@ namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachTintLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kMapBlackTo_Index = 0,
|
||||
kMapWhiteTo_Index = 1,
|
||||
kAmount_Index = 2,
|
||||
// kOpacity_Index = 3, // currently unused (not exported)
|
||||
|
||||
kMax_Index = kAmount_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color0_prop = jprops[kMapBlackTo_Index];
|
||||
const skjson::ObjectValue* color1_prop = jprops[kMapWhiteTo_Index];
|
||||
const skjson::ObjectValue* amount_prop = jprops[ kAmount_Index];
|
||||
|
||||
if (!color0_prop || !color1_prop || !amount_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tint_node = sksg::TintColorFilter::Make(std::move(layer),
|
||||
abuilder->attachColor(*color0_prop, ascope, "v"),
|
||||
abuilder->attachColor(*color1_prop, ascope, "v"));
|
||||
if (!tint_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*amount_prop)["v"], ascope,
|
||||
[tint_node](const ScalarValue& w) {
|
||||
tint_node->setWeight(w / 100); // 100-based
|
||||
});
|
||||
|
||||
return std::move(tint_node);
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
@ -58,7 +98,7 @@ sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue& jprops,
|
||||
color_node->setColor(SkColorSetA(c, a));
|
||||
});
|
||||
|
||||
return sksg::ColorModeFilter::Make(std::move(layer),
|
||||
return sksg::ModeColorFilter::Make(std::move(layer),
|
||||
std::move(color_node),
|
||||
SkBlendMode::kSrcIn);
|
||||
}
|
||||
@ -139,6 +179,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::Array
|
||||
}
|
||||
|
||||
enum : int32_t {
|
||||
kTint_Effect = 20,
|
||||
kFill_Effect = 21,
|
||||
kDropShadow_Effect = 25,
|
||||
};
|
||||
@ -154,6 +195,9 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::Array
|
||||
}
|
||||
|
||||
switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) {
|
||||
case kTint_Effect:
|
||||
layer = AttachTintLayerEffect(*jprops, this, ascope, std::move(layer));
|
||||
break;
|
||||
case kFill_Effect:
|
||||
layer = AttachFillLayerEffect(*jprops, this, ascope, std::move(layer));
|
||||
break;
|
||||
|
@ -20,7 +20,6 @@ class Color;
|
||||
|
||||
/**
|
||||
* Base class for nodes which apply a color filter when rendering their descendants.
|
||||
*
|
||||
*/
|
||||
class ColorFilter : public EffectNode {
|
||||
protected:
|
||||
@ -29,37 +28,64 @@ protected:
|
||||
void onRender(SkCanvas*, const RenderContext*) const final;
|
||||
const RenderNode* onNodeAt(const SkPoint&) const final;
|
||||
|
||||
sk_sp<SkColorFilter> fColorFilter;
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) final;
|
||||
|
||||
virtual sk_sp<SkColorFilter> onRevalidateFilter() = 0;
|
||||
|
||||
private:
|
||||
sk_sp<SkColorFilter> fColorFilter;
|
||||
|
||||
typedef EffectNode INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Concrete SkModeColorFilter Effect node.
|
||||
*
|
||||
*/
|
||||
class ColorModeFilter final : public ColorFilter {
|
||||
class ModeColorFilter final : public ColorFilter {
|
||||
public:
|
||||
~ColorModeFilter() override;
|
||||
~ModeColorFilter() override;
|
||||
|
||||
static sk_sp<ColorModeFilter> Make(sk_sp<RenderNode> child, sk_sp<Color> color,
|
||||
SkBlendMode mode) {
|
||||
return (child && color)
|
||||
? sk_sp<ColorModeFilter>(new ColorModeFilter(std::move(child), std::move(color), mode))
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
SG_ATTRIBUTE(Mode , SkBlendMode, fMode )
|
||||
static sk_sp<ModeColorFilter> Make(sk_sp<RenderNode> child,
|
||||
sk_sp<Color> color,
|
||||
SkBlendMode mode);
|
||||
|
||||
protected:
|
||||
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
|
||||
sk_sp<SkColorFilter> onRevalidateFilter() override;
|
||||
|
||||
private:
|
||||
ColorModeFilter(sk_sp<RenderNode>, sk_sp<Color>, SkBlendMode);
|
||||
ModeColorFilter(sk_sp<RenderNode>, sk_sp<Color>, SkBlendMode);
|
||||
|
||||
sk_sp<Color> fColor;
|
||||
SkBlendMode fMode;
|
||||
const sk_sp<Color> fColor;
|
||||
const SkBlendMode fMode;
|
||||
|
||||
typedef ColorFilter INHERITED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tint color effect: maps RGB colors to the [c0,c1] gradient based on input luminance
|
||||
* (while leaving the alpha channel unchanged), then mixes with the input based on weight.
|
||||
*/
|
||||
|
||||
class TintColorFilter final : public ColorFilter {
|
||||
public:
|
||||
~TintColorFilter() override;
|
||||
|
||||
static sk_sp<TintColorFilter> Make(sk_sp<RenderNode> child,
|
||||
sk_sp<Color> color0,
|
||||
sk_sp<Color> color1);
|
||||
|
||||
SG_ATTRIBUTE(Weight, float, fWeight)
|
||||
|
||||
protected:
|
||||
sk_sp<SkColorFilter> onRevalidateFilter() override;
|
||||
|
||||
private:
|
||||
TintColorFilter(sk_sp<RenderNode>, sk_sp<Color>, sk_sp<Color>);
|
||||
|
||||
const sk_sp<Color> fColor0,
|
||||
fColor1;
|
||||
|
||||
float fWeight = 0;
|
||||
|
||||
typedef ColorFilter INHERITED;
|
||||
};
|
||||
|
@ -29,24 +29,115 @@ const RenderNode* ColorFilter::onNodeAt(const SkPoint& p) const {
|
||||
return this->INHERITED::onNodeAt(p);
|
||||
}
|
||||
|
||||
ColorModeFilter::ColorModeFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode)
|
||||
SkRect ColorFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
|
||||
fColorFilter = this->onRevalidateFilter();
|
||||
|
||||
return this->INHERITED::onRevalidate(ic, ctm);
|
||||
}
|
||||
|
||||
sk_sp<ModeColorFilter> ModeColorFilter::Make(sk_sp<RenderNode> child, sk_sp<Color> color,
|
||||
SkBlendMode mode) {
|
||||
return (child && color) ? sk_sp<ModeColorFilter>(new ModeColorFilter(std::move(child),
|
||||
std::move(color), mode))
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
ModeColorFilter::ModeColorFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode)
|
||||
: INHERITED(std::move(child))
|
||||
, fColor(std::move(color))
|
||||
, fMode(mode) {
|
||||
this->observeInval(fColor);
|
||||
}
|
||||
|
||||
ColorModeFilter::~ColorModeFilter() {
|
||||
ModeColorFilter::~ModeColorFilter() {
|
||||
this->unobserveInval(fColor);
|
||||
}
|
||||
|
||||
SkRect ColorModeFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
|
||||
SkASSERT(this->hasInval());
|
||||
sk_sp<SkColorFilter> ModeColorFilter::onRevalidateFilter() {
|
||||
fColor->revalidate(nullptr, SkMatrix::I());
|
||||
return SkColorFilter::MakeModeFilter(fColor->getColor(), fMode);
|
||||
}
|
||||
|
||||
fColor->revalidate(ic, ctm);
|
||||
fColorFilter = SkColorFilter::MakeModeFilter(fColor->getColor(), fMode);
|
||||
sk_sp<TintColorFilter> TintColorFilter::Make(sk_sp<RenderNode> child,
|
||||
sk_sp<Color> c0, sk_sp<Color> c1) {
|
||||
return (child && c0 && c1) ? sk_sp<TintColorFilter>(new TintColorFilter(std::move(child),
|
||||
std::move(c0),
|
||||
std::move(c1)))
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
return this->INHERITED::onRevalidate(ic, ctm);
|
||||
TintColorFilter::TintColorFilter(sk_sp<RenderNode> child, sk_sp<Color> c0, sk_sp<Color> c1)
|
||||
: INHERITED(std::move(child))
|
||||
, fColor0(std::move(c0))
|
||||
, fColor1(std::move(c1)) {
|
||||
this->observeInval(fColor0);
|
||||
this->observeInval(fColor1);
|
||||
}
|
||||
|
||||
TintColorFilter::~TintColorFilter() {
|
||||
this->unobserveInval(fColor0);
|
||||
this->unobserveInval(fColor1);
|
||||
}
|
||||
|
||||
sk_sp<SkColorFilter> TintColorFilter::onRevalidateFilter() {
|
||||
fColor0->revalidate(nullptr, SkMatrix::I());
|
||||
fColor1->revalidate(nullptr, SkMatrix::I());
|
||||
|
||||
if (fWeight <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto c0 = SkColor4f::FromColor(fColor0->getColor()),
|
||||
c1 = SkColor4f::FromColor(fColor1->getColor());
|
||||
|
||||
// luminance coefficients
|
||||
static constexpr float kR = 0.2126f,
|
||||
kG = 0.7152f,
|
||||
kB = 0.0722f;
|
||||
|
||||
const auto dR = c1.fR - c0.fR,
|
||||
dG = c1.fG - c0.fG,
|
||||
dB = c1.fB - c0.fB;
|
||||
|
||||
// First, we need a luminance:
|
||||
//
|
||||
// L = [r,g,b] . [kR,kG,kB]
|
||||
//
|
||||
// We can compute it using a color matrix (result stored in R):
|
||||
//
|
||||
// | kR, kG, kB, 0, 0 | r' = L
|
||||
// | 0, 0, 0, 0, 0 | g' = 0
|
||||
// | 0, 0, 0, 0, 0 | b' = 0
|
||||
// | 0, 0, 0, 1, 0 | a' = a
|
||||
//
|
||||
// Then we want to interpolate component-wise, based on L:
|
||||
//
|
||||
// r' = c0.r + (c1.r - c0.r) * L = c0.r + dR*L
|
||||
// g' = c0.g + (c1.g - c0.g) * L = c0.g + dG*L
|
||||
// b' = c0.b + (c1.b - c0.b) * L = c0.b + dB*L
|
||||
// a' = a
|
||||
//
|
||||
// This can be expressed as another color matrix (when L is stored in R):
|
||||
//
|
||||
// | dR, 0, 0, 0, c0.r |
|
||||
// | dG, 0, 0, 0, c0.g |
|
||||
// | dB, 0, 0, 0, c0.b |
|
||||
// | 0, 0, 0, 1, 0 |
|
||||
//
|
||||
// Composing these two, we get the total tint matrix:
|
||||
|
||||
const SkScalar tint_matrix[] = {
|
||||
dR*kR, dR*kG, dR*kB, 0, c0.fR * 255,
|
||||
dG*kR, dG*kG, dG*kB, 0, c0.fG * 255,
|
||||
dB*kR, dB*kG, dB*kB, 0, c0.fB * 255,
|
||||
0, 0, 0, 1, 0,
|
||||
};
|
||||
|
||||
return SkColorFilter::MakeMixer(nullptr,
|
||||
SkColorFilter::MakeMatrixFilterRowMajor255(tint_matrix),
|
||||
fWeight);
|
||||
}
|
||||
|
||||
} // namespace sksg
|
||||
|
Loading…
Reference in New Issue
Block a user