xref: /aosp_15_r20/external/skia/src/gpu/graphite/precompile/PrecompileColorFilter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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