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