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, SkUnitMapper* mapper) { 44 return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, 45 data.fCount, tm, mapper); 46 } 47 48 static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, 49 SkShader::TileMode tm, SkUnitMapper* mapper) { 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, mapper); 55 } 56 57 static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, 58 SkShader::TileMode, SkUnitMapper* mapper) { 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, mapper); 64 } 65 66 static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, 67 SkShader::TileMode tm, SkUnitMapper* mapper) { 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, mapper); 77 } 78 79 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data, 80 SkShader::TileMode tm, SkUnitMapper* mapper) { 81 SkPoint center0, center1; 82 SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10); 83 SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3); 84 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 85 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 86 return SkGradientShader::CreateTwoPointConical(center1, radius1, 87 center0, radius0, 88 data.fColors, data.fPos, 89 data.fCount, tm, mapper); 90 } 91 92 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, 93 SkShader::TileMode tm, SkUnitMapper* mapper); 94 static const GradMaker gGradMakers[] = { 95 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical 96 }; 97 98 /////////////////////////////////////////////////////////////////////////////// 99 100 class GradientsGM : public GM { 101 public: 102 GradientsGM() { 103 this->setBGColor(0xFFDDDDDD); 104 } 105 106 protected: 107 SkString onShortName() { 108 return SkString("gradients"); 109 } 110 111 virtual SkISize onISize() { return make_isize(640, 615); } 112 113 virtual void onDraw(SkCanvas* canvas) { 114 115 SkPoint pts[2] = { 116 { 0, 0 }, 117 { SkIntToScalar(100), SkIntToScalar(100) } 118 }; 119 SkShader::TileMode tm = SkShader::kClamp_TileMode; 120 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 121 SkPaint paint; 122 paint.setAntiAlias(true); 123 124 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 125 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 126 canvas->save(); 127 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 128 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL); 129 130 if (i == 5) { // if the clamp case 131 SkMatrix scale; 132 scale.setScale(0.5f, 0.5f); 133 scale.postTranslate(25.f, 25.f); 134 shader->setLocalMatrix(scale); 135 } 136 137 paint.setShader(shader); 138 canvas->drawRect(r, paint); 139 shader->unref(); 140 canvas->translate(0, SkIntToScalar(120)); 141 } 142 canvas->restore(); 143 canvas->translate(SkIntToScalar(120), 0); 144 } 145 } 146 147 private: 148 typedef GM INHERITED; 149 }; 150 151 // Based on the original gradient slide, but with perspective applied to the 152 // gradient shaders' local matrices 153 class GradientsLocalPerspectiveGM : public GM { 154 public: 155 GradientsLocalPerspectiveGM() { 156 this->setBGColor(0xFFDDDDDD); 157 } 158 159 protected: 160 SkString onShortName() { 161 return SkString("gradients_local_perspective"); 162 } 163 164 virtual SkISize onISize() { return make_isize(640, 615); } 165 166 virtual void onDraw(SkCanvas* canvas) { 167 168 SkPoint pts[2] = { 169 { 0, 0 }, 170 { SkIntToScalar(100), SkIntToScalar(100) } 171 }; 172 SkShader::TileMode tm = SkShader::kClamp_TileMode; 173 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 174 SkPaint paint; 175 paint.setAntiAlias(true); 176 177 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 178 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 179 canvas->save(); 180 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 181 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL); 182 183 // apply an increasing y perspective as we move to the right 184 SkMatrix perspective; 185 perspective.setIdentity(); 186 perspective.setPerspY(SkScalarDiv(SkIntToScalar((unsigned) i+1), 187 SkIntToScalar(500))); 188 perspective.setSkewX(SkScalarDiv(SkIntToScalar((unsigned) i+1), 189 SkIntToScalar(10))); 190 191 shader->setLocalMatrix(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 203 private: 204 typedef GM INHERITED; 205 }; 206 207 // Based on the original gradient slide, but with perspective applied to 208 // the view matrix 209 class GradientsViewPerspectiveGM : public GradientsGM { 210 protected: 211 SkString onShortName() { 212 return SkString("gradients_view_perspective"); 213 } 214 215 virtual SkISize onISize() { return make_isize(640, 400); } 216 217 virtual void onDraw(SkCanvas* canvas) { 218 SkMatrix perspective; 219 perspective.setIdentity(); 220 perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000))); 221 perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25))); 222 canvas->concat(perspective); 223 INHERITED::onDraw(canvas); 224 } 225 226 private: 227 typedef GradientsGM INHERITED; 228 }; 229 230 /* 231 Inspired by this <canvas> javascript, where we need to detect that we are not 232 solving a quadratic equation, but must instead solve a linear (since our X^2 233 coefficient is 0) 234 235 ctx.fillStyle = '#f00'; 236 ctx.fillRect(0, 0, 100, 50); 237 238 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150); 239 g.addColorStop(0, '#f00'); 240 g.addColorStop(0.01, '#0f0'); 241 g.addColorStop(0.99, '#0f0'); 242 g.addColorStop(1, '#f00'); 243 ctx.fillStyle = g; 244 ctx.fillRect(0, 0, 100, 50); 245 */ 246 class GradientsDegenrate2PointGM : public GM { 247 public: 248 GradientsDegenrate2PointGM() {} 249 250 protected: 251 SkString onShortName() { 252 return SkString("gradients_degenerate_2pt"); 253 } 254 255 virtual SkISize onISize() { return make_isize(320, 320); } 256 257 void drawBG(SkCanvas* canvas) { 258 canvas->drawColor(SK_ColorBLUE); 259 } 260 261 virtual void onDraw(SkCanvas* canvas) { 262 this->drawBG(canvas); 263 264 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED }; 265 SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 }; 266 SkPoint c0; 267 c0.iset(-80, 25); 268 SkScalar r0 = SkIntToScalar(70); 269 SkPoint c1; 270 c1.iset(0, 25); 271 SkScalar r1 = SkIntToScalar(150); 272 SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors, 273 pos, SK_ARRAY_COUNT(pos), 274 SkShader::kClamp_TileMode); 275 SkPaint paint; 276 paint.setShader(s)->unref(); 277 canvas->drawPaint(paint); 278 } 279 280 private: 281 typedef GM INHERITED; 282 }; 283 284 /// Tests correctness of *optimized* codepaths in gradients. 285 286 class ClampedGradientsGM : public GM { 287 public: 288 ClampedGradientsGM() {} 289 290 protected: 291 SkString onShortName() { return SkString("clamped_gradients"); } 292 293 virtual SkISize onISize() { return make_isize(640, 510); } 294 295 void drawBG(SkCanvas* canvas) { 296 canvas->drawColor(0xFFDDDDDD); 297 } 298 299 virtual void onDraw(SkCanvas* canvas) { 300 this->drawBG(canvas); 301 302 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) }; 303 SkPaint paint; 304 paint.setAntiAlias(true); 305 306 SkPoint center; 307 center.iset(0, 300); 308 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 309 SkShader* shader = SkGradientShader::CreateRadial( 310 SkPoint(center), 311 SkIntToScalar(200), gColors, NULL, 5, 312 SkShader::kClamp_TileMode, NULL); 313 paint.setShader(shader); 314 canvas->drawRect(r, paint); 315 shader->unref(); 316 } 317 318 private: 319 typedef GM INHERITED; 320 }; 321 322 /// Checks quality of large radial gradients, which may display 323 /// some banding. 324 325 class RadialGradientGM : public GM { 326 public: 327 RadialGradientGM() {} 328 329 protected: 330 SkString onShortName() { return SkString("radial_gradient"); } 331 virtual SkISize onISize() { return make_isize(1280, 1280); } 332 void drawBG(SkCanvas* canvas) { 333 canvas->drawColor(0xFF000000); 334 } 335 virtual void onDraw(SkCanvas* canvas) { 336 const SkISize dim = this->getISize(); 337 338 this->drawBG(canvas); 339 340 SkPaint paint; 341 paint.setDither(true); 342 SkPoint center; 343 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2); 344 SkScalar radius = SkIntToScalar(dim.width())/2; 345 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 }; 346 const SkScalar pos[] = { SkFloatToScalar(0.0f), 347 SkFloatToScalar(0.35f), 348 SkFloatToScalar(1.0f) }; 349 SkShader* shader = 350 SkGradientShader::CreateRadial(center, radius, colors, 351 pos, SK_ARRAY_COUNT(pos), 352 SkShader::kClamp_TileMode); 353 paint.setShader(shader)->unref(); 354 SkRect r = { 355 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height()) 356 }; 357 canvas->drawRect(r, paint); 358 } 359 private: 360 typedef GM INHERITED; 361 }; 362 363 364 365 /////////////////////////////////////////////////////////////////////////////// 366 367 static GM* MyFactory(void*) { return new GradientsGM; } 368 static GMRegistry reg(MyFactory); 369 370 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; } 371 static GMRegistry reg2(MyFactory2); 372 373 static GM* MyFactory3(void*) { return new ClampedGradientsGM; } 374 static GMRegistry reg3(MyFactory3); 375 376 static GM* MyFactory4(void*) { return new RadialGradientGM; } 377 static GMRegistry reg4(MyFactory4); 378 379 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; } 380 static GMRegistry reg5(MyFactory5); 381 382 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; } 383 static GMRegistry reg6(MyFactory6); 384 } 385