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