/src/skia/modules/sksg/src/SkSGColorFilter.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
19 | | ColorFilter::ColorFilter(sk_sp<RenderNode> child) |
20 | 10.1k | : INHERITED(std::move(child)) {} |
21 | | |
22 | 1.49k | void ColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const { |
23 | 1.49k | const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter); |
24 | | |
25 | 1.49k | this->INHERITED::onRender(canvas, local_ctx); |
26 | 1.49k | } |
27 | | |
28 | 0 | const RenderNode* ColorFilter::onNodeAt(const SkPoint& p) const { |
29 | | // TODO: we likely need to do something more sophisticated than delegate to descendants here. |
30 | 0 | return this->INHERITED::onNodeAt(p); |
31 | 0 | } |
32 | | |
33 | 10.0k | SkRect ColorFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { |
34 | 10.0k | SkASSERT(this->hasInval()); |
35 | | |
36 | 10.0k | fColorFilter = this->onRevalidateFilter(); |
37 | | |
38 | 10.0k | return this->INHERITED::onRevalidate(ic, ctm); |
39 | 10.0k | } |
40 | | |
41 | 6.84k | sk_sp<ExternalColorFilter> ExternalColorFilter::Make(sk_sp<RenderNode> child) { |
42 | 6.84k | return child ? sk_sp<ExternalColorFilter>(new ExternalColorFilter(std::move(child))) |
43 | 0 | : nullptr; |
44 | 6.84k | } |
45 | | |
46 | 6.84k | ExternalColorFilter::ExternalColorFilter(sk_sp<RenderNode> child) : INHERITED(std::move(child)) {} |
47 | | |
48 | 6.84k | ExternalColorFilter::~ExternalColorFilter() = default; |
49 | | |
50 | 567 | void ExternalColorFilter::onRender(SkCanvas* canvas, const RenderContext* ctx) const { |
51 | 567 | const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateColorFilter(fColorFilter); |
52 | | |
53 | 567 | this->INHERITED::onRender(canvas, local_ctx); |
54 | 567 | } |
55 | | |
56 | | sk_sp<ModeColorFilter> ModeColorFilter::Make(sk_sp<RenderNode> child, sk_sp<Color> color, |
57 | 739 | SkBlendMode mode) { |
58 | 739 | return (child && color) ? sk_sp<ModeColorFilter>(new ModeColorFilter(std::move(child), |
59 | 739 | std::move(color), mode)) |
60 | 0 | : nullptr; |
61 | 739 | } |
62 | | |
63 | | ModeColorFilter::ModeColorFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode) |
64 | | : INHERITED(std::move(child)) |
65 | | , fColor(std::move(color)) |
66 | 739 | , fMode(mode) { |
67 | 739 | this->observeInval(fColor); |
68 | 739 | } |
69 | | |
70 | 739 | ModeColorFilter::~ModeColorFilter() { |
71 | 739 | this->unobserveInval(fColor); |
72 | 739 | } |
73 | | |
74 | 707 | sk_sp<SkColorFilter> ModeColorFilter::onRevalidateFilter() { |
75 | 707 | fColor->revalidate(nullptr, SkMatrix::I()); |
76 | 707 | return SkColorFilters::Blend(fColor->getColor(), fMode); |
77 | 707 | } |
78 | | |
79 | | sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child, |
80 | 6.18k | sk_sp<Color> c0, sk_sp<Color> c1) { |
81 | 6.18k | return Make(std::move(child), { std::move(c0), std::move(c1) }); |
82 | 6.18k | } |
83 | | |
84 | | sk_sp<GradientColorFilter> GradientColorFilter::Make(sk_sp<RenderNode> child, |
85 | 9.36k | std::vector<sk_sp<Color>> colors) { |
86 | 9.36k | return (child && colors.size() > 1) |
87 | 9.36k | ? sk_sp<GradientColorFilter>(new GradientColorFilter(std::move(child), std::move(colors))) |
88 | 0 | : nullptr; |
89 | 9.36k | } |
90 | | |
91 | | GradientColorFilter::GradientColorFilter(sk_sp<RenderNode> child, std::vector<sk_sp<Color>> colors) |
92 | | : INHERITED(std::move(child)) |
93 | 9.36k | , fColors(std::move(colors)) { |
94 | 21.9k | for (const auto& color : fColors) { |
95 | 21.9k | this->observeInval(color); |
96 | 21.9k | } |
97 | 9.36k | } |
98 | | |
99 | 9.36k | GradientColorFilter::~GradientColorFilter() { |
100 | 21.9k | for (const auto& color : fColors) { |
101 | 21.9k | this->unobserveInval(color); |
102 | 21.9k | } |
103 | 9.36k | } |
104 | | |
105 | | namespace { |
106 | | |
107 | 4.12k | sk_sp<SkColorFilter> Make2ColorGradient(const sk_sp<Color>& color0, const sk_sp<Color>& color1) { |
108 | 4.12k | const auto c0 = SkColor4f::FromColor(color0->getColor()), |
109 | 4.12k | c1 = SkColor4f::FromColor(color1->getColor()); |
110 | | |
111 | 4.12k | const auto dR = c1.fR - c0.fR, |
112 | 4.12k | dG = c1.fG - c0.fG, |
113 | 4.12k | 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 | 4.12k | const float tint_matrix[] = { |
144 | 4.12k | dR*SK_LUM_COEFF_R, dR*SK_LUM_COEFF_G, dR*SK_LUM_COEFF_B, 0, c0.fR, |
145 | 4.12k | dG*SK_LUM_COEFF_R, dG*SK_LUM_COEFF_G, dG*SK_LUM_COEFF_B, 0, c0.fG, |
146 | 4.12k | dB*SK_LUM_COEFF_R, dB*SK_LUM_COEFF_G, dB*SK_LUM_COEFF_B, 0, c0.fB, |
147 | 4.12k | 0, 0, 0, 1, 0, |
148 | 4.12k | }; |
149 | | |
150 | 4.12k | return SkColorFilters::Matrix(tint_matrix); |
151 | 4.12k | } |
152 | | |
153 | 3.17k | sk_sp<SkColorFilter> MakeNColorGradient(const std::vector<sk_sp<Color>>& colors) { |
154 | | // For N colors, we build a gradient color table. |
155 | 3.17k | uint8_t rTable[256], gTable[256], bTable[256]; |
156 | | |
157 | 3.17k | SkASSERT(colors.size() > 2); |
158 | 3.17k | const auto span_count = colors.size() - 1; |
159 | | |
160 | 3.17k | size_t span_start = 0; |
161 | 9.53k | for (size_t i = 0; i < span_count; ++i) { |
162 | 6.35k | const auto span_stop = static_cast<size_t>(std::round((i + 1) * 255.0f / span_count)), |
163 | 6.35k | span_size = span_stop - span_start; |
164 | 6.35k | if (span_start > span_stop) { |
165 | | // Degenerate case. |
166 | 0 | continue; |
167 | 0 | } |
168 | 6.35k | SkASSERT(span_stop <= 255); |
169 | | |
170 | | // Fill the gradient in [span_start,span_stop] -> [c0,c1] |
171 | 6.35k | const SkColor c0 = colors[i ]->getColor(), |
172 | 6.35k | c1 = colors[i + 1]->getColor(); |
173 | 6.35k | float r = SkColorGetR(c0), |
174 | 6.35k | g = SkColorGetG(c0), |
175 | 6.35k | b = SkColorGetB(c0); |
176 | 6.35k | const float dR = (SkColorGetR(c1) - r) / span_size, |
177 | 6.35k | dG = (SkColorGetG(c1) - g) / span_size, |
178 | 6.35k | dB = (SkColorGetB(c1) - b) / span_size; |
179 | | |
180 | 819k | for (size_t j = span_start; j <= span_stop; ++j) { |
181 | 813k | rTable[j] = static_cast<uint8_t>(std::round(r)); |
182 | 813k | gTable[j] = static_cast<uint8_t>(std::round(g)); |
183 | 813k | bTable[j] = static_cast<uint8_t>(std::round(b)); |
184 | 813k | r += dR; |
185 | 813k | g += dG; |
186 | 813k | b += dB; |
187 | 813k | } |
188 | | |
189 | | // Ensure we always advance. |
190 | 6.35k | span_start = span_stop + 1; |
191 | 6.35k | } |
192 | 3.17k | SkASSERT(span_start == 256); |
193 | | |
194 | 3.17k | const float luminance_matrix[] = { |
195 | 3.17k | SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // r' = L |
196 | 3.17k | SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // g' = L |
197 | 3.17k | SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B, 0, 0, // b' = L |
198 | 3.17k | 0, 0, 0, 1, 0, // a' = a |
199 | 3.17k | }; |
200 | | |
201 | 3.17k | return SkTableColorFilter::MakeARGB(nullptr, rTable, gTable, bTable) |
202 | 3.17k | ->makeComposed(SkColorFilters::Matrix(luminance_matrix)); |
203 | 3.17k | } |
204 | | |
205 | | } // namespace |
206 | | |
207 | 9.33k | sk_sp<SkColorFilter> GradientColorFilter::onRevalidateFilter() { |
208 | 21.8k | for (const auto& color : fColors) { |
209 | 21.8k | color->revalidate(nullptr, SkMatrix::I()); |
210 | 21.8k | } |
211 | | |
212 | 9.33k | if (fWeight <= 0) { |
213 | 2.03k | return nullptr; |
214 | 2.03k | } |
215 | | |
216 | 7.29k | SkASSERT(fColors.size() > 1); |
217 | 3.17k | auto gradientCF = (fColors.size() > 2) ? MakeNColorGradient(fColors) |
218 | 4.12k | : Make2ColorGradient(fColors[0], fColors[1]); |
219 | | |
220 | 7.29k | return SkColorFilters::Lerp(fWeight, nullptr, std::move(gradientCF)); |
221 | 7.29k | } |
222 | | |
223 | | } // namespace sksg |