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