1/* 2 * Copyright 2014 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/SkMatrix.h" 12#include "include/core/SkPaint.h" 13#include "include/core/SkPoint.h" 14#include "include/core/SkRect.h" 15#include "include/core/SkRefCnt.h" 16#include "include/core/SkScalar.h" 17#include "include/core/SkShader.h" 18#include "include/core/SkSize.h" 19#include "include/core/SkString.h" 20#include "include/core/SkTileMode.h" 21#include "include/core/SkTypes.h" 22#include "include/effects/SkGradientShader.h" 23 24namespace skiagm { 25 26struct GradData { 27 int fCount; 28 const SkColor* fColors; 29 const SkScalar* fPos; 30}; 31 32constexpr SkColor gColors[] = { 33 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK 34}; 35constexpr SkScalar gPos0[] = { 0, SK_Scalar1 }; 36constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; 37constexpr SkScalar gPos2[] = { 38 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 39}; 40 41constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f}; 42constexpr SkColor gColorClamp[] = { 43 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE 44}; 45 46constexpr GradData gGradData[] = { 47 { 2, gColors, gPos0 }, 48 { 2, gColors, gPos1 }, 49 { 5, gColors, gPos2 }, 50 { 4, gColorClamp, gPosClamp } 51}; 52 53static sk_sp<SkShader> Make2ConicalOutside(const SkPoint pts[2], const GradData& data, 54 SkTileMode tm, const SkMatrix& localMatrix) { 55 SkPoint center0, center1; 56 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 57 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 58 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 59 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 60 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 61 data.fPos, data.fCount, tm, 0, &localMatrix); 62} 63 64static sk_sp<SkShader> Make2ConicalOutsideStrip(const SkPoint pts[2], const GradData& data, 65 SkTileMode tm, const SkMatrix& localMatrix) { 66 SkPoint center0, center1; 67 SkScalar radius = (pts[1].fX - pts[0].fX) / 3; 68 center0.set(pts[0].fX, pts[0].fY); 69 center1.set(pts[1].fX, pts[1].fY); 70 return SkGradientShader::MakeTwoPointConical(center0, radius, center1, radius, data.fColors, 71 data.fPos, data.fCount, tm, 0, &localMatrix); 72} 73 74static sk_sp<SkShader> Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data, 75 SkTileMode tm, const SkMatrix& localMatrix) { 76 SkPoint center0, center1; 77 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; 78 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 79 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 80 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 81 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, data.fColors, 82 data.fPos, data.fCount, tm, 0, &localMatrix); 83} 84 85static sk_sp<SkShader> Make2ConicalInside(const SkPoint pts[2], const GradData& data, 86 SkTileMode tm, const SkMatrix& localMatrix) { 87 SkPoint center0, center1; 88 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 89 SkScalarAve(pts[0].fY, pts[1].fY)); 90 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 91 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 92 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7, 93 center0, (pts[1].fX - pts[0].fX) / 2, 94 data.fColors, data.fPos, data.fCount, tm, 95 0, &localMatrix); 96} 97 98static sk_sp<SkShader> Make2ConicalInsideFlip(const SkPoint pts[2], const GradData& data, 99 SkTileMode tm, const SkMatrix& localMatrix) { 100 SkPoint center0, center1; 101 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 102 SkScalarAve(pts[0].fY, pts[1].fY)); 103 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 104 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 105 return SkGradientShader::MakeTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 2, 106 center1, (pts[1].fX - pts[0].fX) / 7, 107 data.fColors, data.fPos, data.fCount, tm, 108 0, &localMatrix); 109} 110 111static sk_sp<SkShader> Make2ConicalInsideCenter(const SkPoint pts[2], const GradData& data, 112 SkTileMode tm, const SkMatrix& localMatrix) { 113 SkPoint center0; 114 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 115 SkScalarAve(pts[0].fY, pts[1].fY)); 116 return SkGradientShader::MakeTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 7, 117 center0, (pts[1].fX - pts[0].fX) / 2, 118 data.fColors, data.fPos, data.fCount, tm, 119 0, &localMatrix); 120} 121 122static sk_sp<SkShader> Make2ConicalInsideCenterReversed(const SkPoint pts[2], const GradData& data, 123 SkTileMode tm, const SkMatrix& localMatrix) { 124 SkPoint center0; 125 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 126 SkScalarAve(pts[0].fY, pts[1].fY)); 127 return SkGradientShader::MakeTwoPointConical(center0, (pts[1].fX - pts[0].fX) / 2, 128 center0, (pts[1].fX - pts[0].fX) / 7, 129 data.fColors, data.fPos, data.fCount, tm, 130 0, &localMatrix); 131} 132 133static sk_sp<SkShader> Make2ConicalZeroRad(const SkPoint pts[2], const GradData& data, 134 SkTileMode tm, const SkMatrix& localMatrix) { 135 SkPoint center0, center1; 136 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 137 SkScalarAve(pts[0].fY, pts[1].fY)); 138 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 139 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 140 return SkGradientShader::MakeTwoPointConical(center1, 0.f, 141 center0, (pts[1].fX - pts[0].fX) / 2, 142 data.fColors, data.fPos, data.fCount, tm, 143 0, &localMatrix); 144} 145 146static sk_sp<SkShader> Make2ConicalZeroRadFlip(const SkPoint pts[2], const GradData& data, 147 SkTileMode tm, const SkMatrix& localMatrix) { 148 SkPoint center0, center1; 149 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 150 SkScalarAve(pts[0].fY, pts[1].fY)); 151 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 152 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 153 return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 2, 154 center0, 0.f, 155 data.fColors, data.fPos, data.fCount, tm, 156 0, &localMatrix); 157} 158 159static sk_sp<SkShader> Make2ConicalZeroRadCenter(const SkPoint pts[2], const GradData& data, 160 SkTileMode tm, const SkMatrix& localMatrix) { 161 SkPoint center0, center1; 162 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 163 SkScalarAve(pts[0].fY, pts[1].fY)); 164 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 165 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 166 return SkGradientShader::MakeTwoPointConical(center0, 0.f, center0, (pts[1].fX - pts[0].fX) / 2, 167 data.fColors, data.fPos, data.fCount, tm, 168 0, &localMatrix); 169} 170 171static sk_sp<SkShader> Make2ConicalZeroRadOutside(const SkPoint pts[2], const GradData& data, 172 SkTileMode tm, 173 const SkMatrix& localMatrix) { 174 SkPoint center0, center1; 175 SkScalar radius0 = 0.f; 176 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 177 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 178 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 179 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, 180 data.fColors, data.fPos, 181 data.fCount, tm, 0, &localMatrix); 182} 183 184static sk_sp<SkShader> Make2ConicalZeroRadFlipOutside(const SkPoint pts[2], const GradData& data, 185 SkTileMode tm, 186 const SkMatrix& localMatrix) { 187 SkPoint center0, center1; 188 SkScalar radius0 = 0.f; 189 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 190 center0.set(pts[0].fX + radius0, pts[0].fY + radius0); 191 center1.set(pts[1].fX - radius1, pts[1].fY - radius1); 192 return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0, data.fColors, 193 data.fPos, data.fCount, tm, 0, &localMatrix); 194} 195 196static sk_sp<SkShader> Make2ConicalEdgeX(const SkPoint pts[2], const GradData& data, 197 SkTileMode tm, const SkMatrix& localMatrix) { 198 SkPoint center0, center1; 199 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; 200 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 201 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 202 SkScalarAve(pts[0].fY, pts[1].fY)); 203 center0.set(center1.fX + radius1, center1.fY); 204 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 205 data.fPos, data.fCount, tm, 0, &localMatrix); 206} 207 208static sk_sp<SkShader> Make2ConicalEdgeY(const SkPoint pts[2], const GradData& data, 209 SkTileMode tm, const SkMatrix& localMatrix) { 210 SkPoint center0, center1; 211 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; 212 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 213 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 214 SkScalarAve(pts[0].fY, pts[1].fY)); 215 center0.set(center1.fX, center1.fY + radius1); 216 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 217 data.fPos, data.fCount, tm, 0, &localMatrix); 218} 219 220static sk_sp<SkShader> Make2ConicalZeroRadEdgeX(const SkPoint pts[2], const GradData& data, 221 SkTileMode tm, 222 const SkMatrix& localMatrix) { 223 SkPoint center0, center1; 224 SkScalar radius0 = 0.f; 225 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 226 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 227 SkScalarAve(pts[0].fY, pts[1].fY)); 228 center0.set(center1.fX + radius1, center1.fY); 229 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 230 data.fPos, data.fCount, tm, 0, &localMatrix); 231} 232 233static sk_sp<SkShader> Make2ConicalZeroRadEdgeY(const SkPoint pts[2], const GradData& data, 234 SkTileMode tm, const SkMatrix& localMatrix) { 235 SkPoint center0, center1; 236 SkScalar radius0 = 0.f; 237 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 238 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 239 SkScalarAve(pts[0].fY, pts[1].fY)); 240 center0.set(center1.fX, center1.fY + radius1); 241 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 242 data.fPos, data.fCount, tm, 0, &localMatrix); 243} 244 245static sk_sp<SkShader> Make2ConicalTouchX(const SkPoint pts[2], const GradData& data, 246 SkTileMode tm, const SkMatrix& localMatrix) { 247 SkPoint center0, center1; 248 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; 249 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 250 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 251 SkScalarAve(pts[0].fY, pts[1].fY)); 252 center0.set(center1.fX - radius1 + radius0, center1.fY); 253 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 254 data.fPos, data.fCount, tm, 0, &localMatrix); 255} 256 257static sk_sp<SkShader> Make2ConicalTouchY(const SkPoint pts[2], const GradData& data, 258 SkTileMode tm, const SkMatrix& localMatrix) { 259 SkPoint center0, center1; 260 SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; 261 SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; 262 center1.set(SkScalarAve(pts[0].fX, pts[1].fX), 263 SkScalarAve(pts[0].fY, pts[1].fY)); 264 center0.set(center1.fX, center1.fY + radius1 - radius0); 265 return SkGradientShader::MakeTwoPointConical(center0, radius0, center1, radius1, data.fColors, 266 data.fPos, data.fCount, tm, 0, &localMatrix); 267} 268 269static sk_sp<SkShader> Make2ConicalInsideSmallRad(const SkPoint pts[2], const GradData& data, 270 SkTileMode tm, const SkMatrix& localMatrix) { 271 SkPoint center0, center1; 272 center0.set(SkScalarAve(pts[0].fX, pts[1].fX), 273 SkScalarAve(pts[0].fY, pts[1].fY)); 274 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), 275 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); 276 return SkGradientShader::MakeTwoPointConical(center0, 0.0000000000000000001f, 277 center0, (pts[1].fX - pts[0].fX) / 2, 278 data.fColors, data.fPos, data.fCount, tm, 279 0, &localMatrix); 280} 281 282typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data, 283 SkTileMode tm, const SkMatrix& localMatrix); 284 285constexpr GradMaker gGradMakersOutside[] = { 286 Make2ConicalOutside, Make2ConicalOutsideFlip, 287 Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside, 288 Make2ConicalOutsideStrip 289}; 290 291constexpr GradMaker gGradMakersInside[] = { 292 Make2ConicalInside, Make2ConicalInsideFlip, Make2ConicalInsideCenter, 293 Make2ConicalZeroRad, Make2ConicalZeroRadFlip, Make2ConicalZeroRadCenter, 294 Make2ConicalInsideCenterReversed 295}; 296 297constexpr GradMaker gGradMakersEdgeCases[] = { 298 Make2ConicalEdgeX, Make2ConicalEdgeY, 299 Make2ConicalZeroRadEdgeX, Make2ConicalZeroRadEdgeY, 300 Make2ConicalTouchX, Make2ConicalTouchY, 301 Make2ConicalInsideSmallRad 302}; 303 304 305constexpr struct { 306 const GradMaker* fMaker; 307 const int fCount; 308 const char* fName; 309} gGradCases[] = { 310 { gGradMakersOutside, SK_ARRAY_COUNT(gGradMakersOutside), "outside" }, 311 { gGradMakersInside, SK_ARRAY_COUNT(gGradMakersInside), "inside" }, 312 { gGradMakersEdgeCases, SK_ARRAY_COUNT(gGradMakersEdgeCases), "edge" }, 313}; 314 315enum GradCaseType { // these must match the order in gGradCases 316 kOutside_GradCaseType, 317 kInside_GradCaseType, 318 kEdge_GradCaseType, 319}; 320 321/////////////////////////////////////////////////////////////////////////////// 322 323class ConicalGradientsGM : public GM { 324public: 325 ConicalGradientsGM(GradCaseType gradCaseType, bool dither, 326 SkTileMode mode = SkTileMode::kClamp) 327 : fGradCaseType(gradCaseType) 328 , fDither(dither) 329 , fMode(mode) { 330 fName.printf("gradients_2pt_conical_%s%s", gGradCases[gradCaseType].fName, 331 fDither ? "" : "_nodither"); 332 switch (mode) { 333 case SkTileMode::kRepeat: 334 fName.appendf("_repeat"); 335 break; 336 case SkTileMode::kMirror: 337 fName.appendf("_mirror"); 338 break; 339 default: 340 break; 341 } 342 } 343 344private: 345 void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); } 346 347 SkString onShortName() override { return fName; } 348 349 SkISize onISize() override { return {840, 815}; } 350 351 void onDraw(SkCanvas* canvas) override { 352 353 SkPoint pts[2] = { 354 { 0, 0 }, 355 { SkIntToScalar(100), SkIntToScalar(100) } 356 }; 357 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; 358 SkPaint paint; 359 paint.setAntiAlias(true); 360 paint.setDither(fDither); 361 362 canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); 363 364 const GradMaker* gradMaker = gGradCases[fGradCaseType].fMaker; 365 const int count = gGradCases[fGradCaseType].fCount; 366 367 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) { 368 canvas->save(); 369 for (int j = 0; j < count; j++) { 370 SkMatrix scale = SkMatrix::I(); 371 372 if (i == 3) { // if the clamp case 373 scale.setScale(0.5f, 0.5f); 374 scale.postTranslate(25.f, 25.f); 375 } 376 377 paint.setShader(gradMaker[j](pts, gGradData[i], fMode, scale)); 378 canvas->drawRect(r, paint); 379 canvas->translate(0, SkIntToScalar(120)); 380 } 381 canvas->restore(); 382 canvas->translate(SkIntToScalar(120), 0); 383 } 384 } 385 386private: 387 GradCaseType fGradCaseType; 388 SkString fName; 389 bool fDither; 390 SkTileMode fMode; 391}; 392/////////////////////////////////////////////////////////////////////////////// 393 394DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true); ) 395DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true); ) 396DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true); ) 397 398DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true, SkTileMode::kRepeat); ) 399DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true, SkTileMode::kRepeat); ) 400DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true, SkTileMode::kRepeat); ) 401 402DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true, SkTileMode::kMirror); ) 403DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true, SkTileMode::kMirror); ) 404DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true, SkTileMode::kMirror); ) 405 406DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, false); ) 407DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, false); ) 408DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, false); ) 409 410} // namespace skiagm 411