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 | |
9 | #include "include/core/SkPathMeasure.h" |
10 | #include "include/core/SkStrokeRec.h" |
11 | #include "include/effects/Sk1DPathEffect.h" |
12 | #include "src/core/SkReadBuffer.h" |
13 | #include "src/core/SkWriteBuffer.h" |
14 | |
15 | // Since we are stepping by a float, the do/while loop might go on forever (or nearly so). |
16 | // Put in a governor to limit crash values from looping too long (and allocating too much ram). |
17 | #define MAX_REASONABLE_ITERATIONS 100000 |
18 | |
19 | bool Sk1DPathEffect::onFilterPath(SkPath* dst, const SkPath& src, |
20 | SkStrokeRec*, const SkRect*) const { |
21 | SkPathMeasure meas(src, false); |
22 | do { |
23 | int governor = MAX_REASONABLE_ITERATIONS; |
24 | SkScalar length = meas.getLength(); |
25 | SkScalar distance = this->begin(length); |
26 | while (distance < length && --governor >= 0) { |
27 | SkScalar delta = this->next(dst, distance, meas); |
28 | if (delta <= 0) { |
29 | break; |
30 | } |
31 | distance += delta; |
32 | } |
33 | } while (meas.nextContour()); |
34 | return true; |
35 | } |
36 | |
37 | /////////////////////////////////////////////////////////////////////////////// |
38 | |
39 | SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, |
40 | Style style) : fPath(path) { |
41 | SkASSERT(advance > 0 && !path.isEmpty()); |
42 | SkASSERT((unsigned)style <= kMorph_Style); |
43 | |
44 | // Make the path thread-safe. |
45 | fPath.updateBoundsCache(); |
46 | (void)fPath.getGenerationID(); |
47 | |
48 | // cleanup their phase parameter, inverting it so that it becomes an |
49 | // offset along the path (to match the interpretation in PostScript) |
50 | if (phase < 0) { |
51 | phase = -phase; |
52 | if (phase > advance) { |
53 | phase = SkScalarMod(phase, advance); |
54 | } |
55 | } else { |
56 | if (phase > advance) { |
57 | phase = SkScalarMod(phase, advance); |
58 | } |
59 | phase = advance - phase; |
60 | } |
61 | // now catch the edge case where phase == advance (within epsilon) |
62 | if (phase >= advance) { |
63 | phase = 0; |
64 | } |
65 | SkASSERT(phase >= 0); |
66 | |
67 | fAdvance = advance; |
68 | fInitialOffset = phase; |
69 | |
70 | if ((unsigned)style > kMorph_Style) { |
71 | SkDEBUGF("SkPath1DPathEffect style enum out of range %d\n" , style); |
72 | } |
73 | fStyle = style; |
74 | } |
75 | |
76 | bool SkPath1DPathEffect::onFilterPath(SkPath* dst, const SkPath& src, |
77 | SkStrokeRec* rec, const SkRect* cullRect) const { |
78 | rec->setFillStyle(); |
79 | return this->INHERITED::onFilterPath(dst, src, rec, cullRect); |
80 | } |
81 | |
82 | static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, |
83 | SkPathMeasure& meas, SkScalar dist) { |
84 | for (int i = 0; i < count; i++) { |
85 | SkPoint pos; |
86 | SkVector tangent; |
87 | |
88 | SkScalar sx = src[i].fX; |
89 | SkScalar sy = src[i].fY; |
90 | |
91 | if (!meas.getPosTan(dist + sx, &pos, &tangent)) { |
92 | return false; |
93 | } |
94 | |
95 | SkMatrix matrix; |
96 | SkPoint pt; |
97 | |
98 | pt.set(sx, sy); |
99 | matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); |
100 | matrix.preTranslate(-sx, 0); |
101 | matrix.postTranslate(pos.fX, pos.fY); |
102 | matrix.mapPoints(&dst[i], &pt, 1); |
103 | } |
104 | return true; |
105 | } |
106 | |
107 | /* TODO |
108 | |
109 | Need differentially more subdivisions when the follow-path is curvy. Not sure how to |
110 | determine that, but we need it. I guess a cheap answer is let the caller tell us, |
111 | but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. |
112 | */ |
113 | static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, |
114 | SkScalar dist) { |
115 | SkPath::Iter iter(src, false); |
116 | SkPoint srcP[4], dstP[3]; |
117 | SkPath::Verb verb; |
118 | |
119 | while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { |
120 | switch (verb) { |
121 | case SkPath::kMove_Verb: |
122 | if (morphpoints(dstP, srcP, 1, meas, dist)) { |
123 | dst->moveTo(dstP[0]); |
124 | } |
125 | break; |
126 | case SkPath::kLine_Verb: |
127 | srcP[2] = srcP[1]; |
128 | srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), |
129 | SkScalarAve(srcP[0].fY, srcP[2].fY)); |
130 | // fall through to quad |
131 | case SkPath::kQuad_Verb: |
132 | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { |
133 | dst->quadTo(dstP[0], dstP[1]); |
134 | } |
135 | break; |
136 | case SkPath::kConic_Verb: |
137 | if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { |
138 | dst->conicTo(dstP[0], dstP[1], iter.conicWeight()); |
139 | } |
140 | break; |
141 | case SkPath::kCubic_Verb: |
142 | if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { |
143 | dst->cubicTo(dstP[0], dstP[1], dstP[2]); |
144 | } |
145 | break; |
146 | case SkPath::kClose_Verb: |
147 | dst->close(); |
148 | break; |
149 | default: |
150 | SkDEBUGFAIL("unknown verb" ); |
151 | break; |
152 | } |
153 | } |
154 | } |
155 | |
156 | SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) const { |
157 | return fInitialOffset; |
158 | } |
159 | |
160 | sk_sp<SkFlattenable> SkPath1DPathEffect::CreateProc(SkReadBuffer& buffer) { |
161 | SkScalar advance = buffer.readScalar(); |
162 | SkPath path; |
163 | buffer.readPath(&path); |
164 | SkScalar phase = buffer.readScalar(); |
165 | Style style = buffer.read32LE(kLastEnum_Style); |
166 | return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr; |
167 | } |
168 | |
169 | void SkPath1DPathEffect::flatten(SkWriteBuffer& buffer) const { |
170 | buffer.writeScalar(fAdvance); |
171 | buffer.writePath(fPath); |
172 | buffer.writeScalar(fInitialOffset); |
173 | buffer.writeUInt(fStyle); |
174 | } |
175 | |
176 | SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, |
177 | SkPathMeasure& meas) const { |
178 | #if defined(IS_FUZZING_WITH_LIBFUZZER) |
179 | if (dst->countPoints() > 100000) { |
180 | return fAdvance; |
181 | } |
182 | #endif |
183 | switch (fStyle) { |
184 | case kTranslate_Style: { |
185 | SkPoint pos; |
186 | if (meas.getPosTan(distance, &pos, nullptr)) { |
187 | dst->addPath(fPath, pos.fX, pos.fY); |
188 | } |
189 | } break; |
190 | case kRotate_Style: { |
191 | SkMatrix matrix; |
192 | if (meas.getMatrix(distance, &matrix)) { |
193 | dst->addPath(fPath, matrix); |
194 | } |
195 | } break; |
196 | case kMorph_Style: |
197 | morphpath(dst, fPath, meas, distance); |
198 | break; |
199 | default: |
200 | SkDEBUGFAIL("unknown Style enum" ); |
201 | break; |
202 | } |
203 | return fAdvance; |
204 | } |
205 | |
206 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
207 | |
208 | sk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advance, SkScalar phase, |
209 | Style style) { |
210 | if (advance <= 0 || !SkScalarIsFinite(advance) || !SkScalarIsFinite(phase) || path.isEmpty()) { |
211 | return nullptr; |
212 | } |
213 | return sk_sp<SkPathEffect>(new SkPath1DPathEffect(path, advance, phase, style)); |
214 | } |
215 | |