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