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