xref: /third_party/skia/gm/runtimeshader.cpp (revision cb93a386)
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
22enum RT_Flags {
23    kAnimate_RTFlag     = 0x1,
24    kBench_RTFlag       = 0x2,
25    kColorFilter_RTFlag = 0x4,
26};
27
28class RuntimeShaderGM : public skiagm::GM {
29public:
30    RuntimeShaderGM(const char* name, SkISize size, const char* sksl, uint32_t flags = 0)
31            : fName(name), fSize(size), fFlags(flags), fSkSL(sksl) {}
32
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
43    bool runAsBench() const override { return SkToBool(fFlags & kBench_RTFlag); }
44    SkString onShortName() override { return fName; }
45    SkISize onISize() override { return fSize; }
46
47    bool onAnimate(double nanos) override {
48        fSecs = nanos / (1000 * 1000 * 1000);
49        return SkToBool(fFlags & kAnimate_RTFlag);
50    }
51
52protected:
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
62class SimpleRT : public RuntimeShaderGM {
63public:
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
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};
84DEF_GM(return new SimpleRT;)
85
86static 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
92static 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
124class ThresholdRT : public RuntimeShaderGM {
125public:
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
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
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};
185DEF_GM(return new ThresholdRT;)
186
187class SpiralRT : public RuntimeShaderGM {
188public:
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
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};
220DEF_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.
226class UnsharpRT : public RuntimeShaderGM {
227public:
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
242    void onOnceBeforeDraw() override {
243        fMandrill      = GetResourceAsImage("images/mandrill_256.png");
244        this->RuntimeShaderGM::onOnceBeforeDraw();
245    }
246
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};
262DEF_GM(return new UnsharpRT;)
263
264class ColorCubeRT : public RuntimeShaderGM {
265public:
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
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
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};
344DEF_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).
348class ColorCubeColorFilterRT : public RuntimeShaderGM {
349public:
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
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
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};
425DEF_GM(return new ColorCubeColorFilterRT;)
426
427class DefaultColorRT : public RuntimeShaderGM {
428public:
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
438    void onOnceBeforeDraw() override {
439        fMandrill      = GetResourceAsImage("images/mandrill_256.png");
440        this->RuntimeShaderGM::onOnceBeforeDraw();
441    }
442
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};
461DEF_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
471class ClipSuperRRect : public RuntimeShaderGM {
472public:
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
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
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
613private:
614    const float fPower;
615};
616DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow2", 2);)
617// DEF_GM(return new ClipSuperRRect("clip_super_rrect_pow3", 3);)
618DEF_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
623DEF_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
647static 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
661static 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
671static 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
685DEF_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.
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