Flutter Engine
The Flutter Engine
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
26
27#include <algorithm>
28#include <cmath>
29#include <cstdint>
30#include <utility>
31
33 fIsSwapped = false;
34 fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
35 if (SkScalarNearlyZero(fFocalX - 1)) {
36 // swap r0, r1
37 matrix->postTranslate(-1, 0);
38 matrix->postScale(-1, 1);
39 std::swap(r0, r1);
40 fFocalX = 0; // because r0 is now 0
41 fIsSwapped = true;
42 }
43
44 // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
45 const SkPoint from[2] = { {fFocalX, 0}, {1, 0} };
46 const SkPoint to[2] = { {0, 0}, {1, 0} };
47 SkMatrix focalMatrix;
48 if (!focalMatrix.setPolyToPoly(from, to, 2)) {
49 return false;
50 }
51 matrix->postConcat(focalMatrix);
52 fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
53
54 // The following transformations are just to accelerate the shader computation by saving
55 // some arithmatic operations.
56 if (this->isFocalOnCircle()) {
57 matrix->postScale(0.5, 0.5);
58 } else {
59 matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
60 }
61 matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
62 return true;
63}
64
65bool SkConicalGradient::MapToUnitX(const SkPoint &startCenter,
66 const SkPoint &endCenter,
67 SkMatrix* dstMatrix) {
68 const SkPoint centers[2] = { startCenter, endCenter };
69 const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
70
71 return dstMatrix->setPolyToPoly(centers, unitvec, 2);
72}
73
75 SkScalar r0,
76 const SkPoint& c1,
77 SkScalar r1,
78 const Descriptor& desc,
79 const SkMatrix* localMatrix) {
80 SkMatrix gradientMatrix;
81 Type gradientType;
82
83 if (SkScalarNearlyZero((c0 - c1).length())) {
84 if (SkScalarNearlyZero(std::max(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
85 // Degenerate case; avoid dividing by zero. Should have been caught by caller but
86 // just in case, recheck here.
87 return nullptr;
88 }
89 // Concentric case: we can pretend we're radial (with a tiny twist).
90 const SkScalar scale = sk_ieee_float_divide(1, std::max(r0, r1));
91 gradientMatrix = SkMatrix::Translate(-c1.x(), -c1.y());
92 gradientMatrix.postScale(scale, scale);
93
94 gradientType = Type::kRadial;
95 } else {
96 if (!MapToUnitX(c0, c1, &gradientMatrix)) {
97 // Degenerate case.
98 return nullptr;
99 }
100
101 gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
102 }
103
104 FocalData focalData;
105 if (gradientType == Type::kFocal) {
106 const auto dCenter = (c0 - c1).length();
107 if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
108 return nullptr;
109 }
110 }
111
112 sk_sp<SkShader> s = sk_make_sp<SkConicalGradient>(
113 c0, r0, c1, r1, desc, gradientType, gradientMatrix, focalData);
114 return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
115}
116
118 SkScalar startRadius,
119 const SkPoint& end,
120 SkScalar endRadius,
121 const Descriptor& desc,
122 Type type,
123 const SkMatrix& gradientMatrix,
124 const FocalData& data)
125 : SkGradientBaseShader(desc, gradientMatrix)
126 , fCenter1(start)
127 , fCenter2(end)
128 , fRadius1(startRadius)
129 , fRadius2(endRadius)
130 , fType(type) {
131 // this is degenerate, and should be caught by our caller
132 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
133 if (type == Type::kFocal) {
134 fFocalData = data;
135 }
136}
137
139 // Because areas outside the cone are left untouched, we cannot treat the
140 // shader as opaque even if the gradient itself is opaque.
141 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
142 return false;
143}
144
145// Returns the original non-sorted version of the gradient
147 SkMatrix* localMatrix) const {
148 if (info) {
150 info->fPoint[0] = fCenter1;
151 info->fPoint[1] = fCenter2;
152 info->fRadius[0] = fRadius1;
153 info->fRadius[1] = fRadius2;
154 }
155 if (localMatrix) {
156 *localMatrix = SkMatrix::I();
157 }
158 return GradientType::kConical;
159}
160
161sk_sp<SkFlattenable> SkConicalGradient::CreateProc(SkReadBuffer& buffer) {
162 DescriptorScope desc;
163 SkMatrix legacyLocalMatrix, *lmPtr = nullptr;
164 if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
165 return nullptr;
166 }
167 if (!legacyLocalMatrix.isIdentity()) {
168 lmPtr = &legacyLocalMatrix;
169 }
170 SkPoint c1 = buffer.readPoint();
171 SkPoint c2 = buffer.readPoint();
172 SkScalar r1 = buffer.readScalar();
173 SkScalar r2 = buffer.readScalar();
174
175 if (!buffer.isValid()) {
176 return nullptr;
177 }
179 r1,
180 c2,
181 r2,
182 desc.fColors,
183 std::move(desc.fColorSpace),
184 desc.fPositions,
185 desc.fColorCount,
186 desc.fTileMode,
187 desc.fInterpolation,
188 lmPtr);
189}
190
192 this->SkGradientBaseShader::flatten(buffer);
193 buffer.writePoint(fCenter1);
194 buffer.writePoint(fCenter2);
195 buffer.writeScalar(fRadius1);
196 buffer.writeScalar(fRadius2);
197}
198
201 SkRasterPipeline* postPipeline) const {
202 const auto dRadius = fRadius2 - fRadius1;
203
204 if (fType == Type::kRadial) {
205 p->append(SkRasterPipelineOp::xy_to_radius);
206
207 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
208 auto scale = std::max(fRadius1, fRadius2) / dRadius;
209 auto bias = -fRadius1 / dRadius;
210
211 p->appendMatrix(alloc, SkMatrix::Translate(bias, 0) * SkMatrix::Scale(scale, 1));
212 return;
213 }
214
215 if (fType == Type::kStrip) {
216 auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
217 SkScalar scaledR0 = fRadius1 / this->getCenterX1();
218 ctx->fP0 = scaledR0 * scaledR0;
219 p->append(SkRasterPipelineOp::xy_to_2pt_conical_strip, ctx);
220 p->append(SkRasterPipelineOp::mask_2pt_conical_nan, ctx);
221 postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
222 return;
223 }
224
225 auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
226 ctx->fP0 = 1 / fFocalData.fR1;
227 ctx->fP1 = fFocalData.fFocalX;
228
229 if (fFocalData.isFocalOnCircle()) {
230 p->append(SkRasterPipelineOp::xy_to_2pt_conical_focal_on_circle);
231 } else if (fFocalData.isWellBehaved()) {
232 p->append(SkRasterPipelineOp::xy_to_2pt_conical_well_behaved, ctx);
233 } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
234 p->append(SkRasterPipelineOp::xy_to_2pt_conical_smaller, ctx);
235 } else {
236 p->append(SkRasterPipelineOp::xy_to_2pt_conical_greater, ctx);
237 }
238
239 if (!fFocalData.isWellBehaved()) {
240 p->append(SkRasterPipelineOp::mask_2pt_conical_degenerates, ctx);
241 }
242 if (1 - fFocalData.fFocalX < 0) {
243 p->append(SkRasterPipelineOp::negate_x);
244 }
245 if (!fFocalData.isNativelyFocal()) {
246 p->append(SkRasterPipelineOp::alter_2pt_conical_compensate_focal, ctx);
247 }
248 if (fFocalData.isSwapped()) {
249 p->append(SkRasterPipelineOp::alter_2pt_conical_unswap);
250 }
251 if (!fFocalData.isWellBehaved()) {
252 postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
253 }
254}
255
256// assumes colors is SkColor4f* and pos is SkScalar*
257#define EXPAND_1_COLOR(count) \
258 SkColor4f tmp[2]; \
259 do { \
260 if (1 == count) { \
261 tmp[0] = tmp[1] = colors[0]; \
262 colors = tmp; \
263 pos = nullptr; \
264 count = 2; \
265 } \
266 } while (0)
267
269 SkScalar startRadius,
270 const SkPoint& end,
271 SkScalar endRadius,
272 const SkColor4f colors[],
273 sk_sp<SkColorSpace> colorSpace,
274 const SkScalar pos[],
275 int colorCount,
277 const Interpolation& interpolation,
278 const SkMatrix* localMatrix) {
279 if (startRadius < 0 || endRadius < 0) {
280 return nullptr;
281 }
282 if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
283 return nullptr;
284 }
286 // If the center positions are the same, then the gradient is the radial variant of a 2 pt
287 // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
288 // (startRadius == endRadius).
290 startRadius, endRadius, SkGradientBaseShader::kDegenerateThreshold)) {
291 // Degenerate case, where the interpolation region area approaches zero. The proper
292 // behavior depends on the tile mode, which is consistent with the default degenerate
293 // gradient behavior, except when mode = clamp and the radii > 0.
294 if (mode == SkTileMode::kClamp &&
296 // The interpolation region becomes an infinitely thin ring at the radius, so the
297 // final gradient will be the first color repeated from p=0 to 1, and then a hard
298 // stop switching to the last color at p=1.
299 static constexpr SkScalar circlePos[3] = {0, 1, 1};
300 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
301 return MakeRadial(start,
302 endRadius,
303 reColors,
304 std::move(colorSpace),
305 circlePos,
306 3,
307 mode,
308 interpolation,
309 localMatrix);
310 } else {
311 // Otherwise use the default degenerate case
313 colors, pos, colorCount, std::move(colorSpace), mode);
314 }
316 // We can treat this gradient as radial, which is faster. If we got here, we know
317 // that endRadius is not equal to 0, so this produces a meaningful gradient
318 return MakeRadial(start,
319 endRadius,
320 colors,
321 std::move(colorSpace),
322 pos,
323 colorCount,
324 mode,
325 interpolation,
326 localMatrix);
327 }
328 // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
329 // regular 2pt constructor.
330 }
331
332 if (localMatrix && !localMatrix->invert(nullptr)) {
333 return nullptr;
334 }
335 EXPAND_1_COLOR(colorCount);
336
338 colors, std::move(colorSpace), pos, colorCount, mode, interpolation);
339 return SkConicalGradient::Create(start, startRadius, end, endRadius, desc, localMatrix);
340}
341
342#undef EXPAND_1_COLOR
343
345 SkScalar startRadius,
346 const SkPoint& end,
347 SkScalar endRadius,
348 const SkColor colors[],
349 const SkScalar pos[],
350 int colorCount,
352 uint32_t flags,
353 const SkMatrix* localMatrix) {
354 SkColorConverter converter(colors, colorCount);
356 startRadius,
357 end,
358 endRadius,
359 converter.fColors4f.begin(),
360 nullptr,
361 pos,
362 colorCount,
363 mode,
364 flags,
365 localMatrix);
366}
367
370 // Previous name
371 SkFlattenable::Register("SkTwoPointConicalGradient", SkConicalGradient::CreateProc);
372}
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)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
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
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Definition: SkArenaAlloc.h:120
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
static bool MapToUnitX(const SkPoint &startCenter, const SkPoint &endCenter, SkMatrix *dstMatrix)
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 sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, 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)
Definition: SkMatrix.cpp:1385
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
bool isIdentity() const
Definition: SkMatrix.h:223
void append(SkRasterPipelineOp, void *=nullptr)
float SkScalar
Definition: extension.cpp:12
struct MyStruct s
FlutterSemanticsFlag flags
glong glong end
static float max(float r, float g, float b)
Definition: hsl.cpp:49
size_t length
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
PODArray< SkColor > colors
Definition: SkRecords.h:276
string converter
Definition: cacheimages.py:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
const Scalar scale
bool set(SkScalar r0, SkScalar r1, SkMatrix *matrix)
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63