1 :
2 : /*
3 : * Copyright 2008 The Android Open Source Project
4 : *
5 : * Use of this source code is governed by a BSD-style license that can be
6 : * found in the LICENSE file.
7 : */
8 :
9 :
10 : #include "SkStrokerPriv.h"
11 : #include "SkGeometry.h"
12 : #include "SkPath.h"
13 :
14 : #define kMaxQuadSubdivide 5
15 : #define kMaxCubicSubdivide 4
16 :
17 0 : static inline bool degenerate_vector(const SkVector& v) {
18 0 : return !SkPoint::CanNormalize(v.fX, v.fY);
19 : }
20 :
21 0 : static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
22 : /* root2/2 is a 45-degree angle
23 : make this constant bigger for more subdivisions (but not >= 1)
24 : */
25 : static const SkScalar kFlatEnoughNormalDotProd =
26 : SK_ScalarSqrt2/2 + SK_Scalar1/10;
27 :
28 : SkASSERT(kFlatEnoughNormalDotProd > 0 &&
29 : kFlatEnoughNormalDotProd < SK_Scalar1);
30 :
31 0 : return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
32 : }
33 :
34 0 : static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
35 : static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
36 :
37 0 : return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
38 : }
39 :
40 0 : static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
41 : SkScalar radius,
42 : SkVector* normal, SkVector* unitNormal) {
43 0 : if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
44 0 : return false;
45 : }
46 0 : unitNormal->rotateCCW();
47 0 : unitNormal->scale(radius, normal);
48 0 : return true;
49 : }
50 :
51 0 : static bool set_normal_unitnormal(const SkVector& vec,
52 : SkScalar radius,
53 : SkVector* normal, SkVector* unitNormal) {
54 0 : if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
55 0 : return false;
56 : }
57 0 : unitNormal->rotateCCW();
58 0 : unitNormal->scale(radius, normal);
59 0 : return true;
60 : }
61 :
62 : ///////////////////////////////////////////////////////////////////////////////
63 :
64 0 : class SkPathStroker {
65 : public:
66 : SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
67 : SkPaint::Join join);
68 :
69 : void moveTo(const SkPoint&);
70 : void lineTo(const SkPoint&);
71 : void quadTo(const SkPoint&, const SkPoint&);
72 : void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
73 0 : void close(bool isLine) { this->finishContour(true, isLine); }
74 :
75 0 : void done(SkPath* dst, bool isLine) {
76 0 : this->finishContour(false, isLine);
77 0 : fOuter.addPath(fExtra);
78 0 : dst->swap(fOuter);
79 0 : }
80 :
81 : private:
82 : SkScalar fRadius;
83 : SkScalar fInvMiterLimit;
84 :
85 : SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
86 : SkPoint fFirstPt, fPrevPt; // on original path
87 : SkPoint fFirstOuterPt;
88 : int fSegmentCount;
89 : bool fPrevIsLine;
90 :
91 : SkStrokerPriv::CapProc fCapper;
92 : SkStrokerPriv::JoinProc fJoiner;
93 :
94 : SkPath fInner, fOuter; // outer is our working answer, inner is temp
95 : SkPath fExtra; // added as extra complete contours
96 :
97 : void finishContour(bool close, bool isLine);
98 : void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
99 : bool isLine);
100 : void postJoinTo(const SkPoint&, const SkVector& normal,
101 : const SkVector& unitNormal);
102 :
103 : void line_to(const SkPoint& currPt, const SkVector& normal);
104 : void quad_to(const SkPoint pts[3],
105 : const SkVector& normalAB, const SkVector& unitNormalAB,
106 : SkVector* normalBC, SkVector* unitNormalBC,
107 : int subDivide);
108 : void cubic_to(const SkPoint pts[4],
109 : const SkVector& normalAB, const SkVector& unitNormalAB,
110 : SkVector* normalCD, SkVector* unitNormalCD,
111 : int subDivide);
112 : };
113 :
114 : ///////////////////////////////////////////////////////////////////////////////
115 :
116 0 : void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
117 : SkVector* unitNormal, bool currIsLine) {
118 0 : SkASSERT(fSegmentCount >= 0);
119 :
120 0 : SkScalar prevX = fPrevPt.fX;
121 0 : SkScalar prevY = fPrevPt.fY;
122 :
123 0 : SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
124 : unitNormal));
125 :
126 0 : if (fSegmentCount == 0) {
127 0 : fFirstNormal = *normal;
128 0 : fFirstUnitNormal = *unitNormal;
129 0 : fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
130 :
131 0 : fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
132 0 : fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
133 : } else { // we have a previous segment
134 : fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
135 0 : fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
136 : }
137 0 : fPrevIsLine = currIsLine;
138 0 : }
139 :
140 0 : void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
141 : const SkVector& unitNormal) {
142 0 : fPrevPt = currPt;
143 0 : fPrevUnitNormal = unitNormal;
144 0 : fPrevNormal = normal;
145 0 : fSegmentCount += 1;
146 0 : }
147 :
148 0 : void SkPathStroker::finishContour(bool close, bool currIsLine) {
149 0 : if (fSegmentCount > 0) {
150 : SkPoint pt;
151 :
152 0 : if (close) {
153 : fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
154 : fFirstUnitNormal, fRadius, fInvMiterLimit,
155 0 : fPrevIsLine, currIsLine);
156 0 : fOuter.close();
157 : // now add fInner as its own contour
158 0 : fInner.getLastPt(&pt);
159 0 : fOuter.moveTo(pt.fX, pt.fY);
160 0 : fOuter.reversePathTo(fInner);
161 0 : fOuter.close();
162 : } else { // add caps to start and end
163 : // cap the end
164 0 : fInner.getLastPt(&pt);
165 : fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
166 0 : currIsLine ? &fInner : NULL);
167 0 : fOuter.reversePathTo(fInner);
168 : // cap the start
169 0 : fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
170 0 : fPrevIsLine ? &fInner : NULL);
171 0 : fOuter.close();
172 : }
173 : }
174 0 : fInner.reset();
175 0 : fSegmentCount = -1;
176 0 : }
177 :
178 : ///////////////////////////////////////////////////////////////////////////////
179 :
180 0 : SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
181 : SkPaint::Cap cap, SkPaint::Join join)
182 0 : : fRadius(radius) {
183 :
184 : /* This is only used when join is miter_join, but we initialize it here
185 : so that it is always defined, to fis valgrind warnings.
186 : */
187 0 : fInvMiterLimit = 0;
188 :
189 0 : if (join == SkPaint::kMiter_Join) {
190 0 : if (miterLimit <= SK_Scalar1) {
191 0 : join = SkPaint::kBevel_Join;
192 : } else {
193 0 : fInvMiterLimit = SkScalarInvert(miterLimit);
194 : }
195 : }
196 0 : fCapper = SkStrokerPriv::CapFactory(cap);
197 0 : fJoiner = SkStrokerPriv::JoinFactory(join);
198 0 : fSegmentCount = -1;
199 0 : fPrevIsLine = false;
200 0 : }
201 :
202 0 : void SkPathStroker::moveTo(const SkPoint& pt) {
203 0 : if (fSegmentCount > 0) {
204 0 : this->finishContour(false, false);
205 : }
206 0 : fSegmentCount = 0;
207 0 : fFirstPt = fPrevPt = pt;
208 0 : }
209 :
210 0 : void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
211 0 : fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
212 0 : fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
213 0 : }
214 :
215 0 : void SkPathStroker::lineTo(const SkPoint& currPt) {
216 0 : if (SkPath::IsLineDegenerate(fPrevPt, currPt)) {
217 0 : return;
218 : }
219 : SkVector normal, unitNormal;
220 :
221 0 : this->preJoinTo(currPt, &normal, &unitNormal, true);
222 0 : this->line_to(currPt, normal);
223 0 : this->postJoinTo(currPt, normal, unitNormal);
224 : }
225 :
226 0 : void SkPathStroker::quad_to(const SkPoint pts[3],
227 : const SkVector& normalAB, const SkVector& unitNormalAB,
228 : SkVector* normalBC, SkVector* unitNormalBC,
229 : int subDivide) {
230 0 : if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
231 0 : normalBC, unitNormalBC)) {
232 : // pts[1] nearly equals pts[2], so just draw a line to pts[2]
233 0 : this->line_to(pts[2], normalAB);
234 0 : *normalBC = normalAB;
235 0 : *unitNormalBC = unitNormalAB;
236 0 : return;
237 : }
238 :
239 0 : if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
240 : SkPoint tmp[5];
241 : SkVector norm, unit;
242 :
243 0 : SkChopQuadAtHalf(pts, tmp);
244 0 : this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
245 0 : this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
246 : } else {
247 : SkVector normalB, unitB;
248 0 : SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
249 : &normalB, &unitB));
250 :
251 0 : fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
252 0 : pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
253 0 : fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
254 0 : pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
255 : }
256 : }
257 :
258 0 : void SkPathStroker::cubic_to(const SkPoint pts[4],
259 : const SkVector& normalAB, const SkVector& unitNormalAB,
260 : SkVector* normalCD, SkVector* unitNormalCD,
261 : int subDivide) {
262 0 : SkVector ab = pts[1] - pts[0];
263 0 : SkVector cd = pts[3] - pts[2];
264 : SkVector normalBC, unitNormalBC;
265 :
266 0 : bool degenerateAB = degenerate_vector(ab);
267 0 : bool degenerateCD = degenerate_vector(cd);
268 :
269 0 : if (degenerateAB && degenerateCD) {
270 : DRAW_LINE:
271 0 : this->line_to(pts[3], normalAB);
272 0 : *normalCD = normalAB;
273 0 : *unitNormalCD = unitNormalAB;
274 0 : return;
275 : }
276 :
277 0 : if (degenerateAB) {
278 0 : ab = pts[2] - pts[0];
279 0 : degenerateAB = degenerate_vector(ab);
280 : }
281 0 : if (degenerateCD) {
282 0 : cd = pts[3] - pts[1];
283 0 : degenerateCD = degenerate_vector(cd);
284 : }
285 0 : if (degenerateAB || degenerateCD) {
286 : goto DRAW_LINE;
287 : }
288 0 : SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
289 0 : bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
290 0 : &normalBC, &unitNormalBC);
291 :
292 0 : if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
293 0 : normals_too_curvy(unitNormalBC, *unitNormalCD)) {
294 : // subdivide if we can
295 0 : if (--subDivide < 0) {
296 0 : goto DRAW_LINE;
297 : }
298 : SkPoint tmp[7];
299 : SkVector norm, unit, dummy, unitDummy;
300 :
301 0 : SkChopCubicAtHalf(pts, tmp);
302 : this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
303 0 : subDivide);
304 : // we use dummys since we already have a valid (and more accurate)
305 : // normals for CD
306 0 : this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
307 : } else {
308 : SkVector normalB, normalC;
309 :
310 : // need normals to inset/outset the off-curve pts B and C
311 :
312 : if (0) { // this is normal to the line between our adjacent pts
313 : normalB = pts[2] - pts[0];
314 : normalB.rotateCCW();
315 : SkAssertResult(normalB.setLength(fRadius));
316 :
317 : normalC = pts[3] - pts[1];
318 : normalC.rotateCCW();
319 : SkAssertResult(normalC.setLength(fRadius));
320 : } else { // miter-join
321 0 : SkVector unitBC = pts[2] - pts[1];
322 0 : unitBC.normalize();
323 0 : unitBC.rotateCCW();
324 :
325 0 : normalB = unitNormalAB + unitBC;
326 0 : normalC = *unitNormalCD + unitBC;
327 :
328 0 : SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
329 0 : SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
330 : SkScalarSqrt((SK_Scalar1 + dot)/2))));
331 0 : dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
332 0 : SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
333 : SkScalarSqrt((SK_Scalar1 + dot)/2))));
334 : }
335 :
336 0 : fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
337 0 : pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
338 0 : pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
339 :
340 0 : fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
341 0 : pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
342 0 : pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
343 : }
344 : }
345 :
346 0 : void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
347 0 : bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
348 0 : bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
349 :
350 0 : if (degenerateAB | degenerateBC) {
351 0 : if (degenerateAB ^ degenerateBC) {
352 0 : this->lineTo(pt2);
353 : }
354 0 : return;
355 : }
356 :
357 : SkVector normalAB, unitAB, normalBC, unitBC;
358 :
359 0 : this->preJoinTo(pt1, &normalAB, &unitAB, false);
360 :
361 : {
362 : SkPoint pts[3], tmp[5];
363 0 : pts[0] = fPrevPt;
364 0 : pts[1] = pt1;
365 0 : pts[2] = pt2;
366 :
367 0 : if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
368 0 : unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
369 0 : unitBC.rotateCCW();
370 0 : if (normals_too_pinchy(unitAB, unitBC)) {
371 0 : normalBC = unitBC;
372 0 : normalBC.scale(fRadius);
373 :
374 0 : fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
375 0 : fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
376 0 : fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
377 :
378 0 : fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
379 0 : fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
380 0 : fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
381 :
382 : fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
383 0 : SkPath::kCW_Direction);
384 : } else {
385 : this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
386 0 : kMaxQuadSubdivide);
387 0 : SkVector n = normalBC;
388 0 : SkVector u = unitBC;
389 : this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
390 0 : kMaxQuadSubdivide);
391 : }
392 : } else {
393 : this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
394 0 : kMaxQuadSubdivide);
395 : }
396 : }
397 :
398 0 : this->postJoinTo(pt2, normalBC, unitBC);
399 : }
400 :
401 0 : void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
402 : const SkPoint& pt3) {
403 0 : bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1);
404 0 : bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
405 0 : bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
406 :
407 0 : if (degenerateAB + degenerateBC + degenerateCD >= 2) {
408 0 : this->lineTo(pt3);
409 0 : return;
410 : }
411 :
412 : SkVector normalAB, unitAB, normalCD, unitCD;
413 :
414 : // find the first tangent (which might be pt1 or pt2
415 : {
416 0 : const SkPoint* nextPt = &pt1;
417 0 : if (degenerateAB)
418 0 : nextPt = &pt2;
419 0 : this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
420 : }
421 :
422 : {
423 : SkPoint pts[4], tmp[13];
424 : int i, count;
425 : SkVector n, u;
426 : SkScalar tValues[3];
427 :
428 0 : pts[0] = fPrevPt;
429 0 : pts[1] = pt1;
430 0 : pts[2] = pt2;
431 0 : pts[3] = pt3;
432 :
433 : #if 1
434 0 : count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
435 : #else
436 : count = 1;
437 : memcpy(tmp, pts, 4 * sizeof(SkPoint));
438 : #endif
439 0 : n = normalAB;
440 0 : u = unitAB;
441 0 : for (i = 0; i < count; i++) {
442 0 : this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
443 0 : kMaxCubicSubdivide);
444 0 : if (i == count - 1) {
445 0 : break;
446 : }
447 0 : n = normalCD;
448 0 : u = unitCD;
449 :
450 : }
451 :
452 : // check for too pinchy
453 0 : for (i = 1; i < count; i++) {
454 : SkPoint p;
455 : SkVector v, c;
456 :
457 0 : SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
458 :
459 0 : SkScalar dot = SkPoint::DotProduct(c, c);
460 0 : v.scale(SkScalarInvert(dot));
461 :
462 0 : if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
463 0 : fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
464 : }
465 : }
466 :
467 : }
468 :
469 0 : this->postJoinTo(pt3, normalCD, unitCD);
470 : }
471 :
472 : ///////////////////////////////////////////////////////////////////////////////
473 : ///////////////////////////////////////////////////////////////////////////////
474 :
475 : #include "SkPaint.h"
476 :
477 0 : SkStroke::SkStroke() {
478 0 : fWidth = SK_DefaultStrokeWidth;
479 0 : fMiterLimit = SK_DefaultMiterLimit;
480 0 : fCap = SkPaint::kDefault_Cap;
481 0 : fJoin = SkPaint::kDefault_Join;
482 0 : fDoFill = false;
483 0 : }
484 :
485 0 : SkStroke::SkStroke(const SkPaint& p) {
486 0 : fWidth = p.getStrokeWidth();
487 0 : fMiterLimit = p.getStrokeMiter();
488 0 : fCap = (uint8_t)p.getStrokeCap();
489 0 : fJoin = (uint8_t)p.getStrokeJoin();
490 0 : fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
491 0 : }
492 :
493 0 : SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
494 0 : fWidth = width;
495 0 : fMiterLimit = p.getStrokeMiter();
496 0 : fCap = (uint8_t)p.getStrokeCap();
497 0 : fJoin = (uint8_t)p.getStrokeJoin();
498 0 : fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
499 0 : }
500 :
501 0 : void SkStroke::setWidth(SkScalar width) {
502 0 : SkASSERT(width >= 0);
503 0 : fWidth = width;
504 0 : }
505 :
506 0 : void SkStroke::setMiterLimit(SkScalar miterLimit) {
507 0 : SkASSERT(miterLimit >= 0);
508 0 : fMiterLimit = miterLimit;
509 0 : }
510 :
511 0 : void SkStroke::setCap(SkPaint::Cap cap) {
512 0 : SkASSERT((unsigned)cap < SkPaint::kCapCount);
513 0 : fCap = SkToU8(cap);
514 0 : }
515 :
516 0 : void SkStroke::setJoin(SkPaint::Join join) {
517 0 : SkASSERT((unsigned)join < SkPaint::kJoinCount);
518 0 : fJoin = SkToU8(join);
519 0 : }
520 :
521 : ///////////////////////////////////////////////////////////////////////////////
522 :
523 : #ifdef SK_SCALAR_IS_FIXED
524 : /* return non-zero if the path is too big, and should be shrunk to avoid
525 : overflows during intermediate calculations. Note that we compute the
526 : bounds for this. If we had a custom callback/walker for paths, we could
527 : perhaps go faster by using that, and just perform the abs | in that
528 : routine
529 : */
530 : static int needs_to_shrink(const SkPath& path) {
531 : const SkRect& r = path.getBounds();
532 : SkFixed mask = SkAbs32(r.fLeft);
533 : mask |= SkAbs32(r.fTop);
534 : mask |= SkAbs32(r.fRight);
535 : mask |= SkAbs32(r.fBottom);
536 : // we need the top 3 bits clear (after abs) to avoid overflow
537 : return mask >> 29;
538 : }
539 :
540 : static void identity_proc(SkPoint pts[], int count) {}
541 : static void shift_down_2_proc(SkPoint pts[], int count) {
542 : for (int i = 0; i < count; i++) {
543 : pts->fX >>= 2;
544 : pts->fY >>= 2;
545 : pts += 1;
546 : }
547 : }
548 : #define APPLY_PROC(proc, pts, count) proc(pts, count)
549 : #else // float does need any of this
550 : #define APPLY_PROC(proc, pts, count)
551 : #endif
552 :
553 0 : void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
554 0 : SkASSERT(&src != NULL && dst != NULL);
555 :
556 0 : SkScalar radius = SkScalarHalf(fWidth);
557 :
558 0 : dst->reset();
559 0 : if (radius <= 0) {
560 0 : return;
561 : }
562 :
563 : #ifdef SK_SCALAR_IS_FIXED
564 : void (*proc)(SkPoint pts[], int count) = identity_proc;
565 : if (needs_to_shrink(src)) {
566 : proc = shift_down_2_proc;
567 : radius >>= 2;
568 : if (radius == 0) {
569 : return;
570 : }
571 : }
572 : #endif
573 :
574 : SkPathStroker stroker(radius, fMiterLimit, this->getCap(),
575 0 : this->getJoin());
576 :
577 0 : SkPath::Iter iter(src, false);
578 : SkPoint pts[4];
579 0 : SkPath::Verb verb, lastSegment = SkPath::kMove_Verb;
580 :
581 0 : while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
582 0 : switch (verb) {
583 : case SkPath::kMove_Verb:
584 : APPLY_PROC(proc, &pts[0], 1);
585 0 : stroker.moveTo(pts[0]);
586 0 : break;
587 : case SkPath::kLine_Verb:
588 : APPLY_PROC(proc, &pts[1], 1);
589 0 : stroker.lineTo(pts[1]);
590 0 : lastSegment = verb;
591 0 : break;
592 : case SkPath::kQuad_Verb:
593 : APPLY_PROC(proc, &pts[1], 2);
594 0 : stroker.quadTo(pts[1], pts[2]);
595 0 : lastSegment = verb;
596 0 : break;
597 : case SkPath::kCubic_Verb:
598 : APPLY_PROC(proc, &pts[1], 3);
599 0 : stroker.cubicTo(pts[1], pts[2], pts[3]);
600 0 : lastSegment = verb;
601 0 : break;
602 : case SkPath::kClose_Verb:
603 0 : stroker.close(lastSegment == SkPath::kLine_Verb);
604 0 : break;
605 : default:
606 0 : break;
607 : }
608 : }
609 0 : stroker.done(dst, lastSegment == SkPath::kLine_Verb);
610 :
611 : #ifdef SK_SCALAR_IS_FIXED
612 : // undo our previous down_shift
613 : if (shift_down_2_proc == proc) {
614 : // need a real shift methid on path. antialias paths could use this too
615 : SkMatrix matrix;
616 : matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
617 : dst->transform(matrix);
618 : }
619 : #endif
620 :
621 0 : if (fDoFill) {
622 0 : dst->addPath(src);
623 : } else {
624 : // Seems like we can assume that a 2-point src would always result in
625 : // a convex stroke, but testing has proved otherwise.
626 : // TODO: fix the stroker to make this assumption true (without making
627 : // it slower that the work that will be done in computeConvexity())
628 : #if 0
629 : // this test results in a non-convex stroke :(
630 : static void test(SkCanvas* canvas) {
631 : SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 };
632 : SkPaint paint;
633 : paint.setStrokeWidth(7);
634 : paint.setStrokeCap(SkPaint::kRound_Cap);
635 : canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
636 : }
637 : #endif
638 : #if 0
639 : if (2 == src.countPoints()) {
640 : dst->setIsConvex(true);
641 : }
642 : #endif
643 : }
644 :
645 : // our answer should preserve the inverseness of the src
646 0 : if (src.isInverseFillType()) {
647 0 : SkASSERT(!dst->isInverseFillType());
648 0 : dst->toggleInverseFillType();
649 : }
650 : }
651 :
652 0 : void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
653 : SkPath* dst) const {
654 0 : SkPath tmp;
655 :
656 0 : tmp.moveTo(p0);
657 0 : tmp.lineTo(p1);
658 0 : this->strokePath(tmp, dst);
659 0 : }
660 :
|