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