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 11namespace skiagm { 12 13struct GradData { 14 int fCount; 15 const SkColor* fColors; 16 const SkScalar* fPos; 17}; 18 19static const SkColor gColors[] = { 20 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK 21}; 22static const SkScalar gPos0[] = { 0, SK_Scalar1 }; 23static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; 24static const SkScalar gPos2[] = { 25 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 26}; 27 28static const SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f}; 29static const SkColor gColorClamp[] = { 30 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE 31}; 32 33static const GradData gGradData[] = { 34 { 2, gColors, NULL }, 35 { 2, gColors, gPos0 }, 36 { 2, gColors, gPos1 }, 37 { 5, gColors, NULL }, 38 { 5, gColors, gPos2 }, 39 { 4, gColorClamp, gPosClamp } 40}; 41 42static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, 43 SkShader::TileMode tm, const SkMatrix& localMatrix) { 44 return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, 45 data.fCount, tm, 0, &localMatrix); 46} 47 48static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, 49 SkShader::TileMode tm, const SkMatrix& localMatrix) { 50 SkPoint center; 51 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 52 SkScalarAve(pts[0].fY, pts[1].fY)); 53 return SkGradientShader::CreateRadial(center, center.fX, data.fColors, 54 data.fPos, data.fCount, tm, 0, &localMatrix); 55} 56 57static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, 58 SkShader::TileMode, const SkMatrix& localMatrix) { 59 SkPoint center; 60 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 61 SkScalarAve(pts[0].fY, pts[1].fY)); 62 return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, 63 data.fPos, data.fCount, 0, &localMatrix); 64} 65 66static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, 67 SkShader::TileMode tm, const SkMatrix& localMatrix) { 68 SkPoint center0, center1; 69 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 70 SkScalarAve(pts[0].fY, pts[1].fY)); 71 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 72 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 73 return SkGradientShader::CreateTwoPointConical( 74 center1, (pts[1].fX - pts[0].fX) / 7, 75 center0, (pts[1].fX - pts[0].fX) / 2, 76 data.fColors, data.fPos, data.fCount, tm, 77 0, &localMatrix); 78} 79 80static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, 81 SkShader::TileMode tm, const SkMatrix& localMatrix) { 82 SkPoint center0, center1; 83 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 84 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 85 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 86 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 87 return SkGradientShader::CreateTwoPointConical(center1, radius1, 88 center0, radius0, 89 data.fColors, data.fPos, 90 data.fCount, tm, 0, &localMatrix); 91} 92 93typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, 94 SkShader::TileMode tm, const SkMatrix& localMatrix); 95static const GradMaker gGradMakers[] = { 96 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical 97}; 98 99/////////////////////////////////////////////////////////////////////////////// 100 101class GradientsGM : public GM { 102public: 103 GradientsGM() { 104 this->setBGColor(0xFFDDDDDD); 105 } 106 107protected: 108 109 SkString onShortName() { 110 return SkString("gradients"); 111 } 112 113 virtual SkISize onISize() { return SkISize::Make(840, 815); } 114 115 virtual void onDraw(SkCanvas* canvas) { 116 117 SkPoint pts[2] = { 118 { 0, 0 }, 119 { SkIntToScalar(100), SkIntToScalar(100) } 120 }; 121 SkShader::TileMode tm = SkShader::kClamp_TileMode; 122 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 123 SkPaint paint; 124 paint.setAntiAlias(true); 125 126 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 127 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 128 canvas->save(); 129 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 130 SkMatrix scale = SkMatrix::I(); 131 132 if (i == 5) { // if the clamp case 133 scale.setScale(0.5f, 0.5f); 134 scale.postTranslate(25.f, 25.f); 135 } 136 137 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, scale); 138 139 paint.setShader(shader); 140 canvas->drawRect(r, paint); 141 shader->unref(); 142 canvas->translate(0, SkIntToScalar(120)); 143 } 144 canvas->restore(); 145 canvas->translate(SkIntToScalar(120), 0); 146 } 147 } 148 149private: 150 typedef GM INHERITED; 151}; 152DEF_GM( return new GradientsGM; ) 153 154// Based on the original gradient slide, but with perspective applied to the 155// gradient shaders' local matrices 156class GradientsLocalPerspectiveGM : public GM { 157public: 158 GradientsLocalPerspectiveGM() { 159 this->setBGColor(0xFFDDDDDD); 160 } 161 162protected: 163 164 SkString onShortName() { 165 return SkString("gradients_local_perspective"); 166 } 167 168 virtual SkISize onISize() { return SkISize::Make(840, 815); } 169 170 virtual void onDraw(SkCanvas* canvas) { 171 172 SkPoint pts[2] = { 173 { 0, 0 }, 174 { SkIntToScalar(100), SkIntToScalar(100) } 175 }; 176 SkShader::TileMode tm = SkShader::kClamp_TileMode; 177 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 178 SkPaint paint; 179 paint.setAntiAlias(true); 180 181 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 182 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 183 canvas->save(); 184 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 185 // apply an increasing y perspective as we move to the right 186 SkMatrix perspective; 187 perspective.setIdentity(); 188 perspective.setPerspY(SkIntToScalar(i+1) / 500); 189 perspective.setSkewX(SkIntToScalar(i+1) / 10); 190 191 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective); 192 193 paint.setShader(shader); 194 canvas->drawRect(r, paint); 195 shader->unref(); 196 canvas->translate(0, SkIntToScalar(120)); 197 } 198 canvas->restore(); 199 canvas->translate(SkIntToScalar(120), 0); 200 } 201 } 202 203private: 204 typedef GM INHERITED; 205}; 206DEF_GM( return new GradientsLocalPerspectiveGM; ) 207 208// Based on the original gradient slide, but with perspective applied to 209// the view matrix 210class GradientsViewPerspectiveGM : public GradientsGM { 211protected: 212 SkString onShortName() { 213 return SkString("gradients_view_perspective"); 214 } 215 216 virtual SkISize onISize() { return SkISize::Make(840, 500); } 217 218 virtual void onDraw(SkCanvas* canvas) { 219 SkMatrix perspective; 220 perspective.setIdentity(); 221 perspective.setPerspY(0.001f); 222 perspective.setSkewX(SkIntToScalar(8) / 25); 223 canvas->concat(perspective); 224 INHERITED::onDraw(canvas); 225 } 226 227private: 228 typedef GradientsGM INHERITED; 229}; 230DEF_GM( return new GradientsViewPerspectiveGM; ) 231 232/* 233 Inspired by this <canvas> javascript, where we need to detect that we are not 234 solving a quadratic equation, but must instead solve a linear (since our X^2 235 coefficient is 0) 236 237 ctx.fillStyle = '#f00'; 238 ctx.fillRect(0, 0, 100, 50); 239 240 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150); 241 g.addColorStop(0, '#f00'); 242 g.addColorStop(0.01, '#0f0'); 243 g.addColorStop(0.99, '#0f0'); 244 g.addColorStop(1, '#f00'); 245 ctx.fillStyle = g; 246 ctx.fillRect(0, 0, 100, 50); 247 */ 248class GradientsDegenrate2PointGM : public GM { 249public: 250 GradientsDegenrate2PointGM() {} 251 252protected: 253 SkString onShortName() { 254 return SkString("gradients_degenerate_2pt"); 255 } 256 257 virtual SkISize onISize() { return SkISize::Make(320, 320); } 258 259 void drawBG(SkCanvas* canvas) { 260 canvas->drawColor(SK_ColorBLUE); 261 } 262 263 virtual void onDraw(SkCanvas* canvas) { 264 this->drawBG(canvas); 265 266 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED }; 267 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 }; 268 SkPoint c0; 269 c0.iset(-80, 25); 270 SkScalar r0 = SkIntToScalar(70); 271 SkPoint c1; 272 c1.iset(0, 25); 273 SkScalar r1 = SkIntToScalar(150); 274 SkShader* s = SkGradientShader::CreateTwoPointConical(c0, r0, c1, r1, colors, 275 pos, SK_ARRAY_COUNT(pos), 276 SkShader::kClamp_TileMode); 277 SkPaint paint; 278 paint.setShader(s)->unref(); 279 canvas->drawPaint(paint); 280 } 281 282private: 283 typedef GM INHERITED; 284}; 285DEF_GM( return new GradientsDegenrate2PointGM; ) 286 287/// Tests correctness of *optimized* codepaths in gradients. 288 289class ClampedGradientsGM : public GM { 290public: 291 ClampedGradientsGM() {} 292 293protected: 294 SkString onShortName() { return SkString("clamped_gradients"); } 295 296 virtual SkISize onISize() { return SkISize::Make(640, 510); } 297 298 void drawBG(SkCanvas* canvas) { 299 canvas->drawColor(0xFFDDDDDD); 300 } 301 302 virtual void onDraw(SkCanvas* canvas) { 303 this->drawBG(canvas); 304 305 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) }; 306 SkPaint paint; 307 paint.setAntiAlias(true); 308 309 SkPoint center; 310 center.iset(0, 300); 311 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 312 SkShader* shader = SkGradientShader::CreateRadial( 313 SkPoint(center), 314 SkIntToScalar(200), gColors, NULL, 5, 315 SkShader::kClamp_TileMode); 316 paint.setShader(shader); 317 canvas->drawRect(r, paint); 318 shader->unref(); 319 } 320 321private: 322 typedef GM INHERITED; 323}; 324DEF_GM( return new ClampedGradientsGM; ) 325 326/// Checks quality of large radial gradients, which may display 327/// some banding. 328 329class RadialGradientGM : public GM { 330public: 331 RadialGradientGM() {} 332 333protected: 334 335 SkString onShortName() override { return SkString("radial_gradient"); } 336 SkISize onISize() override { return SkISize::Make(1280, 1280); } 337 void drawBG(SkCanvas* canvas) { 338 canvas->drawColor(0xFF000000); 339 } 340 void onDraw(SkCanvas* canvas) override { 341 const SkISize dim = this->getISize(); 342 343 this->drawBG(canvas); 344 345 SkPaint paint; 346 paint.setDither(true); 347 SkPoint center; 348 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2); 349 SkScalar radius = SkIntToScalar(dim.width())/2; 350 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 }; 351 const SkScalar pos[] = { 0.0f, 352 0.35f, 353 1.0f }; 354 SkShader* shader = 355 SkGradientShader::CreateRadial(center, radius, colors, 356 pos, SK_ARRAY_COUNT(pos), 357 SkShader::kClamp_TileMode); 358 paint.setShader(shader)->unref(); 359 SkRect r = { 360 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height()) 361 }; 362 canvas->drawRect(r, paint); 363 } 364private: 365 typedef GM INHERITED; 366}; 367DEF_GM( return new RadialGradientGM; ) 368 369class RadialGradient2GM : public GM { 370public: 371 RadialGradient2GM() {} 372 373protected: 374 375 SkString onShortName() override { return SkString("radial_gradient2"); } 376 SkISize onISize() override { return SkISize::Make(800, 400); } 377 void drawBG(SkCanvas* canvas) { 378 canvas->drawColor(0xFF000000); 379 } 380 381 // Reproduces the example given in bug 7671058. 382 void onDraw(SkCanvas* canvas) override { 383 SkPaint paint1, paint2, paint3; 384 paint1.setStyle(SkPaint::kFill_Style); 385 paint2.setStyle(SkPaint::kFill_Style); 386 paint3.setStyle(SkPaint::kFill_Style); 387 388 const SkColor sweep_colors[] = 389 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 }; 390 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 }; 391 const SkColor colors2[] = { 0xFF000000, 0x00000000 }; 392 393 const SkScalar cx = 200, cy = 200, radius = 150; 394 SkPoint center; 395 center.set(cx, cy); 396 397 // We can either interpolate endpoints and premultiply each point (default, more precision), 398 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap). 399 const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag }; 400 401 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) { 402 SkAutoTUnref<SkShader> sweep( 403 SkGradientShader::CreateSweep(cx, cy, sweep_colors, 404 NULL, SK_ARRAY_COUNT(sweep_colors), 405 flags[i], NULL)); 406 SkAutoTUnref<SkShader> radial1( 407 SkGradientShader::CreateRadial(center, radius, colors1, 408 NULL, SK_ARRAY_COUNT(colors1), 409 SkShader::kClamp_TileMode, 410 flags[i], NULL)); 411 SkAutoTUnref<SkShader> radial2( 412 SkGradientShader::CreateRadial(center, radius, colors2, 413 NULL, SK_ARRAY_COUNT(colors2), 414 SkShader::kClamp_TileMode, 415 flags[i], NULL)); 416 paint1.setShader(sweep); 417 paint2.setShader(radial1); 418 paint3.setShader(radial2); 419 420 canvas->drawCircle(cx, cy, radius, paint1); 421 canvas->drawCircle(cx, cy, radius, paint3); 422 canvas->drawCircle(cx, cy, radius, paint2); 423 424 canvas->translate(400, 0); 425 } 426 } 427 428private: 429 typedef GM INHERITED; 430}; 431DEF_GM( return new RadialGradient2GM; ) 432 433// Shallow radial (shows banding on raster) 434class RadialGradient3GM : public GM { 435 SkAutoTUnref<SkShader> fShader; 436 437protected: 438 SkString onShortName() override { return SkString("radial_gradient3"); } 439 440 SkISize onISize() override { return SkISize::Make(500, 500); } 441 442 bool runAsBench() const override { return true; } 443 444 void onOnceBeforeDraw() override { 445 const SkPoint center = { 0, 0 }; 446 const SkScalar kRadius = 3000; 447 const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 }; 448 fShader.reset(SkGradientShader::CreateRadial(center, kRadius, gColors, NULL, 2, 449 SkShader::kClamp_TileMode)); 450 } 451 452 void onDraw(SkCanvas* canvas) override { 453 SkPaint paint; 454 paint.setShader(fShader); 455 canvas->drawRect(SkRect::MakeWH(500, 500), paint); 456 } 457 458private: 459 typedef GM INHERITED; 460}; 461DEF_GM( return new RadialGradient3GM; ) 462 463} 464