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