blob: b04df183d5dec1e93dee7871551741809a75f507 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/sksg/include/SkSGColorFilter.h"
#include "include/core/SkColorFilter.h"
#include "include/effects/SkTableColorFilter.h"
#include "include/private/SkColorData.h"
#include "modules/sksg/include/SkSGPaint.h"
#include <cmath>
namespace sksg {
ColorFilter::ColorFilter(sk_sp<RenderNode> child)
: INHERITED(std::move(child)) {}
void ColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter);
this->INHERITED::onRender(canvas, local_ctx);
}
const RenderNode* ColorFilter::onNodeAt(const SkPoint& p) const {
// TODO: we likely need to do something more sophisticated than delegate to descendants here.
return this->INHERITED::onNodeAt(p);
}
SkRect ColorFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
SkASSERT(this->hasInval());
fColorFilter = this->onRevalidateFilter();
return this->INHERITED::onRevalidate(ic, ctm);
}
sk_sp<ExternalColorFilter> ExternalColorFilter::Make(sk_sp<RenderNode> child) {
return child ? sk_sp<ExternalColorFilter>(new ExternalColorFilter(std::move(child)))
: nullptr;
}
ExternalColorFilter::ExternalColorFilter(sk_sp<RenderNode> child) : INHERITED(std::move(child)) {}
ExternalColorFilter::~ExternalColorFilter() = default;
void ExternalColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter);
this->INHERITED::onRender(canvas, local_ctx);
}
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);
}
ModeColorFilter::~ModeColorFilter() {
this->unobserveInval(fColor);
}
sk_sp<SkColorFilter> ModeColorFilter::onRevalidateFilter() {
fColor->revalidate(nullptr, SkMatrix::I());
return SkColorFilters::Blend(fColor->getColor(), fMode);
}
sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child,
sk_sp<Color> c0, sk_sp<Color> c1) {
return Make(std::move(child), { std::move(c0), std::move(c1) });
}
sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child,
std::vector<sk_sp<Color>> colors) {
return (child && colors.size() > 1)
? sk_sp<GradientColorFilter>(new GradientColorFilter(std::move(child), std::move(colors)))
: nullptr;
}
GradientColorFilter::GradientColorFilter(sk_sp<RenderNode> child, std::vector<sk_sp<Color>> colors)
: INHERITED(std::move(child))
, fColors(std::move(colors)) {
for (const auto& color : fColors) {
this->observeInval(color);
}
}
GradientColorFilter::~GradientColorFilter() {
for (const auto& color : fColors) {
this->unobserveInval(color);
}
}
namespace {
sk_sp<SkColorFilter> Make2ColorGradient(const sk_sp<Color>& color0, const sk_sp<Color>& color1) {
const auto c0 = SkColor4f::FromColor(color0->getColor()),
c1 = SkColor4f::FromColor(color1->getColor());
const auto dR = c1.fR - c0.fR,
dG = c1.fG - c0.fG,
dB = c1.fB - c0.fB;
// A 2-color gradient can be expressed as a color matrix (and combined with the luminance
// calculation). First, the 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 float tint_matrix[] = {
dR*SK_LUM_COEFF_R, dR*SK_LUM_COEFF_G, dR*SK_LUM_COEFF_B, 0, c0.fR,
dG*SK_LUM_COEFF_R, dG*SK_LUM_COEFF_G, dG*SK_LUM_COEFF_B, 0, c0.fG,
dB*SK_LUM_COEFF_R, dB*SK_LUM_COEFF_G, dB*SK_LUM_COEFF_B, 0, c0.fB,
0, 0, 0, 1, 0,
};
return SkColorFilters::Matrix(tint_matrix);
}
sk_sp<SkColorFilter> MakeNColorGradient(const std::vector<sk_sp<Color>>& colors) {
// For N colors, we build a gradient color table.
uint8_t rTable[256], gTable[256], bTable[256];
SkASSERT(colors.size() > 2);
const auto span_count = colors.size() - 1;
size_t span_start = 0;
for (size_t i = 0; i < span_count; ++i) {
const auto span_stop = static_cast<size_t>(std::round((i + 1) * 255.0f / span_count)),
span_size = span_stop - span_start;
if (span_start > span_stop) {
// Degenerate case.
continue;
}
SkASSERT(span_stop <= 255);
// Fill the gradient in [span_start,span_stop] -> [c0,c1]
const SkColor c0 = colors[i ]->getColor(),
c1 = colors[i + 1]->getColor();
float r = SkColorGetR(c0),
g = SkColorGetG(c0),
b = SkColorGetB(c0);
const float dR = (SkColorGetR(c1) - r) / span_size,
dG = (SkColorGetG(c1) - g) / span_size,
dB = (SkColorGetB(c1) - b) / span_size;
for (size_t j = span_start; j <= span_stop; ++j) {
rTable[j] = static_cast<uint8_t>(std::round(r));
gTable[j] = static_cast<uint8_t>(std::round(g));
bTable[j] = static_cast<uint8_t>(std::round(b));
r += dR;
g += dG;
b += dB;
}
// Ensure we always advance.
span_start = span_stop + 1;
}
SkASSERT(span_start == 256);
const float luminance_matrix[] = {
SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // r' = L
SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // g' = L
SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // b' = L
0, 0, 0, 1, 0, // a' = a
};
return SkTableColorFilter::MakeARGB(nullptr, rTable, gTable, bTable)
->makeComposed(SkColorFilters::Matrix(luminance_matrix));
}
} // namespace
sk_sp<SkColorFilter> GradientColorFilter::onRevalidateFilter() {
for (const auto& color : fColors) {
color->revalidate(nullptr, SkMatrix::I());
}
if (fWeight <= 0) {
return nullptr;
}
SkASSERT(fColors.size() > 1);
auto gradientCF = (fColors.size() > 2) ? MakeNColorGradient(fColors)
: Make2ColorGradient(fColors[0], fColors[1]);
return SkColorFilters::Lerp(fWeight, nullptr, std::move(gradientCF));
}
} // namespace sksg