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 "SkScan.h"
9 #include "SkBlitter.h"
10 #include "SkMathPriv.h"
11 #include "SkPaint.h"
12 #include "SkRasterClip.h"
13 #include "SkFDot6.h"
14 #include "SkLineClipper.h"
15 
horiline(int x,int stopx,SkFixed fy,SkFixed dy,SkBlitter * blitter)16 static void horiline(int x, int stopx, SkFixed fy, SkFixed dy,
17                      SkBlitter* blitter) {
18     SkASSERT(x < stopx);
19 
20     do {
21         blitter->blitH(x, fy >> 16, 1);
22         fy += dy;
23     } while (++x < stopx);
24 }
25 
vertline(int y,int stopy,SkFixed fx,SkFixed dx,SkBlitter * blitter)26 static void vertline(int y, int stopy, SkFixed fx, SkFixed dx,
27                      SkBlitter* blitter) {
28     SkASSERT(y < stopy);
29 
30     do {
31         blitter->blitH(fx >> 16, y, 1);
32         fx += dx;
33     } while (++y < stopy);
34 }
35 
36 #ifdef SK_DEBUG
canConvertFDot6ToFixed(SkFDot6 x)37 static bool canConvertFDot6ToFixed(SkFDot6 x) {
38     const int maxDot6 = SK_MaxS32 >> (16 - 6);
39     return SkAbs32(x) <= maxDot6;
40 }
41 #endif
42 
HairLineRgn(const SkPoint array[],int arrayCount,const SkRegion * clip,SkBlitter * origBlitter)43 void SkScan::HairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
44                          SkBlitter* origBlitter) {
45     SkBlitterClipper    clipper;
46     SkIRect clipR, ptsR;
47 
48     const SkScalar max = SkIntToScalar(32767);
49     const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
50 
51     SkRect clipBounds;
52     if (clip) {
53         clipBounds.set(clip->getBounds());
54     }
55 
56     for (int i = 0; i < arrayCount - 1; ++i) {
57         SkBlitter* blitter = origBlitter;
58 
59         SkPoint pts[2];
60 
61         // We have to pre-clip the line to fit in a SkFixed, so we just chop
62         // the line. TODO find a way to actually draw beyond that range.
63         if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
64             continue;
65         }
66 
67         // Perform a clip in scalar space, so we catch huge values which might
68         // be missed after we convert to SkFDot6 (overflow)
69         if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
70             continue;
71         }
72 
73         SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
74         SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
75         SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
76         SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
77 
78         SkASSERT(canConvertFDot6ToFixed(x0));
79         SkASSERT(canConvertFDot6ToFixed(y0));
80         SkASSERT(canConvertFDot6ToFixed(x1));
81         SkASSERT(canConvertFDot6ToFixed(y1));
82 
83         if (clip) {
84             // now perform clipping again, as the rounding to dot6 can wiggle us
85             // our rects are really dot6 rects, but since we've already used
86             // lineclipper, we know they will fit in 32bits (26.6)
87             const SkIRect& bounds = clip->getBounds();
88 
89             clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
90                       SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
91             ptsR.set(x0, y0, x1, y1);
92             ptsR.sort();
93 
94             // outset the right and bottom, to account for how hairlines are
95             // actually drawn, which may hit the pixel to the right or below of
96             // the coordinate
97             ptsR.fRight += SK_FDot6One;
98             ptsR.fBottom += SK_FDot6One;
99 
100             if (!SkIRect::Intersects(ptsR, clipR)) {
101                 continue;
102             }
103             if (!clip->isRect() || !clipR.contains(ptsR)) {
104                 blitter = clipper.apply(origBlitter, clip);
105             }
106         }
107 
108         SkFDot6 dx = x1 - x0;
109         SkFDot6 dy = y1 - y0;
110 
111         if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
112             if (x0 > x1) {   // we want to go left-to-right
113                 SkTSwap<SkFDot6>(x0, x1);
114                 SkTSwap<SkFDot6>(y0, y1);
115             }
116             int ix0 = SkFDot6Round(x0);
117             int ix1 = SkFDot6Round(x1);
118             if (ix0 == ix1) {// too short to draw
119                 continue;
120             }
121 
122             SkFixed slope = SkFixedDiv(dy, dx);
123             SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
124 
125             horiline(ix0, ix1, startY, slope, blitter);
126         } else {              // mostly vertical
127             if (y0 > y1) {   // we want to go top-to-bottom
128                 SkTSwap<SkFDot6>(x0, x1);
129                 SkTSwap<SkFDot6>(y0, y1);
130             }
131             int iy0 = SkFDot6Round(y0);
132             int iy1 = SkFDot6Round(y1);
133             if (iy0 == iy1) { // too short to draw
134                 continue;
135             }
136 
137             SkFixed slope = SkFixedDiv(dx, dy);
138             SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
139 
140             vertline(iy0, iy1, startX, slope, blitter);
141         }
142     }
143 }
144 
145 // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
146 // and double-hit the top-left.
HairRect(const SkRect & rect,const SkRasterClip & clip,SkBlitter * blitter)147 void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, SkBlitter* blitter) {
148     SkAAClipBlitterWrapper wrapper;
149     SkBlitterClipper clipper;
150     // Create the enclosing bounds of the hairrect. i.e. we will stroke the interior of r.
151     SkIRect r = SkIRect::MakeLTRB(SkScalarFloorToInt(rect.fLeft),
152                                   SkScalarFloorToInt(rect.fTop),
153                                   SkScalarFloorToInt(rect.fRight + 1),
154                                   SkScalarFloorToInt(rect.fBottom + 1));
155 
156     // Note: r might be crazy big, if rect was huge, possibly getting pinned to max/min s32.
157     // We need to trim it back to something reasonable before we can query its width etc.
158     // since r.fRight - r.fLeft might wrap around to negative even if fRight > fLeft.
159     //
160     // We outset the clip bounds by 1 before intersecting, since r is being stroked and not filled
161     // so we don't want to pin an edge of it to the clip. The intersect's job is mostly to just
162     // get the actual edge values into a reasonable range (e.g. so width() can't overflow).
163     if (!r.intersect(clip.getBounds().makeOutset(1, 1))) {
164         return;
165     }
166 
167     if (clip.quickReject(r)) {
168         return;
169     }
170     if (!clip.quickContains(r)) {
171         const SkRegion* clipRgn;
172         if (clip.isBW()) {
173             clipRgn = &clip.bwRgn();
174         } else {
175             wrapper.init(clip, blitter);
176             clipRgn = &wrapper.getRgn();
177             blitter = wrapper.getBlitter();
178         }
179         blitter = clipper.apply(blitter, clipRgn);
180     }
181 
182     int width = r.width();
183     int height = r.height();
184 
185     if ((width | height) == 0) {
186         return;
187     }
188     if (width <= 2 || height <= 2) {
189         blitter->blitRect(r.fLeft, r.fTop, width, height);
190         return;
191     }
192     // if we get here, we know we have 4 segments to draw
193     blitter->blitH(r.fLeft, r.fTop, width);                     // top
194     blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
195     blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
196     blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
197 }
198 
199 ///////////////////////////////////////////////////////////////////////////////
200 
201 #include "SkPath.h"
202 #include "SkGeometry.h"
203 #include "SkNx.h"
204 
205 #define kMaxCubicSubdivideLevel 9
206 #define kMaxQuadSubdivideLevel  5
207 
compute_int_quad_dist(const SkPoint pts[3])208 static uint32_t compute_int_quad_dist(const SkPoint pts[3]) {
209     // compute the vector between the control point ([1]) and the middle of the
210     // line connecting the start and end ([0] and [2])
211     SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
212     SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
213     // we want everyone to be positive
214     dx = SkScalarAbs(dx);
215     dy = SkScalarAbs(dy);
216     // convert to whole pixel values (use ceiling to be conservative).
217     // assign to unsigned so we can safely add 1/2 of the smaller and still fit in
218     // uint32_t, since SkScalarCeilToInt() returns 31 bits at most.
219     uint32_t idx = SkScalarCeilToInt(dx);
220     uint32_t idy = SkScalarCeilToInt(dy);
221     // use the cheap approx for distance
222     if (idx > idy) {
223         return idx + (idy >> 1);
224     } else {
225         return idy + (idx >> 1);
226     }
227 }
228 
hair_quad(const SkPoint pts[3],const SkRegion * clip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)229 static void hair_quad(const SkPoint pts[3], const SkRegion* clip,
230                      SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
231     SkASSERT(level <= kMaxQuadSubdivideLevel);
232 
233     SkQuadCoeff coeff(pts);
234 
235     const int lines = 1 << level;
236     Sk2s t(0);
237     Sk2s dt(SK_Scalar1 / lines);
238 
239     SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
240     SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
241 
242     tmp[0] = pts[0];
243     Sk2s A = coeff.fA;
244     Sk2s B = coeff.fB;
245     Sk2s C = coeff.fC;
246     for (int i = 1; i < lines; ++i) {
247         t = t + dt;
248         ((A * t + B) * t + C).store(&tmp[i]);
249     }
250     tmp[lines] = pts[2];
251     lineproc(tmp, lines + 1, clip, blitter);
252 }
253 
compute_nocheck_quad_bounds(const SkPoint pts[3])254 static SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) {
255     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6));
256 
257     Sk2s min = Sk2s::Load(pts);
258     Sk2s max = min;
259     for (int i = 1; i < 3; ++i) {
260         Sk2s pair = Sk2s::Load(pts+i);
261         min = Sk2s::Min(min, pair);
262         max = Sk2s::Max(max, pair);
263     }
264     return { min[0], min[1], max[0], max[1] };
265 }
266 
is_inverted(const SkRect & r)267 static bool is_inverted(const SkRect& r) {
268     return r.fLeft > r.fRight || r.fTop > r.fBottom;
269 }
270 
271 // Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking
272 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_overlap(const SkRect & a,const SkRect & b)273 static bool geometric_overlap(const SkRect& a, const SkRect& b) {
274     SkASSERT(!is_inverted(a) && !is_inverted(b));
275     return a.fLeft < b.fRight && b.fLeft < a.fRight &&
276             a.fTop < b.fBottom && b.fTop < a.fBottom;
277 }
278 
279 // Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking
280 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_contains(const SkRect & outer,const SkRect & inner)281 static bool geometric_contains(const SkRect& outer, const SkRect& inner) {
282     SkASSERT(!is_inverted(outer) && !is_inverted(inner));
283     return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft &&
284             inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop;
285 }
286 
hairquad(const SkPoint pts[3],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)287 static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
288     SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
289     if (insetClip) {
290         SkASSERT(outsetClip);
291         SkRect bounds = compute_nocheck_quad_bounds(pts);
292         if (!geometric_overlap(*outsetClip, bounds)) {
293             return;
294         } else if (geometric_contains(*insetClip, bounds)) {
295             clip = nullptr;
296         }
297     }
298 
299     hair_quad(pts, clip, blitter, level, lineproc);
300 }
301 
abs(const Sk2s & value)302 static inline Sk2s abs(const Sk2s& value) {
303     return Sk2s::Max(value, Sk2s(0)-value);
304 }
305 
max_component(const Sk2s & value)306 static inline SkScalar max_component(const Sk2s& value) {
307     SkScalar components[2];
308     value.store(components);
309     return SkTMax(components[0], components[1]);
310 }
311 
compute_cubic_segs(const SkPoint pts[4])312 static inline int compute_cubic_segs(const SkPoint pts[4]) {
313     Sk2s p0 = from_point(pts[0]);
314     Sk2s p1 = from_point(pts[1]);
315     Sk2s p2 = from_point(pts[2]);
316     Sk2s p3 = from_point(pts[3]);
317 
318     const Sk2s oneThird(1.0f / 3.0f);
319     const Sk2s twoThird(2.0f / 3.0f);
320 
321     Sk2s p13 = oneThird * p3 + twoThird * p0;
322     Sk2s p23 = oneThird * p0 + twoThird * p3;
323 
324     SkScalar diff = max_component(Sk2s::Max(abs(p1 - p13), abs(p2 - p23)));
325     SkScalar tol = SK_Scalar1 / 8;
326 
327     for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
328         if (diff < tol) {
329             return 1 << i;
330         }
331         tol *= 4;
332     }
333     return 1 << kMaxCubicSubdivideLevel;
334 }
335 
lt_90(SkPoint p0,SkPoint pivot,SkPoint p2)336 static bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
337     return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
338 }
339 
340 // The off-curve points are "inside" the limits of the on-curve pts
quick_cubic_niceness_check(const SkPoint pts[4])341 static bool quick_cubic_niceness_check(const SkPoint pts[4]) {
342     return lt_90(pts[1], pts[0], pts[3]) &&
343            lt_90(pts[2], pts[0], pts[3]) &&
344            lt_90(pts[1], pts[3], pts[0]) &&
345            lt_90(pts[2], pts[3], pts[0]);
346 }
347 
hair_cubic(const SkPoint pts[4],const SkRegion * clip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)348 static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
349                        SkScan::HairRgnProc lineproc) {
350     const int lines = compute_cubic_segs(pts);
351     SkASSERT(lines > 0);
352     if (1 == lines) {
353         SkPoint tmp[2] = { pts[0], pts[3] };
354         lineproc(tmp, 2, clip, blitter);
355         return;
356     }
357 
358     SkCubicCoeff coeff(pts);
359 
360     const Sk2s dt(SK_Scalar1 / lines);
361     Sk2s t(0);
362 
363     SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
364     SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
365 
366     tmp[0] = pts[0];
367     Sk2s A = coeff.fA;
368     Sk2s B = coeff.fB;
369     Sk2s C = coeff.fC;
370     Sk2s D = coeff.fD;
371     for (int i = 1; i < lines; ++i) {
372         t = t + dt;
373         (((A * t + B) * t + C) * t + D).store(&tmp[i]);
374     }
375     tmp[lines] = pts[3];
376     lineproc(tmp, lines + 1, clip, blitter);
377 }
378 
compute_nocheck_cubic_bounds(const SkPoint pts[4])379 static SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) {
380     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8));
381 
382     Sk2s min = Sk2s::Load(pts);
383     Sk2s max = min;
384     for (int i = 1; i < 4; ++i) {
385         Sk2s pair = Sk2s::Load(pts+i);
386         min = Sk2s::Min(min, pair);
387         max = Sk2s::Max(max, pair);
388     }
389     return { min[0], min[1], max[0], max[1] };
390 }
391 
haircubic(const SkPoint pts[4],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)392 static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
393                       SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
394     if (insetClip) {
395         SkASSERT(outsetClip);
396         SkRect bounds = compute_nocheck_cubic_bounds(pts);
397         if (!geometric_overlap(*outsetClip, bounds)) {
398             return;
399         } else if (geometric_contains(*insetClip, bounds)) {
400             clip = nullptr;
401         }
402     }
403 
404     if (quick_cubic_niceness_check(pts)) {
405         hair_cubic(pts, clip, blitter, lineproc);
406     } else {
407         SkPoint  tmp[13];
408         SkScalar tValues[3];
409 
410         int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
411         for (int i = 0; i < count; i++) {
412             hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
413         }
414     }
415 }
416 
compute_quad_level(const SkPoint pts[3])417 static int compute_quad_level(const SkPoint pts[3]) {
418     uint32_t d = compute_int_quad_dist(pts);
419     /*  quadratics approach the line connecting their start and end points
420      4x closer with each subdivision, so we compute the number of
421      subdivisions to be the minimum need to get that distance to be less
422      than a pixel.
423      */
424     int level = (33 - SkCLZ(d)) >> 1;
425     // sanity check on level (from the previous version)
426     if (level > kMaxQuadSubdivideLevel) {
427         level = kMaxQuadSubdivideLevel;
428     }
429     return level;
430 }
431 
432 /* Extend the points in the direction of the starting or ending tangent by 1/2 unit to
433    account for a round or square cap. If there's no distance between the end point and
434    the control point, use the next control point to create a tangent. If the curve
435    is degenerate, move the cap out 1/2 unit horizontally. */
436 template <SkPaint::Cap capStyle>
extend_pts(SkPath::Verb prevVerb,SkPath::Verb nextVerb,SkPoint * pts,int ptCount)437 void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) {
438     SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle);
439     // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that.
440     const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8;
441     if (SkPath::kMove_Verb == prevVerb) {
442         SkPoint* first = pts;
443         SkPoint* ctrl = first;
444         int controls = ptCount - 1;
445         SkVector tangent;
446         do {
447             tangent = *first - *++ctrl;
448         } while (tangent.isZero() && --controls > 0);
449         if (tangent.isZero()) {
450             tangent.set(1, 0);
451             controls = ptCount - 1;  // If all points are equal, move all but one
452         } else {
453             tangent.normalize();
454         }
455         do {    // If the end point and control points are equal, loop to move them in tandem.
456             first->fX += tangent.fX * capOutset;
457             first->fY += tangent.fY * capOutset;
458             ++first;
459         } while (++controls < ptCount);
460     }
461     if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb
462             || SkPath::kClose_Verb == nextVerb) {
463         SkPoint* last = &pts[ptCount - 1];
464         SkPoint* ctrl = last;
465         int controls = ptCount - 1;
466         SkVector tangent;
467         do {
468             tangent = *last - *--ctrl;
469         } while (tangent.isZero() && --controls > 0);
470         if (tangent.isZero()) {
471             tangent.set(-1, 0);
472             controls = ptCount - 1;
473         } else {
474             tangent.normalize();
475         }
476         do {
477             last->fX += tangent.fX * capOutset;
478             last->fY += tangent.fY * capOutset;
479             --last;
480         } while (++controls < ptCount);
481     }
482 }
483 
484 template <SkPaint::Cap capStyle>
hair_path(const SkPath & path,const SkRasterClip & rclip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)485 void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
486                       SkScan::HairRgnProc lineproc) {
487     if (path.isEmpty()) {
488         return;
489     }
490 
491     SkAAClipBlitterWrapper wrap;
492     const SkRegion* clip = nullptr;
493     SkRect insetStorage, outsetStorage;
494     const SkRect* insetClip = nullptr;
495     const SkRect* outsetClip = nullptr;
496 
497     {
498         const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
499         const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
500         if (rclip.quickReject(ibounds)) {
501             return;
502         }
503         if (!rclip.quickContains(ibounds)) {
504             if (rclip.isBW()) {
505                 clip = &rclip.bwRgn();
506             } else {
507                 wrap.init(rclip, blitter);
508                 blitter = wrap.getBlitter();
509                 clip = &wrap.getRgn();
510             }
511 
512             /*
513              *  We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
514              *  Since we're hairlining, the "bounds" of the control points isn't necessairly the
515              *  limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
516              *
517              *  Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
518              *  the culling bounds so we can just do a straight compare per segment.
519              *
520              *  insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
521              *  it from the clip-bounds (since segment bounds can be off by 1).
522              *
523              *  outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
524              *  outset it from the clip-bounds.
525              */
526             insetStorage.set(clip->getBounds());
527             outsetStorage = insetStorage.makeOutset(1, 1);
528             insetStorage.inset(1, 1);
529             if (is_inverted(insetStorage)) {
530                 /*
531                  *  our bounds checks assume the rects are never inverted. If insetting has
532                  *  created that, we assume that the area is too small to safely perform a
533                  *  quick-accept, so we just mark the rect as empty (so the quick-accept check
534                  *  will always fail.
535                  */
536                 insetStorage.setEmpty();    // just so we don't pass an inverted rect
537             }
538             if (rclip.isRect()) {
539                 insetClip = &insetStorage;
540             }
541             outsetClip = &outsetStorage;
542         }
543     }
544 
545     SkPath::RawIter     iter(path);
546     SkPoint             pts[4], firstPt, lastPt;
547     SkPath::Verb        verb, prevVerb;
548     SkAutoConicToQuads  converter;
549 
550     if (SkPaint::kButt_Cap != capStyle) {
551         prevVerb = SkPath::kDone_Verb;
552     }
553     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
554         switch (verb) {
555             case SkPath::kMove_Verb:
556                 firstPt = lastPt = pts[0];
557                 break;
558             case SkPath::kLine_Verb:
559                 if (SkPaint::kButt_Cap != capStyle) {
560                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
561                 }
562                 lineproc(pts, 2, clip, blitter);
563                 lastPt = pts[1];
564                 break;
565             case SkPath::kQuad_Verb:
566                 if (SkPaint::kButt_Cap != capStyle) {
567                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
568                 }
569                 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
570                 lastPt = pts[2];
571                 break;
572             case SkPath::kConic_Verb: {
573                 if (SkPaint::kButt_Cap != capStyle) {
574                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
575                 }
576                 // how close should the quads be to the original conic?
577                 const SkScalar tol = SK_Scalar1 / 4;
578                 const SkPoint* quadPts = converter.computeQuads(pts,
579                                                        iter.conicWeight(), tol);
580                 for (int i = 0; i < converter.countQuads(); ++i) {
581                     int level = compute_quad_level(quadPts);
582                     hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
583                     quadPts += 2;
584                 }
585                 lastPt = pts[2];
586                 break;
587             }
588             case SkPath::kCubic_Verb: {
589                 if (SkPaint::kButt_Cap != capStyle) {
590                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
591                 }
592                 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
593                 lastPt = pts[3];
594             } break;
595             case SkPath::kClose_Verb:
596                 pts[0] = lastPt;
597                 pts[1] = firstPt;
598                 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
599                     // cap moveTo/close to match svg expectations for degenerate segments
600                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
601                 }
602                 lineproc(pts, 2, clip, blitter);
603                 break;
604             case SkPath::kDone_Verb:
605                 break;
606         }
607         if (SkPaint::kButt_Cap != capStyle) {
608             if (prevVerb == SkPath::kMove_Verb &&
609                     verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
610                 firstPt = pts[0];  // the curve moved the initial point, so close to it instead
611             }
612             prevVerb = verb;
613         }
614     }
615 }
616 
HairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)617 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
618     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
619 }
620 
AntiHairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)621 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
622     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
623 }
624 
HairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)625 void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
626     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
627 }
628 
AntiHairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)629 void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
630     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
631 }
632 
HairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)633 void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
634     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
635 }
636 
AntiHairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)637 void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
638     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
639 }
640 
641 ///////////////////////////////////////////////////////////////////////////////
642 
FrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)643 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
644                        const SkRasterClip& clip, SkBlitter* blitter) {
645     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
646 
647     if (strokeSize.fX < 0 || strokeSize.fY < 0) {
648         return;
649     }
650 
651     const SkScalar dx = strokeSize.fX;
652     const SkScalar dy = strokeSize.fY;
653     SkScalar rx = SkScalarHalf(dx);
654     SkScalar ry = SkScalarHalf(dy);
655     SkRect   outer, tmp;
656 
657     outer.set(r.fLeft - rx, r.fTop - ry,
658                 r.fRight + rx, r.fBottom + ry);
659 
660     if (r.width() <= dx || r.height() <= dy) {
661         SkScan::FillRect(outer, clip, blitter);
662         return;
663     }
664 
665     tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
666     SkScan::FillRect(tmp, clip, blitter);
667     tmp.fTop = outer.fBottom - dy;
668     tmp.fBottom = outer.fBottom;
669     SkScan::FillRect(tmp, clip, blitter);
670 
671     tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
672     SkScan::FillRect(tmp, clip, blitter);
673     tmp.fLeft = outer.fRight - dx;
674     tmp.fRight = outer.fRight;
675     SkScan::FillRect(tmp, clip, blitter);
676 }
677 
HairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)678 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
679                       SkBlitter* blitter) {
680     if (clip.isBW()) {
681         HairLineRgn(pts, count, &clip.bwRgn(), blitter);
682     } else {
683         const SkRegion* clipRgn = nullptr;
684 
685         SkRect r;
686         r.set(pts, count);
687         r.outset(SK_ScalarHalf, SK_ScalarHalf);
688 
689         SkAAClipBlitterWrapper wrap;
690         if (!clip.quickContains(r.roundOut())) {
691             wrap.init(clip, blitter);
692             blitter = wrap.getBlitter();
693             clipRgn = &wrap.getRgn();
694         }
695         HairLineRgn(pts, count, clipRgn, blitter);
696     }
697 }
698 
AntiHairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)699 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
700                           SkBlitter* blitter) {
701     if (clip.isBW()) {
702         AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
703     } else {
704         const SkRegion* clipRgn = nullptr;
705 
706         SkRect r;
707         r.set(pts, count);
708 
709         SkAAClipBlitterWrapper wrap;
710         if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
711             wrap.init(clip, blitter);
712             blitter = wrap.getBlitter();
713             clipRgn = &wrap.getRgn();
714         }
715         AntiHairLineRgn(pts, count, clipRgn, blitter);
716     }
717 }
718