1 /* 2 * Copyright 2013 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 #include "gm.h" 8 #include "SkGradientShader.h" 9 10 using namespace skiagm; 11 12 struct GradData { 13 int fCount; 14 const SkColor* fColors; 15 const SkScalar* fPos; 16 }; 17 18 constexpr SkColor gColors[] = { 19 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, 20 }; 21 22 constexpr GradData gGradData[] = { 23 { 1, gColors, nullptr }, 24 { 2, gColors, nullptr }, 25 { 3, gColors, nullptr }, 26 { 4, gColors, nullptr }, 27 }; 28 29 static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { 30 return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm); 31 } 32 33 static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { 34 SkPoint center; 35 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 36 SkScalarAve(pts[0].fY, pts[1].fY)); 37 return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount, tm); 38 } 39 40 static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data, SkShader::TileMode) { 41 SkPoint center; 42 center.set(SkScalarAve(pts[0].fX, pts[1].fX), 43 SkScalarAve(pts[0].fY, pts[1].fY)); 44 return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount); 45 } 46 47 static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { 48 SkPoint center0, center1; 49 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 50 SkScalarAve(pts[0].fY, pts[1].fY)); 51 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 52 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 53 return SkGradientShader::MakeTwoPointConical( 54 center1, (pts[1].fX - pts[0].fX) / 7, 55 center0, (pts[1].fX - pts[0].fX) / 2, 56 data.fColors, data.fPos, data.fCount, tm); 57 } 58 59 static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm) { 60 SkPoint center0, center1; 61 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 62 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 63 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 64 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 65 return SkGradientShader::MakeTwoPointConical(center1, radius1, 66 center0, radius0, 67 data.fColors, data.fPos, 68 data.fCount, tm); 69 } 70 71 72 typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data, SkShader::TileMode tm); 73 74 constexpr GradMaker gGradMakers[] = { 75 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical, 76 }; 77 78 /////////////////////////////////////////////////////////////////////////////// 79 80 class GradientsNoTextureGM : public GM { 81 public: 82 GradientsNoTextureGM(bool dither) : fDither(dither) { 83 this->setBGColor(0xFFDDDDDD); 84 } 85 86 protected: 87 88 SkString onShortName() override { 89 return SkString(fDither ? "gradients_no_texture" : "gradients_no_texture_nodither"); 90 } 91 92 SkISize onISize() override { return SkISize::Make(640, 615); } 93 94 void onDraw(SkCanvas* canvas) override { 95 constexpr SkPoint kPts[2] = { { 0, 0 }, 96 { SkIntToScalar(50), SkIntToScalar(50) } }; 97 constexpr SkShader::TileMode kTM = SkShader::kClamp_TileMode; 98 SkRect kRect = { 0, 0, SkIntToScalar(50), SkIntToScalar(50) }; 99 SkPaint paint; 100 paint.setAntiAlias(true); 101 paint.setDither(fDither); 102 103 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 104 constexpr uint8_t kAlphas[] = { 0xff, 0x40 }; 105 for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphas); ++a) { 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 paint.setShader(gGradMakers[j](kPts, gGradData[i], kTM)); 110 paint.setAlpha(kAlphas[a]); 111 canvas->drawRect(kRect, paint); 112 canvas->translate(0, SkIntToScalar(kRect.height() + 20)); 113 } 114 canvas->restore(); 115 canvas->translate(SkIntToScalar(kRect.width() + 20), 0); 116 } 117 } 118 } 119 120 private: 121 bool fDither; 122 123 typedef GM INHERITED; 124 }; 125 126 /////////////////////////////////////////////////////////////////////////////// 127 128 struct ColorPos { 129 SkColor* fColors; 130 SkScalar* fPos; 131 int fCount; 132 133 ColorPos() : fColors(nullptr), fPos(nullptr), fCount(0) {} 134 ~ColorPos() { 135 delete[] fColors; 136 delete[] fPos; 137 } 138 139 void construct(const SkColor colors[], const SkScalar pos[], int count) { 140 fColors = new SkColor[count]; 141 memcpy(fColors, colors, count * sizeof(SkColor)); 142 if (pos) { 143 fPos = new SkScalar[count]; 144 memcpy(fPos, pos, count * sizeof(SkScalar)); 145 fPos[0] = 0; 146 fPos[count - 1] = 1; 147 } 148 fCount = count; 149 } 150 }; 151 152 static void make0(ColorPos* rec) { 153 #if 0 154 From http://jsfiddle.net/3fe2a/ 155 156 background-image: -webkit-linear-gradient(left, #22d1cd 1%, #22d1cd 0.9510157507590116%, #df4b37 2.9510157507590113%, #df4b37 23.695886056604927%, #22d1cd 25.695886056604927%, #22d1cd 25.39321881940624%, #e6de36 27.39321881940624%, #e6de36 31.849399922570655%, #3267ff 33.849399922570655%, #3267ff 44.57735802921938%, #9d47d1 46.57735802921938%, #9d47d1 53.27185850805876%, #3267ff 55.27185850805876%, #3267ff 61.95718972227316%, #5cdd9d 63.95718972227316%, #5cdd9d 69.89166004442%, #3267ff 71.89166004442%, #3267ff 74.45795382765857%, #9d47d1 76.45795382765857%, #9d47d1 82.78364610713776%, #3267ff 84.78364610713776%, #3267ff 94.52743647737229%, #e3d082 96.52743647737229%, #e3d082 96.03934633331295%); 157 height: 30px; 158 #endif 159 160 const SkColor colors[] = { 161 0xFF22d1cd, 0xFF22d1cd, 0xFFdf4b37, 0xFFdf4b37, 0xFF22d1cd, 0xFF22d1cd, 0xFFe6de36, 0xFFe6de36, 162 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFF5cdd9d, 0xFF5cdd9d, 163 0xFF3267ff, 0xFF3267ff, 0xFF9d47d1, 0xFF9d47d1, 0xFF3267ff, 0xFF3267ff, 0xFFe3d082, 0xFFe3d082 164 }; 165 const double percent[] = { 166 1, 0.9510157507590116, 2.9510157507590113, 23.695886056604927, 167 25.695886056604927, 25.39321881940624, 27.39321881940624, 31.849399922570655, 168 33.849399922570655, 44.57735802921938, 46.57735802921938, 53.27185850805876, 169 55.27185850805876, 61.95718972227316, 63.95718972227316, 69.89166004442, 170 71.89166004442, 74.45795382765857, 76.45795382765857, 82.78364610713776, 171 84.78364610713776, 94.52743647737229, 96.52743647737229, 96.03934633331295, 172 }; 173 const int N = SK_ARRAY_COUNT(percent); 174 SkScalar pos[N]; 175 for (int i = 0; i < N; ++i) { 176 pos[i] = SkDoubleToScalar(percent[i] / 100); 177 } 178 rec->construct(colors, pos, N); 179 } 180 181 static void make1(ColorPos* rec) { 182 const SkColor colors[] = { 183 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE, 184 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE, 185 SK_ColorBLACK, 186 }; 187 rec->construct(colors, nullptr, SK_ARRAY_COUNT(colors)); 188 } 189 190 static void make2(ColorPos* rec) { 191 const SkColor colors[] = { 192 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE, 193 SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE, 194 SK_ColorBLACK, 195 }; 196 const int N = SK_ARRAY_COUNT(colors); 197 SkScalar pos[N]; 198 for (int i = 0; i < N; ++i) { 199 pos[i] = SK_Scalar1 * i / (N - 1); 200 } 201 rec->construct(colors, pos, N); 202 } 203 204 static void make3(ColorPos* rec) { 205 const SkColor colors[] = { 206 SK_ColorRED, SK_ColorBLUE, SK_ColorBLUE, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLACK, 207 }; 208 const SkScalar pos[] = { 209 0, 0, 0.5f, 0.5, 1, 1, 210 }; 211 rec->construct(colors, pos, SK_ARRAY_COUNT(colors)); 212 } 213 214 class GradientsManyColorsGM : public GM { 215 enum { 216 W = 800, 217 }; 218 sk_sp<SkShader> fShader; 219 220 typedef void (*Proc)(ColorPos*); 221 public: 222 GradientsManyColorsGM(bool dither) : fDither(dither) {} 223 224 protected: 225 226 SkString onShortName() override { 227 return SkString(fDither ? "gradients_many" : "gradients_many_nodither"); 228 } 229 230 SkISize onISize() override { return SkISize::Make(880, 400); } 231 232 void onDraw(SkCanvas* canvas) override { 233 const Proc procs[] = { 234 make0, make1, make2, make3, 235 }; 236 const SkPoint pts[] = { 237 { 0, 0 }, 238 { SkIntToScalar(W), 0 }, 239 }; 240 const SkRect r = SkRect::MakeWH(SkIntToScalar(W), 30); 241 242 SkPaint paint; 243 paint.setDither(fDither); 244 245 canvas->translate(40, 20); 246 247 for (int i = 0; i <= 8; ++i) { 248 SkScalar x = r.width() * i / 8; 249 canvas->drawLine(x, 0, x, 10000, paint); 250 } 251 252 // expand the drawing rect so we exercise clampping in the gradients 253 const SkRect drawR = r.makeOutset(20, 0); 254 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) { 255 ColorPos rec; 256 procs[i](&rec); 257 paint.setShader(SkGradientShader::MakeLinear(pts, rec.fColors, rec.fPos, rec.fCount, 258 SkShader::kClamp_TileMode)); 259 canvas->drawRect(drawR, paint); 260 261 canvas->save(); 262 canvas->translate(r.centerX(), r.height() + 4); 263 canvas->scale(-1, 1); 264 canvas->translate(-r.centerX(), 0); 265 canvas->drawRect(drawR, paint); 266 canvas->restore(); 267 268 canvas->translate(0, r.height() + 2*r.height() + 8); 269 } 270 } 271 272 private: 273 bool fDither; 274 275 typedef GM INHERITED; 276 }; 277 278 /////////////////////////////////////////////////////////////////////////////// 279 280 DEF_GM(return new GradientsNoTextureGM(true);) 281 DEF_GM(return new GradientsNoTextureGM(false);) 282 DEF_GM(return new GradientsManyColorsGM(true);) 283 DEF_GM(return new GradientsManyColorsGM(false);) 284