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