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