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