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