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