1 /*
2 * Copyright 2012 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 "src/shaders/gradients/SkLinearGradient.h"
9
10 #include "src/core/SkReadBuffer.h"
11 #include "src/core/SkWriteBuffer.h"
12 #include "src/shaders/SkLocalMatrixShader.h"
13
14 #if defined(SK_GRAPHITE)
15 #include "src/gpu/graphite/KeyContext.h"
16 #include "src/gpu/graphite/KeyHelpers.h"
17 #include "src/gpu/graphite/PaintParamsKey.h"
18 #endif
19
pts_to_unit_matrix(const SkPoint pts[2])20 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
21 SkVector vec = pts[1] - pts[0];
22 SkScalar mag = vec.length();
23 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
24
25 vec.scale(inv);
26 SkMatrix matrix;
27 matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
28 matrix.postTranslate(-pts[0].fX, -pts[0].fY);
29 matrix.postScale(inv, inv);
30 return matrix;
31 }
32
33 ///////////////////////////////////////////////////////////////////////////////
34
SkLinearGradient(const SkPoint pts[2],const Descriptor & desc)35 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
36 : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
37 , fStart(pts[0])
38 , fEnd(pts[1]) {
39 }
40
CreateProc(SkReadBuffer & buffer)41 sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
42 DescriptorScope desc;
43 SkMatrix legacyLocalMatrix;
44 if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
45 return nullptr;
46 }
47 SkPoint pts[2];
48 pts[0] = buffer.readPoint();
49 pts[1] = buffer.readPoint();
50 return SkGradientShader::MakeLinear(pts,
51 desc.fColors,
52 std::move(desc.fColorSpace),
53 desc.fPositions,
54 desc.fColorCount,
55 desc.fTileMode,
56 desc.fInterpolation,
57 &legacyLocalMatrix);
58 }
59
flatten(SkWriteBuffer & buffer) const60 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
61 this->INHERITED::flatten(buffer);
62 buffer.writePoint(fStart);
63 buffer.writePoint(fEnd);
64 }
65
appendGradientStages(SkArenaAlloc *,SkRasterPipeline *,SkRasterPipeline *) const66 void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
67 SkRasterPipeline*) const {
68 // No extra stage needed for linear gradients.
69 }
70
transformT(skvm::Builder * p,skvm::Uniforms *,skvm::Coord coord,skvm::I32 * mask) const71 skvm::F32 SkLinearGradient::transformT(skvm::Builder* p, skvm::Uniforms*,
72 skvm::Coord coord, skvm::I32* mask) const {
73 // We've baked getting t in x into the matrix, so this is pretty trivial.
74 return coord.x;
75 }
76
asGradient(GradientInfo * info,SkMatrix * localMatrix) const77 SkShaderBase::GradientType SkLinearGradient::asGradient(GradientInfo* info,
78 SkMatrix* localMatrix) const {
79 if (info) {
80 commonAsAGradient(info);
81 info->fPoint[0] = fStart;
82 info->fPoint[1] = fEnd;
83 }
84 if (localMatrix) {
85 *localMatrix = SkMatrix::I();
86 }
87 return GradientType::kLinear;
88 }
89
90 /////////////////////////////////////////////////////////////////////
91
92 #if defined(SK_GANESH)
93
94 #include "src/gpu/ganesh/gradients/GrGradientShader.h"
95
asFragmentProcessor(const GrFPArgs & args,const MatrixRec & mRec) const96 std::unique_ptr<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(
97 const GrFPArgs& args, const MatrixRec& mRec) const {
98 return GrGradientShader::MakeLinear(*this, args, mRec);
99 }
100
101 #endif
102
103 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const104 void SkLinearGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
105 skgpu::graphite::PaintParamsKeyBuilder* builder,
106 skgpu::graphite::PipelineDataGatherer* gatherer) const {
107 using namespace skgpu::graphite;
108
109 SkColor4fXformer xformedColors(this, keyContext.dstColorInfo().colorSpace());
110 const SkPMColor4f* colors = xformedColors.fColors.begin();
111
112 GradientShaderBlocks::GradientData data(GradientType::kLinear,
113 fStart, fEnd,
114 0.0f, 0.0f,
115 0.0f, 0.0f,
116 fTileMode,
117 fColorCount,
118 colors,
119 fPositions,
120 fInterpolation);
121
122 MakeInterpolatedToDst(keyContext, builder, gatherer,
123 data, fInterpolation,
124 xformedColors.fIntermediateColorSpace.get());
125 }
126 #endif
127
MakeLinear(const SkPoint pts[2],const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,const Interpolation & interpolation,const SkMatrix * localMatrix)128 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
129 const SkColor4f colors[],
130 sk_sp<SkColorSpace> colorSpace,
131 const SkScalar pos[],
132 int colorCount,
133 SkTileMode mode,
134 const Interpolation& interpolation,
135 const SkMatrix* localMatrix) {
136 if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
137 return nullptr;
138 }
139 if (!SkGradientShaderBase::ValidGradient(colors, colorCount, mode, interpolation)) {
140 return nullptr;
141 }
142 if (1 == colorCount) {
143 return SkShaders::Color(colors[0], std::move(colorSpace));
144 }
145 if (localMatrix && !localMatrix->invert(nullptr)) {
146 return nullptr;
147 }
148
149 if (SkScalarNearlyZero((pts[1] - pts[0]).length(),
150 SkGradientShaderBase::kDegenerateThreshold)) {
151 // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
152 // the gradient approaches two half planes of solid color (first and last). However, they
153 // are divided by the line perpendicular to the start and end point, which becomes undefined
154 // once start and end are exactly the same, so just use the end color for a stable solution.
155 return SkGradientShaderBase::MakeDegenerateGradient(colors, pos, colorCount,
156 std::move(colorSpace), mode);
157 }
158
159 SkGradientShaderBase::ColorStopOptimizer opt(colors, pos, colorCount, mode);
160
161 SkGradientShaderBase::Descriptor desc(opt.fColors, std::move(colorSpace), opt.fPos,
162 opt.fCount, mode, interpolation);
163 return SkLocalMatrixShader::MakeWrapped<SkLinearGradient>(localMatrix, pts, desc);
164 }
165
MakeLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)166 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
167 const SkColor colors[],
168 const SkScalar pos[],
169 int colorCount,
170 SkTileMode mode,
171 uint32_t flags,
172 const SkMatrix* localMatrix) {
173 SkColorConverter converter(colors, colorCount);
174 return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
175 localMatrix);
176 }
177
SkRegisterLinearGradientShaderFlattenable()178 void SkRegisterLinearGradientShaderFlattenable() {
179 SK_REGISTER_FLATTENABLE(SkLinearGradient);
180 }
181