Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Sk1DPathEffect.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2006 The Android Open Source Project
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
9
12#include "include/core/SkPath.h"
24
25struct SkRect;
26
27// Since we are stepping by a float, the do/while loop might go on forever (or nearly so).
28// Put in a governor to limit crash values from looping too long (and allocating too much ram).
29#define MAX_REASONABLE_ITERATIONS 100000
30
32public:
33protected:
34 bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
35 const SkMatrix&) const override {
36 SkPathMeasure meas(src, false);
37 do {
38 int governor = MAX_REASONABLE_ITERATIONS;
39 SkScalar length = meas.getLength();
40 SkScalar distance = this->begin(length);
41 while (distance < length && --governor >= 0) {
42 SkScalar delta = this->next(dst, distance, meas);
43 if (delta <= 0) {
44 break;
45 }
46 distance += delta;
47 }
48 if (governor < 0) {
49 return false;
50 }
51 } while (meas.nextContour());
52 return true;
53 }
54
55 /** Called at the start of each contour, returns the initial offset
56 into that contour.
57 */
58 virtual SkScalar begin(SkScalar contourLength) const = 0;
59 /** Called with the current distance along the path, with the current matrix
60 for the point/tangent at the specified distance.
61 Return the distance to travel for the next call. If return <= 0, then that
62 contour is done.
63 */
64 virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
65
66private:
67 // For simplicity, assume fast bounds cannot be computed
68 bool computeFastBounds(SkRect*) const override { return false; }
69};
70
71///////////////////////////////////////////////////////////////////////////////
72
74public:
75 SkPath1DPathEffectImpl(const SkPath& path, SkScalar advance, SkScalar phase,
76 SkPath1DPathEffect::Style style) : fPath(path) {
77 SkASSERT(advance > 0 && !path.isEmpty());
78
79 // Make the path thread-safe.
80 fPath.updateBoundsCache();
81 (void)fPath.getGenerationID();
82
83 // cleanup their phase parameter, inverting it so that it becomes an
84 // offset along the path (to match the interpretation in PostScript)
85 if (phase < 0) {
86 phase = -phase;
87 if (phase > advance) {
88 phase = SkScalarMod(phase, advance);
89 }
90 } else {
91 if (phase > advance) {
92 phase = SkScalarMod(phase, advance);
93 }
94 phase = advance - phase;
95 }
96 // now catch the edge case where phase == advance (within epsilon)
97 if (phase >= advance) {
98 phase = 0;
99 }
100 SkASSERT(phase >= 0);
101
102 fAdvance = advance;
103 fInitialOffset = phase;
104 fStyle = style;
105 }
106
107 bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
108 const SkRect* cullRect, const SkMatrix& ctm) const override {
109 rec->setFillStyle();
110 return this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm);
111 }
112
113 SkScalar begin(SkScalar contourLength) const override {
114 return fInitialOffset;
115 }
116
117 SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override;
118
120 SkScalar advance = buffer.readScalar();
121 SkPath path;
122 buffer.readPath(&path);
123 SkScalar phase = buffer.readScalar();
125 return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr;
126 }
127
128 void flatten(SkWriteBuffer& buffer) const override {
129 buffer.writeScalar(fAdvance);
130 buffer.writePath(fPath);
131 buffer.writeScalar(fInitialOffset);
132 buffer.writeUInt(fStyle);
133 }
134
135 Factory getFactory() const override { return CreateProc; }
136 const char* getTypeName() const override { return "SkPath1DPathEffect"; }
137
138private:
139 SkPath fPath; // copied from constructor
140 SkScalar fAdvance; // copied from constructor
141 SkScalar fInitialOffset; // computed from phase
142 SkPath1DPathEffect::Style fStyle; // copied from constructor
143
144 using INHERITED = Sk1DPathEffect;
145};
146
147static bool morphpoints(SkPoint dst[], const SkPoint src[], int count,
148 SkPathMeasure& meas, SkScalar dist) {
149 for (int i = 0; i < count; i++) {
150 SkPoint pos;
151 SkVector tangent;
152
153 SkScalar sx = src[i].fX;
154 SkScalar sy = src[i].fY;
155
156 if (!meas.getPosTan(dist + sx, &pos, &tangent)) {
157 return false;
158 }
159
160 SkMatrix matrix;
161 SkPoint pt;
162
163 pt.set(sx, sy);
164 matrix.setSinCos(tangent.fY, tangent.fX, 0, 0);
165 matrix.preTranslate(-sx, 0);
166 matrix.postTranslate(pos.fX, pos.fY);
167 matrix.mapPoints(&dst[i], &pt, 1);
168 }
169 return true;
170}
171
172/* TODO
173
174Need differentially more subdivisions when the follow-path is curvy. Not sure how to
175determine that, but we need it. I guess a cheap answer is let the caller tell us,
176but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
177*/
178static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
179 SkScalar dist) {
180 SkPath::Iter iter(src, false);
181 SkPoint srcP[4], dstP[3];
182 SkPath::Verb verb;
183
184 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
185 switch (verb) {
187 if (morphpoints(dstP, srcP, 1, meas, dist)) {
188 dst->moveTo(dstP[0]);
189 }
190 break;
192 srcP[2] = srcP[1];
193 srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX),
194 SkScalarAve(srcP[0].fY, srcP[2].fY));
195 [[fallthrough]];
197 if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
198 dst->quadTo(dstP[0], dstP[1]);
199 }
200 break;
202 if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
203 dst->conicTo(dstP[0], dstP[1], iter.conicWeight());
204 }
205 break;
207 if (morphpoints(dstP, &srcP[1], 3, meas, dist)) {
208 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
209 }
210 break;
212 dst->close();
213 break;
214 default:
215 SkDEBUGFAIL("unknown verb");
216 break;
217 }
218 }
219}
220
222 SkPathMeasure& meas) const {
223#if defined(SK_BUILD_FOR_FUZZER)
224 if (dst->countPoints() > 100000) {
225 return fAdvance;
226 }
227#endif
228 switch (fStyle) {
230 SkPoint pos;
231 if (meas.getPosTan(distance, &pos, nullptr)) {
232 dst->addPath(fPath, pos.fX, pos.fY);
233 }
234 } break;
236 SkMatrix matrix;
237 if (meas.getMatrix(distance, &matrix)) {
238 dst->addPath(fPath, matrix);
239 }
240 } break;
242 morphpath(dst, fPath, meas, distance);
243 break;
244 }
245 return fAdvance;
246}
247
248///////////////////////////////////////////////////////////////////////////////////////////////////
249
251 Style style) {
252 if (advance <= 0 || !SkIsFinite(advance, phase) || path.isEmpty()) {
253 return nullptr;
254 }
255 return sk_sp<SkPathEffect>(new SkPath1DPathEffectImpl(path, advance, phase, style));
256}
257
int count
SkPoint pos
static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, SkPathMeasure &meas, SkScalar dist)
static void morphpath(SkPath *dst, const SkPath &src, SkPathMeasure &meas, SkScalar dist)
#define MAX_REASONABLE_ITERATIONS
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SK_REGISTER_FLATTENABLE(type)
static bool SkIsFinite(T x, Pack... values)
#define SkScalarMod(x, y)
Definition SkScalar.h:41
#define SkScalarAve(a, b)
Definition SkScalar.h:74
virtual SkScalar next(SkPath *dst, SkScalar dist, SkPathMeasure &) const =0
bool onFilterPath(SkPath *dst, const SkPath &src, SkStrokeRec *, const SkRect *, const SkMatrix &) const override
bool computeFastBounds(SkRect *) const override
virtual SkScalar begin(SkScalar contourLength) const =0
sk_sp< SkFlattenable >(* Factory)(SkReadBuffer &)
SkPath1DPathEffectImpl(const SkPath &path, SkScalar advance, SkScalar phase, SkPath1DPathEffect::Style style)
void flatten(SkWriteBuffer &buffer) const override
SkScalar next(SkPath *, SkScalar, SkPathMeasure &) const override
const char * getTypeName() const override
bool onFilterPath(SkPath *dst, const SkPath &src, SkStrokeRec *rec, const SkRect *cullRect, const SkMatrix &ctm) const override
static sk_sp< SkFlattenable > CreateProc(SkReadBuffer &buffer)
Factory getFactory() const override
SkScalar begin(SkScalar contourLength) const override
static void RegisterFlattenables()
static sk_sp< SkPathEffect > Make(const SkPath &path, SkScalar advance, SkScalar phase, Style)
bool getMatrix(SkScalar distance, SkMatrix *matrix, MatrixFlags flags=kGetPosAndTan_MatrixFlag)
SkScalar getLength()
bool getPosTan(SkScalar distance, SkPoint *position, SkVector *tangent)
Verb next(SkPoint pts[4])
Definition SkPath.cpp:1837
SkScalar conicWeight() const
Definition SkPath.h:1527
uint32_t getGenerationID() const
Definition SkPath.cpp:356
void updateBoundsCache() const
Definition SkPath.h:524
@ kClose_Verb
Definition SkPath.h:1463
@ kMove_Verb
Definition SkPath.h:1458
@ kConic_Verb
Definition SkPath.h:1461
@ kDone_Verb
Definition SkPath.h:1464
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
void setFillStyle()
float SkScalar
Definition extension.cpp:12
static const uint8_t buffer[]
size_t length
float fX
x-axis value
void set(float x, float y)
float fY
y-axis value