• 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/base/SkMathPriv.h"
10 #include "src/core/SkBlitter.h"
11 #include "src/core/SkFDot6.h"
12 #include "src/core/SkLineClipper.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 "src/base/SkVx.h"
216 #include "src/core/SkGeometry.h"
217 
218 #define kMaxCubicSubdivideLevel 9
219 #define kMaxQuadSubdivideLevel  5
220 
221 using float2 = skvx::float2;
222 
compute_int_quad_dist(const SkPoint pts[3])223 static uint32_t compute_int_quad_dist(const SkPoint pts[3]) {
224     // compute the vector between the control point ([1]) and the middle of the
225     // line connecting the start and end ([0] and [2])
226     SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
227     SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
228     // we want everyone to be positive
229     dx = SkScalarAbs(dx);
230     dy = SkScalarAbs(dy);
231     // convert to whole pixel values (use ceiling to be conservative).
232     // assign to unsigned so we can safely add 1/2 of the smaller and still fit in
233     // uint32_t, since SkScalarCeilToInt() returns 31 bits at most.
234     uint32_t idx = SkScalarCeilToInt(dx);
235     uint32_t idy = SkScalarCeilToInt(dy);
236     // use the cheap approx for distance
237     if (idx > idy) {
238         return idx + (idy >> 1);
239     } else {
240         return idy + (idx >> 1);
241     }
242 }
243 
hair_quad(const SkPoint pts[3],const SkRegion * clip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)244 static void hair_quad(const SkPoint pts[3], const SkRegion* clip,
245                      SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
246     SkASSERT(level <= kMaxQuadSubdivideLevel);
247 
248     SkQuadCoeff coeff(pts);
249 
250     const int lines = 1 << level;
251     float2 t(0);
252     float2 dt(SK_Scalar1 / lines);
253 
254     SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
255     SkASSERT((unsigned)lines < std::size(tmp));
256 
257     tmp[0] = pts[0];
258     float2 A = coeff.fA;
259     float2 B = coeff.fB;
260     float2 C = coeff.fC;
261     for (int i = 1; i < lines; ++i) {
262         t = t + dt;
263         ((A * t + B) * t + C).store(&tmp[i]);
264     }
265     tmp[lines] = pts[2];
266     lineproc(tmp, lines + 1, clip, blitter);
267 }
268 
compute_nocheck_quad_bounds(const SkPoint pts[3])269 static SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) {
270     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6));
271 
272     float2 min = float2::Load(pts);
273     float2 max = min;
274     for (int i = 1; i < 3; ++i) {
275         float2 pair = float2::Load(pts+i);
276         min = skvx::min(min, pair);
277         max = skvx::max(max, pair);
278     }
279     return { min[0], min[1], max[0], max[1] };
280 }
281 
is_inverted(const SkRect & r)282 static bool is_inverted(const SkRect& r) {
283     return r.fLeft > r.fRight || r.fTop > r.fBottom;
284 }
285 
286 // Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking
287 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_overlap(const SkRect & a,const SkRect & b)288 static bool geometric_overlap(const SkRect& a, const SkRect& b) {
289     SkASSERT(!is_inverted(a) && !is_inverted(b));
290     return a.fLeft < b.fRight && b.fLeft < a.fRight &&
291             a.fTop < b.fBottom && b.fTop < a.fBottom;
292 }
293 
294 // Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking
295 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_contains(const SkRect & outer,const SkRect & inner)296 static bool geometric_contains(const SkRect& outer, const SkRect& inner) {
297     SkASSERT(!is_inverted(outer) && !is_inverted(inner));
298     return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft &&
299             inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop;
300 }
301 
hairquad(const SkPoint pts[3],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)302 static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
303     SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
304     if (insetClip) {
305         SkASSERT(outsetClip);
306         SkRect bounds = compute_nocheck_quad_bounds(pts);
307         if (!geometric_overlap(*outsetClip, bounds)) {
308             return;
309         } else if (geometric_contains(*insetClip, bounds)) {
310             clip = nullptr;
311         }
312     }
313 
314     hair_quad(pts, clip, blitter, level, lineproc);
315 }
316 
max_component(const float2 & value)317 static inline SkScalar max_component(const float2& value) {
318     SkScalar components[2];
319     value.store(components);
320     return std::max(components[0], components[1]);
321 }
322 
compute_cubic_segs(const SkPoint pts[4])323 static inline int compute_cubic_segs(const SkPoint pts[4]) {
324     float2 p0 = from_point(pts[0]);
325     float2 p1 = from_point(pts[1]);
326     float2 p2 = from_point(pts[2]);
327     float2 p3 = from_point(pts[3]);
328 
329     const float2 oneThird(1.0f / 3.0f);
330     const float2 twoThird(2.0f / 3.0f);
331 
332     float2 p13 = oneThird * p3 + twoThird * p0;
333     float2 p23 = oneThird * p0 + twoThird * p3;
334 
335     SkScalar diff = max_component(max(abs(p1 - p13), abs(p2 - p23)));
336     SkScalar tol = SK_Scalar1 / 8;
337 
338     for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
339         if (diff < tol) {
340             return 1 << i;
341         }
342         tol *= 4;
343     }
344     return 1 << kMaxCubicSubdivideLevel;
345 }
346 
lt_90(SkPoint p0,SkPoint pivot,SkPoint p2)347 static bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
348     return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
349 }
350 
351 // The off-curve points are "inside" the limits of the on-curve pts
quick_cubic_niceness_check(const SkPoint pts[4])352 static bool quick_cubic_niceness_check(const SkPoint pts[4]) {
353     return lt_90(pts[1], pts[0], pts[3]) &&
354            lt_90(pts[2], pts[0], pts[3]) &&
355            lt_90(pts[1], pts[3], pts[0]) &&
356            lt_90(pts[2], pts[3], pts[0]);
357 }
358 
359 using mask2 = skvx::Vec<2, uint32_t>;
360 
float2_is_finite(const float2 & x)361 static inline mask2 float2_is_finite(const float2& x) {
362     const mask2 exp_mask = mask2(0xFF << 23);
363     return (skvx::bit_pun<mask2>(x) & exp_mask) != exp_mask;
364 }
365 
hair_cubic(const SkPoint pts[4],const SkRegion * clip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)366 static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
367                        SkScan::HairRgnProc lineproc) {
368     const int lines = compute_cubic_segs(pts);
369     SkASSERT(lines > 0);
370     if (1 == lines) {
371         SkPoint tmp[2] = { pts[0], pts[3] };
372         lineproc(tmp, 2, clip, blitter);
373         return;
374     }
375 
376     SkCubicCoeff coeff(pts);
377 
378     const float2 dt(SK_Scalar1 / lines);
379     float2 t(0);
380 
381     SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
382     SkASSERT((unsigned)lines < std::size(tmp));
383 
384     tmp[0] = pts[0];
385     float2 A = coeff.fA;
386     float2 B = coeff.fB;
387     float2 C = coeff.fC;
388     float2 D = coeff.fD;
389     mask2 is_finite(~0);   // start out as true
390     for (int i = 1; i < lines; ++i) {
391         t = t + dt;
392         float2 p = ((A * t + B) * t + C) * t + D;
393         is_finite &= float2_is_finite(p);
394         p.store(&tmp[i]);
395     }
396     if (all(is_finite)) {
397         tmp[lines] = pts[3];
398         lineproc(tmp, lines + 1, clip, blitter);
399     } // else some point(s) are non-finite, so don't draw
400 }
401 
compute_nocheck_cubic_bounds(const SkPoint pts[4])402 static SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) {
403     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8));
404 
405     float2 min = float2::Load(pts);
406     float2 max = min;
407     for (int i = 1; i < 4; ++i) {
408         float2 pair = float2::Load(pts+i);
409         min = skvx::min(min, pair);
410         max = skvx::max(max, pair);
411     }
412     return { min[0], min[1], max[0], max[1] };
413 }
414 
haircubic(const SkPoint pts[4],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)415 static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
416                       SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
417     if (insetClip) {
418         SkASSERT(outsetClip);
419         SkRect bounds = compute_nocheck_cubic_bounds(pts);
420         if (!geometric_overlap(*outsetClip, bounds)) {
421             return;
422         } else if (geometric_contains(*insetClip, bounds)) {
423             clip = nullptr;
424         }
425     }
426 
427     if (quick_cubic_niceness_check(pts)) {
428         hair_cubic(pts, clip, blitter, lineproc);
429     } else {
430         SkPoint  tmp[13];
431         SkScalar tValues[3];
432 
433         int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
434         for (int i = 0; i < count; i++) {
435             hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
436         }
437     }
438 }
439 
compute_quad_level(const SkPoint pts[3])440 static int compute_quad_level(const SkPoint pts[3]) {
441     uint32_t d = compute_int_quad_dist(pts);
442     /*  quadratics approach the line connecting their start and end points
443      4x closer with each subdivision, so we compute the number of
444      subdivisions to be the minimum need to get that distance to be less
445      than a pixel.
446      */
447     int level = (33 - SkCLZ(d)) >> 1;
448     // safety check on level (from the previous version)
449     if (level > kMaxQuadSubdivideLevel) {
450         level = kMaxQuadSubdivideLevel;
451     }
452     return level;
453 }
454 
455 /* Extend the points in the direction of the starting or ending tangent by 1/2 unit to
456    account for a round or square cap. If there's no distance between the end point and
457    the control point, use the next control point to create a tangent. If the curve
458    is degenerate, move the cap out 1/2 unit horizontally. */
459 template <SkPaint::Cap capStyle>
extend_pts(SkPath::Verb prevVerb,SkPath::Verb nextVerb,SkPoint * pts,int ptCount)460 void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) {
461     SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle);
462     // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that.
463     const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8;
464     if (SkPath::kMove_Verb == prevVerb) {
465         SkPoint* first = pts;
466         SkPoint* ctrl = first;
467         int controls = ptCount - 1;
468         SkVector tangent;
469         do {
470             tangent = *first - *++ctrl;
471         } while (tangent.isZero() && --controls > 0);
472         if (tangent.isZero()) {
473             tangent.set(1, 0);
474             controls = ptCount - 1;  // If all points are equal, move all but one
475         } else {
476             tangent.normalize();
477         }
478         do {    // If the end point and control points are equal, loop to move them in tandem.
479             first->fX += tangent.fX * capOutset;
480             first->fY += tangent.fY * capOutset;
481             ++first;
482         } while (++controls < ptCount);
483     }
484     if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb
485             || SkPath::kClose_Verb == nextVerb) {
486         SkPoint* last = &pts[ptCount - 1];
487         SkPoint* ctrl = last;
488         int controls = ptCount - 1;
489         SkVector tangent;
490         do {
491             tangent = *last - *--ctrl;
492         } while (tangent.isZero() && --controls > 0);
493         if (tangent.isZero()) {
494             tangent.set(-1, 0);
495             controls = ptCount - 1;
496         } else {
497             tangent.normalize();
498         }
499         do {
500             last->fX += tangent.fX * capOutset;
501             last->fY += tangent.fY * capOutset;
502             --last;
503         } while (++controls < ptCount);
504     }
505 }
506 
507 template <SkPaint::Cap capStyle>
hair_path(const SkPath & path,const SkRasterClip & rclip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)508 void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
509                       SkScan::HairRgnProc lineproc) {
510     if (path.isEmpty()) {
511         return;
512     }
513 
514     SkAAClipBlitterWrapper wrap;
515     const SkRegion* clip = nullptr;
516     SkRect insetStorage, outsetStorage;
517     const SkRect* insetClip = nullptr;
518     const SkRect* outsetClip = nullptr;
519 
520     {
521         const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
522         const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
523         if (rclip.quickReject(ibounds)) {
524             return;
525         }
526         if (!rclip.quickContains(ibounds)) {
527             if (rclip.isBW()) {
528                 clip = &rclip.bwRgn();
529             } else {
530                 wrap.init(rclip, blitter);
531                 blitter = wrap.getBlitter();
532                 clip = &wrap.getRgn();
533             }
534 
535             /*
536              *  We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
537              *  Since we're hairlining, the "bounds" of the control points isn't necessairly the
538              *  limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
539              *
540              *  Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
541              *  the culling bounds so we can just do a straight compare per segment.
542              *
543              *  insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
544              *  it from the clip-bounds (since segment bounds can be off by 1).
545              *
546              *  outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
547              *  outset it from the clip-bounds.
548              */
549             insetStorage.set(clip->getBounds());
550             outsetStorage = insetStorage.makeOutset(1, 1);
551             insetStorage.inset(1, 1);
552             if (is_inverted(insetStorage)) {
553                 /*
554                  *  our bounds checks assume the rects are never inverted. If insetting has
555                  *  created that, we assume that the area is too small to safely perform a
556                  *  quick-accept, so we just mark the rect as empty (so the quick-accept check
557                  *  will always fail.
558                  */
559                 insetStorage.setEmpty();    // just so we don't pass an inverted rect
560             }
561             if (rclip.isRect()) {
562                 insetClip = &insetStorage;
563             }
564             outsetClip = &outsetStorage;
565         }
566     }
567 
568     SkPathPriv::RangeIter iter = SkPathPriv::Iterate(path).begin();
569     SkPathPriv::RangeIter end = SkPathPriv::Iterate(path).end();
570     SkPoint               pts[4], firstPt, lastPt;
571     SkPath::Verb          prevVerb;
572     SkAutoConicToQuads    converter;
573 
574     if (SkPaint::kButt_Cap != capStyle) {
575         prevVerb = SkPath::kDone_Verb;
576     }
577     while (iter != end) {
578         auto [pathVerb, pathPts, w] = *iter++;
579         SkPath::Verb verb = (SkPath::Verb)pathVerb;
580         SkPath::Verb nextVerb = (iter != end) ? (SkPath::Verb)iter.peekVerb() : SkPath::kDone_Verb;
581         memcpy(pts, pathPts, SkPathPriv::PtsInIter(verb) * sizeof(SkPoint));
582         switch (verb) {
583             case SkPath::kMove_Verb:
584                 firstPt = lastPt = pts[0];
585                 break;
586             case SkPath::kLine_Verb:
587                 if (SkPaint::kButt_Cap != capStyle) {
588                     extend_pts<capStyle>(prevVerb, nextVerb, pts, 2);
589                 }
590                 lineproc(pts, 2, clip, blitter);
591                 lastPt = pts[1];
592                 break;
593             case SkPath::kQuad_Verb:
594                 if (SkPaint::kButt_Cap != capStyle) {
595                     extend_pts<capStyle>(prevVerb, nextVerb, pts, 3);
596                 }
597                 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
598                 lastPt = pts[2];
599                 break;
600             case SkPath::kConic_Verb: {
601                 if (SkPaint::kButt_Cap != capStyle) {
602                     extend_pts<capStyle>(prevVerb, nextVerb, pts, 3);
603                 }
604                 // how close should the quads be to the original conic?
605                 const SkScalar tol = SK_Scalar1 / 4;
606                 const SkPoint* quadPts = converter.computeQuads(pts, *w, tol);
607                 for (int i = 0; i < converter.countQuads(); ++i) {
608                     int level = compute_quad_level(quadPts);
609                     hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
610                     quadPts += 2;
611                 }
612                 lastPt = pts[2];
613                 break;
614             }
615             case SkPath::kCubic_Verb: {
616                 if (SkPaint::kButt_Cap != capStyle) {
617                     extend_pts<capStyle>(prevVerb, nextVerb, pts, 4);
618                 }
619                 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
620                 lastPt = pts[3];
621             } break;
622             case SkPath::kClose_Verb:
623                 pts[0] = lastPt;
624                 pts[1] = firstPt;
625                 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
626                     // cap moveTo/close to match svg expectations for degenerate segments
627                     extend_pts<capStyle>(prevVerb, nextVerb, pts, 2);
628                 }
629                 lineproc(pts, 2, clip, blitter);
630                 break;
631             case SkPath::kDone_Verb:
632                 break;
633         }
634         if (SkPaint::kButt_Cap != capStyle) {
635             if (prevVerb == SkPath::kMove_Verb &&
636                     verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
637                 firstPt = pts[0];  // the curve moved the initial point, so close to it instead
638             }
639             prevVerb = verb;
640         }
641     }
642 }
643 
HairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)644 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
645     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
646 }
647 
AntiHairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)648 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
649     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
650 }
651 
HairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)652 void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
653     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
654 }
655 
AntiHairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)656 void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
657     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
658 }
659 
HairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)660 void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
661     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
662 }
663 
AntiHairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)664 void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
665     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
666 }
667 
668 ///////////////////////////////////////////////////////////////////////////////
669 
FrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)670 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
671                        const SkRasterClip& clip, SkBlitter* blitter) {
672     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
673 
674     if (strokeSize.fX < 0 || strokeSize.fY < 0) {
675         return;
676     }
677 
678     const SkScalar dx = strokeSize.fX;
679     const SkScalar dy = strokeSize.fY;
680     SkScalar rx = SkScalarHalf(dx);
681     SkScalar ry = SkScalarHalf(dy);
682     SkRect   outer, tmp;
683 
684     outer.setLTRB(r.fLeft - rx, r.fTop - ry, r.fRight + rx, r.fBottom + ry);
685 
686     if (r.width() <= dx || r.height() <= dy) {
687         SkScan::FillRect(outer, clip, blitter);
688         return;
689     }
690 
691     tmp.setLTRB(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
692     SkScan::FillRect(tmp, clip, blitter);
693     tmp.fTop = outer.fBottom - dy;
694     tmp.fBottom = outer.fBottom;
695     SkScan::FillRect(tmp, clip, blitter);
696 
697     tmp.setLTRB(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
698     SkScan::FillRect(tmp, clip, blitter);
699     tmp.fLeft = outer.fRight - dx;
700     tmp.fRight = outer.fRight;
701     SkScan::FillRect(tmp, clip, blitter);
702 }
703 
HairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)704 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
705                       SkBlitter* blitter) {
706     if (clip.isBW()) {
707         HairLineRgn(pts, count, &clip.bwRgn(), blitter);
708     } else {
709         const SkRegion* clipRgn = nullptr;
710 
711         SkRect r;
712         r.setBounds(pts, count);
713         r.outset(SK_ScalarHalf, SK_ScalarHalf);
714 
715         SkAAClipBlitterWrapper wrap;
716         if (!clip.quickContains(r.roundOut())) {
717             wrap.init(clip, blitter);
718             blitter = wrap.getBlitter();
719             clipRgn = &wrap.getRgn();
720         }
721         HairLineRgn(pts, count, clipRgn, blitter);
722     }
723 }
724 
AntiHairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)725 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
726                           SkBlitter* blitter) {
727     if (clip.isBW()) {
728         AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
729     } else {
730         const SkRegion* clipRgn = nullptr;
731 
732         SkRect r;
733         r.setBounds(pts, count);
734 
735         SkAAClipBlitterWrapper wrap;
736         if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
737             wrap.init(clip, blitter);
738             blitter = wrap.getBlitter();
739             clipRgn = &wrap.getRgn();
740         }
741         AntiHairLineRgn(pts, count, clipRgn, blitter);
742     }
743 }
744