blob: 377304a1598fedcb75348398264e71cac75252a1 [file] [log] [blame]
Brian Osmanfa5f0ec2023-09-20 10:35:32 -04001/*
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"
Brian Osman611f0892023-09-13 19:03:21 +000011#include "include/core/SkColor.h"
Brian Osmanfa5f0ec2023-09-20 10:35:32 -040012#include "include/core/SkColorSpace.h"
13#include "include/core/SkData.h"
14#include "include/core/SkPaint.h"
15#include "include/core/SkString.h"
Brian Osman611f0892023-09-13 19:03:21 +000016#include "include/effects/SkGradientShader.h"
Brian Osmanfa5f0ec2023-09-20 10:35:32 -040017#include "include/effects/SkRuntimeEffect.h"
18#include "src/core/SkColorFilterPriv.h"
19
20static 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
Brian Osman611f0892023-09-13 19:03:21 +000032static 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
40static 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
46static 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
52static 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
Brian Osmanfa5f0ec2023-09-20 10:35:32 -040058static 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
64static 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
70static 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
78static sk_sp<SkColorFilter> mode_cf(SkColor4f color) {
79 return SkColorFilters::Blend(color, nullptr, SkBlendMode::kSrc);
80}
81
Brian Osman611f0892023-09-13 19:03:21 +000082static sk_sp<SkColorFilter> spin(sk_sp<SkColorFilter> cf) {
Brian Osmanf346a812023-09-21 10:59:09 -040083 return cf->makeWithWorkingColorSpace(SkColorSpace::MakeSRGB()->makeColorSpin());
Brian Osman611f0892023-09-13 19:03:21 +000084}
85
86static sk_sp<SkShader> spin(sk_sp<SkShader> shader) {
87 return shader->makeWithWorkingColorSpace(SkColorSpace::MakeSRGB()->makeColorSpin());
88}
89
90static sk_sp<SkShader> linear(sk_sp<SkShader> shader) {
91 return shader->makeWithWorkingColorSpace(SkColorSpace::MakeSRGBLinear());
92}
93
94DEF_SIMPLE_GM_CAN_FAIL(workingspace, canvas, errorMsg, 200, 350) {
Brian Osmanfa5f0ec2023-09-20 10:35:32 -040095 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.
Brian Osman611f0892023-09-13 19:03:21 +0000110 //
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).
Brian Osmanfa5f0ec2023-09-20 10:35:32 -0400113
114 canvas->translate(5, 5);
115 canvas->save();
116
Brian Osman611f0892023-09-13 19:03:21 +0000117 auto cell = [&](sk_sp<SkShader> shader,
118 sk_sp<SkColorFilter> colorFilter,
119 SkColor4f paintColor = SkColors::kBlack) {
Brian Osmanfa5f0ec2023-09-20 10:35:32 -0400120 SkPaint paint;
Brian Osman611f0892023-09-13 19:03:21 +0000121 paint.setColor(paintColor);
Brian Osmanfa5f0ec2023-09-20 10:35:32 -0400122 paint.setShader(shader);
123 paint.setColorFilter(colorFilter);
124 canvas->drawRect({0, 0, 40, 40}, paint);
125 canvas->translate(50, 0);
126 };
127
128 auto nextRow = [&]() {
129 canvas->restore();
130 canvas->translate(0, 50);
131 canvas->save();
132 };
133
134 auto blackShader = color_shader(SkColors::kBlack);
135
Brian Osmanfa5f0ec2023-09-20 10:35:32 -0400136 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
Brian Osman611f0892023-09-13 19:03:21 +0000162 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
Brian Osmanfa5f0ec2023-09-20 10:35:32 -0400182 return skiagm::DrawResult::kOk;
183}