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 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 GradData gGradData[] = { 29 { 2, gColors, NULL }, 30 { 2, gColors, gPos0 }, 31 { 2, gColors, gPos1 }, 32 { 5, gColors, NULL }, 33 { 5, gColors, gPos2 } 34}; 35 36static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data, 37 SkShader::TileMode tm, SkUnitMapper* mapper) { 38 return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos, 39 data.fCount, tm, mapper); 40} 41 42static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data, 43 SkShader::TileMode tm, SkUnitMapper* mapper) { 44 SkPoint center; 45 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 46 SkScalarAve(pts[0].fY, pts[1].fY)); 47 return SkGradientShader::CreateRadial(center, center.fX, data.fColors, 48 data.fPos, data.fCount, tm, mapper); 49} 50 51static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data, 52 SkShader::TileMode tm, SkUnitMapper* mapper) { 53 SkPoint center; 54 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 55 SkScalarAve(pts[0].fY, pts[1].fY)); 56 return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors, 57 data.fPos, data.fCount, mapper); 58} 59 60static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data, 61 SkShader::TileMode tm, SkUnitMapper* mapper) { 62 SkPoint center0, center1; 63 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 64 SkScalarAve(pts[0].fY, pts[1].fY)); 65 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 66 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 67 return SkGradientShader::CreateTwoPointRadial( 68 center1, (pts[1].fX - pts[0].fX) / 7, 69 center0, (pts[1].fX - pts[0].fX) / 2, 70 data.fColors, data.fPos, data.fCount, tm, mapper); 71} 72 73typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data, 74 SkShader::TileMode tm, SkUnitMapper* mapper); 75static const GradMaker gGradMakers[] = { 76 MakeLinear, MakeRadial, MakeSweep, Make2Radial 77}; 78 79/////////////////////////////////////////////////////////////////////////////// 80 81class GradientsGM : public GM { 82public: 83 GradientsGM() { 84 this->setBGColor(0xFFDDDDDD); 85 } 86 87protected: 88 SkString onShortName() { 89 return SkString("gradients"); 90 } 91 92 virtual SkISize onISize() { return make_isize(640, 510); } 93 94 virtual void onDraw(SkCanvas* canvas) { 95 96 SkPoint pts[2] = { 97 { 0, 0 }, 98 { SkIntToScalar(100), SkIntToScalar(100) } 99 }; 100 SkShader::TileMode tm = SkShader::kClamp_TileMode; 101 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 102 SkPaint paint; 103 paint.setAntiAlias(true); 104 105 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 106 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 107 canvas->save(); 108 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) { 109 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL); 110 paint.setShader(shader); 111 canvas->drawRect(r, paint); 112 shader->unref(); 113 canvas->translate(0, SkIntToScalar(120)); 114 } 115 canvas->restore(); 116 canvas->translate(SkIntToScalar(120), 0); 117 } 118 } 119 120private: 121 typedef GM INHERITED; 122}; 123 124/* 125 Inspired by this <canvas> javascript, where we need to detect that we are not 126 solving a quadratic equation, but must instead solve a linear (since our X^2 127 coefficient is 0) 128 129 ctx.fillStyle = '#f00'; 130 ctx.fillRect(0, 0, 100, 50); 131 132 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150); 133 g.addColorStop(0, '#f00'); 134 g.addColorStop(0.01, '#0f0'); 135 g.addColorStop(0.99, '#0f0'); 136 g.addColorStop(1, '#f00'); 137 ctx.fillStyle = g; 138 ctx.fillRect(0, 0, 100, 50); 139 */ 140class GradientsDegenrate2PointGM : public GM { 141public: 142 GradientsDegenrate2PointGM() {} 143 144protected: 145 SkString onShortName() { 146 return SkString("gradients_degenerate_2pt"); 147 } 148 149 virtual SkISize onISize() { return make_isize(320, 320); } 150 151 void drawBG(SkCanvas* canvas) { 152 canvas->drawColor(SK_ColorBLUE); 153 } 154 155 virtual void onDraw(SkCanvas* canvas) { 156 this->drawBG(canvas); 157 158 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED }; 159 SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 }; 160 SkPoint c0; 161 c0.iset(-80, 25); 162 SkScalar r0 = SkIntToScalar(70); 163 SkPoint c1; 164 c1.iset(0, 25); 165 SkScalar r1 = SkIntToScalar(150); 166 SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors, 167 pos, SK_ARRAY_COUNT(pos), 168 SkShader::kClamp_TileMode); 169 SkPaint paint; 170 paint.setShader(s)->unref(); 171 canvas->drawPaint(paint); 172 } 173 174private: 175 typedef GM INHERITED; 176}; 177 178/// Tests correctness of *optimized* codepaths in gradients. 179 180class ClampedGradientsGM : public GM { 181public: 182 ClampedGradientsGM() {} 183 184protected: 185 SkString onShortName() { return SkString("clamped_gradients"); } 186 187 virtual SkISize onISize() { return make_isize(640, 510); } 188 189 void drawBG(SkCanvas* canvas) { 190 canvas->drawColor(0xFFDDDDDD); 191 } 192 193 virtual void onDraw(SkCanvas* canvas) { 194 this->drawBG(canvas); 195 196 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) }; 197 SkPaint paint; 198 paint.setAntiAlias(true); 199 200 SkPoint center; 201 center.iset(0, 300); 202 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 203 SkShader* shader = SkGradientShader::CreateRadial( 204 SkPoint(center), 205 SkIntToScalar(200), gColors, NULL, 5, 206 SkShader::kClamp_TileMode, NULL); 207 paint.setShader(shader); 208 canvas->drawRect(r, paint); 209 shader->unref(); 210 } 211 212private: 213 typedef GM INHERITED; 214}; 215 216/// Checks quality of large radial gradients, which may display 217/// some banding. 218 219class RadialGradientGM : public GM { 220public: 221 RadialGradientGM() {} 222 223protected: 224 SkString onShortName() { return SkString("radial_gradient"); } 225 virtual SkISize onISize() { return make_isize(1280, 1280); } 226 void drawBG(SkCanvas* canvas) { 227 canvas->drawColor(0xFF000000); 228 } 229 virtual void onDraw(SkCanvas* canvas) { 230 const SkISize dim = this->getISize(); 231 232 this->drawBG(canvas); 233 234 SkPaint paint; 235 paint.setDither(true); 236 SkPoint center; 237 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2); 238 SkScalar radius = SkIntToScalar(dim.width())/2; 239 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 }; 240 const SkScalar pos[] = { SkFloatToScalar(0.0), 241 SkFloatToScalar(0.35), 242 SkFloatToScalar(1.0) }; 243 SkShader* shader = 244 SkGradientShader::CreateRadial(center, radius, colors, 245 pos, SK_ARRAY_COUNT(pos), 246 SkShader::kClamp_TileMode); 247 paint.setShader(shader)->unref(); 248 SkRect r = { 249 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height()) 250 }; 251 canvas->drawRect(r, paint); 252 } 253private: 254 typedef GM INHERITED; 255}; 256 257 258 259/////////////////////////////////////////////////////////////////////////////// 260 261static GM* MyFactory(void*) { return new GradientsGM; } 262static GMRegistry reg(MyFactory); 263 264static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; } 265static GMRegistry reg2(MyFactory2); 266 267static GM* MyFactory3(void*) { return new ClampedGradientsGM; } 268static GMRegistry reg3(MyFactory3); 269 270static GM* MyFactory4(void*) { return new RadialGradientGM; } 271static GMRegistry reg4(MyFactory4); 272} 273 274