| /* | |
| * Copyright 2014 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "gm/gm.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkShader.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTileMode.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/effects/SkGradient.h" | |
| #include "include/private/SkFloatingPoint.h" | |
| #include <array> | |
| namespace skiagm { | |
| struct GradData { | |
| size_t fCount; | |
| const SkColor4f* fColors; | |
| const float* fPos; | |
| SkGradient grad(SkTileMode tm) const { | |
| return {{{fColors, fCount}, {fPos, fCount}, tm}, {}}; | |
| } | |
| }; | |
| constexpr SkColor4f gColors[] = { | |
| SkColors::kRed, SkColors::kGreen, SkColors::kBlue, SkColors::kWhite, SkColors::kBlack | |
| }; | |
| constexpr SkScalar gPos0[] = { 0, SK_Scalar1 }; | |
| constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 }; | |
| constexpr SkScalar gPos2[] = { | |
| 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1 | |
| }; | |
| constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f}; | |
| constexpr SkColor4f gColorClamp[] = { | |
| SkColors::kRed, SkColors::kGreen, SkColors::kGreen, SkColors::kBlue | |
| }; | |
| static constexpr auto gGradData = std::to_array<GradData>({ | |
| GradData{2, gColors, gPos0}, | |
| GradData{2, gColors, gPos1}, | |
| GradData{5, gColors, gPos2}, | |
| GradData{4, gColorClamp, gPosClamp} | |
| }); | |
| static sk_sp<SkShader> Make2ConicalOutside(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center0.set(pts[0].fX + radius0, pts[0].fY + radius0); | |
| center1.set(pts[1].fX - radius1, pts[1].fY - radius1); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalOutsideStrip(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius = (pts[1].fX - pts[0].fX) / 3; | |
| center0.set(pts[0].fX, pts[0].fY); | |
| center1.set(pts[1].fX, pts[1].fY); | |
| return SkShaders::TwoPointConicalGradient(center0, radius, center1, radius, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center0.set(pts[0].fX + radius0, pts[0].fY + radius0); | |
| center1.set(pts[1].fX - radius1, pts[1].fY - radius1); | |
| return SkShaders::TwoPointConicalGradient(center1, radius1, center0, radius0, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalInside(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center1, (pts[1].fX - pts[0].fX) / 7, | |
| center0, (pts[1].fX - pts[0].fX) / 2, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalInsideFlip(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center0, (pts[1].fX - pts[0].fX) / 2, | |
| center1, (pts[1].fX - pts[0].fX) / 7, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalInsideCenter(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| return SkShaders::TwoPointConicalGradient(center0, (pts[1].fX - pts[0].fX) / 7, | |
| center0, (pts[1].fX - pts[0].fX) / 2, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalInsideCenterReversed(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| return SkShaders::TwoPointConicalGradient(center0, (pts[1].fX - pts[0].fX) / 2, | |
| center0, (pts[1].fX - pts[0].fX) / 7, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRad(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center1, 0.f, | |
| center0, (pts[1].fX - pts[0].fX) / 2, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadFlip(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center1, (pts[1].fX - pts[0].fX) / 2, center0, 0.f, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadCenter(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center0, 0.f, center0, (pts[1].fX - pts[0].fX) / 2, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadOutside(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, | |
| const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = 0.f; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center0.set(pts[0].fX + radius0, pts[0].fY + radius0); | |
| center1.set(pts[1].fX - radius1, pts[1].fY - radius1); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadFlipOutside(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = 0.f; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center0.set(pts[0].fX + radius0, pts[0].fY + radius0); | |
| center1.set(pts[1].fX - radius1, pts[1].fY - radius1); | |
| return SkShaders::TwoPointConicalGradient(center1, radius1, center0, radius0, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalEdgeX(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX + radius1, center1.fY); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalEdgeY(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX, center1.fY + radius1); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadEdgeX(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = 0.f; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX + radius1, center1.fY); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalZeroRadEdgeY(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = 0.f; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX, center1.fY + radius1); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalTouchX(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX - radius1 + radius0, center1.fY); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalTouchY(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| SkScalar radius0 = (pts[1].fX - pts[0].fX) / 7; | |
| SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3; | |
| center1.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center0.set(center1.fX, center1.fY + radius1 - radius0); | |
| return SkShaders::TwoPointConicalGradient(center0, radius0, center1, radius1, | |
| data.grad(tm), &localMatrix); | |
| } | |
| static sk_sp<SkShader> Make2ConicalInsideSmallRad(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix) { | |
| SkPoint center0, center1; | |
| center0.set(sk_float_midpoint(pts[0].fX, pts[1].fX), | |
| sk_float_midpoint(pts[0].fY, pts[1].fY)); | |
| center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5), | |
| SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4)); | |
| return SkShaders::TwoPointConicalGradient(center0, 0.0000000000000000001f, | |
| center0, (pts[1].fX - pts[0].fX) / 2, | |
| data.grad(tm), &localMatrix); | |
| } | |
| typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data, | |
| SkTileMode tm, const SkMatrix& localMatrix); | |
| constexpr GradMaker gGradMakersOutside[] = { | |
| Make2ConicalOutside, Make2ConicalOutsideFlip, | |
| Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside, | |
| Make2ConicalOutsideStrip | |
| }; | |
| constexpr GradMaker gGradMakersInside[] = { | |
| Make2ConicalInside, Make2ConicalInsideFlip, Make2ConicalInsideCenter, | |
| Make2ConicalZeroRad, Make2ConicalZeroRadFlip, Make2ConicalZeroRadCenter, | |
| Make2ConicalInsideCenterReversed | |
| }; | |
| constexpr GradMaker gGradMakersEdgeCases[] = { | |
| Make2ConicalEdgeX, Make2ConicalEdgeY, | |
| Make2ConicalZeroRadEdgeX, Make2ConicalZeroRadEdgeY, | |
| Make2ConicalTouchX, Make2ConicalTouchY, | |
| Make2ConicalInsideSmallRad | |
| }; | |
| struct GradCase { | |
| const GradMaker* fMaker; | |
| const int fCount; | |
| const char* fName; | |
| }; | |
| static constexpr auto gGradCases = std::to_array<GradCase>({ | |
| GradCase{ gGradMakersOutside, std::size(gGradMakersOutside), "outside"}, | |
| GradCase{ gGradMakersInside, std::size(gGradMakersInside), "inside"}, | |
| GradCase{gGradMakersEdgeCases, std::size(gGradMakersEdgeCases), "edge"}, | |
| }); | |
| enum GradCaseType { // these must match the order in gGradCases | |
| kOutside_GradCaseType, | |
| kInside_GradCaseType, | |
| kEdge_GradCaseType, | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| class ConicalGradientsGM : public GM { | |
| public: | |
| ConicalGradientsGM(GradCaseType gradCaseType, bool dither, | |
| SkTileMode mode = SkTileMode::kClamp) | |
| : fGradCaseType(gradCaseType) | |
| , fDither(dither) | |
| , fMode(mode) { | |
| fName.printf("gradients_2pt_conical_%s%s", gGradCases[gradCaseType].fName, | |
| fDither ? "" : "_nodither"); | |
| switch (mode) { | |
| case SkTileMode::kRepeat: | |
| fName.appendf("_repeat"); | |
| break; | |
| case SkTileMode::kMirror: | |
| fName.appendf("_mirror"); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| private: | |
| void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); } | |
| SkString getName() const override { return fName; } | |
| SkISize getISize() override { return {840, 815}; } | |
| void onDraw(SkCanvas* canvas) override { | |
| SkPoint pts[2] = { | |
| { 0, 0 }, | |
| { SkIntToScalar(100), SkIntToScalar(100) } | |
| }; | |
| SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) }; | |
| SkPaint paint; | |
| paint.setAntiAlias(true); | |
| paint.setDither(fDither); | |
| canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); | |
| const GradMaker* gradMaker = gGradCases[fGradCaseType].fMaker; | |
| const int count = gGradCases[fGradCaseType].fCount; | |
| for (size_t i = 0; i < std::size(gGradData); i++) { | |
| canvas->save(); | |
| for (int j = 0; j < count; j++) { | |
| SkMatrix scale = SkMatrix::I(); | |
| if (i == 3) { // if the clamp case | |
| scale.setScale(0.5f, 0.5f); | |
| scale.postTranslate(25.f, 25.f); | |
| } | |
| paint.setShader(gradMaker[j](pts, gGradData[i], fMode, scale)); | |
| canvas->drawRect(r, paint); | |
| canvas->translate(0, SkIntToScalar(120)); | |
| } | |
| canvas->restore(); | |
| canvas->translate(SkIntToScalar(120), 0); | |
| } | |
| } | |
| private: | |
| GradCaseType fGradCaseType; | |
| SkString fName; | |
| bool fDither; | |
| SkTileMode fMode; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true); ) | |
| DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true); ) | |
| DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true); ) | |
| DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true, SkTileMode::kRepeat); ) | |
| DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true, SkTileMode::kRepeat); ) | |
| DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true, SkTileMode::kRepeat); ) | |
| DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, true, SkTileMode::kMirror); ) | |
| DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, true, SkTileMode::kMirror); ) | |
| DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, true, SkTileMode::kMirror); ) | |
| DEF_GM( return new ConicalGradientsGM(kInside_GradCaseType, false); ) | |
| DEF_GM( return new ConicalGradientsGM(kOutside_GradCaseType, false); ) | |
| DEF_GM( return new ConicalGradientsGM(kEdge_GradCaseType, false); ) | |
| } // namespace skiagm |