1 /* 2 * Copyright 2011 Google Inc. 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.h" 9 #include "sk_tool_utils.h" 10 #include "SkGradientShader.h" 11 #include "SkLinearGradient.h" 12 13 namespace skiagm { 14 15 struct GradData { 16 int fCount; 17 const SkColor* fColors; 18 const SkColor4f* fColors4f; 19 const SkScalar* fPos; 20 }; 21 22 constexpr SkColor gColors[] = { 23 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK 24 }; 25 constexpr SkColor4f gColors4f[] ={ 26 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red 27 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green 28 { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue 29 { 1.0f, 1.0f, 1.0f, 1.0f }, // White 30 { 0.0f, 0.0f, 0.0f, 1.0f } // Black 31 }; 32 constexpr SkScalar gPos0[] = { 0, SK_Scalar1 }; 33 constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; 34 constexpr SkScalar gPos2[] = { 35 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 36 }; 37 38 constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f}; 39 constexpr SkColor gColorClamp[] = { 40 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE 41 }; 42 constexpr SkColor4f gColor4fClamp[] ={ 43 { 1.0f, 0.0f, 0.0f, 1.0f }, // Red 44 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green 45 { 0.0f, 1.0f, 0.0f, 1.0f }, // Green 46 { 0.0f, 0.0f, 1.0f, 1.0f } // Blue 47 }; 48 constexpr GradData gGradData[] = { 49 { 2, gColors, gColors4f, nullptr }, 50 { 2, gColors, gColors4f, gPos0 }, 51 { 2, gColors, gColors4f, gPos1 }, 52 { 5, gColors, gColors4f, nullptr }, 53 { 5, gColors, gColors4f, gPos2 }, 54 { 4, gColorClamp, gColor4fClamp, gPosClamp } 55 }; 56 57 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data, 58 SkShader::TileMode tm, const SkMatrix& localMatrix) { 59 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0, 60 &localMatrix); 61 } 62 63 static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data, 64 SkShader::TileMode tm, const SkMatrix& localMatrix) { 65 auto srgb = SkColorSpace::MakeSRGBLinear(); 66 return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0, 67 &localMatrix); 68 } 69 70 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data, 71 SkShader::TileMode tm, const SkMatrix& localMatrix) { 72 SkPoint center; 73 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 74 SkScalarAve(pts[0].fY, pts[1].fY)); 75 return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount, 76 tm, 0, &localMatrix); 77 } 78 79 static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data, 80 SkShader::TileMode tm, const SkMatrix& localMatrix) { 81 SkPoint center; 82 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 83 SkScalarAve(pts[0].fY, pts[1].fY)); 84 auto srgb = SkColorSpace::MakeSRGBLinear(); 85 return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos, 86 data.fCount, tm, 0, &localMatrix); 87 } 88 89 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data, 90 SkShader::TileMode, const SkMatrix& localMatrix) { 91 SkPoint center; 92 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 93 SkScalarAve(pts[0].fY, pts[1].fY)); 94 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount, 95 0, &localMatrix); 96 } 97 98 static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data, 99 SkShader::TileMode, const SkMatrix& localMatrix) { 100 SkPoint center; 101 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 102 SkScalarAve(pts[0].fY, pts[1].fY)); 103 auto srgb = SkColorSpace::MakeSRGBLinear(); 104 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos, 105 data.fCount, 0, &localMatrix); 106 } 107 108 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data, 109 SkShader::TileMode tm, const SkMatrix& localMatrix) { 110 SkPoint center0, center1; 111 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 112 SkScalarAve(pts[0].fY, pts[1].fY)); 113 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 114 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 115 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, 116 center0, (pts[1].fX - pts[0].fX) / 2, 117 data.fColors, data.fPos, data.fCount, tm, 118 0, &localMatrix); 119 } 120 121 static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data, 122 SkShader::TileMode tm, const SkMatrix& localMatrix) { 123 SkPoint center0, center1; 124 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 125 SkScalarAve(pts[0].fY, pts[1].fY)); 126 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5), 127 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4)); 128 auto srgb = SkColorSpace::MakeSRGBLinear(); 129 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, 130 center0, (pts[1].fX - pts[0].fX) / 2, 131 data.fColors4f, srgb, data.fPos, data.fCount, tm, 132 0, &localMatrix); 133 } 134 135 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data, 136 SkShader::TileMode tm, const SkMatrix& localMatrix) { 137 SkPoint center0, center1; 138 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 139 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 140 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 141 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 142 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, 143 data.fColors, data.fPos, 144 data.fCount, tm, 0, &localMatrix); 145 } 146 147 static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data, 148 SkShader::TileMode tm, const SkMatrix& localMatrix) { 149 SkPoint center0, center1; 150 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 151 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 152 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 153 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 154 auto srgb = SkColorSpace::MakeSRGBLinear(); 155 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, 156 data.fColors4f, srgb, data.fPos, 157 data.fCount, tm, 0, &localMatrix); 158 } 159 160 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data, 161 SkShader::TileMode tm, const SkMatrix& localMatrix); 162 constexpr GradMaker gGradMakers[] = { 163 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical 164 }; 165 constexpr GradMaker gGradMakers4f[] ={ 166 MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f 167 }; 168 169 /////////////////////////////////////////////////////////////////////////////// 170 171 class GradientsGM : public GM { 172 public: 173 GradientsGM(bool dither) : fDither(dither) { 174 this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD)); 175 } 176 177 protected: 178 179 SkString onShortName() { 180 return SkString(fDither ? "gradients" : "gradients_nodither"); 181 } 182 183 virtual SkISize onISize() { return SkISize::Make(840, 815); } 184 185 virtual void onDraw(SkCanvas* canvas) { 186 187 SkPoint pts[2] = { 188 { 0, 0 }, 189 { SkIntToScalar(100), SkIntToScalar(100) } 190 }; 191 SkShader::TileMode tm = SkShader::kClamp_TileMode; 192 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 193 SkPaint paint; 194 paint.setAntiAlias(true); 195 paint.setDither(fDither); 196 197 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 198 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 199 canvas->save(); 200 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 201 SkMatrix scale = SkMatrix::I(); 202 203 if (i == 5) { // if the clamp case 204 scale.setScale(0.5f, 0.5f); 205 scale.postTranslate(25.f, 25.f); 206 } 207 208 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale)); 209 canvas->drawRect(r, paint); 210 canvas->translate(0, SkIntToScalar(120)); 211 } 212 canvas->restore(); 213 canvas->translate(SkIntToScalar(120), 0); 214 } 215 } 216 217 protected: 218 bool fDither; 219 220 private: 221 typedef GM INHERITED; 222 }; 223 DEF_GM( return new GradientsGM(true); ) 224 DEF_GM( return new GradientsGM(false); ) 225 226 // Like the original gradients GM, but using the SkColor4f shader factories. Should be identical. 227 class Gradients4fGM : public GM { 228 public: 229 Gradients4fGM(bool dither) : fDither(dither) { 230 this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD)); 231 } 232 233 protected: 234 235 SkString onShortName() { 236 return SkString(fDither ? "gradients4f" : "gradients4f_nodither"); 237 } 238 239 virtual SkISize onISize() { return SkISize::Make(840, 815); } 240 241 virtual void onDraw(SkCanvas* canvas) { 242 243 SkPoint pts[2] ={ 244 { 0, 0 }, 245 { SkIntToScalar(100), SkIntToScalar(100) } 246 }; 247 SkShader::TileMode tm = SkShader::kClamp_TileMode; 248 SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 249 SkPaint paint; 250 paint.setAntiAlias(true); 251 paint.setDither(fDither); 252 253 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 254 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 255 canvas->save(); 256 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) { 257 SkMatrix scale = SkMatrix::I(); 258 259 if (i == 5) { // if the clamp case 260 scale.setScale(0.5f, 0.5f); 261 scale.postTranslate(25.f, 25.f); 262 } 263 264 paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale)); 265 canvas->drawRect(r, paint); 266 canvas->translate(0, SkIntToScalar(120)); 267 } 268 canvas->restore(); 269 canvas->translate(SkIntToScalar(120), 0); 270 } 271 } 272 273 protected: 274 bool fDither; 275 276 private: 277 typedef GM INHERITED; 278 }; 279 DEF_GM(return new Gradients4fGM(true); ) 280 DEF_GM(return new Gradients4fGM(false); ) 281 282 // Based on the original gradient slide, but with perspective applied to the 283 // gradient shaders' local matrices 284 class GradientsLocalPerspectiveGM : public GM { 285 public: 286 GradientsLocalPerspectiveGM(bool dither) : fDither(dither) { 287 this->setBGColor(sk_tool_utils::color_to_565(0xFFDDDDDD)); 288 } 289 290 protected: 291 292 SkString onShortName() { 293 return SkString(fDither ? "gradients_local_perspective" : 294 "gradients_local_perspective_nodither"); 295 } 296 297 virtual SkISize onISize() { return SkISize::Make(840, 815); } 298 299 virtual void onDraw(SkCanvas* canvas) { 300 301 SkPoint pts[2] = { 302 { 0, 0 }, 303 { SkIntToScalar(100), SkIntToScalar(100) } 304 }; 305 SkShader::TileMode tm = SkShader::kClamp_TileMode; 306 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 307 SkPaint paint; 308 paint.setAntiAlias(true); 309 paint.setDither(fDither); 310 311 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 312 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 313 canvas->save(); 314 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 315 // apply an increasing y perspective as we move to the right 316 SkMatrix perspective; 317 perspective.setIdentity(); 318 perspective.setPerspY(SkIntToScalar(i+1) / 500); 319 perspective.setSkewX(SkIntToScalar(i+1) / 10); 320 321 paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective)); 322 canvas->drawRect(r, paint); 323 canvas->translate(0, SkIntToScalar(120)); 324 } 325 canvas->restore(); 326 canvas->translate(SkIntToScalar(120), 0); 327 } 328 } 329 330 private: 331 bool fDither; 332 333 typedef GM INHERITED; 334 }; 335 DEF_GM( return new GradientsLocalPerspectiveGM(true); ) 336 DEF_GM( return new GradientsLocalPerspectiveGM(false); ) 337 338 // Based on the original gradient slide, but with perspective applied to 339 // the view matrix 340 class GradientsViewPerspectiveGM : public GradientsGM { 341 public: 342 GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { } 343 344 protected: 345 SkString onShortName() { 346 return SkString(fDither ? "gradients_view_perspective" : 347 "gradients_view_perspective_nodither"); 348 } 349 350 virtual SkISize onISize() { return SkISize::Make(840, 500); } 351 352 virtual void onDraw(SkCanvas* canvas) { 353 SkMatrix perspective; 354 perspective.setIdentity(); 355 perspective.setPerspY(0.001f); 356 perspective.setSkewX(SkIntToScalar(8) / 25); 357 canvas->concat(perspective); 358 INHERITED::onDraw(canvas); 359 } 360 361 private: 362 typedef GradientsGM INHERITED; 363 }; 364 DEF_GM( return new GradientsViewPerspectiveGM(true); ) 365 DEF_GM( return new GradientsViewPerspectiveGM(false); ) 366 367 /* 368 Inspired by this <canvas> javascript, where we need to detect that we are not 369 solving a quadratic equation, but must instead solve a linear (since our X^2 370 coefficient is 0) 371 372 ctx.fillStyle = '#f00'; 373 ctx.fillRect(0, 0, 100, 50); 374 375 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150); 376 g.addColorStop(0, '#f00'); 377 g.addColorStop(0.01, '#0f0'); 378 g.addColorStop(0.99, '#0f0'); 379 g.addColorStop(1, '#f00'); 380 ctx.fillStyle = g; 381 ctx.fillRect(0, 0, 100, 50); 382 */ 383 class GradientsDegenrate2PointGM : public GM { 384 public: 385 GradientsDegenrate2PointGM(bool dither) : fDither(dither) {} 386 387 protected: 388 SkString onShortName() { 389 return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither"); 390 } 391 392 virtual SkISize onISize() { return SkISize::Make(320, 320); } 393 394 void drawBG(SkCanvas* canvas) { 395 canvas->drawColor(SK_ColorBLUE); 396 } 397 398 virtual void onDraw(SkCanvas* canvas) { 399 this->drawBG(canvas); 400 401 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED }; 402 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 }; 403 SkPoint c0; 404 c0.iset(-80, 25); 405 SkScalar r0 = SkIntToScalar(70); 406 SkPoint c1; 407 c1.iset(0, 25); 408 SkScalar r1 = SkIntToScalar(150); 409 SkPaint paint; 410 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, 411 pos, SK_ARRAY_COUNT(pos), 412 SkShader::kClamp_TileMode)); 413 paint.setDither(fDither); 414 canvas->drawPaint(paint); 415 } 416 417 private: 418 bool fDither; 419 420 typedef GM INHERITED; 421 }; 422 DEF_GM( return new GradientsDegenrate2PointGM(true); ) 423 DEF_GM( return new GradientsDegenrate2PointGM(false); ) 424 425 /* bug.skia.org/517 426 <canvas id="canvas"></canvas> 427 <script> 428 var c = document.getElementById("canvas"); 429 var ctx = c.getContext("2d"); 430 ctx.fillStyle = '#ff0'; 431 ctx.fillRect(0, 0, 100, 50); 432 433 var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10); 434 g.addColorStop(0, '#0f0'); 435 g.addColorStop(0.003, '#f00'); // 0.004 makes this work 436 g.addColorStop(1, '#ff0'); 437 ctx.fillStyle = g; 438 ctx.fillRect(0, 0, 100, 50); 439 </script> 440 */ 441 442 // should draw only green 443 DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) { 444 SkColor colors[] = { SK_ColorGREEN, SK_ColorRED, SK_ColorYELLOW }; 445 SkScalar pos[] = { 0, 0.003f, SK_Scalar1 }; // 0.004f makes this work 446 SkPoint c0 = { 200, 25 }; 447 SkScalar r0 = 20; 448 SkPoint c1 = { 200, 25 }; 449 SkScalar r1 = 10; 450 451 SkPaint paint; 452 paint.setColor(SK_ColorYELLOW); 453 canvas->drawRect(SkRect::MakeWH(100, 150), paint); 454 paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos, 455 SK_ARRAY_COUNT(pos), 456 SkShader::kClamp_TileMode)); 457 canvas->drawRect(SkRect::MakeWH(100, 150), paint); 458 } 459 460 461 /// Tests correctness of *optimized* codepaths in gradients. 462 463 class ClampedGradientsGM : public GM { 464 public: 465 ClampedGradientsGM(bool dither) : fDither(dither) {} 466 467 protected: 468 SkString onShortName() { 469 return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither"); 470 } 471 472 virtual SkISize onISize() { return SkISize::Make(640, 510); } 473 474 void drawBG(SkCanvas* canvas) { 475 canvas->drawColor(sk_tool_utils::color_to_565(0xFFDDDDDD)); 476 } 477 478 virtual void onDraw(SkCanvas* canvas) { 479 this->drawBG(canvas); 480 481 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) }; 482 SkPaint paint; 483 paint.setDither(fDither); 484 paint.setAntiAlias(true); 485 486 SkPoint center; 487 center.iset(0, 300); 488 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 489 paint.setShader(SkGradientShader::MakeRadial( 490 SkPoint(center), 491 SkIntToScalar(200), gColors, nullptr, 5, 492 SkShader::kClamp_TileMode)); 493 canvas->drawRect(r, paint); 494 } 495 496 private: 497 bool fDither; 498 499 typedef GM INHERITED; 500 }; 501 DEF_GM( return new ClampedGradientsGM(true); ) 502 DEF_GM( return new ClampedGradientsGM(false); ) 503 504 /// Checks quality of large radial gradients, which may display 505 /// some banding. 506 507 class RadialGradientGM : public GM { 508 public: 509 RadialGradientGM() {} 510 511 protected: 512 513 SkString onShortName() override { return SkString("radial_gradient"); } 514 SkISize onISize() override { return SkISize::Make(1280, 1280); } 515 void drawBG(SkCanvas* canvas) { 516 canvas->drawColor(0xFF000000); 517 } 518 void onDraw(SkCanvas* canvas) override { 519 const SkISize dim = this->getISize(); 520 521 this->drawBG(canvas); 522 523 SkPaint paint; 524 paint.setDither(true); 525 SkPoint center; 526 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2); 527 SkScalar radius = SkIntToScalar(dim.width())/2; 528 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 }; 529 const SkScalar pos[] = { 0.0f, 530 0.35f, 531 1.0f }; 532 paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos, 533 SK_ARRAY_COUNT(pos), 534 SkShader::kClamp_TileMode)); 535 SkRect r = { 536 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height()) 537 }; 538 canvas->drawRect(r, paint); 539 } 540 private: 541 typedef GM INHERITED; 542 }; 543 DEF_GM( return new RadialGradientGM; ) 544 545 class RadialGradient2GM : public GM { 546 public: 547 RadialGradient2GM(bool dither) : fDither(dither) {} 548 549 protected: 550 551 SkString onShortName() override { 552 return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither"); 553 } 554 555 SkISize onISize() override { return SkISize::Make(800, 400); } 556 void drawBG(SkCanvas* canvas) { 557 canvas->drawColor(0xFF000000); 558 } 559 560 // Reproduces the example given in bug 7671058. 561 void onDraw(SkCanvas* canvas) override { 562 SkPaint paint1, paint2, paint3; 563 paint1.setStyle(SkPaint::kFill_Style); 564 paint2.setStyle(SkPaint::kFill_Style); 565 paint3.setStyle(SkPaint::kFill_Style); 566 567 const SkColor sweep_colors[] = 568 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 }; 569 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 }; 570 const SkColor colors2[] = { 0xFF000000, 0x00000000 }; 571 572 const SkScalar cx = 200, cy = 200, radius = 150; 573 SkPoint center; 574 center.set(cx, cy); 575 576 // We can either interpolate endpoints and premultiply each point (default, more precision), 577 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap). 578 const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag }; 579 580 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) { 581 paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors, 582 nullptr, SK_ARRAY_COUNT(sweep_colors), 583 flags[i], nullptr)); 584 paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1, 585 nullptr, SK_ARRAY_COUNT(colors1), 586 SkShader::kClamp_TileMode, 587 flags[i], nullptr)); 588 paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2, 589 nullptr, SK_ARRAY_COUNT(colors2), 590 SkShader::kClamp_TileMode, 591 flags[i], nullptr)); 592 paint1.setDither(fDither); 593 paint2.setDither(fDither); 594 paint3.setDither(fDither); 595 596 canvas->drawCircle(cx, cy, radius, paint1); 597 canvas->drawCircle(cx, cy, radius, paint3); 598 canvas->drawCircle(cx, cy, radius, paint2); 599 600 canvas->translate(400, 0); 601 } 602 } 603 604 private: 605 bool fDither; 606 607 typedef GM INHERITED; 608 }; 609 DEF_GM( return new RadialGradient2GM(true); ) 610 DEF_GM( return new RadialGradient2GM(false); ) 611 612 // Shallow radial (shows banding on raster) 613 class RadialGradient3GM : public GM { 614 public: 615 RadialGradient3GM(bool dither) : fDither(dither) { } 616 617 protected: 618 SkString onShortName() override { 619 return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither"); 620 } 621 622 SkISize onISize() override { return SkISize::Make(500, 500); } 623 624 bool runAsBench() const override { return true; } 625 626 void onOnceBeforeDraw() override { 627 const SkPoint center = { 0, 0 }; 628 const SkScalar kRadius = 3000; 629 const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 }; 630 fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2, 631 SkShader::kClamp_TileMode); 632 } 633 634 void onDraw(SkCanvas* canvas) override { 635 SkPaint paint; 636 paint.setShader(fShader); 637 paint.setDither(fDither); 638 canvas->drawRect(SkRect::MakeWH(500, 500), paint); 639 } 640 641 private: 642 sk_sp<SkShader> fShader; 643 bool fDither; 644 645 typedef GM INHERITED; 646 }; 647 DEF_GM( return new RadialGradient3GM(true); ) 648 DEF_GM( return new RadialGradient3GM(false); ) 649 650 class RadialGradient4GM : public GM { 651 public: 652 RadialGradient4GM(bool dither) : fDither(dither) { } 653 654 protected: 655 SkString onShortName() override { 656 return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither"); 657 } 658 659 SkISize onISize() override { return SkISize::Make(500, 500); } 660 661 void onOnceBeforeDraw() override { 662 const SkPoint center = { 250, 250 }; 663 const SkScalar kRadius = 250; 664 const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE, 665 SK_ColorRED }; 666 const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 }; 667 fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos, 668 SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode); 669 } 670 671 void onDraw(SkCanvas* canvas) override { 672 SkPaint paint; 673 paint.setAntiAlias(true); 674 paint.setDither(fDither); 675 paint.setShader(fShader); 676 canvas->drawRect(SkRect::MakeWH(500, 500), paint); 677 } 678 679 private: 680 sk_sp<SkShader> fShader; 681 bool fDither; 682 683 typedef GM INHERITED; 684 }; 685 DEF_GM( return new RadialGradient4GM(true); ) 686 DEF_GM( return new RadialGradient4GM(false); ) 687 688 class LinearGradientGM : public GM { 689 public: 690 LinearGradientGM(bool dither) : fDither(dither) { } 691 692 protected: 693 SkString onShortName() override { 694 return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither"); 695 } 696 697 const SkScalar kWidthBump = 30.f; 698 const SkScalar kHeight = 5.f; 699 const SkScalar kMinWidth = 540.f; 700 701 SkISize onISize() override { return SkISize::Make(500, 500); } 702 703 void onOnceBeforeDraw() override { 704 SkPoint pts[2] = { {0, 0}, {0, 0} }; 705 const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200, 706 SK_ColorWHITE, SK_ColorWHITE }; 707 const SkScalar unitPos[] = { 0, 50, 70, 500, 540 }; 708 SkScalar pos[6]; 709 pos[5] = 1; 710 for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) { 711 pts[1].fX = 500.f + index * kWidthBump; 712 for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) { 713 pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump); 714 } 715 fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos, 716 SK_ARRAY_COUNT(gColors), SkShader::kClamp_TileMode); 717 } 718 } 719 720 void onDraw(SkCanvas* canvas) override { 721 SkPaint paint; 722 paint.setAntiAlias(true); 723 paint.setDither(fDither); 724 for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) { 725 paint.setShader(fShader[index]); 726 canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump, 727 (index + 1) * kHeight), paint); 728 } 729 } 730 731 private: 732 sk_sp<SkShader> fShader[100]; 733 bool fDither; 734 735 typedef GM INHERITED; 736 }; 737 DEF_GM( return new LinearGradientGM(true); ) 738 DEF_GM( return new LinearGradientGM(false); ) 739 740 class LinearGradientTinyGM : public GM { 741 public: 742 LinearGradientTinyGM(uint32_t flags, const char* suffix = nullptr) 743 : fName("linear_gradient_tiny") 744 , fFlags(flags) { 745 fName.append(suffix); 746 } 747 748 protected: 749 SkString onShortName() override { 750 return fName; 751 } 752 753 SkISize onISize() override { 754 return SkISize::Make(600, 500); 755 } 756 757 void onDraw(SkCanvas* canvas) override { 758 const SkScalar kRectSize = 100; 759 const unsigned kStopCount = 3; 760 const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN }; 761 const struct { 762 SkPoint pts[2]; 763 SkScalar pos[kStopCount]; 764 } configs[] = { 765 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999f, 1 }}, 766 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000001f, 1 }}, 767 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999999f, 1 }}, 768 { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000000001f, 1 }}, 769 770 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999f, 1 }}, 771 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000001f, 1 }}, 772 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999999f, 1 }}, 773 { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000000001f, 1 }}, 774 775 { { SkPoint::Make(0, 0), SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }}, 776 { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) }, { 0, 0.5f, 1 }}, 777 { { SkPoint::Make(0, 0), SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }}, 778 { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) }, { 0, 0.5f, 1 }}, 779 }; 780 781 SkPaint paint; 782 for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) { 783 SkAutoCanvasRestore acr(canvas, true); 784 paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos, 785 kStopCount, SkShader::kClamp_TileMode, 786 fFlags, nullptr)); 787 canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f), 788 kRectSize * ((i / 4) * 1.5f + 0.25f)); 789 790 canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint); 791 } 792 } 793 794 private: 795 typedef GM INHERITED; 796 797 SkString fName; 798 uint32_t fFlags; 799 }; 800 DEF_GM( return new LinearGradientTinyGM(0); ) 801 DEF_GM( return new LinearGradientTinyGM(SkLinearGradient::kForce4fContext_PrivateFlag, "_4f"); ) 802 } 803 804 /////////////////////////////////////////////////////////////////////////////////////////////////// 805 806 struct GradRun { 807 SkColor fColors[4]; 808 SkScalar fPos[4]; 809 int fCount; 810 }; 811 812 #define SIZE 121 813 814 static sk_sp<SkShader> make_linear(const GradRun& run, SkShader::TileMode mode) { 815 const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } }; 816 return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode); 817 } 818 819 static sk_sp<SkShader> make_radial(const GradRun& run, SkShader::TileMode mode) { 820 const SkScalar half = SIZE * 0.5f; 821 return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos, 822 run.fCount, mode); 823 } 824 825 static sk_sp<SkShader> make_conical(const GradRun& run, SkShader::TileMode mode) { 826 const SkScalar half = SIZE * 0.5f; 827 const SkPoint center { half, half }; 828 return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10, 829 run.fColors, run.fPos, run.fCount, mode); 830 } 831 832 static sk_sp<SkShader> make_sweep(const GradRun& run, SkShader::TileMode) { 833 const SkScalar half = SIZE * 0.5f; 834 return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount); 835 } 836 837 /* 838 * Exercise duplicate color-stops, at the ends, and in the middle 839 * 840 * At the time of this writing, only Linear correctly deals with duplicates at the ends, 841 * and then only correctly on CPU backend. 842 */ 843 DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) { 844 const SkColor preColor = 0xFFFF0000; // clamp color before start 845 const SkColor postColor = 0xFF0000FF; // clamp color after end 846 const SkColor color0 = 0xFF000000; 847 const SkColor color1 = 0xFF00FF00; 848 const SkColor badColor = 0xFF3388BB; // should never be seen, fills out fixed-size array 849 850 const GradRun runs[] = { 851 { { color0, color1, badColor, badColor }, 852 { 0, 1, -1, -1 }, 853 2, 854 }, 855 { { preColor, color0, color1, badColor }, 856 { 0, 0, 1, -1 }, 857 3, 858 }, 859 { { color0, color1, postColor, badColor }, 860 { 0, 1, 1, -1 }, 861 3, 862 }, 863 { { preColor, color0, color1, postColor }, 864 { 0, 0, 1, 1 }, 865 4, 866 }, 867 { { color0, color0, color1, color1 }, 868 { 0, 0.5f, 0.5f, 1 }, 869 4, 870 }, 871 }; 872 sk_sp<SkShader> (*factories[])(const GradRun&, SkShader::TileMode) { 873 make_linear, make_radial, make_conical, make_sweep 874 }; 875 876 const SkRect rect = SkRect::MakeWH(SIZE, SIZE); 877 const SkScalar dx = SIZE + 20; 878 const SkScalar dy = SIZE + 20; 879 const SkShader::TileMode mode = SkShader::kClamp_TileMode; 880 881 SkPaint paint; 882 canvas->translate(10, 10 - dy); 883 for (auto factory : factories) { 884 canvas->translate(0, dy); 885 SkAutoCanvasRestore acr(canvas, true); 886 for (const auto& run : runs) { 887 paint.setShader(factory(run, mode)); 888 canvas->drawRect(rect, paint); 889 canvas->translate(dx, 0); 890 } 891 } 892 } 893 894 static void draw_many_stops(SkCanvas* canvas, uint32_t flags) { 895 const unsigned kStopCount = 200; 896 const SkPoint pts[] = { {50, 50}, {450, 465}}; 897 898 SkColor colors[kStopCount]; 899 for (unsigned i = 0; i < kStopCount; i++) { 900 switch (i % 5) { 901 case 0: colors[i] = SK_ColorRED; break; 902 case 1: colors[i] = SK_ColorGREEN; break; 903 case 2: colors[i] = SK_ColorGREEN; break; 904 case 3: colors[i] = SK_ColorBLUE; break; 905 case 4: colors[i] = SK_ColorRED; break; 906 } 907 } 908 909 SkPaint p; 910 p.setShader(SkGradientShader::MakeLinear( 911 pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, flags, nullptr)); 912 913 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p); 914 } 915 916 DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) { 917 draw_many_stops(canvas, 0); 918 } 919 920 DEF_SIMPLE_GM(gradient_many_stops_4f, canvas, 500, 500) { 921 draw_many_stops(canvas, SkLinearGradient::kForce4fContext_PrivateFlag); 922 } 923 924 static void draw_subpixel_gradient(SkCanvas* canvas, uint32_t flags) { 925 const SkPoint pts[] = { {50, 50}, {50.1f, 50.1f}}; 926 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; 927 SkPaint p; 928 p.setShader(SkGradientShader::MakeLinear( 929 pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkShader::kRepeat_TileMode, flags, nullptr)); 930 canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p); 931 } 932 933 DEF_SIMPLE_GM(gradient_subpixel, canvas, 500, 500) { 934 draw_subpixel_gradient(canvas, 0); 935 } 936 937 DEF_SIMPLE_GM(gradient_subpixel_4f, canvas, 500, 500) { 938 draw_subpixel_gradient(canvas, SkLinearGradient::kForce4fContext_PrivateFlag); 939 } 940