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