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