/src/skia/src/effects/Sk1DPathEffect.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
8 | | #include "include/effects/Sk1DPathEffect.h" |
9 | | |
10 | | #include "include/core/SkFlattenable.h" |
11 | | #include "include/core/SkMatrix.h" |
12 | | #include "include/core/SkPath.h" |
13 | | #include "include/core/SkPathEffect.h" |
14 | | #include "include/core/SkPathMeasure.h" |
15 | | #include "include/core/SkPoint.h" |
16 | | #include "include/core/SkRefCnt.h" |
17 | | #include "include/core/SkScalar.h" |
18 | | #include "include/core/SkStrokeRec.h" |
19 | | #include "include/core/SkTypes.h" |
20 | | #include "include/private/base/SkFloatingPoint.h" |
21 | | #include "src/core/SkPathEffectBase.h" |
22 | | #include "src/core/SkReadBuffer.h" |
23 | | #include "src/core/SkWriteBuffer.h" |
24 | | |
25 | | struct 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 | 72.3k | #define MAX_REASONABLE_ITERATIONS 100000 |
30 | | |
31 | | class Sk1DPathEffect : public SkPathEffectBase { |
32 | | public: |
33 | | protected: |
34 | | bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, |
35 | 14.7k | const SkMatrix&) const override { |
36 | 14.7k | SkPathMeasure meas(src, false); |
37 | 72.3k | do { |
38 | 72.3k | int governor = MAX_REASONABLE_ITERATIONS; |
39 | 72.3k | SkScalar length = meas.getLength(); |
40 | 72.3k | SkScalar distance = this->begin(length); |
41 | 216M | while (distance < length && --governor >= 0) { |
42 | 216M | SkScalar delta = this->next(dst, distance, meas); |
43 | 216M | if (delta <= 0) { |
44 | 0 | break; |
45 | 0 | } |
46 | 216M | distance += delta; |
47 | 216M | } |
48 | 72.3k | if (governor < 0) { |
49 | 2.11k | return false; |
50 | 2.11k | } |
51 | 72.3k | } while (meas.nextContour()); |
52 | 12.6k | return true; |
53 | 14.7k | } |
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 | | |
66 | | private: |
67 | | // For simplicity, assume fast bounds cannot be computed |
68 | 4.42k | bool computeFastBounds(SkRect*) const override { return false; } |
69 | | }; |
70 | | |
71 | | /////////////////////////////////////////////////////////////////////////////// |
72 | | |
73 | | class SkPath1DPathEffectImpl : public Sk1DPathEffect { |
74 | | public: |
75 | | SkPath1DPathEffectImpl(const SkPath& path, SkScalar advance, SkScalar phase, |
76 | 2.35k | SkPath1DPathEffect::Style style) : fPath(path) { |
77 | 2.35k | SkASSERT(advance > 0 && !path.isEmpty()); |
78 | | |
79 | | // Make the path thread-safe. |
80 | 2.35k | fPath.updateBoundsCache(); |
81 | 2.35k | (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 | 2.35k | if (phase < 0) { |
86 | 389 | phase = -phase; |
87 | 389 | if (phase > advance) { |
88 | 262 | phase = SkScalarMod(phase, advance); |
89 | 262 | } |
90 | 1.96k | } else { |
91 | 1.96k | if (phase > advance) { |
92 | 480 | phase = SkScalarMod(phase, advance); |
93 | 480 | } |
94 | 1.96k | phase = advance - phase; |
95 | 1.96k | } |
96 | | // now catch the edge case where phase == advance (within epsilon) |
97 | 2.35k | if (phase >= advance) { |
98 | 1.14k | phase = 0; |
99 | 1.14k | } |
100 | 2.35k | SkASSERT(phase >= 0); |
101 | | |
102 | 2.35k | fAdvance = advance; |
103 | 2.35k | fInitialOffset = phase; |
104 | 2.35k | fStyle = style; |
105 | 2.35k | } |
106 | | |
107 | | bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, |
108 | 14.7k | const SkRect* cullRect, const SkMatrix& ctm) const override { |
109 | 14.7k | rec->setFillStyle(); |
110 | 14.7k | return this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm); |
111 | 14.7k | } |
112 | | |
113 | 72.3k | SkScalar begin(SkScalar contourLength) const override { |
114 | 72.3k | return fInitialOffset; |
115 | 72.3k | } |
116 | | |
117 | | SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override; |
118 | | |
119 | 366 | static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { |
120 | 366 | SkScalar advance = buffer.readScalar(); |
121 | 366 | SkPath path; |
122 | 366 | buffer.readPath(&path); |
123 | 366 | SkScalar phase = buffer.readScalar(); |
124 | 366 | SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style); |
125 | 366 | return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr; |
126 | 366 | } |
127 | | |
128 | 2.22k | void flatten(SkWriteBuffer& buffer) const override { |
129 | 2.22k | buffer.writeScalar(fAdvance); |
130 | 2.22k | buffer.writePath(fPath); |
131 | 2.22k | buffer.writeScalar(fInitialOffset); |
132 | 2.22k | buffer.writeUInt(fStyle); |
133 | 2.22k | } |
134 | | |
135 | 2.22k | Factory getFactory() const override { return CreateProc; } |
136 | 2.22k | const char* getTypeName() const override { return "SkPath1DPathEffect"; } |
137 | | |
138 | | private: |
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 | | |
147 | | static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, |
148 | 45.4M | SkPathMeasure& meas, SkScalar dist) { |
149 | 114M | for (int i = 0; i < count; i++) { |
150 | 72.2M | SkPoint pos; |
151 | 72.2M | SkVector tangent; |
152 | | |
153 | 72.2M | SkScalar sx = src[i].fX; |
154 | 72.2M | SkScalar sy = src[i].fY; |
155 | | |
156 | 72.2M | if (!meas.getPosTan(dist + sx, &pos, &tangent)) { |
157 | 3.34M | return false; |
158 | 3.34M | } |
159 | | |
160 | 68.8M | SkMatrix matrix; |
161 | 68.8M | SkPoint pt; |
162 | | |
163 | 68.8M | pt.set(sx, sy); |
164 | 68.8M | matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); |
165 | 68.8M | matrix.preTranslate(-sx, 0); |
166 | 68.8M | matrix.postTranslate(pos.fX, pos.fY); |
167 | 68.8M | matrix.mapPoints(&dst[i], &pt, 1); |
168 | 68.8M | } |
169 | 42.1M | return true; |
170 | 45.4M | } |
171 | | |
172 | | /* TODO |
173 | | |
174 | | Need differentially more subdivisions when the follow-path is curvy. Not sure how to |
175 | | determine that, but we need it. I guess a cheap answer is let the caller tell us, |
176 | | but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. |
177 | | */ |
178 | | static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, |
179 | 134M | SkScalar dist) { |
180 | 134M | SkPath::Iter iter(src, false); |
181 | 134M | SkPoint srcP[4], dstP[3]; |
182 | 134M | SkPath::Verb verb; |
183 | | |
184 | 182M | while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { |
185 | 47.8M | switch (verb) { |
186 | 21.5M | case SkPath::kMove_Verb: |
187 | 21.5M | if (morphpoints(dstP, srcP, 1, meas, dist)) { |
188 | 20.8M | dst->moveTo(dstP[0]); |
189 | 20.8M | } |
190 | 21.5M | break; |
191 | 7.72M | case SkPath::kLine_Verb: |
192 | 7.72M | srcP[2] = srcP[1]; |
193 | 7.72M | srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), |
194 | 7.72M | SkScalarAve(srcP[0].fY, srcP[2].fY)); |
195 | 7.72M | [[fallthrough]]; |
196 | 9.12M | case SkPath::kQuad_Verb: |
197 | 9.12M | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { |
198 | 7.42M | dst->quadTo(dstP[0], dstP[1]); |
199 | 7.42M | } |
200 | 9.12M | break; |
201 | 8.70M | case SkPath::kConic_Verb: |
202 | 8.70M | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { |
203 | 8.56M | dst->conicTo(dstP[0], dstP[1], iter.conicWeight()); |
204 | 8.56M | } |
205 | 8.70M | break; |
206 | 6.07M | case SkPath::kCubic_Verb: |
207 | 6.07M | if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { |
208 | 5.33M | dst->cubicTo(dstP[0], dstP[1], dstP[2]); |
209 | 5.33M | } |
210 | 6.07M | break; |
211 | 2.35M | case SkPath::kClose_Verb: |
212 | 2.35M | dst->close(); |
213 | 2.35M | break; |
214 | 0 | default: |
215 | 0 | SkDEBUGFAIL("unknown verb"); |
216 | 0 | break; |
217 | 47.8M | } |
218 | 47.8M | } |
219 | 134M | } Sk1DPathEffect.cpp:morphpath(SkPath*, SkPath const&, SkPathMeasure&, float) Line | Count | Source | 179 | 134M | SkScalar dist) { | 180 | 134M | SkPath::Iter iter(src, false); | 181 | 134M | SkPoint srcP[4], dstP[3]; | 182 | 134M | SkPath::Verb verb; | 183 | | | 184 | 182M | while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { | 185 | 47.8M | switch (verb) { | 186 | 21.5M | case SkPath::kMove_Verb: | 187 | 21.5M | if (morphpoints(dstP, srcP, 1, meas, dist)) { | 188 | 20.8M | dst->moveTo(dstP[0]); | 189 | 20.8M | } | 190 | 21.5M | break; | 191 | 7.72M | case SkPath::kLine_Verb: | 192 | 7.72M | srcP[2] = srcP[1]; | 193 | 7.72M | srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), | 194 | 7.72M | SkScalarAve(srcP[0].fY, srcP[2].fY)); | 195 | 7.72M | [[fallthrough]]; | 196 | 9.12M | case SkPath::kQuad_Verb: | 197 | 9.12M | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { | 198 | 7.42M | dst->quadTo(dstP[0], dstP[1]); | 199 | 7.42M | } | 200 | 9.12M | break; | 201 | 8.70M | case SkPath::kConic_Verb: | 202 | 8.70M | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { | 203 | 8.56M | dst->conicTo(dstP[0], dstP[1], iter.conicWeight()); | 204 | 8.56M | } | 205 | 8.70M | break; | 206 | 6.07M | case SkPath::kCubic_Verb: | 207 | 6.07M | if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { | 208 | 5.33M | dst->cubicTo(dstP[0], dstP[1], dstP[2]); | 209 | 5.33M | } | 210 | 6.07M | break; | 211 | 2.35M | case SkPath::kClose_Verb: | 212 | 2.35M | dst->close(); | 213 | 2.35M | break; | 214 | 0 | default: | 215 | 0 | SkDEBUGFAIL("unknown verb"); | 216 | 0 | break; | 217 | 47.8M | } | 218 | 47.8M | } | 219 | 134M | } |
Unexecuted instantiation: Sk1DPathEffect.cpp:morphpath(SkPath*, SkPath const&, SkPathMeasure&, float) |
220 | | |
221 | | SkScalar SkPath1DPathEffectImpl::next(SkPath* dst, SkScalar distance, |
222 | 216M | SkPathMeasure& meas) const { |
223 | 216M | #if defined(SK_BUILD_FOR_FUZZER) |
224 | 216M | if (dst->countPoints() > 100000) { |
225 | 76.9M | return fAdvance; |
226 | 76.9M | } |
227 | 139M | #endif |
228 | 139M | switch (fStyle) { |
229 | 3.46M | case SkPath1DPathEffect::kTranslate_Style: { |
230 | 3.46M | SkPoint pos; |
231 | 3.46M | if (meas.getPosTan(distance, &pos, nullptr)) { |
232 | 3.46M | dst->addPath(fPath, pos.fX, pos.fY); |
233 | 3.46M | } |
234 | 3.46M | } break; |
235 | 1.44M | case SkPath1DPathEffect::kRotate_Style: { |
236 | 1.44M | SkMatrix matrix; |
237 | 1.44M | if (meas.getMatrix(distance, &matrix)) { |
238 | 1.44M | dst->addPath(fPath, matrix); |
239 | 1.44M | } |
240 | 1.44M | } break; |
241 | 134M | case SkPath1DPathEffect::kMorph_Style: |
242 | 134M | morphpath(dst, fPath, meas, distance); |
243 | 134M | break; |
244 | 139M | } |
245 | 139M | return fAdvance; |
246 | 139M | } |
247 | | |
248 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
249 | | |
250 | | sk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advance, SkScalar phase, |
251 | 2.98k | Style style) { |
252 | 2.98k | if (advance <= 0 || !SkIsFinite(advance, phase) || path.isEmpty()) { |
253 | 635 | return nullptr; |
254 | 635 | } |
255 | 2.35k | return sk_sp<SkPathEffect>(new SkPath1DPathEffectImpl(path, advance, phase, style)); |
256 | 2.98k | } |
257 | | |
258 | 3 | void SkPath1DPathEffect::RegisterFlattenables() { |
259 | 3 | SK_REGISTER_FLATTENABLE(SkPath1DPathEffectImpl); |
260 | 3 | } |