• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkCanvas.h"
10 #include "include/core/SkData.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkSize.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkSurface.h"
16 #include "include/effects/SkGradientShader.h"
17 #include "include/effects/SkImageFilters.h"
18 #include "include/effects/SkRuntimeEffect.h"
19 #include "include/utils/SkRandom.h"
20 #include "tools/Resources.h"
21 
22 enum RT_Flags {
23     kAnimate_RTFlag     = 0x1,
24     kBench_RTFlag       = 0x2,
25     kColorFilter_RTFlag = 0x4,
26 };
27 
28 class RuntimeShaderGM : public skiagm::GM {
29 public:
RuntimeShaderGM(const char * name,SkISize size,const char * sksl,uint32_t flags=0)30     RuntimeShaderGM(const char* name, SkISize size, const char* sksl, uint32_t flags = 0)
31             : fName(name), fSize(size), fFlags(flags), fSkSL(sksl) {}
32 
onOnceBeforeDraw()33     void onOnceBeforeDraw() override {
34         auto [effect, error] = (fFlags & kColorFilter_RTFlag)
35                                        ? SkRuntimeEffect::MakeForColorFilter(fSkSL)
36                                        : SkRuntimeEffect::MakeForShader(fSkSL);
37         if (!effect) {
38             SkDebugf("RuntimeShader error: %s\n", error.c_str());
39         }
40         fEffect = std::move(effect);
41     }
42 
runAsBench() const43     bool runAsBench() const override { return SkToBool(fFlags & kBench_RTFlag); }
onShortName()44     SkString onShortName() override { return fName; }
onISize()45     SkISize onISize() override { return fSize; }
46 
onAnimate(double nanos)47     bool onAnimate(double nanos) override {
48         fSecs = nanos / (1000 * 1000 * 1000);
49         return SkToBool(fFlags & kAnimate_RTFlag);
50     }
51 
52 protected:
53     SkString fName;
54     SkISize  fSize;
55     uint32_t fFlags;
56     float    fSecs = 0.0f;
57 
58     SkString fSkSL;
59     sk_sp<SkRuntimeEffect> fEffect;
60 };
61 
62 class SimpleRT : public RuntimeShaderGM {
63 public:
SimpleRT()64     SimpleRT() : RuntimeShaderGM("runtime_shader", {512, 256}, R"(
65         uniform half4 gColor;
66 
67         half4 main(float2 p) {
68             return half4(p*(1.0/255), gColor.b, 1);
69         }
70     )", kBench_RTFlag) {}
71 
onDraw(SkCanvas * canvas)72     void onDraw(SkCanvas* canvas) override {
73         SkRuntimeShaderBuilder builder(fEffect);
74 
75         SkMatrix localM;
76         localM.setRotate(90, 128, 128);
77         builder.uniform("gColor") = SkColor4f{1, 0, 0, 1};
78 
79         SkPaint p;
80         p.setShader(builder.makeShader(&localM, true));
81         canvas->drawRect({0, 0, 256, 256}, p);
82     }
83 };
DEF_GM(return new SimpleRT;)84 DEF_GM(return new SimpleRT;)
85 
86 static sk_sp<SkShader> make_shader(sk_sp<SkImage> img, SkISize size) {
87     SkMatrix scale = SkMatrix::Scale(size.width()  / (float)img->width(),
88                                      size.height() / (float)img->height());
89     return img->makeShader(SkSamplingOptions(), scale);
90 }
91 
make_threshold(SkISize size)92 static sk_sp<SkShader> make_threshold(SkISize size) {
93     auto info = SkImageInfo::Make(size.width(), size.height(), kAlpha_8_SkColorType,
94                                   kPremul_SkAlphaType);
95     auto surf = SkSurface::MakeRaster(info);
96     auto canvas = surf->getCanvas();
97 
98     const SkScalar rad = 50;
99     SkColor colors[] = {SK_ColorBLACK, 0};
100     SkPaint paint;
101     paint.setAntiAlias(true);
102     paint.setShader(SkGradientShader::MakeRadial({0,0}, rad, colors, nullptr, 2, SkTileMode::kClamp));
103 
104     SkPaint layerPaint;
105     const SkScalar sigma = 16.0f;
106     layerPaint.setImageFilter(SkImageFilters::Blur(sigma, sigma, nullptr));
107     canvas->saveLayer(nullptr, &layerPaint);
108 
109     SkRandom rand;
110     for (int i = 0; i < 25; ++i) {
111         SkScalar x = rand.nextF() * size.width();
112         SkScalar y = rand.nextF() * size.height();
113         canvas->save();
114         canvas->translate(x, y);
115         canvas->drawCircle(0, 0, rad, paint);
116         canvas->restore();
117     }
118 
119     canvas->restore();  // apply the blur
120 
121     return surf->makeImageSnapshot()->makeShader(SkSamplingOptions());
122 }
123 
124 class ThresholdRT : public RuntimeShaderGM {
125 public:
ThresholdRT()126     ThresholdRT() : RuntimeShaderGM("threshold_rt", {256, 256}, R"(
127         uniform shader before_map;
128         uniform shader after_map;
129         uniform shader threshold_map;
130 
131         uniform float cutoff;
132         uniform float slope;
133 
134         float smooth_cutoff(float x) {
135             x = x * slope + (0.5 - slope * cutoff);
136             return clamp(x, 0, 1);
137         }
138 
139         half4 main(float2 xy) {
140             half4 before = before_map.eval(xy);
141             half4 after = after_map.eval(xy);
142 
143             float m = smooth_cutoff(threshold_map.eval(xy).a);
144             return mix(before, after, m);
145         }
146     )", kAnimate_RTFlag | kBench_RTFlag) {}
147 
148     sk_sp<SkShader> fBefore, fAfter, fThreshold;
149 
onOnceBeforeDraw()150     void onOnceBeforeDraw() override {
151         const SkISize size = {256, 256};
152         fThreshold = make_threshold(size);
153         fBefore = make_shader(GetResourceAsImage("images/mandrill_256.png"), size);
154         fAfter = make_shader(GetResourceAsImage("images/dog.jpg"), size);
155 
156         this->RuntimeShaderGM::onOnceBeforeDraw();
157     }
158 
onDraw(SkCanvas * canvas)159     void onDraw(SkCanvas* canvas) override {
160         SkRuntimeShaderBuilder builder(fEffect);
161 
162         builder.uniform("cutoff") = sin(fSecs) * 0.55f + 0.5f;
163         builder.uniform("slope")  = 10.0f;
164 
165         builder.child("before_map")    = fBefore;
166         builder.child("after_map")     = fAfter;
167         builder.child("threshold_map") = fThreshold;
168 
169         SkPaint paint;
170         paint.setShader(builder.makeShader(nullptr, true));
171         canvas->drawRect({0, 0, 256, 256}, paint);
172 
173         auto draw = [&](SkScalar x, SkScalar y, sk_sp<SkShader> shader) {
174             paint.setShader(shader);
175             canvas->save();
176             canvas->translate(x, y);
177             canvas->drawRect({0, 0, 256, 256}, paint);
178             canvas->restore();
179         };
180         draw(256,   0, fThreshold);
181         draw(  0, 256, fBefore);
182         draw(256, 256, fAfter);
183     }
184 };
185 DEF_GM(return new ThresholdRT;)
186 
187 class SpiralRT : public RuntimeShaderGM {
188 public:
SpiralRT()189     SpiralRT() : RuntimeShaderGM("spiral_rt", {512, 512}, R"(
190         uniform float rad_scale;
191         uniform float2 in_center;
192         layout(srgb_unpremul) uniform float4 in_colors0;
193         layout(srgb_unpremul) uniform float4 in_colors1;
194 
195         half4 main(float2 p) {
196             float2 pp = p - in_center;
197             float radius = length(pp);
198             radius = sqrt(radius);
199             float angle = atan(pp.y / pp.x);
200             float t = (angle + 3.1415926/2) / (3.1415926);
201             t += radius * rad_scale;
202             t = fract(t);
203             return in_colors0 * (1-t) + in_colors1 * t;
204         }
205     )", kAnimate_RTFlag | kBench_RTFlag) {}
206 
onDraw(SkCanvas * canvas)207     void onDraw(SkCanvas* canvas) override {
208         SkRuntimeShaderBuilder builder(fEffect);
209 
210         builder.uniform("rad_scale")  = std::sin(fSecs * 0.5f + 2.0f) / 5;
211         builder.uniform("in_center")  = SkV2{256, 256};
212         builder.uniform("in_colors0") = SkV4{1, 0, 0, 1};
213         builder.uniform("in_colors1") = SkV4{0, 1, 0, 1};
214 
215         SkPaint paint;
216         paint.setShader(builder.makeShader(nullptr, true));
217         canvas->drawRect({0, 0, 512, 512}, paint);
218     }
219 };
220 DEF_GM(return new SpiralRT;)
221 
222 // Test case for sampling with both unmodified input coordinates, and explicit coordinates.
223 // The first version of skbug.com/11869 suffered a bug where all samples of a child were treated
224 // as pass-through if *at least one* used the unmodified coordinates. This was detected & tracked
225 // in b/181092919. This GM is similar, and demonstrates the bug before the fix was applied.
226 class UnsharpRT : public RuntimeShaderGM {
227 public:
UnsharpRT()228     UnsharpRT() : RuntimeShaderGM("unsharp_rt", {512, 256}, R"(
229         uniform shader child;
230         half4 main(float2 xy) {
231             half4 c = child.eval(xy) * 5;
232             c -= child.eval(xy + float2( 1,  0));
233             c -= child.eval(xy + float2(-1,  0));
234             c -= child.eval(xy + float2( 0,  1));
235             c -= child.eval(xy + float2( 0, -1));
236             return c;
237         }
238     )") {}
239 
240     sk_sp<SkImage> fMandrill;
241 
onOnceBeforeDraw()242     void onOnceBeforeDraw() override {
243         fMandrill      = GetResourceAsImage("images/mandrill_256.png");
244         this->RuntimeShaderGM::onOnceBeforeDraw();
245     }
246 
onDraw(SkCanvas * canvas)247     void onDraw(SkCanvas* canvas) override {
248         // First we draw the unmodified image
249         canvas->drawImage(fMandrill,      0,   0);
250 
251         // Now draw the image with our unsharp mask applied
252         SkRuntimeShaderBuilder builder(fEffect);
253         const SkSamplingOptions sampling(SkFilterMode::kNearest);
254         builder.child("child") = fMandrill->makeShader(sampling);
255 
256         SkPaint paint;
257         paint.setShader(builder.makeShader(nullptr, true));
258         canvas->translate(256, 0);
259         canvas->drawRect({ 0, 0, 256, 256 }, paint);
260     }
261 };
262 DEF_GM(return new UnsharpRT;)
263 
264 class ColorCubeRT : public RuntimeShaderGM {
265 public:
ColorCubeRT()266     ColorCubeRT() : RuntimeShaderGM("color_cube_rt", {512, 512}, R"(
267         uniform shader child;
268         uniform shader color_cube;
269 
270         uniform float rg_scale;
271         uniform float rg_bias;
272         uniform float b_scale;
273         uniform float inv_size;
274 
275         half4 main(float2 xy) {
276             float4 c = unpremul(child.eval(xy));
277 
278             // Map to cube coords:
279             float3 cubeCoords = float3(c.rg * rg_scale + rg_bias, c.b * b_scale);
280 
281             // Compute slice coordinate
282             float2 coords1 = float2((floor(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
283             float2 coords2 = float2(( ceil(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
284 
285             // Two bilinear fetches, plus a manual lerp for the third axis:
286             half4 color = mix(color_cube.eval(coords1), color_cube.eval(coords2),
287                               fract(cubeCoords.b));
288 
289             // Premul again
290             color.rgb *= color.a;
291 
292             return color;
293         }
294     )") {}
295 
296     sk_sp<SkImage> fMandrill, fMandrillSepia, fIdentityCube, fSepiaCube;
297 
onOnceBeforeDraw()298     void onOnceBeforeDraw() override {
299         fMandrill      = GetResourceAsImage("images/mandrill_256.png");
300         fMandrillSepia = GetResourceAsImage("images/mandrill_sepia.png");
301         fIdentityCube  = GetResourceAsImage("images/lut_identity.png");
302         fSepiaCube     = GetResourceAsImage("images/lut_sepia.png");
303 
304         this->RuntimeShaderGM::onOnceBeforeDraw();
305     }
306 
onDraw(SkCanvas * canvas)307     void onDraw(SkCanvas* canvas) override {
308         SkRuntimeShaderBuilder builder(fEffect);
309 
310         // First we draw the unmodified image, and a copy that was sepia-toned in Photoshop:
311         canvas->drawImage(fMandrill,      0,   0);
312         canvas->drawImage(fMandrillSepia, 0, 256);
313 
314         // LUT dimensions should be (kSize^2, kSize)
315         constexpr float kSize = 16.0f;
316 
317         const SkSamplingOptions sampling(SkFilterMode::kLinear);
318 
319         builder.uniform("rg_scale")     = (kSize - 1) / kSize;
320         builder.uniform("rg_bias")      = 0.5f / kSize;
321         builder.uniform("b_scale")      = kSize - 1;
322         builder.uniform("inv_size")     = 1.0f / kSize;
323 
324         builder.child("child")        = fMandrill->makeShader(sampling);
325 
326         SkPaint paint;
327 
328         // TODO: Should we add SkImage::makeNormalizedShader() to handle this automatically?
329         SkMatrix normalize = SkMatrix::Scale(1.0f / (kSize * kSize), 1.0f / kSize);
330 
331         // Now draw the image with an identity color cube - it should look like the original
332         builder.child("color_cube") = fIdentityCube->makeShader(sampling, normalize);
333         paint.setShader(builder.makeShader(nullptr, true));
334         canvas->translate(256, 0);
335         canvas->drawRect({ 0, 0, 256, 256 }, paint);
336 
337         // ... and with a sepia-tone color cube. This should match the sepia-toned image.
338         builder.child("color_cube") = fSepiaCube->makeShader(sampling, normalize);
339         paint.setShader(builder.makeShader(nullptr, true));
340         canvas->translate(0, 256);
341         canvas->drawRect({ 0, 0, 256, 256 }, paint);
342     }
343 };
344 DEF_GM(return new ColorCubeRT;)
345 
346 // Same as above, but demonstrating how to implement this as a runtime color filter (that samples
347 // a shader child for the LUT).
348 class ColorCubeColorFilterRT : public RuntimeShaderGM {
349 public:
ColorCubeColorFilterRT()350     ColorCubeColorFilterRT() : RuntimeShaderGM("color_cube_cf_rt", {512, 512}, R"(
351         uniform shader color_cube;
352 
353         uniform float rg_scale;
354         uniform float rg_bias;
355         uniform float b_scale;
356         uniform float inv_size;
357 
358         half4 main(half4 inColor) {
359             float4 c = unpremul(inColor);
360 
361             // Map to cube coords:
362             float3 cubeCoords = float3(c.rg * rg_scale + rg_bias, c.b * b_scale);
363 
364             // Compute slice coordinate
365             float2 coords1 = float2((floor(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
366             float2 coords2 = float2(( ceil(cubeCoords.b) + cubeCoords.r) * inv_size, cubeCoords.g);
367 
368             // Two bilinear fetches, plus a manual lerp for the third axis:
369             half4 color = mix(color_cube.eval(coords1), color_cube.eval(coords2),
370                               fract(cubeCoords.b));
371 
372             // Premul again
373             color.rgb *= color.a;
374 
375             return color;
376         }
377     )", kColorFilter_RTFlag) {}
378 
379     sk_sp<SkImage> fMandrill, fMandrillSepia, fIdentityCube, fSepiaCube;
380 
onOnceBeforeDraw()381     void onOnceBeforeDraw() override {
382         fMandrill      = GetResourceAsImage("images/mandrill_256.png");
383         fMandrillSepia = GetResourceAsImage("images/mandrill_sepia.png");
384         fIdentityCube  = GetResourceAsImage("images/lut_identity.png");
385         fSepiaCube     = GetResourceAsImage("images/lut_sepia.png");
386 
387         this->RuntimeShaderGM::onOnceBeforeDraw();
388     }
389 
onDraw(SkCanvas * canvas)390     void onDraw(SkCanvas* canvas) override {
391         // First we draw the unmodified image, and a copy that was sepia-toned in Photoshop:
392         canvas->drawImage(fMandrill,      0,   0);
393         canvas->drawImage(fMandrillSepia, 0, 256);
394 
395         // LUT dimensions should be (kSize^2, kSize)
396         constexpr float kSize = 16.0f;
397 
398         const SkSamplingOptions sampling(SkFilterMode::kLinear);
399 
400         float uniforms[] = {
401                 (kSize - 1) / kSize,  // rg_scale
402                 0.5f / kSize,         // rg_bias
403                 kSize - 1,            // b_scale
404                 1.0f / kSize,         // inv_size
405         };
406 
407         SkPaint paint;
408 
409         // TODO: Should we add SkImage::makeNormalizedShader() to handle this automatically?
410         SkMatrix normalize = SkMatrix::Scale(1.0f / (kSize * kSize), 1.0f / kSize);
411 
412         // Now draw the image with an identity color cube - it should look like the original
413         SkRuntimeEffect::ChildPtr children[] = {fIdentityCube->makeShader(sampling, normalize)};
414         paint.setColorFilter(fEffect->makeColorFilter(
415                 SkData::MakeWithCopy(uniforms, sizeof(uniforms)), SkMakeSpan(children)));
416         canvas->drawImage(fMandrill, 256, 0, sampling, &paint);
417 
418         // ... and with a sepia-tone color cube. This should match the sepia-toned image.
419         children[0] = fSepiaCube->makeShader(sampling, normalize);
420         paint.setColorFilter(fEffect->makeColorFilter(
421                 SkData::MakeWithCopy(uniforms, sizeof(uniforms)), SkMakeSpan(children)));
422         canvas->drawImage(fMandrill, 256, 256, sampling, &paint);
423     }
424 };
425 DEF_GM(return new ColorCubeColorFilterRT;)
426 
427 class DefaultColorRT : public RuntimeShaderGM {
428 public:
DefaultColorRT()429     DefaultColorRT() : RuntimeShaderGM("default_color_rt", {512, 256}, R"(
430         uniform shader child;
431         half4 main(float2 xy) {
432             return child.eval(xy);
433         }
434     )") {}
435 
436     sk_sp<SkImage> fMandrill;
437 
onOnceBeforeDraw()438     void onOnceBeforeDraw() override {
439         fMandrill      = GetResourceAsImage("images/mandrill_256.png");
440         this->RuntimeShaderGM::onOnceBeforeDraw();
441     }
442 
onDraw(SkCanvas * canvas)443     void onDraw(SkCanvas* canvas) override {
444         SkRuntimeShaderBuilder builder(fEffect);
445 
446         // First, we leave the child as null, so sampling it returns the default (paint) color
447         SkPaint paint;
448         paint.setColor4f({ 0.25f, 0.75f, 0.75f, 1.0f });
449         paint.setShader(builder.makeShader(nullptr, false));
450         canvas->drawRect({ 0, 0, 256, 256 }, paint);
451 
452         // Now we bind an image shader as the child. This (by convention) scales by the paint alpha
453         builder.child("child") = fMandrill->makeShader(SkSamplingOptions());
454         paint.setColor4f({ 1.0f, 1.0f, 1.0f, 0.5f });
455         paint.setShader(builder.makeShader(nullptr, false));
456         canvas->translate(256, 0);
457         canvas->drawRect({ 0, 0, 256, 256 }, paint);
458 
459     }
460 };
461 DEF_GM(return new DefaultColorRT;)
462 
463 // Emits coverage for a rounded rectangle whose corners are superellipses defined by the boundary:
464 //
465 //   x^n + y^n == 1
466 //
467 // Where x and y are normalized, clamped coordinates ranging from 0..1 inside the nearest corner's
468 // bounding box.
469 //
470 // See: https://en.wikipedia.org/wiki/Superellipse
471 class ClipSuperRRect : public RuntimeShaderGM {
472 public:
ClipSuperRRect(const char * name,float power)473     ClipSuperRRect(const char* name, float power) : RuntimeShaderGM(name, {500, 500}, R"(
474         uniform float power_minus1;
475         uniform float2 stretch_factor;
476         uniform float2x2 derivatives;
477         half4 main(float2 xy) {
478             xy = max(abs(xy) + stretch_factor, 0);
479             float2 exp_minus1 = pow(xy, power_minus1.xx);  // If power == 3.5: xy * xy * sqrt(xy)
480             float f = dot(exp_minus1, xy) - 1;  // f = x^n + y^n - 1
481             float2 grad = exp_minus1 * derivatives;
482             float fwidth = abs(grad.x) + abs(grad.y) + 1e-12;  // 1e-12 to avoid a divide by zero.
483             return half4(saturate(.5 - f/fwidth)); // Approx coverage by riding the gradient to f=0.
484         }
485     )"), fPower(power) {}
486 
drawSuperRRect(SkCanvas * canvas,const SkRect & superRRect,float radX,float radY,SkColor color)487     void drawSuperRRect(SkCanvas* canvas, const SkRect& superRRect, float radX, float radY,
488                         SkColor color) {
489         SkPaint paint;
490         paint.setColor(color);
491 
492         if (fPower == 2) {
493             // Draw a normal round rect for the sake of testing.
494             SkRRect rrect = SkRRect::MakeRectXY(superRRect, radX, radY);
495             paint.setAntiAlias(true);
496             canvas->drawRRect(rrect, paint);
497             return;
498         }
499 
500         SkRuntimeShaderBuilder builder(fEffect);
501         builder.uniform("power_minus1") = fPower - 1;
502 
503         // Size the corners such that the "apex" of our "super" rounded corner is in the same
504         // location that the apex of a circular rounded corner would be with the given radii. We
505         // define the apex as the point on the rounded corner that is 45 degrees between the
506         // horizontal and vertical edges.
507         float scale = (1 - SK_ScalarRoot2Over2) / (1 - exp2f(-1/fPower));
508         float cornerWidth = radX * scale;
509         float cornerHeight = radY * scale;
510         cornerWidth = std::min(cornerWidth, superRRect.width() * .5f);
511         cornerHeight = std::min(cornerHeight, superRRect.height() * .5f);
512         // The stretch factor controls how long the flat edge should be between rounded corners.
513         builder.uniform("stretch_factor") = SkV2{1 - superRRect.width()*.5f / cornerWidth,
514                                                  1 - superRRect.height()*.5f / cornerHeight};
515 
516         // Calculate a 2x2 "derivatives" matrix that the shader will use to find the gradient.
517         //
518         //     f = s^n + t^n - 1   [s,t are "super" rounded corner coords in normalized 0..1 space]
519         //
520         //     gradient = [df/dx  df/dy] = [ns^(n-1)  nt^(n-1)] * |ds/dx  ds/dy|
521         //                                                        |dt/dx  dt/dy|
522         //
523         //              = [s^(n-1)  t^(n-1)] * |n  0| * |ds/dx  ds/dy|
524         //                                     |0  n|   |dt/dx  dt/dy|
525         //
526         //              = [s^(n-1)  t^(n-1)] * |2n/cornerWidth   0| * mat2x2(canvasMatrix)^-1
527         //                                     |0  2n/cornerHeight|
528         //
529         //              = [s^(n-1)  t^(n-1)] * "derivatives"
530         //
531         const SkMatrix& M = canvas->getTotalMatrix();
532         float a=M.getScaleX(), b=M.getSkewX(), c=M.getSkewY(), d=M.getScaleY();
533         float determinant = a*d - b*c;
534         float dx = fPower / (cornerWidth * determinant);
535         float dy = fPower / (cornerHeight * determinant);
536         builder.uniform("derivatives") = SkV4{d*dx, -c*dy, -b*dx, a*dy};
537 
538         // This matrix will be inverted by the effect system, giving a matrix that converts local
539         // coordinates to (almost) coner coordinates. To get the rest of the way to the nearest
540         // corner's space, the shader will have to take the absolute value, add the stretch_factor,
541         // then clamp above zero.
542         SkMatrix cornerToLocal;
543         cornerToLocal.setScaleTranslate(cornerWidth, cornerHeight, superRRect.centerX(),
544                                         superRRect.centerY());
545         canvas->clipShader(builder.makeShader(&cornerToLocal, false));
546 
547         // Bloat the outer edges of the rect we will draw so it contains all the antialiased pixels.
548         // Bloat by a full pixel instead of half in case Skia is in a mode that draws this rect with
549         // unexpected AA of its own.
550         float inverseDet = 1 / fabsf(determinant);
551         float bloatX = (fabsf(d) + fabsf(c)) * inverseDet;
552         float bloatY = (fabsf(b) + fabsf(a)) * inverseDet;
553         canvas->drawRect(superRRect.makeOutset(bloatX, bloatY), paint);
554     }
555 
onDraw(SkCanvas * canvas)556     void onDraw(SkCanvas* canvas) override {
557         SkRandom rand(2);
558 
559         canvas->save();
560         canvas->translate(canvas->imageInfo().width() / 2.f, canvas->imageInfo().height() / 2.f);
561 
562         canvas->save();
563         canvas->rotate(21);
564         this->drawSuperRRect(canvas, SkRect::MakeXYWH(-5, 25, 175, 100), 50, 30,
565                              rand.nextU() | 0xff808080);
566         canvas->restore();
567 
568         canvas->save();
569         canvas->rotate(94);
570         this->drawSuperRRect(canvas, SkRect::MakeXYWH(95, 75, 125, 100), 30, 30,
571                              rand.nextU() | 0xff808080);
572         canvas->restore();
573 
574         canvas->save();
575         canvas->rotate(132);
576         this->drawSuperRRect(canvas, SkRect::MakeXYWH(0, 75, 150, 100), 40, 30,
577                              rand.nextU() | 0xff808080);
578         canvas->restore();
579 
580         canvas->save();
581         canvas->rotate(282);
582         this->drawSuperRRect(canvas, SkRect::MakeXYWH(15, -20, 100, 100), 20, 20,
583                              rand.nextU() | 0xff808080);
584         canvas->restore();
585 
586         canvas->save();
587         canvas->rotate(0);
588         this->drawSuperRRect(canvas, SkRect::MakeXYWH(140, -50, 90, 110), 25, 25,
589                              rand.nextU() | 0xff808080);
590         canvas->restore();
591 
592         canvas->save();
593         canvas->rotate(-35);
594         this->drawSuperRRect(canvas, SkRect::MakeXYWH(160, -60, 60, 90), 18, 18,
595                              rand.nextU() | 0xff808080);
596         canvas->restore();
597 
598         canvas->save();
599         canvas->rotate(65);
600         this->drawSuperRRect(canvas, SkRect::MakeXYWH(220, -120, 60, 90), 18, 18,
601                              rand.nextU() | 0xff808080);
602         canvas->restore();
603 
604         canvas->save();
605         canvas->rotate(265);
606         this->drawSuperRRect(canvas, SkRect::MakeXYWH(150, -129, 80, 160), 24, 39,
607                              rand.nextU() | 0xff808080);
608         canvas->restore();
609 
610         canvas->restore();
611     }
612 
613 private:
614     const float fPower;
615 };
616 DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow2", 2);)
617 // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow3", 3);)
618 DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow3.5", 3.5);)
619 // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow4", 4);)
620 // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow4.5", 4.5);)
621 // DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow5", 5);)
622 
623 DEF_SIMPLE_GM(child_sampling_rt, canvas, 256,256) {
624     static constexpr char scale[] =
625         "uniform shader child;"
626         "half4 main(float2 xy) {"
627         "    return child.eval(xy*0.1);"
628         "}";
629 
630     SkPaint p;
631     p.setColor(SK_ColorRED);
632     p.setAntiAlias(true);
633     p.setStyle(SkPaint::kStroke_Style);
634     p.setStrokeWidth(1);
635 
636     auto surf = SkSurface::MakeRasterN32Premul(100,100);
637     surf->getCanvas()->drawLine(0, 0, 100, 100, p);
638     auto shader = surf->makeImageSnapshot()->makeShader(SkSamplingOptions(SkFilterMode::kLinear));
639 
640     SkRuntimeShaderBuilder builder(SkRuntimeEffect::MakeForShader(SkString(scale)).effect);
641     builder.child("child") = shader;
642     p.setShader(builder.makeShader(nullptr, false));
643 
644     canvas->drawPaint(p);
645 }
646 
normal_map_shader()647 static sk_sp<SkShader> normal_map_shader() {
648     // Produces a hemispherical normal:
649     static const char* kSrc = R"(
650         half4 main(vec2 p) {
651             p = (p / 256) * 2 - 1;
652             float p2 = dot(p, p);
653             vec3 v = (p2 > 1) ? vec3(0, 0, 1) : vec3(p, sqrt(1 - p2));
654             return (v * 0.5 + 0.5).xyz1;
655         }
656     )";
657     auto effect = SkRuntimeEffect::MakeForShader(SkString(kSrc)).effect;
658     return effect->makeShader(nullptr, {}, nullptr, true);
659 }
660 
normal_map_image_shader()661 static sk_sp<SkShader> normal_map_image_shader() {
662     // Above, baked into an image:
663     auto surface = SkSurface::MakeRasterN32Premul(256, 256);
664     SkPaint p;
665     p.setShader(normal_map_shader());
666     surface->getCanvas()->drawPaint(p);
667     auto image = surface->makeImageSnapshot();
668     return image->makeShader(SkSamplingOptions{});
669 }
670 
lit_shader(sk_sp<SkShader> normals)671 static sk_sp<SkShader> lit_shader(sk_sp<SkShader> normals) {
672     // Simple N.L against a fixed, directional light:
673     static const char* kSrc = R"(
674         uniform shader normals;
675         half4 main(vec2 p) {
676             vec3 n = normalize(normals.eval(p).xyz * 2 - 1);
677             vec3 l = normalize(vec3(1, -1, 1));
678             return saturate(dot(n, l)).xxx1;
679         }
680     )";
681     auto effect = SkRuntimeEffect::MakeForShader(SkString(kSrc)).effect;
682     return effect->makeShader(nullptr, &normals, 1, nullptr, true);
683 }
684 
685 DEF_SIMPLE_GM(paint_alpha_normals_rt, canvas, 512,512) {
686     // Various draws, with non-opaque paint alpha. This demonstrates several issues around how
687     // paint alpha is applied differently on CPU (globally, after all shaders) and GPU (per shader,
688     // inconsistently). See: skbug.com/11942
689     //
690     // When this works, it will be a demo of applying paint alpha to fade out a complex effect.
__anon0ac199ea0202(int x, int y, sk_sp<SkShader> shader) 691     auto draw_shader = [=](int x, int y, sk_sp<SkShader> shader) {
692         SkPaint p;
693         p.setAlpha(164);
694         p.setShader(shader);
695 
696         canvas->save();
697         canvas->translate(x, y);
698         canvas->clipRect({0, 0, 256, 256});
699         canvas->drawPaint(p);
700         canvas->restore();
701     };
702 
703     draw_shader(0, 0, normal_map_shader());
704     draw_shader(0, 256, normal_map_image_shader());
705 
706     draw_shader(256, 0, lit_shader(normal_map_shader()));
707     draw_shader(256, 256, lit_shader(normal_map_image_shader()));
708 }
709