Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkConicalGradient.cpp
Go to the documentation of this file.
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 */
8
27
28#include <algorithm>
29#include <cmath>
30#include <cstdint>
31#include <utility>
32
34 fIsSwapped = false;
35 fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
36 if (SkScalarNearlyZero(fFocalX - 1)) {
37 // swap r0, r1
38 matrix->postTranslate(-1, 0);
39 matrix->postScale(-1, 1);
40 std::swap(r0, r1);
41 fFocalX = 0; // because r0 is now 0
42 fIsSwapped = true;
43 }
44
45 // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
46 const SkPoint from[2] = { {fFocalX, 0}, {1, 0} };
47 const SkPoint to[2] = { {0, 0}, {1, 0} };
48 SkMatrix focalMatrix;
49 if (!focalMatrix.setPolyToPoly(from, to, 2)) {
50 return false;
51 }
52 matrix->postConcat(focalMatrix);
53 fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
54
55 // The following transformations are just to accelerate the shader computation by saving
56 // some arithmatic operations.
57 if (this->isFocalOnCircle()) {
58 matrix->postScale(0.5, 0.5);
59 } else {
60 matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
61 }
62 matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
63 return true;
64}
65
67 SkScalar r0,
68 const SkPoint& c1,
69 SkScalar r1,
70 const Descriptor& desc,
71 const SkMatrix* localMatrix) {
72 SkMatrix gradientMatrix;
73 Type gradientType;
74
75 if (SkScalarNearlyZero((c0 - c1).length())) {
76 if (SkScalarNearlyZero(std::max(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
77 // Degenerate case; avoid dividing by zero. Should have been caught by caller but
78 // just in case, recheck here.
79 return nullptr;
80 }
81 // Concentric case: we can pretend we're radial (with a tiny twist).
82 const SkScalar scale = sk_ieee_float_divide(1, std::max(r0, r1));
83 gradientMatrix = SkMatrix::Translate(-c1.x(), -c1.y());
84 gradientMatrix.postScale(scale, scale);
85
86 gradientType = Type::kRadial;
87 } else {
88 const SkPoint centers[2] = { c0 , c1 };
89 const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
90
91 if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
92 // Degenerate case.
93 return nullptr;
94 }
95
96 gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
97 }
98
99 FocalData focalData;
100 if (gradientType == Type::kFocal) {
101 const auto dCenter = (c0 - c1).length();
102 if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
103 return nullptr;
104 }
105 }
106 return SkLocalMatrixShader::MakeWrapped<SkConicalGradient>(
107 localMatrix, c0, r0, c1, r1, desc, gradientType, gradientMatrix, focalData);
108}
109
111 SkScalar startRadius,
112 const SkPoint& end,
113 SkScalar endRadius,
114 const Descriptor& desc,
115 Type type,
116 const SkMatrix& gradientMatrix,
117 const FocalData& data)
118 : SkGradientBaseShader(desc, gradientMatrix)
119 , fCenter1(start)
120 , fCenter2(end)
121 , fRadius1(startRadius)
122 , fRadius2(endRadius)
123 , fType(type) {
124 // this is degenerate, and should be caught by our caller
125 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
126 if (type == Type::kFocal) {
127 fFocalData = data;
128 }
129}
130
132 // Because areas outside the cone are left untouched, we cannot treat the
133 // shader as opaque even if the gradient itself is opaque.
134 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
135 return false;
136}
137
138// Returns the original non-sorted version of the gradient
140 SkMatrix* localMatrix) const {
141 if (info) {
143 info->fPoint[0] = fCenter1;
144 info->fPoint[1] = fCenter2;
145 info->fRadius[0] = fRadius1;
146 info->fRadius[1] = fRadius2;
147 }
148 if (localMatrix) {
149 *localMatrix = SkMatrix::I();
150 }
151 return GradientType::kConical;
152}
153
154sk_sp<SkFlattenable> SkConicalGradient::CreateProc(SkReadBuffer& buffer) {
155 DescriptorScope desc;
156 SkMatrix legacyLocalMatrix, *lmPtr = nullptr;
157 if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
158 return nullptr;
159 }
160 if (!legacyLocalMatrix.isIdentity()) {
161 lmPtr = &legacyLocalMatrix;
162 }
163 SkPoint c1 = buffer.readPoint();
164 SkPoint c2 = buffer.readPoint();
165 SkScalar r1 = buffer.readScalar();
166 SkScalar r2 = buffer.readScalar();
167
168 if (!buffer.isValid()) {
169 return nullptr;
170 }
172 r1,
173 c2,
174 r2,
175 desc.fColors,
176 std::move(desc.fColorSpace),
177 desc.fPositions,
178 desc.fColorCount,
179 desc.fTileMode,
180 desc.fInterpolation,
181 lmPtr);
182}
183
185 this->SkGradientBaseShader::flatten(buffer);
186 buffer.writePoint(fCenter1);
187 buffer.writePoint(fCenter2);
188 buffer.writeScalar(fRadius1);
189 buffer.writeScalar(fRadius2);
190}
191
194 SkRasterPipeline* postPipeline) const {
195 const auto dRadius = fRadius2 - fRadius1;
196
197 if (fType == Type::kRadial) {
198 p->append(SkRasterPipelineOp::xy_to_radius);
199
200 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
201 auto scale = std::max(fRadius1, fRadius2) / dRadius;
202 auto bias = -fRadius1 / dRadius;
203
204 p->appendMatrix(alloc, SkMatrix::Translate(bias, 0) * SkMatrix::Scale(scale, 1));
205 return;
206 }
207
208 if (fType == Type::kStrip) {
209 auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
210 SkScalar scaledR0 = fRadius1 / this->getCenterX1();
211 ctx->fP0 = scaledR0 * scaledR0;
212 p->append(SkRasterPipelineOp::xy_to_2pt_conical_strip, ctx);
213 p->append(SkRasterPipelineOp::mask_2pt_conical_nan, ctx);
214 postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
215 return;
216 }
217
218 auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
219 ctx->fP0 = 1 / fFocalData.fR1;
220 ctx->fP1 = fFocalData.fFocalX;
221
222 if (fFocalData.isFocalOnCircle()) {
223 p->append(SkRasterPipelineOp::xy_to_2pt_conical_focal_on_circle);
224 } else if (fFocalData.isWellBehaved()) {
225 p->append(SkRasterPipelineOp::xy_to_2pt_conical_well_behaved, ctx);
226 } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
227 p->append(SkRasterPipelineOp::xy_to_2pt_conical_smaller, ctx);
228 } else {
229 p->append(SkRasterPipelineOp::xy_to_2pt_conical_greater, ctx);
230 }
231
232 if (!fFocalData.isWellBehaved()) {
233 p->append(SkRasterPipelineOp::mask_2pt_conical_degenerates, ctx);
234 }
235 if (1 - fFocalData.fFocalX < 0) {
236 p->append(SkRasterPipelineOp::negate_x);
237 }
238 if (!fFocalData.isNativelyFocal()) {
239 p->append(SkRasterPipelineOp::alter_2pt_conical_compensate_focal, ctx);
240 }
241 if (fFocalData.isSwapped()) {
242 p->append(SkRasterPipelineOp::alter_2pt_conical_unswap);
243 }
244 if (!fFocalData.isWellBehaved()) {
245 postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
246 }
247}
248
249// assumes colors is SkColor4f* and pos is SkScalar*
250#define EXPAND_1_COLOR(count) \
251 SkColor4f tmp[2]; \
252 do { \
253 if (1 == count) { \
254 tmp[0] = tmp[1] = colors[0]; \
255 colors = tmp; \
256 pos = nullptr; \
257 count = 2; \
258 } \
259 } while (0)
260
262 SkScalar startRadius,
263 const SkPoint& end,
264 SkScalar endRadius,
265 const SkColor4f colors[],
266 sk_sp<SkColorSpace> colorSpace,
267 const SkScalar pos[],
268 int colorCount,
269 SkTileMode mode,
270 const Interpolation& interpolation,
271 const SkMatrix* localMatrix) {
272 if (startRadius < 0 || endRadius < 0) {
273 return nullptr;
274 }
275 if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
276 return nullptr;
277 }
279 // If the center positions are the same, then the gradient is the radial variant of a 2 pt
280 // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
281 // (startRadius == endRadius).
283 startRadius, endRadius, SkGradientBaseShader::kDegenerateThreshold)) {
284 // Degenerate case, where the interpolation region area approaches zero. The proper
285 // behavior depends on the tile mode, which is consistent with the default degenerate
286 // gradient behavior, except when mode = clamp and the radii > 0.
287 if (mode == SkTileMode::kClamp &&
289 // The interpolation region becomes an infinitely thin ring at the radius, so the
290 // final gradient will be the first color repeated from p=0 to 1, and then a hard
291 // stop switching to the last color at p=1.
292 static constexpr SkScalar circlePos[3] = {0, 1, 1};
293 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
294 return MakeRadial(start,
295 endRadius,
296 reColors,
297 std::move(colorSpace),
298 circlePos,
299 3,
300 mode,
301 interpolation,
302 localMatrix);
303 } else {
304 // Otherwise use the default degenerate case
306 colors, pos, colorCount, std::move(colorSpace), mode);
307 }
309 // We can treat this gradient as radial, which is faster. If we got here, we know
310 // that endRadius is not equal to 0, so this produces a meaningful gradient
311 return MakeRadial(start,
312 endRadius,
313 colors,
314 std::move(colorSpace),
315 pos,
316 colorCount,
317 mode,
318 interpolation,
319 localMatrix);
320 }
321 // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
322 // regular 2pt constructor.
323 }
324
325 if (localMatrix && !localMatrix->invert(nullptr)) {
326 return nullptr;
327 }
328 EXPAND_1_COLOR(colorCount);
329
331 colors, std::move(colorSpace), pos, colorCount, mode, interpolation);
332 return SkConicalGradient::Create(start, startRadius, end, endRadius, desc, localMatrix);
333}
334
335#undef EXPAND_1_COLOR
336
338 SkScalar startRadius,
339 const SkPoint& end,
340 SkScalar endRadius,
341 const SkColor colors[],
342 const SkScalar pos[],
343 int colorCount,
344 SkTileMode mode,
345 uint32_t flags,
346 const SkMatrix* localMatrix) {
347 SkColorConverter converter(colors, colorCount);
349 startRadius,
350 end,
351 endRadius,
352 converter.fColors4f.begin(),
353 nullptr,
354 pos,
355 colorCount,
356 mode,
357 flags,
358 localMatrix);
359}
360
363 // Previous name
364 SkFlattenable::Register("SkTwoPointConicalGradient", SkConicalGradient::CreateProc);
365}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
SkPoint pos
#define SkASSERT(cond)
Definition SkAssert.h:116
uint32_t SkColor
Definition SkColor.h:37
void SkRegisterConicalGradientShaderFlattenable()
#define EXPAND_1_COLOR(count)
#define SK_REGISTER_FLATTENABLE(type)
static constexpr float sk_ieee_float_divide(float numer, float denom)
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:107
#define SkScalarAbs(x)
Definition SkScalar.h:39
SkTileMode
Definition SkTileMode.h:13
static sk_sp< SkShader > MakeRadial()
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
SkConicalGradient(const SkPoint &c0, SkScalar r0, const SkPoint &c1, SkScalar r1, const Descriptor &, Type, const SkMatrix &, const FocalData &)
SkScalar getCenterX1() const
void flatten(SkWriteBuffer &buffer) const override
GradientType asGradient(GradientInfo *info, SkMatrix *localMatrix) const override
void appendGradientStages(SkArenaAlloc *alloc, SkRasterPipeline *tPipeline, SkRasterPipeline *postPipeline) const override
static sk_sp< SkShader > Create(const SkPoint &start, SkScalar startRadius, const SkPoint &end, SkScalar endRadius, const Descriptor &, const SkMatrix *localMatrix)
bool isOpaque() const override
static void Register(const char name[], Factory)
static constexpr SkScalar kDegenerateThreshold
static bool ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode, const Interpolation &interpolation)
static sk_sp< SkShader > MakeDegenerateGradient(const SkColor4f colors[], const SkScalar pos[], int colorCount, sk_sp< SkColorSpace > colorSpace, SkTileMode mode)
void flatten(SkWriteBuffer &) const override
void commonAsAGradient(GradientInfo *) const
ShaderType type() const final
static sk_sp< SkShader > MakeTwoPointConical(const SkPoint &start, SkScalar startRadius, const SkPoint &end, SkScalar endRadius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:360
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count)
bool invert(SkMatrix *inverse) const
Definition SkMatrix.h:1206
static const SkMatrix & I()
bool isIdentity() const
Definition SkMatrix.h:223
void append(SkRasterPipelineOp, void *=nullptr)
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
glong glong end
static const uint8_t buffer[]
size_t length
const Scalar scale
bool set(SkScalar r0, SkScalar r1, SkMatrix *matrix)
constexpr float y() const
constexpr float x() const