xref: /aosp_15_r20/external/skia/gm/workingspace.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2023 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 "gm/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkString.h"
16 #include "include/effects/SkGradientShader.h"
17 #include "include/effects/SkRuntimeEffect.h"
18 #include "src/core/SkColorFilterPriv.h"
19 
color_shader(SkColor4f color)20 static sk_sp<SkShader> color_shader(SkColor4f color) {
21     // Why not use SkShaders::Color? We want a shader that inhibits any paint optimization by any
22     // backend. The CPU backend will ask the shader portion of the pipeline if it's constant.
23     // If so, that portion of the pipeline is executed to get the color, and the color filter is
24     // directly applied to the result. The only way to have the color filter run as part of the
25     // full CPU pipeline is to have a shader that returns false for isConstant:
26     SkBitmap bmp;
27     bmp.allocPixels(SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
28     bmp.eraseColor(color);
29     return bmp.makeShader(SkFilterMode::kNearest);
30 }
31 
paint_color_shader()32 static sk_sp<SkShader> paint_color_shader() {
33     // This will return the paint color (unless it's a child of a runtime effect)
34     SkBitmap bmp;
35     bmp.allocPixels(SkImageInfo::MakeA8(1, 1));
36     bmp.eraseColor(SkColors::kWhite);
37     return bmp.makeShader(SkFilterMode::kNearest);
38 }
39 
raw_shader(SkColor4f color)40 static sk_sp<SkShader> raw_shader(SkColor4f color) {
41     return SkRuntimeEffect::MakeForShader(SkString("uniform half4 c;"
42                                                    "half4 main(float2 xy) { return c; }"))
43             .effect->makeShader(SkData::MakeWithCopy(&color, sizeof(SkColor4f)), {});
44 }
45 
managed_shader(SkColor4f color)46 static sk_sp<SkShader> managed_shader(SkColor4f color) {
47     return SkRuntimeEffect::MakeForShader(SkString("layout(color) uniform half4 c;"
48                                                    "half4 main(float2 xy) { return c; }"))
49             .effect->makeShader(SkData::MakeWithCopy(&color, sizeof(SkColor4f)), {});
50 }
51 
gradient_shader()52 static sk_sp<SkShader> gradient_shader() {
53     const SkPoint pts[] = {{0, 0}, {40, 40}};
54     const SkColor4f colors[] = {SkColors::kRed, SkColors::kGreen};
55     return SkGradientShader::MakeLinear(pts, colors, nullptr, nullptr, 2, SkTileMode::kClamp);
56 }
57 
raw_cf(SkColor4f color)58 static sk_sp<SkColorFilter> raw_cf(SkColor4f color) {
59     return SkRuntimeEffect::MakeForColorFilter(SkString("uniform half4 c;"
60                                                         "half4 main(half4 color) { return c; }"))
61             .effect->makeColorFilter(SkData::MakeWithCopy(&color, sizeof(SkColor4f)));
62 }
63 
managed_cf(SkColor4f color)64 static sk_sp<SkColorFilter> managed_cf(SkColor4f color) {
65     return SkRuntimeEffect::MakeForColorFilter(SkString("layout(color) uniform half4 c;"
66                                                         "half4 main(half4 color) { return c; }"))
67             .effect->makeColorFilter(SkData::MakeWithCopy(&color, sizeof(SkColor4f)));
68 }
69 
indirect_cf(SkColor4f color)70 static sk_sp<SkColorFilter> indirect_cf(SkColor4f color) {
71     SkRuntimeEffect::ChildPtr children[] = {color_shader(color)};
72     return SkRuntimeEffect::MakeForColorFilter(
73                    SkString("uniform shader s;"
74                             "half4 main(half4 color) { return s.eval(float2(0)); }"))
75             .effect->makeColorFilter(nullptr, children);
76 }
77 
mode_cf(SkColor4f color)78 static sk_sp<SkColorFilter> mode_cf(SkColor4f color) {
79     return SkColorFilters::Blend(color, nullptr, SkBlendMode::kSrc);
80 }
81 
spin(sk_sp<SkColorFilter> cf)82 static sk_sp<SkColorFilter> spin(sk_sp<SkColorFilter> cf) {
83     return cf->makeWithWorkingColorSpace(SkColorSpace::MakeSRGB()->makeColorSpin());
84 }
85 
spin(sk_sp<SkShader> shader)86 static sk_sp<SkShader> spin(sk_sp<SkShader> shader) {
87     return shader->makeWithWorkingColorSpace(SkColorSpace::MakeSRGB()->makeColorSpin());
88 }
89 
linear(sk_sp<SkShader> shader)90 static sk_sp<SkShader> linear(sk_sp<SkShader> shader) {
91     return shader->makeWithWorkingColorSpace(SkColorSpace::MakeSRGBLinear());
92 }
93 
94 DEF_SIMPLE_GM_CAN_FAIL(workingspace, canvas, errorMsg, 200, 350) {
95     if (!canvas->getSurface()) {
96         // The only backend that really fails is DDL (because the color filter fails to evaluate on
97         // the CPU when we do paint optimization). We can't identify DDL separate from other
98         // recording backends, so skip this GM for all of them:
99         *errorMsg = "Not supported in recording/DDL mode";
100         return skiagm::DrawResult::kSkip;
101     }
102 
103     // This GM checks that changing the working color space of a color filter does the right thing.
104     // We create a variety of color filters that are sensitive to the working space, and test them
105     // with both fixed-input color (so that paint optimization can apply the filter on the CPU),
106     // and with a shader input (so they're forced to evaluate in the drawing pipeline).
107     //
108     // In all cases, the tests are designed to draw green if implemented correctly. Any other color
109     // (red or blue, most likely) is an error.
110     //
111     // The bottom row is the exception - it draws red-to-green gradients. The first two should be
112     // "ugly" (via brown). The last one should be "nice" (via yellow).
113 
114     canvas->translate(5, 5);
115     canvas->save();
116 
117     auto cell = [&](sk_sp<SkShader> shader,
118                     sk_sp<SkColorFilter> colorFilter,
__anonb244be1f0102(sk_sp<SkShader> shader, sk_sp<SkColorFilter> colorFilter, SkColor4f paintColor = SkColors::kBlack) 119                     SkColor4f paintColor = SkColors::kBlack) {
120         SkPaint paint;
121         paint.setColor(paintColor);
122         paint.setShader(shader);
123         paint.setColorFilter(colorFilter);
124         canvas->drawRect({0, 0, 40, 40}, paint);
125         canvas->translate(50, 0);
126     };
127 
__anonb244be1f0202() 128     auto nextRow = [&]() {
129         canvas->restore();
130         canvas->translate(0, 50);
131         canvas->save();
132     };
133 
134     auto blackShader = color_shader(SkColors::kBlack);
135 
136     cell(nullptr, raw_cf(SkColors::kGreen));
137     cell(nullptr, managed_cf(SkColors::kGreen));
138     cell(nullptr, indirect_cf(SkColors::kGreen));
139     cell(nullptr, mode_cf(SkColors::kGreen));
140 
141     nextRow();
142 
143     cell(blackShader, raw_cf(SkColors::kGreen));
144     cell(blackShader, managed_cf(SkColors::kGreen));
145     cell(blackShader, indirect_cf(SkColors::kGreen));
146     cell(blackShader, mode_cf(SkColors::kGreen));
147 
148     nextRow();
149 
150     cell(nullptr, spin(raw_cf(SkColors::kRed)));  // Un-managed red turns into green
151     cell(nullptr, spin(managed_cf(SkColors::kGreen)));
152     cell(nullptr, spin(indirect_cf(SkColors::kGreen)));
153     cell(nullptr, spin(mode_cf(SkColors::kGreen)));
154 
155     nextRow();
156 
157     cell(blackShader, spin(raw_cf(SkColors::kRed)));  // Un-managed red turns into green
158     cell(blackShader, spin(managed_cf(SkColors::kGreen)));
159     cell(blackShader, spin(indirect_cf(SkColors::kGreen)));
160     cell(blackShader, spin(mode_cf(SkColors::kGreen)));
161 
162     nextRow();
163 
164     cell(raw_shader(SkColors::kGreen), nullptr);
165     cell(managed_shader(SkColors::kGreen), nullptr);
166     cell(color_shader(SkColors::kGreen), nullptr);
167     cell(paint_color_shader(), nullptr, SkColors::kGreen);
168 
169     nextRow();
170 
171     cell(spin(raw_shader(SkColors::kRed)), nullptr);  // Un-managed red turns into green
172     cell(spin(managed_shader(SkColors::kGreen)), nullptr);
173     cell(spin(color_shader(SkColors::kGreen)), nullptr);
174     cell(spin(paint_color_shader()), nullptr, SkColors::kGreen);
175 
176     nextRow();
177 
178     cell(gradient_shader(), nullptr);          // Red to green, via ugly brown
179     cell(spin(gradient_shader()), nullptr);    // Same (spin doesn't change anything)
180     cell(linear(gradient_shader()), nullptr);  // Red to green, via bright yellow
181 
182     return skiagm::DrawResult::kOk;
183 }
184