1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "modules/sksg/include/SkSGColorFilter.h"
9
10 #include "include/core/SkColorFilter.h"
11 #include "include/effects/SkTableColorFilter.h"
12 #include "include/private/SkColorData.h"
13 #include "modules/sksg/include/SkSGPaint.h"
14
15 #include <cmath>
16
17 namespace sksg {
18
ColorFilter(sk_sp<RenderNode> child)19 ColorFilter::ColorFilter(sk_sp<RenderNode> child)
20 : INHERITED(std::move(child)) {}
21
onRender(SkCanvas * canvas,const RenderContext * ctx) const22 void ColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
23 const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter);
24
25 this->INHERITED::onRender(canvas, local_ctx);
26 }
27
onNodeAt(const SkPoint & p) const28 const RenderNode* ColorFilter::onNodeAt(const SkPoint& p) const {
29 // TODO: we likely need to do something more sophisticated than delegate to descendants here.
30 return this->INHERITED::onNodeAt(p);
31 }
32
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)33 SkRect ColorFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
34 SkASSERT(this->hasInval());
35
36 fColorFilter = this->onRevalidateFilter();
37
38 return this->INHERITED::onRevalidate(ic, ctm);
39 }
40
Make(sk_sp<RenderNode> child)41 sk_sp<ExternalColorFilter> ExternalColorFilter::Make(sk_sp<RenderNode> child) {
42 return child ? sk_sp<ExternalColorFilter>(new ExternalColorFilter(std::move(child)))
43 : nullptr;
44 }
45
ExternalColorFilter(sk_sp<RenderNode> child)46 ExternalColorFilter::ExternalColorFilter(sk_sp<RenderNode> child) : INHERITED(std::move(child)) {}
47
48 ExternalColorFilter::~ExternalColorFilter() = default;
49
onRender(SkCanvas * canvas,const RenderContext * ctx) const50 void ExternalColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
51 const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter);
52
53 this->INHERITED::onRender(canvas, local_ctx);
54 }
55
Make(sk_sp<RenderNode> child,sk_sp<Color> color,SkBlendMode mode)56 sk_sp<ModeColorFilter> ModeColorFilter::Make(sk_sp<RenderNode> child, sk_sp<Color> color,
57 SkBlendMode mode) {
58 return (child && color) ? sk_sp<ModeColorFilter>(new ModeColorFilter(std::move(child),
59 std::move(color), mode))
60 : nullptr;
61 }
62
ModeColorFilter(sk_sp<RenderNode> child,sk_sp<Color> color,SkBlendMode mode)63 ModeColorFilter::ModeColorFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode)
64 : INHERITED(std::move(child))
65 , fColor(std::move(color))
66 , fMode(mode) {
67 this->observeInval(fColor);
68 }
69
~ModeColorFilter()70 ModeColorFilter::~ModeColorFilter() {
71 this->unobserveInval(fColor);
72 }
73
onRevalidateFilter()74 sk_sp<SkColorFilter> ModeColorFilter::onRevalidateFilter() {
75 fColor->revalidate(nullptr, SkMatrix::I());
76 return SkColorFilters::Blend(fColor->getColor(), fMode);
77 }
78
Make(sk_sp<RenderNode> child,sk_sp<Color> c0,sk_sp<Color> c1)79 sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child,
80 sk_sp<Color> c0, sk_sp<Color> c1) {
81 return Make(std::move(child), { std::move(c0), std::move(c1) });
82 }
83
Make(sk_sp<RenderNode> child,std::vector<sk_sp<Color>> colors)84 sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child,
85 std::vector<sk_sp<Color>> colors) {
86 return (child && colors.size() > 1)
87 ? sk_sp<GradientColorFilter>(new GradientColorFilter(std::move(child), std::move(colors)))
88 : nullptr;
89 }
90
GradientColorFilter(sk_sp<RenderNode> child,std::vector<sk_sp<Color>> colors)91 GradientColorFilter::GradientColorFilter(sk_sp<RenderNode> child, std::vector<sk_sp<Color>> colors)
92 : INHERITED(std::move(child))
93 , fColors(std::move(colors)) {
94 for (const auto& color : fColors) {
95 this->observeInval(color);
96 }
97 }
98
~GradientColorFilter()99 GradientColorFilter::~GradientColorFilter() {
100 for (const auto& color : fColors) {
101 this->unobserveInval(color);
102 }
103 }
104
105 namespace {
106
Make2ColorGradient(const sk_sp<Color> & color0,const sk_sp<Color> & color1)107 sk_sp<SkColorFilter> Make2ColorGradient(const sk_sp<Color>& color0, const sk_sp<Color>& color1) {
108 const auto c0 = SkColor4f::FromColor(color0->getColor()),
109 c1 = SkColor4f::FromColor(color1->getColor());
110
111 const auto dR = c1.fR - c0.fR,
112 dG = c1.fG - c0.fG,
113 dB = c1.fB - c0.fB;
114
115 // A 2-color gradient can be expressed as a color matrix (and combined with the luminance
116 // calculation). First, the luminance:
117 //
118 // L = [r,g,b] . [kR,kG,kB]
119 //
120 // We can compute it using a color matrix (result stored in R):
121 //
122 // | kR, kG, kB, 0, 0 | r' = L
123 // | 0, 0, 0, 0, 0 | g' = 0
124 // | 0, 0, 0, 0, 0 | b' = 0
125 // | 0, 0, 0, 1, 0 | a' = a
126 //
127 // Then we want to interpolate component-wise, based on L:
128 //
129 // r' = c0.r + (c1.r - c0.r) * L = c0.r + dR*L
130 // g' = c0.g + (c1.g - c0.g) * L = c0.g + dG*L
131 // b' = c0.b + (c1.b - c0.b) * L = c0.b + dB*L
132 // a' = a
133 //
134 // This can be expressed as another color matrix (when L is stored in R):
135 //
136 // | dR, 0, 0, 0, c0.r |
137 // | dG, 0, 0, 0, c0.g |
138 // | dB, 0, 0, 0, c0.b |
139 // | 0, 0, 0, 1, 0 |
140 //
141 // Composing these two, we get the total tint matrix:
142
143 const float tint_matrix[] = {
144 dR*SK_LUM_COEFF_R, dR*SK_LUM_COEFF_G, dR*SK_LUM_COEFF_B, 0, c0.fR,
145 dG*SK_LUM_COEFF_R, dG*SK_LUM_COEFF_G, dG*SK_LUM_COEFF_B, 0, c0.fG,
146 dB*SK_LUM_COEFF_R, dB*SK_LUM_COEFF_G, dB*SK_LUM_COEFF_B, 0, c0.fB,
147 0, 0, 0, 1, 0,
148 };
149
150 return SkColorFilters::Matrix(tint_matrix);
151 }
152
MakeNColorGradient(const std::vector<sk_sp<Color>> & colors)153 sk_sp<SkColorFilter> MakeNColorGradient(const std::vector<sk_sp<Color>>& colors) {
154 // For N colors, we build a gradient color table.
155 uint8_t rTable[256], gTable[256], bTable[256];
156
157 SkASSERT(colors.size() > 2);
158 const auto span_count = colors.size() - 1;
159
160 size_t span_start = 0;
161 for (size_t i = 0; i < span_count; ++i) {
162 const auto span_stop = static_cast<size_t>(std::round((i + 1) * 255.0f / span_count)),
163 span_size = span_stop - span_start;
164 if (span_start > span_stop) {
165 // Degenerate case.
166 continue;
167 }
168 SkASSERT(span_stop <= 255);
169
170 // Fill the gradient in [span_start,span_stop] -> [c0,c1]
171 const SkColor c0 = colors[i ]->getColor(),
172 c1 = colors[i + 1]->getColor();
173 float r = SkColorGetR(c0),
174 g = SkColorGetG(c0),
175 b = SkColorGetB(c0);
176 const float dR = (SkColorGetR(c1) - r) / span_size,
177 dG = (SkColorGetG(c1) - g) / span_size,
178 dB = (SkColorGetB(c1) - b) / span_size;
179
180 for (size_t j = span_start; j <= span_stop; ++j) {
181 rTable[j] = static_cast<uint8_t>(std::round(r));
182 gTable[j] = static_cast<uint8_t>(std::round(g));
183 bTable[j] = static_cast<uint8_t>(std::round(b));
184 r += dR;
185 g += dG;
186 b += dB;
187 }
188
189 // Ensure we always advance.
190 span_start = span_stop + 1;
191 }
192 SkASSERT(span_start == 256);
193
194 const float luminance_matrix[] = {
195 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // r' = L
196 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // g' = L
197 SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // b' = L
198 0, 0, 0, 1, 0, // a' = a
199 };
200
201 return SkTableColorFilter::MakeARGB(nullptr, rTable, gTable, bTable)
202 ->makeComposed(SkColorFilters::Matrix(luminance_matrix));
203 }
204
205 } // namespace
206
onRevalidateFilter()207 sk_sp<SkColorFilter> GradientColorFilter::onRevalidateFilter() {
208 for (const auto& color : fColors) {
209 color->revalidate(nullptr, SkMatrix::I());
210 }
211
212 if (fWeight <= 0) {
213 return nullptr;
214 }
215
216 SkASSERT(fColors.size() > 1);
217 auto gradientCF = (fColors.size() > 2) ? MakeNColorGradient(fColors)
218 : Make2ColorGradient(fColors[0], fColors[1]);
219
220 return SkColorFilters::Lerp(fWeight, nullptr, std::move(gradientCF));
221 }
222
223 } // namespace sksg
224