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 || SkPath::kClose_Verb == nextVerb) {
452 SkPoint* last = &pts[ptCount - 1];
453 SkPoint* ctrl = last;
454 int controls = ptCount - 1;
455 SkVector tangent;
456 do {
457 tangent = *last - *--ctrl;
458 } while (tangent.isZero() && --controls > 0);
459 if (tangent.isZero()) {
460 tangent.set(-1, 0);
461 controls = ptCount - 1;
462 } else {
463 tangent.normalize();
464 }
465 do {
466 last->fX += tangent.fX * capOutset;
467 last->fY += tangent.fY * capOutset;
468 --last;
469 } while (++controls < ptCount);
470 }
471 }
472
473 template <SkPaint::Cap capStyle>
hair_path(const SkPath & path,const SkRasterClip & rclip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)474 void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
475 SkScan::HairRgnProc lineproc) {
476 if (path.isEmpty()) {
477 return;
478 }
479
480 SkAAClipBlitterWrapper wrap;
481 const SkRegion* clip = nullptr;
482 SkRect insetStorage, outsetStorage;
483 const SkRect* insetClip = nullptr;
484 const SkRect* outsetClip = nullptr;
485
486 {
487 const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
488 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
489 if (rclip.quickReject(ibounds)) {
490 return;
491 }
492 if (!rclip.quickContains(ibounds)) {
493 if (rclip.isBW()) {
494 clip = &rclip.bwRgn();
495 } else {
496 wrap.init(rclip, blitter);
497 blitter = wrap.getBlitter();
498 clip = &wrap.getRgn();
499 }
500
501 /*
502 * We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
503 * Since we're hairlining, the "bounds" of the control points isn't necessairly the
504 * limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
505 *
506 * Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
507 * the culling bounds so we can just do a straight compare per segment.
508 *
509 * insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
510 * it from the clip-bounds (since segment bounds can be off by 1).
511 *
512 * outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
513 * outset it from the clip-bounds.
514 */
515 insetStorage.set(clip->getBounds());
516 outsetStorage = insetStorage.makeOutset(1, 1);
517 insetStorage.inset(1, 1);
518 if (is_inverted(insetStorage)) {
519 /*
520 * our bounds checks assume the rects are never inverted. If insetting has
521 * created that, we assume that the area is too small to safely perform a
522 * quick-accept, so we just mark the rect as empty (so the quick-accept check
523 * will always fail.
524 */
525 insetStorage.setEmpty(); // just so we don't pass an inverted rect
526 }
527 if (rclip.isRect()) {
528 insetClip = &insetStorage;
529 }
530 outsetClip = &outsetStorage;
531 }
532 }
533
534 SkPath::RawIter iter(path);
535 SkPoint pts[4], firstPt, lastPt;
536 SkPath::Verb verb, prevVerb;
537 SkAutoConicToQuads converter;
538
539 if (SkPaint::kButt_Cap != capStyle) {
540 prevVerb = SkPath::kDone_Verb;
541 }
542 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
543 switch (verb) {
544 case SkPath::kMove_Verb:
545 firstPt = lastPt = pts[0];
546 break;
547 case SkPath::kLine_Verb:
548 if (SkPaint::kButt_Cap != capStyle) {
549 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
550 }
551 lineproc(pts, 2, clip, blitter);
552 lastPt = pts[1];
553 break;
554 case SkPath::kQuad_Verb:
555 if (SkPaint::kButt_Cap != capStyle) {
556 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
557 }
558 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
559 lastPt = pts[2];
560 break;
561 case SkPath::kConic_Verb: {
562 if (SkPaint::kButt_Cap != capStyle) {
563 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
564 }
565 // how close should the quads be to the original conic?
566 const SkScalar tol = SK_Scalar1 / 4;
567 const SkPoint* quadPts = converter.computeQuads(pts,
568 iter.conicWeight(), tol);
569 for (int i = 0; i < converter.countQuads(); ++i) {
570 int level = compute_quad_level(quadPts);
571 hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
572 quadPts += 2;
573 }
574 lastPt = pts[2];
575 break;
576 }
577 case SkPath::kCubic_Verb: {
578 if (SkPaint::kButt_Cap != capStyle) {
579 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
580 }
581 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
582 lastPt = pts[3];
583 } break;
584 case SkPath::kClose_Verb:
585 pts[0] = lastPt;
586 pts[1] = firstPt;
587 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
588 // cap moveTo/close to match svg expectations for degenerate segments
589 extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
590 }
591 lineproc(pts, 2, clip, blitter);
592 break;
593 case SkPath::kDone_Verb:
594 break;
595 }
596 if (SkPaint::kButt_Cap != capStyle) {
597 if (prevVerb == SkPath::kMove_Verb &&
598 verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
599 firstPt = pts[0]; // the curve moved the initial point, so close to it instead
600 }
601 prevVerb = verb;
602 }
603 }
604 }
605
HairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)606 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
607 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
608 }
609
AntiHairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)610 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
611 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
612 }
613
HairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)614 void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
615 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
616 }
617
AntiHairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)618 void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
619 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
620 }
621
HairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)622 void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
623 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
624 }
625
AntiHairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)626 void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
627 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
628 }
629
630 ///////////////////////////////////////////////////////////////////////////////
631
FrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)632 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
633 const SkRasterClip& clip, SkBlitter* blitter) {
634 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
635
636 if (strokeSize.fX < 0 || strokeSize.fY < 0) {
637 return;
638 }
639
640 const SkScalar dx = strokeSize.fX;
641 const SkScalar dy = strokeSize.fY;
642 SkScalar rx = SkScalarHalf(dx);
643 SkScalar ry = SkScalarHalf(dy);
644 SkRect outer, tmp;
645
646 outer.set(r.fLeft - rx, r.fTop - ry,
647 r.fRight + rx, r.fBottom + ry);
648
649 if (r.width() <= dx || r.height() <= dy) {
650 SkScan::FillRect(outer, clip, blitter);
651 return;
652 }
653
654 tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
655 SkScan::FillRect(tmp, clip, blitter);
656 tmp.fTop = outer.fBottom - dy;
657 tmp.fBottom = outer.fBottom;
658 SkScan::FillRect(tmp, clip, blitter);
659
660 tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
661 SkScan::FillRect(tmp, clip, blitter);
662 tmp.fLeft = outer.fRight - dx;
663 tmp.fRight = outer.fRight;
664 SkScan::FillRect(tmp, clip, blitter);
665 }
666
HairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)667 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
668 SkBlitter* blitter) {
669 if (clip.isBW()) {
670 HairLineRgn(pts, count, &clip.bwRgn(), blitter);
671 } else {
672 const SkRegion* clipRgn = nullptr;
673
674 SkRect r;
675 r.set(pts, count);
676 r.outset(SK_ScalarHalf, SK_ScalarHalf);
677
678 SkAAClipBlitterWrapper wrap;
679 if (!clip.quickContains(r.roundOut())) {
680 wrap.init(clip, blitter);
681 blitter = wrap.getBlitter();
682 clipRgn = &wrap.getRgn();
683 }
684 HairLineRgn(pts, count, clipRgn, blitter);
685 }
686 }
687
AntiHairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)688 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
689 SkBlitter* blitter) {
690 if (clip.isBW()) {
691 AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
692 } else {
693 const SkRegion* clipRgn = nullptr;
694
695 SkRect r;
696 r.set(pts, count);
697
698 SkAAClipBlitterWrapper wrap;
699 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
700 wrap.init(clip, blitter);
701 blitter = wrap.getBlitter();
702 clipRgn = &wrap.getRgn();
703 }
704 AntiHairLineRgn(pts, count, clipRgn, blitter);
705 }
706 }
707