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