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 SkString onShortName() { return SkString("clamped_gradients"); } 299 300 virtual SkISize onISize() { return SkISize::Make(640, 510); } 301 302 void drawBG(SkCanvas* canvas) { 303 canvas->drawColor(0xFFDDDDDD); 304 } 305 306 virtual void onDraw(SkCanvas* canvas) { 307 this->drawBG(canvas); 308 309 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) }; 310 SkPaint paint; 311 paint.setAntiAlias(true); 312 313 SkPoint center; 314 center.iset(0, 300); 315 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 316 SkShader* shader = SkGradientShader::CreateRadial( 317 SkPoint(center), 318 SkIntToScalar(200), gColors, NULL, 5, 319 SkShader::kClamp_TileMode); 320 paint.setShader(shader); 321 canvas->drawRect(r, paint); 322 shader->unref(); 323 } 324 325 private: 326 typedef GM INHERITED; 327 }; 328 329 /// Checks quality of large radial gradients, which may display 330 /// some banding. 331 332 class RadialGradientGM : public GM { 333 public: 334 RadialGradientGM() {} 335 336 protected: 337 virtual uint32_t onGetFlags() const SK_OVERRIDE { 338 return kSkipTiled_Flag; 339 } 340 341 SkString onShortName() { return SkString("radial_gradient"); } 342 virtual SkISize onISize() { return SkISize::Make(1280, 1280); } 343 void drawBG(SkCanvas* canvas) { 344 canvas->drawColor(0xFF000000); 345 } 346 virtual void onDraw(SkCanvas* canvas) { 347 const SkISize dim = this->getISize(); 348 349 this->drawBG(canvas); 350 351 SkPaint paint; 352 paint.setDither(true); 353 SkPoint center; 354 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2); 355 SkScalar radius = SkIntToScalar(dim.width())/2; 356 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 }; 357 const SkScalar pos[] = { 0.0f, 358 0.35f, 359 1.0f }; 360 SkShader* shader = 361 SkGradientShader::CreateRadial(center, radius, colors, 362 pos, SK_ARRAY_COUNT(pos), 363 SkShader::kClamp_TileMode); 364 paint.setShader(shader)->unref(); 365 SkRect r = { 366 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height()) 367 }; 368 canvas->drawRect(r, paint); 369 } 370 private: 371 typedef GM INHERITED; 372 }; 373 374 375 class RadialGradient2GM : public GM { 376 public: 377 RadialGradient2GM() {} 378 379 protected: 380 virtual uint32_t onGetFlags() const SK_OVERRIDE { 381 return kSkipTiled_Flag; 382 } 383 384 SkString onShortName() { return SkString("radial_gradient2"); } 385 virtual SkISize onISize() { return SkISize::Make(800, 400); } 386 void drawBG(SkCanvas* canvas) { 387 canvas->drawColor(0xFF000000); 388 } 389 390 // Reproduces the example given in bug 7671058. 391 virtual void onDraw(SkCanvas* canvas) { 392 SkPaint paint1, paint2, paint3; 393 paint1.setStyle(SkPaint::kFill_Style); 394 paint2.setStyle(SkPaint::kFill_Style); 395 paint3.setStyle(SkPaint::kFill_Style); 396 397 const SkColor sweep_colors[] = 398 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 }; 399 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 }; 400 const SkColor colors2[] = { 0xFF000000, 0x00000000 }; 401 402 const SkScalar cx = 200, cy = 200, radius = 150; 403 SkPoint center; 404 center.set(cx, cy); 405 406 // We can either interpolate endpoints and premultiply each point (default, more precision), 407 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap). 408 const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag }; 409 410 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) { 411 SkAutoTUnref<SkShader> sweep( 412 SkGradientShader::CreateSweep(cx, cy, sweep_colors, 413 NULL, SK_ARRAY_COUNT(sweep_colors), 414 flags[i], NULL)); 415 SkAutoTUnref<SkShader> radial1( 416 SkGradientShader::CreateRadial(center, radius, colors1, 417 NULL, SK_ARRAY_COUNT(colors1), 418 SkShader::kClamp_TileMode, 419 flags[i], NULL)); 420 SkAutoTUnref<SkShader> radial2( 421 SkGradientShader::CreateRadial(center, radius, colors2, 422 NULL, SK_ARRAY_COUNT(colors2), 423 SkShader::kClamp_TileMode, 424 flags[i], NULL)); 425 paint1.setShader(sweep); 426 paint2.setShader(radial1); 427 paint3.setShader(radial2); 428 429 canvas->drawCircle(cx, cy, radius, paint1); 430 canvas->drawCircle(cx, cy, radius, paint3); 431 canvas->drawCircle(cx, cy, radius, paint2); 432 433 canvas->translate(400, 0); 434 } 435 } 436 437 private: 438 typedef GM INHERITED; 439 }; 440 441 /////////////////////////////////////////////////////////////////////////////// 442 443 static GM* MyFactory(void*) { return new GradientsGM; } 444 static GMRegistry reg(MyFactory); 445 446 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; } 447 static GMRegistry reg2(MyFactory2); 448 449 static GM* MyFactory3(void*) { return new ClampedGradientsGM; } 450 static GMRegistry reg3(MyFactory3); 451 452 static GM* MyFactory4(void*) { return new RadialGradientGM; } 453 static GMRegistry reg4(MyFactory4); 454 455 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; } 456 static GMRegistry reg5(MyFactory5); 457 458 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; } 459 static GMRegistry reg6(MyFactory6); 460 461 static GM* MyFactory7(void*) { return new RadialGradient2GM; } 462 static GMRegistry reg7(MyFactory7); 463 } 464