1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2019 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkGradientShader.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkImageFilters.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpacePriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRuntimeEffectPriv.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/DecodeUtils.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker enum RT_Flags {
29*c8dee2aaSAndroid Build Coastguard Worker kAnimate_RTFlag = 0x1,
30*c8dee2aaSAndroid Build Coastguard Worker kBench_RTFlag = 0x2,
31*c8dee2aaSAndroid Build Coastguard Worker kColorFilter_RTFlag = 0x4,
32*c8dee2aaSAndroid Build Coastguard Worker };
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker class RuntimeShaderGM : public skiagm::GM {
35*c8dee2aaSAndroid Build Coastguard Worker public:
RuntimeShaderGM(const char * name,SkISize size,const char * sksl,uint32_t flags=0)36*c8dee2aaSAndroid Build Coastguard Worker RuntimeShaderGM(const char* name, SkISize size, const char* sksl, uint32_t flags = 0)
37*c8dee2aaSAndroid Build Coastguard Worker : fName(name), fSize(size), fFlags(flags), fSkSL(sksl) {}
38*c8dee2aaSAndroid Build Coastguard Worker
onOnceBeforeDraw()39*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override {
40*c8dee2aaSAndroid Build Coastguard Worker auto [effect, error] = (fFlags & kColorFilter_RTFlag)
41*c8dee2aaSAndroid Build Coastguard Worker ? SkRuntimeEffect::MakeForColorFilter(fSkSL)
42*c8dee2aaSAndroid Build Coastguard Worker : SkRuntimeEffect::MakeForShader(fSkSL);
43*c8dee2aaSAndroid Build Coastguard Worker if (!effect) {
44*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("RuntimeShader error: %s\n", error.c_str());
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker fEffect = std::move(effect);
47*c8dee2aaSAndroid Build Coastguard Worker }
48*c8dee2aaSAndroid Build Coastguard Worker
runAsBench() const49*c8dee2aaSAndroid Build Coastguard Worker bool runAsBench() const override { return SkToBool(fFlags & kBench_RTFlag); }
getName() const50*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return fName; }
getISize()51*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override { return fSize; }
52*c8dee2aaSAndroid Build Coastguard Worker
onAnimate(double nanos)53*c8dee2aaSAndroid Build Coastguard Worker bool onAnimate(double nanos) override {
54*c8dee2aaSAndroid Build Coastguard Worker fSecs = nanos / (1000 * 1000 * 1000);
55*c8dee2aaSAndroid Build Coastguard Worker return SkToBool(fFlags & kAnimate_RTFlag);
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker protected:
59*c8dee2aaSAndroid Build Coastguard Worker SkString fName;
60*c8dee2aaSAndroid Build Coastguard Worker SkISize fSize;
61*c8dee2aaSAndroid Build Coastguard Worker uint32_t fFlags;
62*c8dee2aaSAndroid Build Coastguard Worker float fSecs = 0.0f;
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker SkString fSkSL;
65*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkRuntimeEffect> fEffect;
66*c8dee2aaSAndroid Build Coastguard Worker };
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker class SimpleRT : public RuntimeShaderGM {
69*c8dee2aaSAndroid Build Coastguard Worker public:
SimpleRT()70*c8dee2aaSAndroid Build Coastguard Worker SimpleRT() : RuntimeShaderGM("runtime_shader", {512, 256}, R"(
71*c8dee2aaSAndroid Build Coastguard Worker uniform half4 gColor;
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) {
74*c8dee2aaSAndroid Build Coastguard Worker return half4(p*(1.0/255), gColor.b, 1);
75*c8dee2aaSAndroid Build Coastguard Worker }
76*c8dee2aaSAndroid Build Coastguard Worker )", kBench_RTFlag) {}
77*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)78*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
79*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker SkMatrix localM;
82*c8dee2aaSAndroid Build Coastguard Worker localM.setRotate(90, 128, 128);
83*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("gColor") = SkColor4f{1, 0, 0, 1};
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
86*c8dee2aaSAndroid Build Coastguard Worker p.setShader(builder.makeShader(&localM));
87*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 256, 256}, p);
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker };
DEF_GM(return new SimpleRT;)90*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new SimpleRT;)
91*c8dee2aaSAndroid Build Coastguard Worker
92*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> make_shader(sk_sp<SkImage> img, SkISize size) {
93*c8dee2aaSAndroid Build Coastguard Worker SkMatrix scale = SkMatrix::Scale(size.width() / (float)img->width(),
94*c8dee2aaSAndroid Build Coastguard Worker size.height() / (float)img->height());
95*c8dee2aaSAndroid Build Coastguard Worker return img->makeShader(SkSamplingOptions(), scale);
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker
make_threshold(SkISize size)98*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> make_threshold(SkISize size) {
99*c8dee2aaSAndroid Build Coastguard Worker auto info = SkImageInfo::Make(size.width(), size.height(), kAlpha_8_SkColorType,
100*c8dee2aaSAndroid Build Coastguard Worker kPremul_SkAlphaType);
101*c8dee2aaSAndroid Build Coastguard Worker auto surf = SkSurfaces::Raster(info);
102*c8dee2aaSAndroid Build Coastguard Worker auto canvas = surf->getCanvas();
103*c8dee2aaSAndroid Build Coastguard Worker
104*c8dee2aaSAndroid Build Coastguard Worker const SkScalar rad = 50;
105*c8dee2aaSAndroid Build Coastguard Worker SkColor colors[] = {SK_ColorBLACK, 0};
106*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
107*c8dee2aaSAndroid Build Coastguard Worker paint.setAntiAlias(true);
108*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(SkGradientShader::MakeRadial({0,0}, rad, colors, nullptr, 2, SkTileMode::kClamp));
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker SkPaint layerPaint;
111*c8dee2aaSAndroid Build Coastguard Worker const SkScalar sigma = 16.0f;
112*c8dee2aaSAndroid Build Coastguard Worker layerPaint.setImageFilter(SkImageFilters::Blur(sigma, sigma, nullptr));
113*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, &layerPaint);
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
116*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 25; ++i) {
117*c8dee2aaSAndroid Build Coastguard Worker SkScalar x = rand.nextF() * size.width();
118*c8dee2aaSAndroid Build Coastguard Worker SkScalar y = rand.nextF() * size.height();
119*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
120*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(x, y);
121*c8dee2aaSAndroid Build Coastguard Worker canvas->drawCircle(0, 0, rad, paint);
122*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
123*c8dee2aaSAndroid Build Coastguard Worker }
124*c8dee2aaSAndroid Build Coastguard Worker
125*c8dee2aaSAndroid Build Coastguard Worker canvas->restore(); // apply the blur
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker return surf->makeImageSnapshot()->makeShader(SkSamplingOptions());
128*c8dee2aaSAndroid Build Coastguard Worker }
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker class ThresholdRT : public RuntimeShaderGM {
131*c8dee2aaSAndroid Build Coastguard Worker public:
ThresholdRT()132*c8dee2aaSAndroid Build Coastguard Worker ThresholdRT() : RuntimeShaderGM("threshold_rt", {256, 256}, R"(
133*c8dee2aaSAndroid Build Coastguard Worker uniform shader before_map;
134*c8dee2aaSAndroid Build Coastguard Worker uniform shader after_map;
135*c8dee2aaSAndroid Build Coastguard Worker uniform shader threshold_map;
136*c8dee2aaSAndroid Build Coastguard Worker
137*c8dee2aaSAndroid Build Coastguard Worker uniform float cutoff;
138*c8dee2aaSAndroid Build Coastguard Worker uniform float slope;
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker float smooth_cutoff(float x) {
141*c8dee2aaSAndroid Build Coastguard Worker x = x * slope + (0.5 - slope * cutoff);
142*c8dee2aaSAndroid Build Coastguard Worker return clamp(x, 0, 1);
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 xy) {
146*c8dee2aaSAndroid Build Coastguard Worker half4 before = before_map.eval(xy);
147*c8dee2aaSAndroid Build Coastguard Worker half4 after = after_map.eval(xy);
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker float m = smooth_cutoff(threshold_map.eval(xy).a);
150*c8dee2aaSAndroid Build Coastguard Worker return mix(before, after, m);
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker )", kAnimate_RTFlag | kBench_RTFlag) {}
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> fBefore, fAfter, fThreshold;
155*c8dee2aaSAndroid Build Coastguard Worker
onOnceBeforeDraw()156*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override {
157*c8dee2aaSAndroid Build Coastguard Worker const SkISize size = {256, 256};
158*c8dee2aaSAndroid Build Coastguard Worker fThreshold = make_threshold(size);
159*c8dee2aaSAndroid Build Coastguard Worker fBefore = make_shader(ToolUtils::GetResourceAsImage("images/mandrill_256.png"), size);
160*c8dee2aaSAndroid Build Coastguard Worker fAfter = make_shader(ToolUtils::GetResourceAsImage("images/dog.jpg"), size);
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker this->RuntimeShaderGM::onOnceBeforeDraw();
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)165*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
166*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
167*c8dee2aaSAndroid Build Coastguard Worker
168*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("cutoff") = sinf(fSecs) * 0.55f + 0.5f;
169*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("slope") = 10.0f;
170*c8dee2aaSAndroid Build Coastguard Worker
171*c8dee2aaSAndroid Build Coastguard Worker builder.child("before_map") = fBefore;
172*c8dee2aaSAndroid Build Coastguard Worker builder.child("after_map") = fAfter;
173*c8dee2aaSAndroid Build Coastguard Worker builder.child("threshold_map") = fThreshold;
174*c8dee2aaSAndroid Build Coastguard Worker
175*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
176*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
177*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 256, 256}, paint);
178*c8dee2aaSAndroid Build Coastguard Worker
179*c8dee2aaSAndroid Build Coastguard Worker auto draw = [&](SkScalar x, SkScalar y, sk_sp<SkShader> shader) {
180*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader);
181*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
182*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(x, y);
183*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 256, 256}, paint);
184*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
185*c8dee2aaSAndroid Build Coastguard Worker };
186*c8dee2aaSAndroid Build Coastguard Worker draw(256, 0, fThreshold);
187*c8dee2aaSAndroid Build Coastguard Worker draw( 0, 256, fBefore);
188*c8dee2aaSAndroid Build Coastguard Worker draw(256, 256, fAfter);
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker };
191*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ThresholdRT;)
192*c8dee2aaSAndroid Build Coastguard Worker
193*c8dee2aaSAndroid Build Coastguard Worker class SpiralRT : public RuntimeShaderGM {
194*c8dee2aaSAndroid Build Coastguard Worker public:
SpiralRT()195*c8dee2aaSAndroid Build Coastguard Worker SpiralRT() : RuntimeShaderGM("spiral_rt", {512, 512}, R"(
196*c8dee2aaSAndroid Build Coastguard Worker uniform float rad_scale;
197*c8dee2aaSAndroid Build Coastguard Worker uniform float2 in_center;
198*c8dee2aaSAndroid Build Coastguard Worker layout(color) uniform float4 in_colors0;
199*c8dee2aaSAndroid Build Coastguard Worker layout(color) uniform float4 in_colors1;
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) {
202*c8dee2aaSAndroid Build Coastguard Worker float2 pp = p - in_center;
203*c8dee2aaSAndroid Build Coastguard Worker float radius = length(pp);
204*c8dee2aaSAndroid Build Coastguard Worker radius = sqrt(radius);
205*c8dee2aaSAndroid Build Coastguard Worker float angle = atan(pp.y / pp.x);
206*c8dee2aaSAndroid Build Coastguard Worker float t = (angle + 3.1415926/2) / (3.1415926);
207*c8dee2aaSAndroid Build Coastguard Worker t += radius * rad_scale;
208*c8dee2aaSAndroid Build Coastguard Worker t = fract(t);
209*c8dee2aaSAndroid Build Coastguard Worker return in_colors0 * (1-t) + in_colors1 * t;
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker )", kAnimate_RTFlag | kBench_RTFlag) {}
212*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)213*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
214*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rad_scale") = std::sin(fSecs * 0.5f + 2.0f) / 5;
217*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("in_center") = SkV2{256, 256};
218*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("in_colors0") = SkColors::kRed;
219*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("in_colors1") = SkColors::kGreen;
220*c8dee2aaSAndroid Build Coastguard Worker
221*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
222*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
223*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 512, 512}, paint);
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker };
226*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new SpiralRT;)
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker // Test case for sampling with both unmodified input coordinates, and explicit coordinates.
229*c8dee2aaSAndroid Build Coastguard Worker // The first version of skbug.com/11869 suffered a bug where all samples of a child were treated
230*c8dee2aaSAndroid Build Coastguard Worker // as pass-through if *at least one* used the unmodified coordinates. This was detected & tracked
231*c8dee2aaSAndroid Build Coastguard Worker // in b/181092919. This GM is similar, and demonstrates the bug before the fix was applied.
232*c8dee2aaSAndroid Build Coastguard Worker class UnsharpRT : public RuntimeShaderGM {
233*c8dee2aaSAndroid Build Coastguard Worker public:
UnsharpRT()234*c8dee2aaSAndroid Build Coastguard Worker UnsharpRT() : RuntimeShaderGM("unsharp_rt", {512, 256}, R"(
235*c8dee2aaSAndroid Build Coastguard Worker uniform shader child;
236*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 xy) {
237*c8dee2aaSAndroid Build Coastguard Worker half4 c = child.eval(xy) * 5;
238*c8dee2aaSAndroid Build Coastguard Worker c -= child.eval(xy + float2( 1, 0));
239*c8dee2aaSAndroid Build Coastguard Worker c -= child.eval(xy + float2(-1, 0));
240*c8dee2aaSAndroid Build Coastguard Worker c -= child.eval(xy + float2( 0, 1));
241*c8dee2aaSAndroid Build Coastguard Worker c -= child.eval(xy + float2( 0, -1));
242*c8dee2aaSAndroid Build Coastguard Worker return c;
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker )") {}
245*c8dee2aaSAndroid Build Coastguard Worker
246*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fMandrill;
247*c8dee2aaSAndroid Build Coastguard Worker
onOnceBeforeDraw()248*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override {
249*c8dee2aaSAndroid Build Coastguard Worker fMandrill = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
250*c8dee2aaSAndroid Build Coastguard Worker this->RuntimeShaderGM::onOnceBeforeDraw();
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)253*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
254*c8dee2aaSAndroid Build Coastguard Worker // First we draw the unmodified image
255*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrill, 0, 0);
256*c8dee2aaSAndroid Build Coastguard Worker
257*c8dee2aaSAndroid Build Coastguard Worker // Now draw the image with our unsharp mask applied
258*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
259*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions sampling(SkFilterMode::kNearest);
260*c8dee2aaSAndroid Build Coastguard Worker builder.child("child") = fMandrill->makeShader(sampling);
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
263*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
264*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(256, 0);
265*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({ 0, 0, 256, 256 }, paint);
266*c8dee2aaSAndroid Build Coastguard Worker }
267*c8dee2aaSAndroid Build Coastguard Worker };
268*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new UnsharpRT;)
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker class ColorCubeRT : public RuntimeShaderGM {
271*c8dee2aaSAndroid Build Coastguard Worker public:
ColorCubeRT()272*c8dee2aaSAndroid Build Coastguard Worker ColorCubeRT() : RuntimeShaderGM("color_cube_rt", {512, 512}, R"(
273*c8dee2aaSAndroid Build Coastguard Worker uniform shader child;
274*c8dee2aaSAndroid Build Coastguard Worker uniform shader color_cube;
275*c8dee2aaSAndroid Build Coastguard Worker
276*c8dee2aaSAndroid Build Coastguard Worker uniform float rg_scale;
277*c8dee2aaSAndroid Build Coastguard Worker uniform float rg_bias;
278*c8dee2aaSAndroid Build Coastguard Worker uniform float b_scale;
279*c8dee2aaSAndroid Build Coastguard Worker uniform float inv_size;
280*c8dee2aaSAndroid Build Coastguard Worker
281*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 xy) {
282*c8dee2aaSAndroid Build Coastguard Worker float4 c = unpremul(child.eval(xy));
283*c8dee2aaSAndroid Build Coastguard Worker
284*c8dee2aaSAndroid Build Coastguard Worker // Map to cube coords:
285*c8dee2aaSAndroid Build Coastguard Worker float3 cubeCoords = float3(c.rg * rg_scale + rg_bias, c.b * b_scale);
286*c8dee2aaSAndroid Build Coastguard Worker
287*c8dee2aaSAndroid Build Coastguard Worker // Compute slice coordinate
288*c8dee2aaSAndroid Build Coastguard Worker float2 coords1 = float2((floor(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
289*c8dee2aaSAndroid Build Coastguard Worker float2 coords2 = float2(( ceil(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
290*c8dee2aaSAndroid Build Coastguard Worker
291*c8dee2aaSAndroid Build Coastguard Worker // Two bilinear fetches, plus a manual lerp for the third axis:
292*c8dee2aaSAndroid Build Coastguard Worker half4 color = mix(color_cube.eval(coords1), color_cube.eval(coords2),
293*c8dee2aaSAndroid Build Coastguard Worker fract(cubeCoords.b));
294*c8dee2aaSAndroid Build Coastguard Worker
295*c8dee2aaSAndroid Build Coastguard Worker // Premul again
296*c8dee2aaSAndroid Build Coastguard Worker color.rgb *= color.a;
297*c8dee2aaSAndroid Build Coastguard Worker
298*c8dee2aaSAndroid Build Coastguard Worker return color;
299*c8dee2aaSAndroid Build Coastguard Worker }
300*c8dee2aaSAndroid Build Coastguard Worker )") {}
301*c8dee2aaSAndroid Build Coastguard Worker
302*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fMandrill, fMandrillSepia, fIdentityCube, fSepiaCube;
303*c8dee2aaSAndroid Build Coastguard Worker
onOnceBeforeDraw()304*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override {
305*c8dee2aaSAndroid Build Coastguard Worker fMandrill = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
306*c8dee2aaSAndroid Build Coastguard Worker fMandrillSepia = ToolUtils::GetResourceAsImage("images/mandrill_sepia.png");
307*c8dee2aaSAndroid Build Coastguard Worker fIdentityCube = ToolUtils::GetResourceAsImage("images/lut_identity.png");
308*c8dee2aaSAndroid Build Coastguard Worker fSepiaCube = ToolUtils::GetResourceAsImage("images/lut_sepia.png");
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker this->RuntimeShaderGM::onOnceBeforeDraw();
311*c8dee2aaSAndroid Build Coastguard Worker }
312*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)313*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
314*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
315*c8dee2aaSAndroid Build Coastguard Worker
316*c8dee2aaSAndroid Build Coastguard Worker // First we draw the unmodified image, and a copy that was sepia-toned in Photoshop:
317*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrill, 0, 0);
318*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrillSepia, 0, 256);
319*c8dee2aaSAndroid Build Coastguard Worker
320*c8dee2aaSAndroid Build Coastguard Worker // LUT dimensions should be (kSize^2, kSize)
321*c8dee2aaSAndroid Build Coastguard Worker constexpr float kSize = 16.0f;
322*c8dee2aaSAndroid Build Coastguard Worker
323*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions sampling(SkFilterMode::kLinear);
324*c8dee2aaSAndroid Build Coastguard Worker
325*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rg_scale") = (kSize - 1) / kSize;
326*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rg_bias") = 0.5f / kSize;
327*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("b_scale") = kSize - 1;
328*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("inv_size") = 1.0f / kSize;
329*c8dee2aaSAndroid Build Coastguard Worker
330*c8dee2aaSAndroid Build Coastguard Worker builder.child("child") = fMandrill->makeShader(sampling);
331*c8dee2aaSAndroid Build Coastguard Worker
332*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
333*c8dee2aaSAndroid Build Coastguard Worker
334*c8dee2aaSAndroid Build Coastguard Worker // TODO: Should we add SkImage::makeNormalizedShader() to handle this automatically?
335*c8dee2aaSAndroid Build Coastguard Worker SkMatrix normalize = SkMatrix::Scale(1.0f / (kSize * kSize), 1.0f / kSize);
336*c8dee2aaSAndroid Build Coastguard Worker
337*c8dee2aaSAndroid Build Coastguard Worker // Now draw the image with an identity color cube - it should look like the original
338*c8dee2aaSAndroid Build Coastguard Worker builder.child("color_cube") = fIdentityCube->makeShader(sampling, normalize);
339*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
340*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(256, 0);
341*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({ 0, 0, 256, 256 }, paint);
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker // ... and with a sepia-tone color cube. This should match the sepia-toned image.
344*c8dee2aaSAndroid Build Coastguard Worker builder.child("color_cube") = fSepiaCube->makeShader(sampling, normalize);
345*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
346*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(0, 256);
347*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({ 0, 0, 256, 256 }, paint);
348*c8dee2aaSAndroid Build Coastguard Worker }
349*c8dee2aaSAndroid Build Coastguard Worker };
350*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ColorCubeRT;)
351*c8dee2aaSAndroid Build Coastguard Worker
352*c8dee2aaSAndroid Build Coastguard Worker // Same as above, but demonstrating how to implement this as a runtime color filter (that samples
353*c8dee2aaSAndroid Build Coastguard Worker // a shader child for the LUT).
354*c8dee2aaSAndroid Build Coastguard Worker class ColorCubeColorFilterRT : public RuntimeShaderGM {
355*c8dee2aaSAndroid Build Coastguard Worker public:
ColorCubeColorFilterRT()356*c8dee2aaSAndroid Build Coastguard Worker ColorCubeColorFilterRT() : RuntimeShaderGM("color_cube_cf_rt", {512, 512}, R"(
357*c8dee2aaSAndroid Build Coastguard Worker uniform shader color_cube;
358*c8dee2aaSAndroid Build Coastguard Worker
359*c8dee2aaSAndroid Build Coastguard Worker uniform float rg_scale;
360*c8dee2aaSAndroid Build Coastguard Worker uniform float rg_bias;
361*c8dee2aaSAndroid Build Coastguard Worker uniform float b_scale;
362*c8dee2aaSAndroid Build Coastguard Worker uniform float inv_size;
363*c8dee2aaSAndroid Build Coastguard Worker
364*c8dee2aaSAndroid Build Coastguard Worker half4 main(half4 inColor) {
365*c8dee2aaSAndroid Build Coastguard Worker float4 c = unpremul(inColor);
366*c8dee2aaSAndroid Build Coastguard Worker
367*c8dee2aaSAndroid Build Coastguard Worker // Map to cube coords:
368*c8dee2aaSAndroid Build Coastguard Worker float3 cubeCoords = float3(c.rg * rg_scale + rg_bias, c.b * b_scale);
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker // Compute slice coordinate
371*c8dee2aaSAndroid Build Coastguard Worker float2 coords1 = float2((floor(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
372*c8dee2aaSAndroid Build Coastguard Worker float2 coords2 = float2(( ceil(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
373*c8dee2aaSAndroid Build Coastguard Worker
374*c8dee2aaSAndroid Build Coastguard Worker // Two bilinear fetches, plus a manual lerp for the third axis:
375*c8dee2aaSAndroid Build Coastguard Worker half4 color = mix(color_cube.eval(coords1), color_cube.eval(coords2),
376*c8dee2aaSAndroid Build Coastguard Worker fract(cubeCoords.b));
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker // Premul again
379*c8dee2aaSAndroid Build Coastguard Worker color.rgb *= color.a;
380*c8dee2aaSAndroid Build Coastguard Worker
381*c8dee2aaSAndroid Build Coastguard Worker return color;
382*c8dee2aaSAndroid Build Coastguard Worker }
383*c8dee2aaSAndroid Build Coastguard Worker )", kColorFilter_RTFlag) {}
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fMandrill, fMandrillSepia, fIdentityCube, fSepiaCube;
386*c8dee2aaSAndroid Build Coastguard Worker
onOnceBeforeDraw()387*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override {
388*c8dee2aaSAndroid Build Coastguard Worker fMandrill = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
389*c8dee2aaSAndroid Build Coastguard Worker fMandrillSepia = ToolUtils::GetResourceAsImage("images/mandrill_sepia.png");
390*c8dee2aaSAndroid Build Coastguard Worker fIdentityCube = ToolUtils::GetResourceAsImage("images/lut_identity.png");
391*c8dee2aaSAndroid Build Coastguard Worker fSepiaCube = ToolUtils::GetResourceAsImage("images/lut_sepia.png");
392*c8dee2aaSAndroid Build Coastguard Worker
393*c8dee2aaSAndroid Build Coastguard Worker this->RuntimeShaderGM::onOnceBeforeDraw();
394*c8dee2aaSAndroid Build Coastguard Worker }
395*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)396*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
397*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeColorFilterBuilder builder(fEffect);
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker // First we draw the unmodified image, and a copy that was sepia-toned in Photoshop:
400*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrill, 0, 0);
401*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrillSepia, 0, 256);
402*c8dee2aaSAndroid Build Coastguard Worker
403*c8dee2aaSAndroid Build Coastguard Worker // LUT dimensions should be (kSize^2, kSize)
404*c8dee2aaSAndroid Build Coastguard Worker constexpr float kSize = 16.0f;
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions sampling(SkFilterMode::kLinear);
407*c8dee2aaSAndroid Build Coastguard Worker
408*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rg_scale") = (kSize - 1) / kSize;
409*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rg_bias") = 0.5f / kSize;
410*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("b_scale") = kSize - 1;
411*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("inv_size") = 1.0f / kSize;
412*c8dee2aaSAndroid Build Coastguard Worker
413*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker // TODO: Should we add SkImage::makeNormalizedShader() to handle this automatically?
416*c8dee2aaSAndroid Build Coastguard Worker SkMatrix normalize = SkMatrix::Scale(1.0f / (kSize * kSize), 1.0f / kSize);
417*c8dee2aaSAndroid Build Coastguard Worker
418*c8dee2aaSAndroid Build Coastguard Worker // Now draw the image with an identity color cube - it should look like the original
419*c8dee2aaSAndroid Build Coastguard Worker builder.child("color_cube") = fIdentityCube->makeShader(sampling, normalize);
420*c8dee2aaSAndroid Build Coastguard Worker
421*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(builder.makeColorFilter());
422*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrill, 256, 0, sampling, &paint);
423*c8dee2aaSAndroid Build Coastguard Worker
424*c8dee2aaSAndroid Build Coastguard Worker // ... and with a sepia-tone color cube. This should match the sepia-toned image.
425*c8dee2aaSAndroid Build Coastguard Worker builder.child("color_cube") = fSepiaCube->makeShader(sampling, normalize);
426*c8dee2aaSAndroid Build Coastguard Worker
427*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(builder.makeColorFilter());
428*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(fMandrill, 256, 256, sampling, &paint);
429*c8dee2aaSAndroid Build Coastguard Worker }
430*c8dee2aaSAndroid Build Coastguard Worker };
431*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ColorCubeColorFilterRT;)
432*c8dee2aaSAndroid Build Coastguard Worker
433*c8dee2aaSAndroid Build Coastguard Worker // Emits coverage for a rounded rectangle whose corners are superellipses defined by the boundary:
434*c8dee2aaSAndroid Build Coastguard Worker //
435*c8dee2aaSAndroid Build Coastguard Worker // x^n + y^n == 1
436*c8dee2aaSAndroid Build Coastguard Worker //
437*c8dee2aaSAndroid Build Coastguard Worker // Where x and y are normalized, clamped coordinates ranging from 0..1 inside the nearest corner's
438*c8dee2aaSAndroid Build Coastguard Worker // bounding box.
439*c8dee2aaSAndroid Build Coastguard Worker //
440*c8dee2aaSAndroid Build Coastguard Worker // See: https://en.wikipedia.org/wiki/Superellipse
441*c8dee2aaSAndroid Build Coastguard Worker class ClipSuperRRect : public RuntimeShaderGM {
442*c8dee2aaSAndroid Build Coastguard Worker public:
ClipSuperRRect(const char * name,float power)443*c8dee2aaSAndroid Build Coastguard Worker ClipSuperRRect(const char* name, float power) : RuntimeShaderGM(name, {500, 500}, R"(
444*c8dee2aaSAndroid Build Coastguard Worker uniform float power_minus1;
445*c8dee2aaSAndroid Build Coastguard Worker uniform float2 stretch_factor;
446*c8dee2aaSAndroid Build Coastguard Worker uniform float2x2 derivatives;
447*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 xy) {
448*c8dee2aaSAndroid Build Coastguard Worker xy = max(abs(xy) + stretch_factor, 0);
449*c8dee2aaSAndroid Build Coastguard Worker float2 exp_minus1 = pow(xy, power_minus1.xx); // If power == 3.5: xy * xy * sqrt(xy)
450*c8dee2aaSAndroid Build Coastguard Worker float f = dot(exp_minus1, xy) - 1; // f = x^n + y^n - 1
451*c8dee2aaSAndroid Build Coastguard Worker float2 grad = exp_minus1 * derivatives;
452*c8dee2aaSAndroid Build Coastguard Worker float fwidth = abs(grad.x) + abs(grad.y) + 1e-12; // 1e-12 to avoid a divide by zero.
453*c8dee2aaSAndroid Build Coastguard Worker return half4(saturate(.5 - f/fwidth)); // Approx coverage by riding the gradient to f=0.
454*c8dee2aaSAndroid Build Coastguard Worker }
455*c8dee2aaSAndroid Build Coastguard Worker )"), fPower(power) {}
456*c8dee2aaSAndroid Build Coastguard Worker
drawSuperRRect(SkCanvas * canvas,const SkRect & superRRect,float radX,float radY,SkColor color)457*c8dee2aaSAndroid Build Coastguard Worker void drawSuperRRect(SkCanvas* canvas, const SkRect& superRRect, float radX, float radY,
458*c8dee2aaSAndroid Build Coastguard Worker SkColor color) {
459*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
460*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(color);
461*c8dee2aaSAndroid Build Coastguard Worker
462*c8dee2aaSAndroid Build Coastguard Worker if (fPower == 2) {
463*c8dee2aaSAndroid Build Coastguard Worker // Draw a normal round rect for the sake of testing.
464*c8dee2aaSAndroid Build Coastguard Worker SkRRect rrect = SkRRect::MakeRectXY(superRRect, radX, radY);
465*c8dee2aaSAndroid Build Coastguard Worker paint.setAntiAlias(true);
466*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRRect(rrect, paint);
467*c8dee2aaSAndroid Build Coastguard Worker return;
468*c8dee2aaSAndroid Build Coastguard Worker }
469*c8dee2aaSAndroid Build Coastguard Worker
470*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
471*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("power_minus1") = fPower - 1;
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker // Size the corners such that the "apex" of our "super" rounded corner is in the same
474*c8dee2aaSAndroid Build Coastguard Worker // location that the apex of a circular rounded corner would be with the given radii. We
475*c8dee2aaSAndroid Build Coastguard Worker // define the apex as the point on the rounded corner that is 45 degrees between the
476*c8dee2aaSAndroid Build Coastguard Worker // horizontal and vertical edges.
477*c8dee2aaSAndroid Build Coastguard Worker float scale = (1 - SK_ScalarRoot2Over2) / (1 - exp2f(-1/fPower));
478*c8dee2aaSAndroid Build Coastguard Worker float cornerWidth = radX * scale;
479*c8dee2aaSAndroid Build Coastguard Worker float cornerHeight = radY * scale;
480*c8dee2aaSAndroid Build Coastguard Worker cornerWidth = std::min(cornerWidth, superRRect.width() * .5f);
481*c8dee2aaSAndroid Build Coastguard Worker cornerHeight = std::min(cornerHeight, superRRect.height() * .5f);
482*c8dee2aaSAndroid Build Coastguard Worker // The stretch factor controls how long the flat edge should be between rounded corners.
483*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("stretch_factor") = SkV2{1 - superRRect.width()*.5f / cornerWidth,
484*c8dee2aaSAndroid Build Coastguard Worker 1 - superRRect.height()*.5f / cornerHeight};
485*c8dee2aaSAndroid Build Coastguard Worker
486*c8dee2aaSAndroid Build Coastguard Worker // Calculate a 2x2 "derivatives" matrix that the shader will use to find the gradient.
487*c8dee2aaSAndroid Build Coastguard Worker //
488*c8dee2aaSAndroid Build Coastguard Worker // f = s^n + t^n - 1 [s,t are "super" rounded corner coords in normalized 0..1 space]
489*c8dee2aaSAndroid Build Coastguard Worker //
490*c8dee2aaSAndroid Build Coastguard Worker // gradient = [df/dx df/dy] = [ns^(n-1) nt^(n-1)] * |ds/dx ds/dy|
491*c8dee2aaSAndroid Build Coastguard Worker // |dt/dx dt/dy|
492*c8dee2aaSAndroid Build Coastguard Worker //
493*c8dee2aaSAndroid Build Coastguard Worker // = [s^(n-1) t^(n-1)] * |n 0| * |ds/dx ds/dy|
494*c8dee2aaSAndroid Build Coastguard Worker // |0 n| |dt/dx dt/dy|
495*c8dee2aaSAndroid Build Coastguard Worker //
496*c8dee2aaSAndroid Build Coastguard Worker // = [s^(n-1) t^(n-1)] * |2n/cornerWidth 0| * mat2x2(canvasMatrix)^-1
497*c8dee2aaSAndroid Build Coastguard Worker // |0 2n/cornerHeight|
498*c8dee2aaSAndroid Build Coastguard Worker //
499*c8dee2aaSAndroid Build Coastguard Worker // = [s^(n-1) t^(n-1)] * "derivatives"
500*c8dee2aaSAndroid Build Coastguard Worker //
501*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& M = canvas->getTotalMatrix();
502*c8dee2aaSAndroid Build Coastguard Worker float a=M.getScaleX(), b=M.getSkewX(), c=M.getSkewY(), d=M.getScaleY();
503*c8dee2aaSAndroid Build Coastguard Worker float determinant = a*d - b*c;
504*c8dee2aaSAndroid Build Coastguard Worker float dx = fPower / (cornerWidth * determinant);
505*c8dee2aaSAndroid Build Coastguard Worker float dy = fPower / (cornerHeight * determinant);
506*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("derivatives") = SkV4{d*dx, -c*dy, -b*dx, a*dy};
507*c8dee2aaSAndroid Build Coastguard Worker
508*c8dee2aaSAndroid Build Coastguard Worker // This matrix will be inverted by the effect system, giving a matrix that converts local
509*c8dee2aaSAndroid Build Coastguard Worker // coordinates to (almost) coner coordinates. To get the rest of the way to the nearest
510*c8dee2aaSAndroid Build Coastguard Worker // corner's space, the shader will have to take the absolute value, add the stretch_factor,
511*c8dee2aaSAndroid Build Coastguard Worker // then clamp above zero.
512*c8dee2aaSAndroid Build Coastguard Worker SkMatrix cornerToLocal;
513*c8dee2aaSAndroid Build Coastguard Worker cornerToLocal.setScaleTranslate(cornerWidth, cornerHeight, superRRect.centerX(),
514*c8dee2aaSAndroid Build Coastguard Worker superRRect.centerY());
515*c8dee2aaSAndroid Build Coastguard Worker canvas->clipShader(builder.makeShader(&cornerToLocal));
516*c8dee2aaSAndroid Build Coastguard Worker
517*c8dee2aaSAndroid Build Coastguard Worker // Bloat the outer edges of the rect we will draw so it contains all the antialiased pixels.
518*c8dee2aaSAndroid Build Coastguard Worker // Bloat by a full pixel instead of half in case Skia is in a mode that draws this rect with
519*c8dee2aaSAndroid Build Coastguard Worker // unexpected AA of its own.
520*c8dee2aaSAndroid Build Coastguard Worker float inverseDet = 1 / fabsf(determinant);
521*c8dee2aaSAndroid Build Coastguard Worker float bloatX = (fabsf(d) + fabsf(c)) * inverseDet;
522*c8dee2aaSAndroid Build Coastguard Worker float bloatY = (fabsf(b) + fabsf(a)) * inverseDet;
523*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(superRRect.makeOutset(bloatX, bloatY), paint);
524*c8dee2aaSAndroid Build Coastguard Worker }
525*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)526*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
527*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand(2);
528*c8dee2aaSAndroid Build Coastguard Worker
529*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
530*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(canvas->imageInfo().width() / 2.f, canvas->imageInfo().height() / 2.f);
531*c8dee2aaSAndroid Build Coastguard Worker
532*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
533*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(21);
534*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(-5, 25, 175, 100), 50, 30,
535*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
536*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
537*c8dee2aaSAndroid Build Coastguard Worker
538*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
539*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(94);
540*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(95, 75, 125, 100), 30, 30,
541*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
542*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
543*c8dee2aaSAndroid Build Coastguard Worker
544*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
545*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(132);
546*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(0, 75, 150, 100), 40, 30,
547*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
548*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
549*c8dee2aaSAndroid Build Coastguard Worker
550*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
551*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(282);
552*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(15, -20, 100, 100), 20, 20,
553*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
554*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
555*c8dee2aaSAndroid Build Coastguard Worker
556*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
557*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(0);
558*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(140, -50, 90, 110), 25, 25,
559*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
560*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
561*c8dee2aaSAndroid Build Coastguard Worker
562*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
563*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(-35);
564*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(160, -60, 60, 90), 18, 18,
565*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
566*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
567*c8dee2aaSAndroid Build Coastguard Worker
568*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
569*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(65);
570*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(220, -120, 60, 90), 18, 18,
571*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
572*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
573*c8dee2aaSAndroid Build Coastguard Worker
574*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
575*c8dee2aaSAndroid Build Coastguard Worker canvas->rotate(265);
576*c8dee2aaSAndroid Build Coastguard Worker this->drawSuperRRect(canvas, SkRect::MakeXYWH(150, -129, 80, 160), 24, 39,
577*c8dee2aaSAndroid Build Coastguard Worker rand.nextU() | 0xff808080);
578*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
579*c8dee2aaSAndroid Build Coastguard Worker
580*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
581*c8dee2aaSAndroid Build Coastguard Worker }
582*c8dee2aaSAndroid Build Coastguard Worker
583*c8dee2aaSAndroid Build Coastguard Worker private:
584*c8dee2aaSAndroid Build Coastguard Worker const float fPower;
585*c8dee2aaSAndroid Build Coastguard Worker };
586*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow2", 2);)
587*c8dee2aaSAndroid Build Coastguard Worker // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow3", 3);)
588*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow3.5", 3.5);)
589*c8dee2aaSAndroid Build Coastguard Worker // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow4", 4);)
590*c8dee2aaSAndroid Build Coastguard Worker // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow4.5", 4.5);)
591*c8dee2aaSAndroid Build Coastguard Worker // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow5", 5);)
592*c8dee2aaSAndroid Build Coastguard Worker
593*c8dee2aaSAndroid Build Coastguard Worker class LinearGradientRT : public RuntimeShaderGM {
594*c8dee2aaSAndroid Build Coastguard Worker public:
LinearGradientRT()595*c8dee2aaSAndroid Build Coastguard Worker LinearGradientRT() : RuntimeShaderGM("linear_gradient_rt", {256 + 10, 128 + 15}, R"(
596*c8dee2aaSAndroid Build Coastguard Worker layout(color) uniform vec4 in_colors0;
597*c8dee2aaSAndroid Build Coastguard Worker layout(color) uniform vec4 in_colors1;
598*c8dee2aaSAndroid Build Coastguard Worker
599*c8dee2aaSAndroid Build Coastguard Worker vec4 main(vec2 p) {
600*c8dee2aaSAndroid Build Coastguard Worker float t = p.x / 256;
601*c8dee2aaSAndroid Build Coastguard Worker if (p.y < 32) {
602*c8dee2aaSAndroid Build Coastguard Worker return mix(in_colors0, in_colors1, t);
603*c8dee2aaSAndroid Build Coastguard Worker } else {
604*c8dee2aaSAndroid Build Coastguard Worker vec3 linColor0 = toLinearSrgb(in_colors0.rgb);
605*c8dee2aaSAndroid Build Coastguard Worker vec3 linColor1 = toLinearSrgb(in_colors1.rgb);
606*c8dee2aaSAndroid Build Coastguard Worker vec3 linColor = mix(linColor0, linColor1, t);
607*c8dee2aaSAndroid Build Coastguard Worker return fromLinearSrgb(linColor).rgb1;
608*c8dee2aaSAndroid Build Coastguard Worker }
609*c8dee2aaSAndroid Build Coastguard Worker }
610*c8dee2aaSAndroid Build Coastguard Worker )") {}
611*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)612*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
613*c8dee2aaSAndroid Build Coastguard Worker // Colors chosen to use values other than 0 and 1 - so that it's obvious if the conversion
614*c8dee2aaSAndroid Build Coastguard Worker // intrinsics are doing anything. (Most transfer functions map 0 -> 0 and 1 -> 1).
615*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(fEffect);
616*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("in_colors0") = SkColor4f{0.75f, 0.25f, 0.0f, 1.0f};
617*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("in_colors1") = SkColor4f{0.0f, 0.75f, 0.25f, 1.0f};
618*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
619*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(builder.makeShader());
620*c8dee2aaSAndroid Build Coastguard Worker
621*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
622*c8dee2aaSAndroid Build Coastguard Worker canvas->clear(SK_ColorWHITE);
623*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(5, 5);
624*c8dee2aaSAndroid Build Coastguard Worker
625*c8dee2aaSAndroid Build Coastguard Worker // We draw everything twice. First to a surface with no color management, where the
626*c8dee2aaSAndroid Build Coastguard Worker // intrinsics should do nothing (eg, the top bar should look the same in the top and bottom
627*c8dee2aaSAndroid Build Coastguard Worker // halves). Then to an sRGB surface, where they should produce linearly interpolated
628*c8dee2aaSAndroid Build Coastguard Worker // gradients (the bottom half of the second bar should be brighter than the top half).
629*c8dee2aaSAndroid Build Coastguard Worker for (auto cs : {static_cast<SkColorSpace*>(nullptr), sk_srgb_singleton()}) {
630*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo info = SkImageInfo::Make(
631*c8dee2aaSAndroid Build Coastguard Worker 256, 64, kN32_SkColorType, kPremul_SkAlphaType, sk_ref_sp(cs));
632*c8dee2aaSAndroid Build Coastguard Worker auto surface = canvas->makeSurface(info);
633*c8dee2aaSAndroid Build Coastguard Worker if (!surface) {
634*c8dee2aaSAndroid Build Coastguard Worker surface = SkSurfaces::Raster(info);
635*c8dee2aaSAndroid Build Coastguard Worker }
636*c8dee2aaSAndroid Build Coastguard Worker
637*c8dee2aaSAndroid Build Coastguard Worker surface->getCanvas()->drawRect({0, 0, 256, 64}, paint);
638*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
639*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(0, 64 + 5);
640*c8dee2aaSAndroid Build Coastguard Worker }
641*c8dee2aaSAndroid Build Coastguard Worker
642*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
643*c8dee2aaSAndroid Build Coastguard Worker }
644*c8dee2aaSAndroid Build Coastguard Worker };
645*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new LinearGradientRT;)
646*c8dee2aaSAndroid Build Coastguard Worker
647*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(child_sampling_rt, canvas, 256,256) {
648*c8dee2aaSAndroid Build Coastguard Worker static constexpr char scale[] =
649*c8dee2aaSAndroid Build Coastguard Worker "uniform shader child;"
650*c8dee2aaSAndroid Build Coastguard Worker "half4 main(float2 xy) {"
651*c8dee2aaSAndroid Build Coastguard Worker " return child.eval(xy*0.1);"
652*c8dee2aaSAndroid Build Coastguard Worker "}";
653*c8dee2aaSAndroid Build Coastguard Worker
654*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
655*c8dee2aaSAndroid Build Coastguard Worker p.setColor(SK_ColorRED);
656*c8dee2aaSAndroid Build Coastguard Worker p.setAntiAlias(true);
657*c8dee2aaSAndroid Build Coastguard Worker p.setStyle(SkPaint::kStroke_Style);
658*c8dee2aaSAndroid Build Coastguard Worker p.setStrokeWidth(1);
659*c8dee2aaSAndroid Build Coastguard Worker
660*c8dee2aaSAndroid Build Coastguard Worker auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100));
661*c8dee2aaSAndroid Build Coastguard Worker surf->getCanvas()->drawLine(0, 0, 100, 100, p);
662*c8dee2aaSAndroid Build Coastguard Worker auto shader = surf->makeImageSnapshot()->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
663*c8dee2aaSAndroid Build Coastguard Worker
664*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(SkRuntimeEffect::MakeForShader(SkString(scale)).effect);
665*c8dee2aaSAndroid Build Coastguard Worker builder.child("child") = shader;
666*c8dee2aaSAndroid Build Coastguard Worker p.setShader(builder.makeShader());
667*c8dee2aaSAndroid Build Coastguard Worker
668*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPaint(p);
669*c8dee2aaSAndroid Build Coastguard Worker }
670*c8dee2aaSAndroid Build Coastguard Worker
normal_map_shader()671*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> normal_map_shader() {
672*c8dee2aaSAndroid Build Coastguard Worker // Produces a hemispherical normal:
673*c8dee2aaSAndroid Build Coastguard Worker static const char* kSrc = R"(
674*c8dee2aaSAndroid Build Coastguard Worker half4 main(vec2 p) {
675*c8dee2aaSAndroid Build Coastguard Worker p = (p / 256) * 2 - 1;
676*c8dee2aaSAndroid Build Coastguard Worker float p2 = dot(p, p);
677*c8dee2aaSAndroid Build Coastguard Worker vec3 v = (p2 > 1) ? vec3(0, 0, 1) : vec3(p, sqrt(1 - p2));
678*c8dee2aaSAndroid Build Coastguard Worker return (v * 0.5 + 0.5).xyz1;
679*c8dee2aaSAndroid Build Coastguard Worker }
680*c8dee2aaSAndroid Build Coastguard Worker )";
681*c8dee2aaSAndroid Build Coastguard Worker auto effect = SkRuntimeEffect::MakeForShader(SkString(kSrc)).effect;
682*c8dee2aaSAndroid Build Coastguard Worker return effect->makeShader(nullptr, {});
683*c8dee2aaSAndroid Build Coastguard Worker }
684*c8dee2aaSAndroid Build Coastguard Worker
normal_map_image()685*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> normal_map_image() {
686*c8dee2aaSAndroid Build Coastguard Worker // Above, baked into an image:
687*c8dee2aaSAndroid Build Coastguard Worker auto info = SkImageInfo::Make(256, 256, kN32_SkColorType, kPremul_SkAlphaType);
688*c8dee2aaSAndroid Build Coastguard Worker auto surface = SkSurfaces::Raster(info);
689*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
690*c8dee2aaSAndroid Build Coastguard Worker p.setShader(normal_map_shader());
691*c8dee2aaSAndroid Build Coastguard Worker surface->getCanvas()->drawPaint(p);
692*c8dee2aaSAndroid Build Coastguard Worker return surface->makeImageSnapshot();
693*c8dee2aaSAndroid Build Coastguard Worker }
694*c8dee2aaSAndroid Build Coastguard Worker
normal_map_image_shader()695*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> normal_map_image_shader() {
696*c8dee2aaSAndroid Build Coastguard Worker return normal_map_image()->makeShader(SkFilterMode::kNearest);
697*c8dee2aaSAndroid Build Coastguard Worker }
698*c8dee2aaSAndroid Build Coastguard Worker
normal_map_raw_image_shader()699*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> normal_map_raw_image_shader() {
700*c8dee2aaSAndroid Build Coastguard Worker return normal_map_image()->makeRawShader(SkFilterMode::kNearest);
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker
normal_map_unpremul_image()703*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> normal_map_unpremul_image() {
704*c8dee2aaSAndroid Build Coastguard Worker auto image = normal_map_image();
705*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pm;
706*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(image->peekPixels(&pm));
707*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bmp;
708*c8dee2aaSAndroid Build Coastguard Worker bmp.allocPixels(image->imageInfo().makeAlphaType(kUnpremul_SkAlphaType));
709*c8dee2aaSAndroid Build Coastguard Worker // Copy all pixels over, but set alpha to 0
710*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < pm.height(); y++) {
711*c8dee2aaSAndroid Build Coastguard Worker for (int x = 0; x < pm.width(); x++) {
712*c8dee2aaSAndroid Build Coastguard Worker *bmp.getAddr32(x, y) = *pm.addr32(x, y) & 0x00FFFFFF;
713*c8dee2aaSAndroid Build Coastguard Worker }
714*c8dee2aaSAndroid Build Coastguard Worker }
715*c8dee2aaSAndroid Build Coastguard Worker return bmp.asImage();
716*c8dee2aaSAndroid Build Coastguard Worker }
717*c8dee2aaSAndroid Build Coastguard Worker
normal_map_unpremul_image_shader()718*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> normal_map_unpremul_image_shader() {
719*c8dee2aaSAndroid Build Coastguard Worker return normal_map_unpremul_image()->makeShader(SkFilterMode::kNearest);
720*c8dee2aaSAndroid Build Coastguard Worker }
721*c8dee2aaSAndroid Build Coastguard Worker
normal_map_raw_unpremul_image_shader()722*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> normal_map_raw_unpremul_image_shader() {
723*c8dee2aaSAndroid Build Coastguard Worker return normal_map_unpremul_image()->makeRawShader(SkFilterMode::kNearest);
724*c8dee2aaSAndroid Build Coastguard Worker }
725*c8dee2aaSAndroid Build Coastguard Worker
lit_shader(sk_sp<SkShader> normals)726*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> lit_shader(sk_sp<SkShader> normals) {
727*c8dee2aaSAndroid Build Coastguard Worker // Simple N-dot-L against a fixed, directional light:
728*c8dee2aaSAndroid Build Coastguard Worker static const char* kSrc = R"(
729*c8dee2aaSAndroid Build Coastguard Worker uniform shader normals;
730*c8dee2aaSAndroid Build Coastguard Worker half4 main(vec2 p) {
731*c8dee2aaSAndroid Build Coastguard Worker vec3 n = normalize(normals.eval(p).xyz * 2 - 1);
732*c8dee2aaSAndroid Build Coastguard Worker vec3 l = normalize(vec3(1, -1, 1));
733*c8dee2aaSAndroid Build Coastguard Worker return saturate(dot(n, l)).xxx1;
734*c8dee2aaSAndroid Build Coastguard Worker }
735*c8dee2aaSAndroid Build Coastguard Worker )";
736*c8dee2aaSAndroid Build Coastguard Worker auto effect = SkRuntimeEffect::MakeForShader(SkString(kSrc)).effect;
737*c8dee2aaSAndroid Build Coastguard Worker return effect->makeShader(/* uniforms= */ nullptr, &normals, /* childCount= */ 1);
738*c8dee2aaSAndroid Build Coastguard Worker }
739*c8dee2aaSAndroid Build Coastguard Worker
lit_shader_linear(sk_sp<SkShader> normals)740*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkShader> lit_shader_linear(sk_sp<SkShader> normals) {
741*c8dee2aaSAndroid Build Coastguard Worker // Simple N-dot-L against a fixed, directional light, done in linear space:
742*c8dee2aaSAndroid Build Coastguard Worker static const char* kSrc = R"(
743*c8dee2aaSAndroid Build Coastguard Worker uniform shader normals;
744*c8dee2aaSAndroid Build Coastguard Worker half4 main(vec2 p) {
745*c8dee2aaSAndroid Build Coastguard Worker vec3 n = normalize(normals.eval(p).xyz * 2 - 1);
746*c8dee2aaSAndroid Build Coastguard Worker vec3 l = normalize(vec3(1, -1, 1));
747*c8dee2aaSAndroid Build Coastguard Worker return fromLinearSrgb(saturate(dot(n, l)).xxx).xxx1;
748*c8dee2aaSAndroid Build Coastguard Worker }
749*c8dee2aaSAndroid Build Coastguard Worker )";
750*c8dee2aaSAndroid Build Coastguard Worker auto effect = SkRuntimeEffect::MakeForShader(SkString(kSrc)).effect;
751*c8dee2aaSAndroid Build Coastguard Worker return effect->makeShader(/* uniforms= */ nullptr, &normals, /* childCount= */ 1);
752*c8dee2aaSAndroid Build Coastguard Worker }
753*c8dee2aaSAndroid Build Coastguard Worker
754*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(paint_alpha_normals_rt, canvas, 512,512) {
755*c8dee2aaSAndroid Build Coastguard Worker // Various draws, with non-opaque paint alpha. This demonstrates several issues around how
756*c8dee2aaSAndroid Build Coastguard Worker // paint alpha is applied differently on CPU (globally, after all shaders) and GPU (per shader,
757*c8dee2aaSAndroid Build Coastguard Worker // inconsistently). See: skbug.com/11942
758*c8dee2aaSAndroid Build Coastguard Worker //
759*c8dee2aaSAndroid Build Coastguard Worker // When this works, it will be a demo of applying paint alpha to fade out a complex effect.
__anon6019730d0202(int x, int y, sk_sp<SkShader> shader) 760*c8dee2aaSAndroid Build Coastguard Worker auto draw_shader = [=](int x, int y, sk_sp<SkShader> shader) {
761*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
762*c8dee2aaSAndroid Build Coastguard Worker p.setAlpha(164);
763*c8dee2aaSAndroid Build Coastguard Worker p.setShader(shader);
764*c8dee2aaSAndroid Build Coastguard Worker
765*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
766*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(x, y);
767*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect({0, 0, 256, 256});
768*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPaint(p);
769*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
770*c8dee2aaSAndroid Build Coastguard Worker };
771*c8dee2aaSAndroid Build Coastguard Worker
772*c8dee2aaSAndroid Build Coastguard Worker draw_shader(0, 0, normal_map_shader());
773*c8dee2aaSAndroid Build Coastguard Worker draw_shader(0, 256, normal_map_image_shader());
774*c8dee2aaSAndroid Build Coastguard Worker
775*c8dee2aaSAndroid Build Coastguard Worker draw_shader(256, 0, lit_shader(normal_map_shader()));
776*c8dee2aaSAndroid Build Coastguard Worker draw_shader(256, 256, lit_shader(normal_map_image_shader()));
777*c8dee2aaSAndroid Build Coastguard Worker }
778*c8dee2aaSAndroid Build Coastguard Worker
779*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(raw_image_shader_normals_rt, canvas, 768, 512) {
780*c8dee2aaSAndroid Build Coastguard Worker // Demonstrates the utility of SkImage::makeRawShader, for non-color child shaders.
781*c8dee2aaSAndroid Build Coastguard Worker
782*c8dee2aaSAndroid Build Coastguard Worker // First, make an offscreen surface, so we can control the destination color space:
783*c8dee2aaSAndroid Build Coastguard Worker auto surfInfo = SkImageInfo::Make(512, 512,
784*c8dee2aaSAndroid Build Coastguard Worker kN32_SkColorType,
785*c8dee2aaSAndroid Build Coastguard Worker kPremul_SkAlphaType,
786*c8dee2aaSAndroid Build Coastguard Worker SkColorSpace::MakeSRGB()->makeColorSpin());
787*c8dee2aaSAndroid Build Coastguard Worker auto surface = canvas->makeSurface(surfInfo);
788*c8dee2aaSAndroid Build Coastguard Worker if (!surface) {
789*c8dee2aaSAndroid Build Coastguard Worker surface = SkSurfaces::Raster(surfInfo);
790*c8dee2aaSAndroid Build Coastguard Worker }
791*c8dee2aaSAndroid Build Coastguard Worker
__anon6019730d0302(int x, int y, sk_sp<SkShader> shader, SkCanvas* canvas) 792*c8dee2aaSAndroid Build Coastguard Worker auto draw_shader = [](int x, int y, sk_sp<SkShader> shader, SkCanvas* canvas) {
793*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
794*c8dee2aaSAndroid Build Coastguard Worker p.setShader(shader);
795*c8dee2aaSAndroid Build Coastguard Worker
796*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
797*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(x, y);
798*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect({0, 0, 256, 256});
799*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPaint(p);
800*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
801*c8dee2aaSAndroid Build Coastguard Worker };
802*c8dee2aaSAndroid Build Coastguard Worker
803*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> colorNormals = normal_map_image_shader(),
804*c8dee2aaSAndroid Build Coastguard Worker rawNormals = normal_map_raw_image_shader();
805*c8dee2aaSAndroid Build Coastguard Worker
806*c8dee2aaSAndroid Build Coastguard Worker // Draw our normal map as colors (will be color-rotated), and raw (untransformed)
807*c8dee2aaSAndroid Build Coastguard Worker draw_shader(0, 0, colorNormals, surface->getCanvas());
808*c8dee2aaSAndroid Build Coastguard Worker draw_shader(0, 256, rawNormals, surface->getCanvas());
809*c8dee2aaSAndroid Build Coastguard Worker
810*c8dee2aaSAndroid Build Coastguard Worker // Now draw our lighting shader using the normal and raw versions of the normals as children.
811*c8dee2aaSAndroid Build Coastguard Worker // The top image will have the normals rotated (incorrectly), so the lighting is very dark.
812*c8dee2aaSAndroid Build Coastguard Worker draw_shader(256, 0, lit_shader(colorNormals), surface->getCanvas());
813*c8dee2aaSAndroid Build Coastguard Worker draw_shader(256, 256, lit_shader(rawNormals), surface->getCanvas());
814*c8dee2aaSAndroid Build Coastguard Worker
815*c8dee2aaSAndroid Build Coastguard Worker // Now draw the offscreen surface back to our original canvas. If we do this naively, the image
816*c8dee2aaSAndroid Build Coastguard Worker // will be un-transformed back to the canvas' color space. That will have the effect of undoing
817*c8dee2aaSAndroid Build Coastguard Worker // the color spin on the upper-left, and APPLYING a color-spin on the bottom left. To preserve
818*c8dee2aaSAndroid Build Coastguard Worker // the intent of this GM (and make it draw consistently whether or not the original surface has
819*c8dee2aaSAndroid Build Coastguard Worker // a color space attached), we reinterpret the offscreen image as being in sRGB:
820*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(
821*c8dee2aaSAndroid Build Coastguard Worker surface->makeImageSnapshot()->reinterpretColorSpace(SkColorSpace::MakeSRGB()), 0, 0);
822*c8dee2aaSAndroid Build Coastguard Worker
823*c8dee2aaSAndroid Build Coastguard Worker // Finally, to demonstrate that raw unpremul image shaders don't premul, draw lighting two more
824*c8dee2aaSAndroid Build Coastguard Worker // times, with an unpremul normal map (containing ZERO in the alpha channel). THe top will
825*c8dee2aaSAndroid Build Coastguard Worker // premultiply the normals, resulting in totally dark lighting. The bottom will retain the RGB
826*c8dee2aaSAndroid Build Coastguard Worker // encoded normals, even with zero alpha:
827*c8dee2aaSAndroid Build Coastguard Worker draw_shader(512, 0, lit_shader(normal_map_unpremul_image_shader()), canvas);
828*c8dee2aaSAndroid Build Coastguard Worker draw_shader(512, 256, lit_shader(normal_map_raw_unpremul_image_shader()), canvas);
829*c8dee2aaSAndroid Build Coastguard Worker }
830*c8dee2aaSAndroid Build Coastguard Worker
831*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(lit_shader_linear_rt, canvas, 512, 256) {
832*c8dee2aaSAndroid Build Coastguard Worker // First, make an offscreen surface, so we can control the destination color space:
833*c8dee2aaSAndroid Build Coastguard Worker auto surfInfo = SkImageInfo::Make(512, 256,
834*c8dee2aaSAndroid Build Coastguard Worker kN32_SkColorType,
835*c8dee2aaSAndroid Build Coastguard Worker kPremul_SkAlphaType,
836*c8dee2aaSAndroid Build Coastguard Worker SkColorSpace::MakeSRGB());
837*c8dee2aaSAndroid Build Coastguard Worker auto surface = canvas->makeSurface(surfInfo);
838*c8dee2aaSAndroid Build Coastguard Worker if (!surface) {
839*c8dee2aaSAndroid Build Coastguard Worker surface = SkSurfaces::Raster(surfInfo);
840*c8dee2aaSAndroid Build Coastguard Worker }
841*c8dee2aaSAndroid Build Coastguard Worker
__anon6019730d0402(int x, int y, sk_sp<SkShader> shader, SkCanvas* canvas) 842*c8dee2aaSAndroid Build Coastguard Worker auto draw_shader = [](int x, int y, sk_sp<SkShader> shader, SkCanvas* canvas) {
843*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
844*c8dee2aaSAndroid Build Coastguard Worker p.setShader(shader);
845*c8dee2aaSAndroid Build Coastguard Worker
846*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
847*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(x, y);
848*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect({0, 0, 256, 256});
849*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPaint(p);
850*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
851*c8dee2aaSAndroid Build Coastguard Worker };
852*c8dee2aaSAndroid Build Coastguard Worker
853*c8dee2aaSAndroid Build Coastguard Worker // We draw two lit spheres - one does math in the working space (so gamma-encoded). The second
854*c8dee2aaSAndroid Build Coastguard Worker // works in linear space, then converts to sRGB. This produces (more accurate) sharp falloff:
855*c8dee2aaSAndroid Build Coastguard Worker draw_shader(0, 0, lit_shader(normal_map_shader()), surface->getCanvas());
856*c8dee2aaSAndroid Build Coastguard Worker draw_shader(256, 0, lit_shader_linear(normal_map_shader()), surface->getCanvas());
857*c8dee2aaSAndroid Build Coastguard Worker
858*c8dee2aaSAndroid Build Coastguard Worker // Now draw the offscreen surface back to our original canvas:
859*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
860*c8dee2aaSAndroid Build Coastguard Worker }
861*c8dee2aaSAndroid Build Coastguard Worker
862*c8dee2aaSAndroid Build Coastguard Worker // skbug.com/13598 GPU was double applying the local matrix.
863*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(local_matrix_shader_rt, canvas, 256, 256) {
864*c8dee2aaSAndroid Build Coastguard Worker SkString passthrough(R"(
865*c8dee2aaSAndroid Build Coastguard Worker uniform shader s;
866*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) { return s.eval(p); }
867*c8dee2aaSAndroid Build Coastguard Worker )");
868*c8dee2aaSAndroid Build Coastguard Worker auto [rte, error] = SkRuntimeEffect::MakeForShader(passthrough, {});
869*c8dee2aaSAndroid Build Coastguard Worker if (!rte) {
870*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("%s\n", error.c_str());
871*c8dee2aaSAndroid Build Coastguard Worker return;
872*c8dee2aaSAndroid Build Coastguard Worker }
873*c8dee2aaSAndroid Build Coastguard Worker
874*c8dee2aaSAndroid Build Coastguard Worker auto image = ToolUtils::GetResourceAsImage("images/mandrill_128.png");
875*c8dee2aaSAndroid Build Coastguard Worker auto imgShader = image->makeShader(SkFilterMode::kNearest);
876*c8dee2aaSAndroid Build Coastguard Worker
877*c8dee2aaSAndroid Build Coastguard Worker auto r = SkRect::MakeWH(image->width(), image->height());
878*c8dee2aaSAndroid Build Coastguard Worker
879*c8dee2aaSAndroid Build Coastguard Worker auto lm = SkMatrix::RotateDeg(90.f, {image->width()/2.f, image->height()/2.f});
880*c8dee2aaSAndroid Build Coastguard Worker
881*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
882*c8dee2aaSAndroid Build Coastguard Worker
883*c8dee2aaSAndroid Build Coastguard Worker // image
884*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(imgShader);
885*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(r, paint);
886*c8dee2aaSAndroid Build Coastguard Worker
887*c8dee2aaSAndroid Build Coastguard Worker // passthrough(image)
888*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
889*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(image->width(), 0);
890*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(rte->makeShader(/* uniforms= */ nullptr, &imgShader, /* childCount= */ 1));
891*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(r, paint);
892*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
893*c8dee2aaSAndroid Build Coastguard Worker
894*c8dee2aaSAndroid Build Coastguard Worker // localmatrix(image)
895*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
896*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(0, image->height());
897*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(imgShader->makeWithLocalMatrix(lm));
898*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(r, paint);
899*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
900*c8dee2aaSAndroid Build Coastguard Worker
901*c8dee2aaSAndroid Build Coastguard Worker // localmatrix(passthrough(image)) This was the bug.
902*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
903*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(image->width(), image->height());
904*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(rte->makeShader(/* uniforms= */ nullptr, &imgShader, /* childCount= */ 1)
905*c8dee2aaSAndroid Build Coastguard Worker ->makeWithLocalMatrix(lm));
906*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect(r, paint);
907*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
908*c8dee2aaSAndroid Build Coastguard Worker }
909*c8dee2aaSAndroid Build Coastguard Worker
910*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(null_child_rt, canvas, 150, 100) {
911*c8dee2aaSAndroid Build Coastguard Worker using ChildPtr = SkRuntimeEffect::ChildPtr;
912*c8dee2aaSAndroid Build Coastguard Worker
913*c8dee2aaSAndroid Build Coastguard Worker // Every swatch should evaluate to the same shade of purple.
914*c8dee2aaSAndroid Build Coastguard Worker // Paint with a shader evaluating a null shader.
915*c8dee2aaSAndroid Build Coastguard Worker // Point passed to eval() is ignored; transparent black is returned.
916*c8dee2aaSAndroid Build Coastguard Worker {
917*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalShader{R"(
918*c8dee2aaSAndroid Build Coastguard Worker uniform shader s;
919*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) { return s.eval(p) + half4(0.5, 0, 0.5, 1); }
920*c8dee2aaSAndroid Build Coastguard Worker )"};
921*c8dee2aaSAndroid Build Coastguard Worker auto [rtShader, error] = SkRuntimeEffect::MakeForShader(kEvalShader);
922*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtShader);
923*c8dee2aaSAndroid Build Coastguard Worker
924*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
925*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkShader>{nullptr}}};
926*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(rtShader->makeShader(/*uniforms=*/nullptr, children));
927*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00)); // green (ignored)
928*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
929*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
930*c8dee2aaSAndroid Build Coastguard Worker }
931*c8dee2aaSAndroid Build Coastguard Worker // Paint with a shader evaluating a null color filter.
932*c8dee2aaSAndroid Build Coastguard Worker // Color passed to eval() is returned; paint color is ignored.
933*c8dee2aaSAndroid Build Coastguard Worker {
934*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalColorFilter{R"(
935*c8dee2aaSAndroid Build Coastguard Worker uniform colorFilter cf;
936*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) { return cf.eval(half4(0.5, 0, 0.5, 1)); }
937*c8dee2aaSAndroid Build Coastguard Worker )"};
938*c8dee2aaSAndroid Build Coastguard Worker auto [rtShader, error] = SkRuntimeEffect::MakeForShader(kEvalColorFilter);
939*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtShader);
940*c8dee2aaSAndroid Build Coastguard Worker
941*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
942*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkColorFilter>{nullptr}}};
943*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(rtShader->makeShader(/*uniforms=*/nullptr, children));
944*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF)); // green (does not contribute)
945*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
946*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
947*c8dee2aaSAndroid Build Coastguard Worker }
948*c8dee2aaSAndroid Build Coastguard Worker // Paint with a shader evaluating a null blender.
949*c8dee2aaSAndroid Build Coastguard Worker // Colors passed to eval() are blended via src-over; paint color is ignored.
950*c8dee2aaSAndroid Build Coastguard Worker {
951*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalBlender{R"(
952*c8dee2aaSAndroid Build Coastguard Worker uniform blender b;
953*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) { return b.eval(half4(0.5, 0, 0, 0.5), half4(0, 0, 1, 1)); }
954*c8dee2aaSAndroid Build Coastguard Worker )"};
955*c8dee2aaSAndroid Build Coastguard Worker auto [rtShader, error] = SkRuntimeEffect::MakeForShader(kEvalBlender);
956*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtShader);
957*c8dee2aaSAndroid Build Coastguard Worker
958*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
959*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkBlender>{nullptr}}};
960*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(rtShader->makeShader(/*uniforms=*/nullptr, children));
961*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF)); // green (does not contribute)
962*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
963*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
964*c8dee2aaSAndroid Build Coastguard Worker }
965*c8dee2aaSAndroid Build Coastguard Worker
966*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(-150, 50);
967*c8dee2aaSAndroid Build Coastguard Worker
968*c8dee2aaSAndroid Build Coastguard Worker // Paint with a color filter evaluating a null shader.
969*c8dee2aaSAndroid Build Coastguard Worker // Point passed to eval() is ignored; transparent black is returned.
970*c8dee2aaSAndroid Build Coastguard Worker {
971*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalShader{R"(
972*c8dee2aaSAndroid Build Coastguard Worker uniform shader s;
973*c8dee2aaSAndroid Build Coastguard Worker half4 main(half4 c) { return s.eval(float2(0)) + half4(0.5, 0, 0.5, 1); }
974*c8dee2aaSAndroid Build Coastguard Worker )"};
975*c8dee2aaSAndroid Build Coastguard Worker auto [rtFilter, error] = SkRuntimeEffect::MakeForColorFilter(kEvalShader);
976*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtFilter);
977*c8dee2aaSAndroid Build Coastguard Worker
978*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
979*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkShader>{nullptr}}};
980*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(rtFilter->makeColorFilter(/*uniforms=*/nullptr, children));
981*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00)); // green (ignored)
982*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
983*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
984*c8dee2aaSAndroid Build Coastguard Worker }
985*c8dee2aaSAndroid Build Coastguard Worker // Paint with a color filter evaluating a null color filter.
986*c8dee2aaSAndroid Build Coastguard Worker // Color passed to eval() is returned; paint color is ignored.
987*c8dee2aaSAndroid Build Coastguard Worker {
988*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalColorFilter{R"(
989*c8dee2aaSAndroid Build Coastguard Worker uniform colorFilter cf;
990*c8dee2aaSAndroid Build Coastguard Worker half4 main(half4 c) { return cf.eval(half4(0.5, 0, 0.5, 1)); }
991*c8dee2aaSAndroid Build Coastguard Worker )"};
992*c8dee2aaSAndroid Build Coastguard Worker auto [rtFilter, error] = SkRuntimeEffect::MakeForColorFilter(kEvalColorFilter);
993*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtFilter);
994*c8dee2aaSAndroid Build Coastguard Worker
995*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
996*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkColorFilter>{nullptr}}};
997*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(rtFilter->makeColorFilter(/*uniforms=*/nullptr, children));
998*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF)); // green (does not contribute)
999*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
1000*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
1001*c8dee2aaSAndroid Build Coastguard Worker }
1002*c8dee2aaSAndroid Build Coastguard Worker // Paint with a color filter evaluating a null blender.
1003*c8dee2aaSAndroid Build Coastguard Worker // Colors passed to eval() are blended via src-over; paint color is ignored.
1004*c8dee2aaSAndroid Build Coastguard Worker {
1005*c8dee2aaSAndroid Build Coastguard Worker const SkString kEvalBlender{R"(
1006*c8dee2aaSAndroid Build Coastguard Worker uniform blender b;
1007*c8dee2aaSAndroid Build Coastguard Worker half4 main(half4 c) { return b.eval(half4(0.5, 0, 0, 0.5), half4(0, 0, 1, 1)); }
1008*c8dee2aaSAndroid Build Coastguard Worker )"};
1009*c8dee2aaSAndroid Build Coastguard Worker auto [rtFilter, error] = SkRuntimeEffect::MakeForColorFilter(kEvalBlender);
1010*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(rtFilter);
1011*c8dee2aaSAndroid Build Coastguard Worker
1012*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
1013*c8dee2aaSAndroid Build Coastguard Worker ChildPtr children[1] = {ChildPtr{sk_sp<SkBlender>{nullptr}}};
1014*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(rtFilter->makeColorFilter(/*uniforms=*/nullptr, children));
1015*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF)); // green (does not contribute)
1016*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
1017*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
1018*c8dee2aaSAndroid Build Coastguard Worker }
1019*c8dee2aaSAndroid Build Coastguard Worker }
1020*c8dee2aaSAndroid Build Coastguard Worker
1021*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_CAN_FAIL(deferred_shader_rt, canvas, errorMsg, 150, 50) {
1022*c8dee2aaSAndroid Build Coastguard Worker // Skip this GM on recording devices. It actually works okay on serialize-8888, but pic-8888
1023*c8dee2aaSAndroid Build Coastguard Worker // does not. Ultimately, behavior on CPU is potentially strange (especially with SkRP), because
1024*c8dee2aaSAndroid Build Coastguard Worker // SkRP will build the shader more than once per draw.
1025*c8dee2aaSAndroid Build Coastguard Worker if (canvas->imageInfo().colorType() == kUnknown_SkColorType) {
1026*c8dee2aaSAndroid Build Coastguard Worker return skiagm::DrawResult::kSkip;
1027*c8dee2aaSAndroid Build Coastguard Worker }
1028*c8dee2aaSAndroid Build Coastguard Worker
1029*c8dee2aaSAndroid Build Coastguard Worker const SkString kShader{R"(
1030*c8dee2aaSAndroid Build Coastguard Worker uniform half4 color;
1031*c8dee2aaSAndroid Build Coastguard Worker half4 main(float2 p) { return color; }
1032*c8dee2aaSAndroid Build Coastguard Worker )"};
1033*c8dee2aaSAndroid Build Coastguard Worker auto [effect, error] = SkRuntimeEffect::MakeForShader(kShader);
1034*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(effect);
1035*c8dee2aaSAndroid Build Coastguard Worker
1036*c8dee2aaSAndroid Build Coastguard Worker SkColor4f color = SkColors::kRed;
__anon6019730d0502(const SkRuntimeEffectPriv::UniformsCallbackContext&) 1037*c8dee2aaSAndroid Build Coastguard Worker auto makeUniforms = [color](const SkRuntimeEffectPriv::UniformsCallbackContext&) mutable {
1038*c8dee2aaSAndroid Build Coastguard Worker auto result = SkData::MakeWithCopy(&color, sizeof(color));
1039*c8dee2aaSAndroid Build Coastguard Worker color = {color.fB, color.fR, color.fG, color.fA};
1040*c8dee2aaSAndroid Build Coastguard Worker return result;
1041*c8dee2aaSAndroid Build Coastguard Worker };
1042*c8dee2aaSAndroid Build Coastguard Worker
1043*c8dee2aaSAndroid Build Coastguard Worker auto shader =
1044*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffectPriv::MakeDeferredShader(effect.get(), makeUniforms, /*children=*/{});
1045*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(shader);
1046*c8dee2aaSAndroid Build Coastguard Worker
1047*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
1048*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader);
1049*c8dee2aaSAndroid Build Coastguard Worker
1050*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 3; ++i) {
1051*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 50, 50}, paint);
1052*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
1053*c8dee2aaSAndroid Build Coastguard Worker }
1054*c8dee2aaSAndroid Build Coastguard Worker
1055*c8dee2aaSAndroid Build Coastguard Worker return skiagm::DrawResult::kOk;
1056*c8dee2aaSAndroid Build Coastguard Worker }
1057*c8dee2aaSAndroid Build Coastguard Worker
paint_color_shader()1058*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> paint_color_shader() {
1059*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bmp;
1060*c8dee2aaSAndroid Build Coastguard Worker bmp.allocPixels(SkImageInfo::Make(1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
1061*c8dee2aaSAndroid Build Coastguard Worker bmp.eraseColor(SK_ColorWHITE);
1062*c8dee2aaSAndroid Build Coastguard Worker return bmp.makeShader(SkFilterMode::kNearest);
1063*c8dee2aaSAndroid Build Coastguard Worker }
1064*c8dee2aaSAndroid Build Coastguard Worker
1065*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_CAN_FAIL(alpha_image_shader_rt, canvas, errorMsg, 350, 50) {
1066*c8dee2aaSAndroid Build Coastguard Worker if (!canvas->getSurface()) {
1067*c8dee2aaSAndroid Build Coastguard Worker // The only backend that really fails is DDL (because the color filter fails to evaluate on
1068*c8dee2aaSAndroid Build Coastguard Worker // the CPU when we do paint optimization). We can't identify DDL separate from other
1069*c8dee2aaSAndroid Build Coastguard Worker // recording backends, so skip this GM for all of them:
1070*c8dee2aaSAndroid Build Coastguard Worker *errorMsg = "Not supported in recording/DDL mode";
1071*c8dee2aaSAndroid Build Coastguard Worker return skiagm::DrawResult::kSkip;
1072*c8dee2aaSAndroid Build Coastguard Worker }
1073*c8dee2aaSAndroid Build Coastguard Worker
1074*c8dee2aaSAndroid Build Coastguard Worker // Skia typically applies the paint color (or input color, for more complex GPU-FP trees)
1075*c8dee2aaSAndroid Build Coastguard Worker // to alpha-only images. This is useful in trivial cases, but surprising and inconsistent in
1076*c8dee2aaSAndroid Build Coastguard Worker // more complex situations, especially when using SkSL.
1077*c8dee2aaSAndroid Build Coastguard Worker //
1078*c8dee2aaSAndroid Build Coastguard Worker // This GM checks that we suppress the paint-color tinting from SkSL, and always get {0,0,0,a}.
1079*c8dee2aaSAndroid Build Coastguard Worker auto checkerboard = ToolUtils::create_checkerboard_shader(SK_ColorBLACK, SK_ColorWHITE, 4);
1080*c8dee2aaSAndroid Build Coastguard Worker auto paint_shader = paint_color_shader();
1081*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::ChildPtr children[1] = { paint_shader };
1082*c8dee2aaSAndroid Build Coastguard Worker
1083*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
1084*c8dee2aaSAndroid Build Coastguard Worker paint.setColor({0.5f, 0, 0.5f, 1.0f});
1085*c8dee2aaSAndroid Build Coastguard Worker
__anon6019730d0602() 1086*c8dee2aaSAndroid Build Coastguard Worker auto rect = [&]() {
1087*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRect({0, 0, 48, 48}, paint);
1088*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(50, 0);
1089*c8dee2aaSAndroid Build Coastguard Worker };
1090*c8dee2aaSAndroid Build Coastguard Worker
1091*c8dee2aaSAndroid Build Coastguard Worker // Two simple cases: just paint color, then the "paint color" shader.
1092*c8dee2aaSAndroid Build Coastguard Worker // These should both be PURPLE
1093*c8dee2aaSAndroid Build Coastguard Worker rect();
1094*c8dee2aaSAndroid Build Coastguard Worker
1095*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(paint_shader);
1096*c8dee2aaSAndroid Build Coastguard Worker rect();
1097*c8dee2aaSAndroid Build Coastguard Worker
1098*c8dee2aaSAndroid Build Coastguard Worker // All remaining cases should be BLACK
1099*c8dee2aaSAndroid Build Coastguard Worker
1100*c8dee2aaSAndroid Build Coastguard Worker // Shader that evaluates the "paint color" shader.
1101*c8dee2aaSAndroid Build Coastguard Worker // For color-filter and blender, we test them with and without an actual SkShader on the paint.
1102*c8dee2aaSAndroid Build Coastguard Worker // These should all be BLACK
1103*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(
1104*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::MakeForShader(SkString("uniform shader s;"
1105*c8dee2aaSAndroid Build Coastguard Worker "half4 main(float2 p) { return s.eval(p); }"))
1106*c8dee2aaSAndroid Build Coastguard Worker .effect->makeShader(/* uniforms= */ nullptr, children));
1107*c8dee2aaSAndroid Build Coastguard Worker rect();
1108*c8dee2aaSAndroid Build Coastguard Worker
1109*c8dee2aaSAndroid Build Coastguard Worker // Color-filter that evaluates the "paint color" shader, with and without a shader on the paint
1110*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(nullptr);
1111*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(SkRuntimeEffect::MakeForColorFilter(
1112*c8dee2aaSAndroid Build Coastguard Worker SkString("uniform shader s;"
1113*c8dee2aaSAndroid Build Coastguard Worker "half4 main(half4 color) { return s.eval(float2(0)); }"))
1114*c8dee2aaSAndroid Build Coastguard Worker .effect->makeColorFilter(nullptr, children));
1115*c8dee2aaSAndroid Build Coastguard Worker rect();
1116*c8dee2aaSAndroid Build Coastguard Worker
1117*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(checkerboard);
1118*c8dee2aaSAndroid Build Coastguard Worker rect();
1119*c8dee2aaSAndroid Build Coastguard Worker
1120*c8dee2aaSAndroid Build Coastguard Worker // Blender that evaluates the "paint color" shader, with and without a shader on the paint
1121*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(nullptr);
1122*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(nullptr);
1123*c8dee2aaSAndroid Build Coastguard Worker paint.setBlender(
1124*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::MakeForBlender(
1125*c8dee2aaSAndroid Build Coastguard Worker SkString("uniform shader s;"
1126*c8dee2aaSAndroid Build Coastguard Worker "half4 main(half4 src, half4 dst) { return s.eval(float2(0)); }"))
1127*c8dee2aaSAndroid Build Coastguard Worker .effect->makeBlender(nullptr, children));
1128*c8dee2aaSAndroid Build Coastguard Worker rect();
1129*c8dee2aaSAndroid Build Coastguard Worker
1130*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(checkerboard);
1131*c8dee2aaSAndroid Build Coastguard Worker rect();
1132*c8dee2aaSAndroid Build Coastguard Worker
1133*c8dee2aaSAndroid Build Coastguard Worker return skiagm::DrawResult::kOk;
1134*c8dee2aaSAndroid Build Coastguard Worker }
1135