Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(433)

Side by Side Diff: src/effects/SkDashPathEffect.cpp

Issue 314623004: Move Dashing filterPath to a dashing utils file (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Use gypi instead of gyp Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « include/effects/SkDashPathEffect.h ('k') | src/utils/SkDashPath.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2006 The Android Open Source Project 2 * Copyright 2006 The Android Open Source Project
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkDashPathEffect.h" 8 #include "SkDashPathEffect.h"
9
10 #include "SkDashPathPriv.h"
9 #include "SkReadBuffer.h" 11 #include "SkReadBuffer.h"
10 #include "SkWriteBuffer.h" 12 #include "SkWriteBuffer.h"
11 #include "SkPathMeasure.h"
12
13 static inline int is_even(int x) {
14 return (~x) << 31;
15 }
16
17 static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
18 int32_t* index, int count) {
19 for (int i = 0; i < count; ++i) {
20 if (phase > intervals[i]) {
21 phase -= intervals[i];
22 } else {
23 *index = i;
24 return intervals[i] - phase;
25 }
26 }
27 // If we get here, phase "appears" to be larger than our length. This
28 // shouldn't happen with perfect precision, but we can accumulate errors
29 // during the initial length computation (rounding can make our sum be too
30 // big or too small. In that event, we just have to eat the error here.
31 *index = 0;
32 return intervals[0];
33 }
34
35 void SkDashPathEffect::setInternalMembers(SkScalar phase) {
36 SkScalar len = 0;
37 for (int i = 0; i < fCount; i++) {
38 len += fIntervals[i];
39 }
40 fIntervalLength = len;
41
42 // watch out for values that might make us go out of bounds
43 if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
44
45 // Adjust phase to be between 0 and len, "flipping" phase if negative.
46 // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
47 if (phase < 0) {
48 phase = -phase;
49 if (phase > len) {
50 phase = SkScalarMod(phase, len);
51 }
52 phase = len - phase;
53
54 // Due to finite precision, it's possible that phase == len,
55 // even after the subtract (if len >>> phase), so fix that here.
56 // This fixes http://crbug.com/124652 .
57 SkASSERT(phase <= len);
58 if (phase == len) {
59 phase = 0;
60 }
61 } else if (phase >= len) {
62 phase = SkScalarMod(phase, len);
63 }
64 SkASSERT(phase >= 0 && phase < len);
65
66 fPhase = phase;
67
68 fInitialDashLength = FindFirstInterval(fIntervals, fPhase,
69 &fInitialDashIndex, fCount);
70
71 SkASSERT(fInitialDashLength >= 0);
72 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
73 } else {
74 fInitialDashLength = -1; // signal bad dash intervals
75 }
76 }
77 13
78 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, 14 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
79 SkScalar phase) { 15 SkScalar phase) {
80 SkASSERT(intervals); 16 SkASSERT(intervals);
81 SkASSERT(count > 1 && SkAlign2(count) == count); 17 SkASSERT(count > 1 && SkAlign2(count) == count);
82 18
83 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); 19 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
84 fCount = count; 20 fCount = count;
85 for (int i = 0; i < count; i++) { 21 for (int i = 0; i < count; i++) {
86 SkASSERT(intervals[i] >= 0); 22 SkASSERT(intervals[i] >= 0);
87 fIntervals[i] = intervals[i]; 23 fIntervals[i] = intervals[i];
88 } 24 }
89 25
90 this->setInternalMembers(phase); 26 // set the internal data members
27 SkDashPath::CalcDashParameters(phase, fIntervals, fCount, &fInitialDashLengt h,
28 &fInitialDashIndex, &fIntervalLength, &fPhase );
91 } 29 }
92 30
93 SkDashPathEffect::~SkDashPathEffect() { 31 SkDashPathEffect::~SkDashPathEffect() {
94 sk_free(fIntervals); 32 sk_free(fIntervals);
95 } 33 }
96 34
97 static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
98 SkScalar radius = SkScalarHalf(rec.getWidth());
99 if (0 == radius) {
100 radius = SK_Scalar1; // hairlines
101 }
102 if (SkPaint::kMiter_Join == rec.getJoin()) {
103 radius = SkScalarMul(radius, rec.getMiter());
104 }
105 rect->outset(radius, radius);
106 }
107
108 // Only handles lines for now. If returns true, dstPath is the new (smaller)
109 // path. If returns false, then dstPath parameter is ignored.
110 static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
111 const SkRect* cullRect, SkScalar intervalLength,
112 SkPath* dstPath) {
113 if (NULL == cullRect) {
114 return false;
115 }
116
117 SkPoint pts[2];
118 if (!srcPath.isLine(pts)) {
119 return false;
120 }
121
122 SkRect bounds = *cullRect;
123 outset_for_stroke(&bounds, rec);
124
125 SkScalar dx = pts[1].x() - pts[0].x();
126 SkScalar dy = pts[1].y() - pts[0].y();
127
128 // just do horizontal lines for now (lazy)
129 if (dy) {
130 return false;
131 }
132
133 SkScalar minX = pts[0].fX;
134 SkScalar maxX = pts[1].fX;
135
136 if (maxX < bounds.fLeft || minX > bounds.fRight) {
137 return false;
138 }
139
140 if (dx < 0) {
141 SkTSwap(minX, maxX);
142 }
143
144 // Now we actually perform the chop, removing the excess to the left and
145 // right of the bounds (keeping our new line "in phase" with the dash,
146 // hence the (mod intervalLength).
147
148 if (minX < bounds.fLeft) {
149 minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX,
150 intervalLength);
151 }
152 if (maxX > bounds.fRight) {
153 maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight,
154 intervalLength);
155 }
156
157 SkASSERT(maxX >= minX);
158 if (dx < 0) {
159 SkTSwap(minX, maxX);
160 }
161 pts[0].fX = minX;
162 pts[1].fX = maxX;
163
164 dstPath->moveTo(pts[0]);
165 dstPath->lineTo(pts[1]);
166 return true;
167 }
168
169 class SpecialLineRec {
170 public:
171 bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
172 int intervalCount, SkScalar intervalLength) {
173 if (rec->isHairlineStyle() || !src.isLine(fPts)) {
174 return false;
175 }
176
177 // can relax this in the future, if we handle square and round caps
178 if (SkPaint::kButt_Cap != rec->getCap()) {
179 return false;
180 }
181
182 SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
183
184 fTangent = fPts[1] - fPts[0];
185 if (fTangent.isZero()) {
186 return false;
187 }
188
189 fPathLength = pathLength;
190 fTangent.scale(SkScalarInvert(pathLength));
191 fTangent.rotateCCW(&fNormal);
192 fNormal.scale(SkScalarHalf(rec->getWidth()));
193
194 // now estimate how many quads will be added to the path
195 // resulting segments = pathLen * intervalCount / intervalLen
196 // resulting points = 4 * segments
197
198 SkScalar ptCount = SkScalarMulDiv(pathLength,
199 SkIntToScalar(intervalCount),
200 intervalLength);
201 int n = SkScalarCeilToInt(ptCount) << 2;
202 dst->incReserve(n);
203
204 // we will take care of the stroking
205 rec->setFillStyle();
206 return true;
207 }
208
209 void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
210 SkASSERT(d0 < fPathLength);
211 // clamp the segment to our length
212 if (d1 > fPathLength) {
213 d1 = fPathLength;
214 }
215
216 SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0);
217 SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1);
218 SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0);
219 SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1);
220
221 SkPoint pts[4];
222 pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY); // moveTo
223 pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY); // lineTo
224 pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY); // lineTo
225 pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY); // lineTo
226
227 path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
228 }
229
230 private:
231 SkPoint fPts[2];
232 SkVector fTangent;
233 SkVector fNormal;
234 SkScalar fPathLength;
235 };
236
237 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, 35 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
238 SkStrokeRec* rec, const SkRect* cullRect) const { 36 SkStrokeRec* rec, const SkRect* cullRect) const {
239 // we do nothing if the src wants to be filled, or if our dashlength is 0 37 return SkDashPath::FilterDashPath(dst, src, rec, cullRect, fIntervals, fCoun t,
240 if (rec->isFillStyle() || fInitialDashLength < 0) { 38 fInitialDashLength, fInitialDashIndex, fIn tervalLength);
241 return false;
242 }
243
244 const SkScalar* intervals = fIntervals;
245 SkScalar dashCount = 0;
246 int segCount = 0;
247
248 SkPath cullPathStorage;
249 const SkPath* srcPtr = &src;
250 if (cull_path(src, *rec, cullRect, fIntervalLength, &cullPathStorage)) {
251 srcPtr = &cullPathStorage;
252 }
253
254 SpecialLineRec lineRec;
255 bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLen gth);
256
257 SkPathMeasure meas(*srcPtr, false);
258
259 do {
260 bool skipFirstSegment = meas.isClosed();
261 bool addedSegment = false;
262 SkScalar length = meas.getLength();
263 int index = fInitialDashIndex;
264
265 // Since the path length / dash length ratio may be arbitrarily large, w e can exert
266 // significant memory pressure while attempting to build the filtered pa th. To avoid this,
267 // we simply give up dashing beyond a certain threshold.
268 //
269 // The original bug report (http://crbug.com/165432) is based on a path yielding more than
270 // 90 million dash segments and crashing the memory allocator. A limit o f 1 million
271 // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
272 // maximum dash memory overhead at roughly 17MB per path.
273 static const SkScalar kMaxDashCount = 1000000;
274 dashCount += length * (fCount >> 1) / fIntervalLength;
275 if (dashCount > kMaxDashCount) {
276 dst->reset();
277 return false;
278 }
279
280 // Using double precision to avoid looping indefinitely due to single pr ecision rounding
281 // (for extreme path_length/dash_length ratios). See test_infinite_dash( ) unittest.
282 double distance = 0;
283 double dlen = fInitialDashLength;
284
285 while (distance < length) {
286 SkASSERT(dlen >= 0);
287 addedSegment = false;
288 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
289 addedSegment = true;
290 ++segCount;
291
292 if (specialLine) {
293 lineRec.addSegment(SkDoubleToScalar(distance),
294 SkDoubleToScalar(distance + dlen),
295 dst);
296 } else {
297 meas.getSegment(SkDoubleToScalar(distance),
298 SkDoubleToScalar(distance + dlen),
299 dst, true);
300 }
301 }
302 distance += dlen;
303
304 // clear this so we only respect it the first time around
305 skipFirstSegment = false;
306
307 // wrap around our intervals array if necessary
308 index += 1;
309 SkASSERT(index <= fCount);
310 if (index == fCount) {
311 index = 0;
312 }
313
314 // fetch our next dlen
315 dlen = intervals[index];
316 }
317
318 // extend if we ended on a segment and we need to join up with the (skip ped) initial segment
319 if (meas.isClosed() && is_even(fInitialDashIndex) &&
320 fInitialDashLength > 0) {
321 meas.getSegment(0, fInitialDashLength, dst, !addedSegment);
322 ++segCount;
323 }
324 } while (meas.nextContour());
325
326 if (segCount > 1) {
327 dst->setConvexity(SkPath::kConcave_Convexity);
328 }
329
330 return true;
331 } 39 }
332 40
333 // Currently asPoints is more restrictive then it needs to be. In the future 41 // Currently asPoints is more restrictive then it needs to be. In the future
334 // we need to: 42 // we need to:
335 // allow kRound_Cap capping (could allow rotations in the matrix with this) 43 // allow kRound_Cap capping (could allow rotations in the matrix with this)
336 // allow paths to be returned 44 // allow paths to be returned
337 bool SkDashPathEffect::asPoints(PointData* results, 45 bool SkDashPathEffect::asPoints(PointData* results,
338 const SkPath& src, 46 const SkPath& src,
339 const SkStrokeRec& rec, 47 const SkStrokeRec& rec,
340 const SkMatrix& matrix, 48 const SkMatrix& matrix,
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
563 271
564 if (useOldPic) { 272 if (useOldPic) {
565 fPhase = 0; 273 fPhase = 0;
566 if (fInitialDashLength != -1) { // Signal for bad dash interval 274 if (fInitialDashLength != -1) { // Signal for bad dash interval
567 for (int i = 0; i < fInitialDashIndex; ++i) { 275 for (int i = 0; i < fInitialDashIndex; ++i) {
568 fPhase += fIntervals[i]; 276 fPhase += fIntervals[i];
569 } 277 }
570 fPhase += fIntervals[fInitialDashIndex] - fInitialDashLength; 278 fPhase += fIntervals[fInitialDashIndex] - fInitialDashLength;
571 } 279 }
572 } else { 280 } else {
573 this->setInternalMembers(fPhase); 281 // set the internal data members, fPhase should have been between 0 and intervalLength
282 // when written to buffer so no need to adjust it
283 SkDashPath::CalcDashParameters(fPhase, fIntervals, fCount, &fInitialDash Length,
284 &fInitialDashIndex, &fIntervalLength);
574 } 285 }
575 } 286 }
OLDNEW
« no previous file with comments | « include/effects/SkDashPathEffect.h ('k') | src/utils/SkDashPath.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698