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