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         SkPoint* last = &pts[ptCount - 1];
452         SkPoint* ctrl = last;
453         int controls = ptCount - 1;
454         SkVector tangent;
455         do {
456             tangent = *last - *--ctrl;
457         } while (tangent.isZero() && --controls > 0);
458         if (tangent.isZero()) {
459             tangent.set(-1, 0);
460             controls = ptCount - 1;
461         } else {
462             tangent.normalize();
463         }
464         do {
465             last->fX += tangent.fX * capOutset;
466             last->fY += tangent.fY * capOutset;
467             --last;
468         } while (++controls < ptCount);
469     }
470 }
471 
472 template <SkPaint::Cap capStyle>
hair_path(const SkPath & path,const SkRasterClip & rclip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)473 void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
474                       SkScan::HairRgnProc lineproc) {
475     if (path.isEmpty()) {
476         return;
477     }
478 
479     SkAAClipBlitterWrapper wrap;
480     const SkRegion* clip = nullptr;
481     SkRect insetStorage, outsetStorage;
482     const SkRect* insetClip = nullptr;
483     const SkRect* outsetClip = nullptr;
484 
485     {
486         const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
487         const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
488         if (rclip.quickReject(ibounds)) {
489             return;
490         }
491         if (!rclip.quickContains(ibounds)) {
492             if (rclip.isBW()) {
493                 clip = &rclip.bwRgn();
494             } else {
495                 wrap.init(rclip, blitter);
496                 blitter = wrap.getBlitter();
497                 clip = &wrap.getRgn();
498             }
499 
500             /*
501              *  We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
502              *  Since we're hairlining, the "bounds" of the control points isn't necessairly the
503              *  limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
504              *
505              *  Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
506              *  the culling bounds so we can just do a straight compare per segment.
507              *
508              *  insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
509              *  it from the clip-bounds (since segment bounds can be off by 1).
510              *
511              *  outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
512              *  outset it from the clip-bounds.
513              */
514             insetStorage.set(clip->getBounds());
515             outsetStorage = insetStorage.makeOutset(1, 1);
516             insetStorage.inset(1, 1);
517             if (is_inverted(insetStorage)) {
518                 /*
519                  *  our bounds checks assume the rects are never inverted. If insetting has
520                  *  created that, we assume that the area is too small to safely perform a
521                  *  quick-accept, so we just mark the rect as empty (so the quick-accept check
522                  *  will always fail.
523                  */
524                 insetStorage.setEmpty();    // just so we don't pass an inverted rect
525             }
526             if (rclip.isRect()) {
527                 insetClip = &insetStorage;
528             }
529             outsetClip = &outsetStorage;
530         }
531     }
532 
533     SkPath::RawIter     iter(path);
534     SkPoint             pts[4], firstPt, lastPt;
535     SkPath::Verb        verb, prevVerb;
536     SkAutoConicToQuads  converter;
537 
538     if (SkPaint::kButt_Cap != capStyle) {
539         prevVerb = SkPath::kDone_Verb;
540     }
541     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
542         switch (verb) {
543             case SkPath::kMove_Verb:
544                 firstPt = lastPt = pts[0];
545                 break;
546             case SkPath::kLine_Verb:
547                 if (SkPaint::kButt_Cap != capStyle) {
548                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
549                 }
550                 lineproc(pts, 2, clip, blitter);
551                 lastPt = pts[1];
552                 break;
553             case SkPath::kQuad_Verb:
554                 if (SkPaint::kButt_Cap != capStyle) {
555                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
556                 }
557                 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
558                 lastPt = pts[2];
559                 break;
560             case SkPath::kConic_Verb: {
561                 if (SkPaint::kButt_Cap != capStyle) {
562                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
563                 }
564                 // how close should the quads be to the original conic?
565                 const SkScalar tol = SK_Scalar1 / 4;
566                 const SkPoint* quadPts = converter.computeQuads(pts,
567                                                        iter.conicWeight(), tol);
568                 for (int i = 0; i < converter.countQuads(); ++i) {
569                     int level = compute_quad_level(quadPts);
570                     hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
571                     quadPts += 2;
572                 }
573                 lastPt = pts[2];
574                 break;
575             }
576             case SkPath::kCubic_Verb: {
577                 if (SkPaint::kButt_Cap != capStyle) {
578                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
579                 }
580                 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
581                 lastPt = pts[3];
582             } break;
583             case SkPath::kClose_Verb:
584                 pts[0] = lastPt;
585                 pts[1] = firstPt;
586                 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
587                     // cap moveTo/close to match svg expectations for degenerate segments
588                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
589                 }
590                 lineproc(pts, 2, clip, blitter);
591                 break;
592             case SkPath::kDone_Verb:
593                 break;
594         }
595         if (SkPaint::kButt_Cap != capStyle) {
596             if (prevVerb == SkPath::kMove_Verb &&
597                     verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
598                 firstPt = pts[0];  // the curve moved the initial point, so close to it instead
599             }
600             prevVerb = verb;
601         }
602     }
603 }
604 
HairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)605 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
606     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
607 }
608 
AntiHairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)609 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
610     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
611 }
612 
HairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)613 void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
614     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
615 }
616 
AntiHairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)617 void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
618     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
619 }
620 
HairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)621 void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
622     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
623 }
624 
AntiHairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)625 void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
626     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
627 }
628 
629 ///////////////////////////////////////////////////////////////////////////////
630 
FrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)631 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
632                        const SkRasterClip& clip, SkBlitter* blitter) {
633     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
634 
635     if (strokeSize.fX < 0 || strokeSize.fY < 0) {
636         return;
637     }
638 
639     const SkScalar dx = strokeSize.fX;
640     const SkScalar dy = strokeSize.fY;
641     SkScalar rx = SkScalarHalf(dx);
642     SkScalar ry = SkScalarHalf(dy);
643     SkRect   outer, tmp;
644 
645     outer.set(r.fLeft - rx, r.fTop - ry,
646                 r.fRight + rx, r.fBottom + ry);
647 
648     if (r.width() <= dx || r.height() <= dy) {
649         SkScan::FillRect(outer, clip, blitter);
650         return;
651     }
652 
653     tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
654     SkScan::FillRect(tmp, clip, blitter);
655     tmp.fTop = outer.fBottom - dy;
656     tmp.fBottom = outer.fBottom;
657     SkScan::FillRect(tmp, clip, blitter);
658 
659     tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
660     SkScan::FillRect(tmp, clip, blitter);
661     tmp.fLeft = outer.fRight - dx;
662     tmp.fRight = outer.fRight;
663     SkScan::FillRect(tmp, clip, blitter);
664 }
665 
HairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)666 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
667                       SkBlitter* blitter) {
668     if (clip.isBW()) {
669         HairLineRgn(pts, count, &clip.bwRgn(), blitter);
670     } else {
671         const SkRegion* clipRgn = nullptr;
672 
673         SkRect r;
674         r.set(pts, count);
675         r.outset(SK_ScalarHalf, SK_ScalarHalf);
676 
677         SkAAClipBlitterWrapper wrap;
678         if (!clip.quickContains(r.roundOut())) {
679             wrap.init(clip, blitter);
680             blitter = wrap.getBlitter();
681             clipRgn = &wrap.getRgn();
682         }
683         HairLineRgn(pts, count, clipRgn, blitter);
684     }
685 }
686 
AntiHairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)687 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
688                           SkBlitter* blitter) {
689     if (clip.isBW()) {
690         AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
691     } else {
692         const SkRegion* clipRgn = nullptr;
693 
694         SkRect r;
695         r.set(pts, count);
696 
697         SkAAClipBlitterWrapper wrap;
698         if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
699             wrap.init(clip, blitter);
700             blitter = wrap.getBlitter();
701             clipRgn = &wrap.getRgn();
702         }
703         AntiHairLineRgn(pts, count, clipRgn, blitter);
704     }
705 }
706