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