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