1 /*
2 * Copyright 2024 Google LLC
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 "include/gpu/graphite/precompile/PrecompileColorFilter.h"
9
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h"
12 #include "include/private/SkColorData.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkKnownRuntimeEffects.h"
15 #include "src/gpu/graphite/BuiltInCodeSnippetID.h"
16 #include "src/gpu/graphite/KeyHelpers.h"
17 #include "src/gpu/graphite/PaintParams.h"
18 #include "src/gpu/graphite/PaintParamsKey.h"
19 #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h"
20 #include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
21 #include "src/gpu/graphite/precompile/PrecompileBlenderPriv.h"
22 #include "src/gpu/graphite/precompile/PrecompileColorFiltersPriv.h"
23
24 namespace skgpu::graphite {
25
26 //--------------------------------------------------------------------------------------------------
27 PrecompileColorFilter::~PrecompileColorFilter() = default;
28
makeComposed(sk_sp<PrecompileColorFilter> inner) const29 sk_sp<PrecompileColorFilter> PrecompileColorFilter::makeComposed(
30 sk_sp<PrecompileColorFilter> inner) const {
31 if (!inner) {
32 return sk_ref_sp(this);
33 }
34
35 return PrecompileColorFilters::Compose({ sk_ref_sp(this) }, { std::move(inner) });
36 }
37
38 //--------------------------------------------------------------------------------------------------
39 //--------------------------------------------------------------------------------------------------
40 namespace {
41
42 // If all the options are null the span is considered empty
is_empty(SkSpan<const sk_sp<PrecompileColorFilter>> options)43 bool is_empty(SkSpan<const sk_sp<PrecompileColorFilter>> options) {
44 if (options.empty()) {
45 return true;
46 }
47
48 for (const auto& o : options) {
49 if (o) {
50 return false;
51 }
52 }
53
54 return true;
55 }
56
57 } // anonymous namespace
58
59 //--------------------------------------------------------------------------------------------------
60 class PrecompileComposeColorFilter : public PrecompileColorFilter {
61 public:
PrecompileComposeColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions,SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions)62 PrecompileComposeColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions,
63 SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions)
64 : fOuterOptions(outerOptions.begin(), outerOptions.end())
65 , fInnerOptions(innerOptions.begin(), innerOptions.end()) {
66
67 fNumOuterCombos = 0;
68 for (const auto& outerOption : fOuterOptions) {
69 fNumOuterCombos += outerOption ? outerOption->priv().numCombinations() : 1;
70 }
71
72 fNumInnerCombos = 0;
73 for (const auto& innerOption : fInnerOptions) {
74 fNumInnerCombos += innerOption ? innerOption->priv().numCombinations() : 1;
75 }
76 }
77
78 private:
numChildCombinations() const79 int numChildCombinations() const override { return fNumOuterCombos * fNumInnerCombos; }
80
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const81 void addToKey(const KeyContext& keyContext,
82 PaintParamsKeyBuilder* builder,
83 PipelineDataGatherer* gatherer,
84 int desiredCombination) const override {
85 SkASSERT(desiredCombination < this->numCombinations());
86
87 const int desiredOuterCombination = desiredCombination % fNumOuterCombos;
88 int remainingCombinations = desiredCombination / fNumOuterCombos;
89
90 const int desiredInnerCombination = remainingCombinations % fNumInnerCombos;
91 remainingCombinations /= fNumInnerCombos;
92
93 SkASSERT(!remainingCombinations);
94
95 sk_sp<PrecompileColorFilter> inner, outer;
96 int innerChildOptions, outerChildOptions;
97
98 std::tie(outer, outerChildOptions) = SelectOption<PrecompileColorFilter>(
99 fOuterOptions, desiredOuterCombination);
100 std::tie(inner, innerChildOptions) = SelectOption<PrecompileColorFilter>(
101 fInnerOptions, desiredInnerCombination);
102
103 if (!inner && !outer) {
104 // A "passthrough" color filter returns the input color as-is.
105 builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
106 } else if (!inner) {
107 outer->priv().addToKey(keyContext, builder, gatherer, outerChildOptions);
108 } else if (!outer) {
109 inner->priv().addToKey(keyContext, builder, gatherer, innerChildOptions);
110 } else {
111 Compose(keyContext, builder, gatherer,
112 /* addInnerToKey= */ [&]() -> void {
113 inner->priv().addToKey(keyContext, builder, gatherer, innerChildOptions);
114 },
115 /* addOuterToKey= */ [&]() -> void {
116 outer->priv().addToKey(keyContext, builder, gatherer, outerChildOptions);
117 });
118 }
119 }
120
121 std::vector<sk_sp<PrecompileColorFilter>> fOuterOptions;
122 std::vector<sk_sp<PrecompileColorFilter>> fInnerOptions;
123
124 int fNumOuterCombos;
125 int fNumInnerCombos;
126 };
127
Compose(SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions,SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions)128 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Compose(
129 SkSpan<const sk_sp<PrecompileColorFilter>> outerOptions,
130 SkSpan<const sk_sp<PrecompileColorFilter>> innerOptions) {
131 if (is_empty(outerOptions) && is_empty(innerOptions)) {
132 return nullptr;
133 }
134
135 return sk_make_sp<PrecompileComposeColorFilter>(outerOptions, innerOptions);
136 }
137
138 //--------------------------------------------------------------------------------------------------
139 class PrecompileBlendModeColorFilter : public PrecompileColorFilter {
140 public:
PrecompileBlendModeColorFilter(SkSpan<const SkBlendMode> blendModes)141 PrecompileBlendModeColorFilter(SkSpan<const SkBlendMode> blendModes)
142 : fBlendOptions(blendModes) {}
143
144 private:
numIntrinsicCombinations() const145 int numIntrinsicCombinations() const override {
146 return fBlendOptions.numCombinations();
147 }
148
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const149 void addToKey(const KeyContext& keyContext,
150 PaintParamsKeyBuilder* builder,
151 PipelineDataGatherer* gatherer,
152 int desiredCombination) const override {
153 auto [blender, option ] = fBlendOptions.selectOption(desiredCombination);
154 SkASSERT(option == 0 && blender->priv().asBlendMode().has_value());
155
156 SkBlendMode representativeBlendMode = *blender->priv().asBlendMode();
157
158 // Here the color is just a stand-in for a later value.
159 AddBlendModeColorFilter(keyContext, builder, gatherer,
160 representativeBlendMode, SK_PMColor4fWHITE);
161 }
162
163 // NOTE: The BlendMode color filter can only be created with SkBlendModes, not arbitrary
164 // SkBlenders, so this list will only contain consolidated blend functions or fixed blend mode
165 // options.
166 PrecompileBlenderList fBlendOptions;
167 };
168
Blend()169 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Blend() {
170 static constexpr SkBlendMode kAllBlendOptions[15] = {
171 SkBlendMode::kSrcOver, // Trigger porter-duff blends
172 SkBlendMode::kHue, // Trigger HSLC blends
173 // All remaining fixed blend modes:
174 SkBlendMode::kPlus,
175 SkBlendMode::kModulate,
176 SkBlendMode::kScreen,
177 SkBlendMode::kOverlay,
178 SkBlendMode::kDarken,
179 SkBlendMode::kLighten,
180 SkBlendMode::kColorDodge,
181 SkBlendMode::kColorBurn,
182 SkBlendMode::kHardLight,
183 SkBlendMode::kSoftLight,
184 SkBlendMode::kDifference,
185 SkBlendMode::kExclusion,
186 SkBlendMode::kMultiply
187 };
188 return Blend(kAllBlendOptions);
189 }
190
Blend(SkSpan<const SkBlendMode> blendModes)191 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Blend(SkSpan<const SkBlendMode> blendModes) {
192 return sk_make_sp<PrecompileBlendModeColorFilter>(blendModes);
193 }
194
195 //--------------------------------------------------------------------------------------------------
196 class PrecompileMatrixColorFilter : public PrecompileColorFilter {
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const197 void addToKey(const KeyContext& keyContext,
198 PaintParamsKeyBuilder* builder,
199 PipelineDataGatherer* gatherer,
200 int desiredCombination) const override {
201 SkASSERT(desiredCombination == 0);
202
203 static constexpr float kIdentity[20] = { 1, 0, 0, 0, 0,
204 0, 1, 0, 0, 0,
205 0, 0, 1, 0, 0,
206 0, 0, 0, 1, 0 };
207
208 MatrixColorFilterBlock::MatrixColorFilterData matrixCFData(
209 kIdentity, /* inHSLA= */ false, /* clamp= */ true);
210
211 MatrixColorFilterBlock::AddBlock(keyContext, builder, gatherer, matrixCFData);
212 }
213 };
214
Matrix()215 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Matrix() {
216 return sk_make_sp<PrecompileMatrixColorFilter>();
217 }
218
HSLAMatrix()219 sk_sp<PrecompileColorFilter> PrecompileColorFilters::HSLAMatrix() {
220 return sk_make_sp<PrecompileMatrixColorFilter>();
221 }
222
223 //--------------------------------------------------------------------------------------------------
224 class PrecompileColorSpaceXformColorFilter : public PrecompileColorFilter {
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const225 void addToKey(const KeyContext& keyContext,
226 PaintParamsKeyBuilder* builder,
227 PipelineDataGatherer* gatherer,
228 int desiredCombination) const override {
229 SkASSERT(desiredCombination == 0);
230
231 constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
232 ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType,
233 sk_srgb_singleton(), kAlphaType);
234
235 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
236 }
237 };
238
LinearToSRGBGamma()239 sk_sp<PrecompileColorFilter> PrecompileColorFilters::LinearToSRGBGamma() {
240 return sk_make_sp<PrecompileColorSpaceXformColorFilter>();
241 }
242
SRGBToLinearGamma()243 sk_sp<PrecompileColorFilter> PrecompileColorFilters::SRGBToLinearGamma() {
244 return sk_make_sp<PrecompileColorSpaceXformColorFilter>();
245 }
246
ColorSpaceXform()247 sk_sp<PrecompileColorFilter> PrecompileColorFiltersPriv::ColorSpaceXform() {
248 return sk_make_sp<PrecompileColorSpaceXformColorFilter>();
249 }
250
251 //--------------------------------------------------------------------------------------------------
Lerp(SkSpan<const sk_sp<PrecompileColorFilter>> dstOptions,SkSpan<const sk_sp<PrecompileColorFilter>> srcOptions)252 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Lerp(
253 SkSpan<const sk_sp<PrecompileColorFilter>> dstOptions,
254 SkSpan<const sk_sp<PrecompileColorFilter>> srcOptions) {
255
256 if (dstOptions.empty() && srcOptions.empty()) {
257 return nullptr;
258 }
259
260 const SkRuntimeEffect* lerpEffect =
261 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLerp);
262
263 skia_private::TArray<sk_sp<PrecompileBase>> dsts, srcs;
264 dsts.reserve(dstOptions.size());
265 for (const sk_sp<PrecompileColorFilter>& d : dstOptions) {
266 dsts.push_back(d);
267 }
268
269 srcs.reserve(srcOptions.size());
270 for (const sk_sp<PrecompileColorFilter>& s : srcOptions) {
271 srcs.push_back(s);
272 }
273
274 return PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(lerpEffect),
275 { dsts, srcs });
276 }
277
278 //--------------------------------------------------------------------------------------------------
279 class PrecompileTableColorFilter : public PrecompileColorFilter {
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const280 void addToKey(const KeyContext& keyContext,
281 PaintParamsKeyBuilder* builder,
282 PipelineDataGatherer* gatherer,
283 int desiredCombination) const override {
284 SkASSERT(desiredCombination == 0);
285
286 TableColorFilterBlock::TableColorFilterData data(/* proxy= */ nullptr);
287
288 TableColorFilterBlock::AddBlock(keyContext, builder, gatherer, data);
289 }
290 };
291
Table()292 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Table() {
293 return sk_make_sp<PrecompileTableColorFilter>();
294 }
295
296 //--------------------------------------------------------------------------------------------------
Lighting()297 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Lighting() {
298 return PrecompileColorFilters::Matrix();
299 }
300
301 //--------------------------------------------------------------------------------------------------
HighContrast()302 sk_sp<PrecompileColorFilter> PrecompileColorFilters::HighContrast() {
303 const SkRuntimeEffect* highContrastEffect =
304 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kHighContrast);
305
306 sk_sp<PrecompileColorFilter> cf =
307 PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(highContrastEffect));
308 if (!cf) {
309 return nullptr;
310 }
311 return PrecompileColorFiltersPriv::WithWorkingFormat({ std::move(cf) });
312 }
313
314 //--------------------------------------------------------------------------------------------------
Luma()315 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Luma() {
316 const SkRuntimeEffect* lumaEffect =
317 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kLuma);
318
319 return PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(lumaEffect));
320 }
321
322 //--------------------------------------------------------------------------------------------------
Overdraw()323 sk_sp<PrecompileColorFilter> PrecompileColorFilters::Overdraw() {
324 const SkRuntimeEffect* overdrawEffect =
325 GetKnownRuntimeEffect(SkKnownRuntimeEffects::StableKey::kOverdraw);
326
327 return PrecompileRuntimeEffects::MakePrecompileColorFilter(sk_ref_sp(overdrawEffect));
328 }
329
330 //--------------------------------------------------------------------------------------------------
331 class PrecompileGaussianColorFilter : public PrecompileColorFilter {
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const332 void addToKey(const KeyContext& keyContext,
333 PaintParamsKeyBuilder* builder,
334 PipelineDataGatherer* gatherer,
335 int desiredCombination) const override {
336 SkASSERT(desiredCombination == 0);
337
338 builder->addBlock(BuiltInCodeSnippetID::kGaussianColorFilter);
339 }
340 };
341
Gaussian()342 sk_sp<PrecompileColorFilter> PrecompileColorFiltersPriv::Gaussian() {
343 return sk_make_sp<PrecompileGaussianColorFilter>();
344 }
345
346 //--------------------------------------------------------------------------------------------------
347 class PrecompileWithWorkingFormatColorFilter : public PrecompileColorFilter {
348 public:
PrecompileWithWorkingFormatColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> childOptions)349 PrecompileWithWorkingFormatColorFilter(SkSpan<const sk_sp<PrecompileColorFilter>> childOptions)
350 : fChildOptions(childOptions.begin(), childOptions.end()) {
351
352 fNumChildCombos = 0;
353 for (const auto& childOption : fChildOptions) {
354 fNumChildCombos += childOption->priv().numCombinations();
355 }
356 }
357
358 private:
numChildCombinations() const359 int numChildCombinations() const override { return fNumChildCombos; }
360
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const361 void addToKey(const KeyContext& keyContext,
362 PaintParamsKeyBuilder* builder,
363 PipelineDataGatherer* gatherer,
364 int desiredCombination) const override {
365 SkASSERT(desiredCombination < fNumChildCombos);
366
367 constexpr SkAlphaType kAlphaType = kPremul_SkAlphaType;
368 ColorSpaceTransformBlock::ColorSpaceTransformData csData(sk_srgb_singleton(), kAlphaType,
369 sk_srgb_singleton(), kAlphaType);
370
371 // Use two nested compose blocks to chain (dst->working), child, and (working->dst) together
372 // while appearing as one block to the parent node.
373 Compose(keyContext, builder, gatherer,
374 /* addInnerToKey= */ [&]() -> void {
375 // Inner compose
376 Compose(keyContext, builder, gatherer,
377 /* addInnerToKey= */ [&]() -> void {
378 // Innermost (inner of inner compose)
379 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer,
380 csData);
381 },
382 /* addOuterToKey= */ [&]() -> void {
383 // Middle (outer of inner compose)
384 AddToKey<PrecompileColorFilter>(keyContext, builder, gatherer,
385 fChildOptions, desiredCombination);
386 });
387 },
388 /* addOuterToKey= */ [&]() -> void {
389 // Outermost (outer of outer compose)
390 ColorSpaceTransformBlock::AddBlock(keyContext, builder, gatherer, csData);
391 });
392 }
393
394 std::vector<sk_sp<PrecompileColorFilter>> fChildOptions;
395
396 int fNumChildCombos;
397 };
398
WithWorkingFormat(SkSpan<const sk_sp<PrecompileColorFilter>> childOptions)399 sk_sp<PrecompileColorFilter> PrecompileColorFiltersPriv::WithWorkingFormat(
400 SkSpan<const sk_sp<PrecompileColorFilter>> childOptions) {
401 return sk_make_sp<PrecompileWithWorkingFormatColorFilter>(childOptions);
402 }
403
404 } // namespace skgpu::graphite
405