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/base/SkMathPriv.h"
10 #include "src/core/SkBlitter.h"
11 #include "src/core/SkFDot6.h"
12 #include "src/core/SkLineClipper.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 "src/base/SkVx.h"
216 #include "src/core/SkGeometry.h"
217
218 #define kMaxCubicSubdivideLevel 9
219 #define kMaxQuadSubdivideLevel 5
220
221 using float2 = skvx::float2;
222
compute_int_quad_dist(const SkPoint pts[3])223 static uint32_t compute_int_quad_dist(const SkPoint pts[3]) {
224 // compute the vector between the control point ([1]) and the middle of the
225 // line connecting the start and end ([0] and [2])
226 SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
227 SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
228 // we want everyone to be positive
229 dx = SkScalarAbs(dx);
230 dy = SkScalarAbs(dy);
231 // convert to whole pixel values (use ceiling to be conservative).
232 // assign to unsigned so we can safely add 1/2 of the smaller and still fit in
233 // uint32_t, since SkScalarCeilToInt() returns 31 bits at most.
234 uint32_t idx = SkScalarCeilToInt(dx);
235 uint32_t idy = SkScalarCeilToInt(dy);
236 // use the cheap approx for distance
237 if (idx > idy) {
238 return idx + (idy >> 1);
239 } else {
240 return idy + (idx >> 1);
241 }
242 }
243
hair_quad(const SkPoint pts[3],const SkRegion * clip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)244 static void hair_quad(const SkPoint pts[3], const SkRegion* clip,
245 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
246 SkASSERT(level <= kMaxQuadSubdivideLevel);
247
248 SkQuadCoeff coeff(pts);
249
250 const int lines = 1 << level;
251 float2 t(0);
252 float2 dt(SK_Scalar1 / lines);
253
254 SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
255 SkASSERT((unsigned)lines < std::size(tmp));
256
257 tmp[0] = pts[0];
258 float2 A = coeff.fA;
259 float2 B = coeff.fB;
260 float2 C = coeff.fC;
261 for (int i = 1; i < lines; ++i) {
262 t = t + dt;
263 ((A * t + B) * t + C).store(&tmp[i]);
264 }
265 tmp[lines] = pts[2];
266 lineproc(tmp, lines + 1, clip, blitter);
267 }
268
compute_nocheck_quad_bounds(const SkPoint pts[3])269 static SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) {
270 SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6));
271
272 float2 min = float2::Load(pts);
273 float2 max = min;
274 for (int i = 1; i < 3; ++i) {
275 float2 pair = float2::Load(pts+i);
276 min = skvx::min(min, pair);
277 max = skvx::max(max, pair);
278 }
279 return { min[0], min[1], max[0], max[1] };
280 }
281
is_inverted(const SkRect & r)282 static bool is_inverted(const SkRect& r) {
283 return r.fLeft > r.fRight || r.fTop > r.fBottom;
284 }
285
286 // Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking
287 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_overlap(const SkRect & a,const SkRect & b)288 static bool geometric_overlap(const SkRect& a, const SkRect& b) {
289 SkASSERT(!is_inverted(a) && !is_inverted(b));
290 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
291 a.fTop < b.fBottom && b.fTop < a.fBottom;
292 }
293
294 // Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking
295 // something to be stroked, so empty can still draw something (e.g. horizontal line)
geometric_contains(const SkRect & outer,const SkRect & inner)296 static bool geometric_contains(const SkRect& outer, const SkRect& inner) {
297 SkASSERT(!is_inverted(outer) && !is_inverted(inner));
298 return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft &&
299 inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop;
300 }
301
hairquad(const SkPoint pts[3],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)302 static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
303 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
304 if (insetClip) {
305 SkASSERT(outsetClip);
306 SkRect bounds = compute_nocheck_quad_bounds(pts);
307 if (!geometric_overlap(*outsetClip, bounds)) {
308 return;
309 } else if (geometric_contains(*insetClip, bounds)) {
310 clip = nullptr;
311 }
312 }
313
314 hair_quad(pts, clip, blitter, level, lineproc);
315 }
316
max_component(const float2 & value)317 static inline SkScalar max_component(const float2& value) {
318 SkScalar components[2];
319 value.store(components);
320 return std::max(components[0], components[1]);
321 }
322
compute_cubic_segs(const SkPoint pts[4])323 static inline int compute_cubic_segs(const SkPoint pts[4]) {
324 float2 p0 = from_point(pts[0]);
325 float2 p1 = from_point(pts[1]);
326 float2 p2 = from_point(pts[2]);
327 float2 p3 = from_point(pts[3]);
328
329 const float2 oneThird(1.0f / 3.0f);
330 const float2 twoThird(2.0f / 3.0f);
331
332 float2 p13 = oneThird * p3 + twoThird * p0;
333 float2 p23 = oneThird * p0 + twoThird * p3;
334
335 SkScalar diff = max_component(max(abs(p1 - p13), abs(p2 - p23)));
336 SkScalar tol = SK_Scalar1 / 8;
337
338 for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
339 if (diff < tol) {
340 return 1 << i;
341 }
342 tol *= 4;
343 }
344 return 1 << kMaxCubicSubdivideLevel;
345 }
346
lt_90(SkPoint p0,SkPoint pivot,SkPoint p2)347 static bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
348 return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
349 }
350
351 // The off-curve points are "inside" the limits of the on-curve pts
quick_cubic_niceness_check(const SkPoint pts[4])352 static bool quick_cubic_niceness_check(const SkPoint pts[4]) {
353 return lt_90(pts[1], pts[0], pts[3]) &&
354 lt_90(pts[2], pts[0], pts[3]) &&
355 lt_90(pts[1], pts[3], pts[0]) &&
356 lt_90(pts[2], pts[3], pts[0]);
357 }
358
359 using mask2 = skvx::Vec<2, uint32_t>;
360
float2_is_finite(const float2 & x)361 static inline mask2 float2_is_finite(const float2& x) {
362 const mask2 exp_mask = mask2(0xFF << 23);
363 return (skvx::bit_pun<mask2>(x) & exp_mask) != exp_mask;
364 }
365
hair_cubic(const SkPoint pts[4],const SkRegion * clip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)366 static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
367 SkScan::HairRgnProc lineproc) {
368 const int lines = compute_cubic_segs(pts);
369 SkASSERT(lines > 0);
370 if (1 == lines) {
371 SkPoint tmp[2] = { pts[0], pts[3] };
372 lineproc(tmp, 2, clip, blitter);
373 return;
374 }
375
376 SkCubicCoeff coeff(pts);
377
378 const float2 dt(SK_Scalar1 / lines);
379 float2 t(0);
380
381 SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
382 SkASSERT((unsigned)lines < std::size(tmp));
383
384 tmp[0] = pts[0];
385 float2 A = coeff.fA;
386 float2 B = coeff.fB;
387 float2 C = coeff.fC;
388 float2 D = coeff.fD;
389 mask2 is_finite(~0); // start out as true
390 for (int i = 1; i < lines; ++i) {
391 t = t + dt;
392 float2 p = ((A * t + B) * t + C) * t + D;
393 is_finite &= float2_is_finite(p);
394 p.store(&tmp[i]);
395 }
396 if (all(is_finite)) {
397 tmp[lines] = pts[3];
398 lineproc(tmp, lines + 1, clip, blitter);
399 } // else some point(s) are non-finite, so don't draw
400 }
401
compute_nocheck_cubic_bounds(const SkPoint pts[4])402 static SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) {
403 SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8));
404
405 float2 min = float2::Load(pts);
406 float2 max = min;
407 for (int i = 1; i < 4; ++i) {
408 float2 pair = float2::Load(pts+i);
409 min = skvx::min(min, pair);
410 max = skvx::max(max, pair);
411 }
412 return { min[0], min[1], max[0], max[1] };
413 }
414
haircubic(const SkPoint pts[4],const SkRegion * clip,const SkRect * insetClip,const SkRect * outsetClip,SkBlitter * blitter,int level,SkScan::HairRgnProc lineproc)415 static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
416 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
417 if (insetClip) {
418 SkASSERT(outsetClip);
419 SkRect bounds = compute_nocheck_cubic_bounds(pts);
420 if (!geometric_overlap(*outsetClip, bounds)) {
421 return;
422 } else if (geometric_contains(*insetClip, bounds)) {
423 clip = nullptr;
424 }
425 }
426
427 if (quick_cubic_niceness_check(pts)) {
428 hair_cubic(pts, clip, blitter, lineproc);
429 } else {
430 SkPoint tmp[13];
431 SkScalar tValues[3];
432
433 int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
434 for (int i = 0; i < count; i++) {
435 hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
436 }
437 }
438 }
439
compute_quad_level(const SkPoint pts[3])440 static int compute_quad_level(const SkPoint pts[3]) {
441 uint32_t d = compute_int_quad_dist(pts);
442 /* quadratics approach the line connecting their start and end points
443 4x closer with each subdivision, so we compute the number of
444 subdivisions to be the minimum need to get that distance to be less
445 than a pixel.
446 */
447 int level = (33 - SkCLZ(d)) >> 1;
448 // safety check on level (from the previous version)
449 if (level > kMaxQuadSubdivideLevel) {
450 level = kMaxQuadSubdivideLevel;
451 }
452 return level;
453 }
454
455 /* Extend the points in the direction of the starting or ending tangent by 1/2 unit to
456 account for a round or square cap. If there's no distance between the end point and
457 the control point, use the next control point to create a tangent. If the curve
458 is degenerate, move the cap out 1/2 unit horizontally. */
459 template <SkPaint::Cap capStyle>
extend_pts(SkPath::Verb prevVerb,SkPath::Verb nextVerb,SkPoint * pts,int ptCount)460 void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) {
461 SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle);
462 // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that.
463 const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8;
464 if (SkPath::kMove_Verb == prevVerb) {
465 SkPoint* first = pts;
466 SkPoint* ctrl = first;
467 int controls = ptCount - 1;
468 SkVector tangent;
469 do {
470 tangent = *first - *++ctrl;
471 } while (tangent.isZero() && --controls > 0);
472 if (tangent.isZero()) {
473 tangent.set(1, 0);
474 controls = ptCount - 1; // If all points are equal, move all but one
475 } else {
476 tangent.normalize();
477 }
478 do { // If the end point and control points are equal, loop to move them in tandem.
479 first->fX += tangent.fX * capOutset;
480 first->fY += tangent.fY * capOutset;
481 ++first;
482 } while (++controls < ptCount);
483 }
484 if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb
485 || SkPath::kClose_Verb == nextVerb) {
486 SkPoint* last = &pts[ptCount - 1];
487 SkPoint* ctrl = last;
488 int controls = ptCount - 1;
489 SkVector tangent;
490 do {
491 tangent = *last - *--ctrl;
492 } while (tangent.isZero() && --controls > 0);
493 if (tangent.isZero()) {
494 tangent.set(-1, 0);
495 controls = ptCount - 1;
496 } else {
497 tangent.normalize();
498 }
499 do {
500 last->fX += tangent.fX * capOutset;
501 last->fY += tangent.fY * capOutset;
502 --last;
503 } while (++controls < ptCount);
504 }
505 }
506
507 template <SkPaint::Cap capStyle>
hair_path(const SkPath & path,const SkRasterClip & rclip,SkBlitter * blitter,SkScan::HairRgnProc lineproc)508 void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
509 SkScan::HairRgnProc lineproc) {
510 if (path.isEmpty()) {
511 return;
512 }
513
514 SkAAClipBlitterWrapper wrap;
515 const SkRegion* clip = nullptr;
516 SkRect insetStorage, outsetStorage;
517 const SkRect* insetClip = nullptr;
518 const SkRect* outsetClip = nullptr;
519
520 {
521 const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
522 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
523 if (rclip.quickReject(ibounds)) {
524 return;
525 }
526 if (!rclip.quickContains(ibounds)) {
527 if (rclip.isBW()) {
528 clip = &rclip.bwRgn();
529 } else {
530 wrap.init(rclip, blitter);
531 blitter = wrap.getBlitter();
532 clip = &wrap.getRgn();
533 }
534
535 /*
536 * We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
537 * Since we're hairlining, the "bounds" of the control points isn't necessairly the
538 * limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
539 *
540 * Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
541 * the culling bounds so we can just do a straight compare per segment.
542 *
543 * insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
544 * it from the clip-bounds (since segment bounds can be off by 1).
545 *
546 * outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
547 * outset it from the clip-bounds.
548 */
549 insetStorage.set(clip->getBounds());
550 outsetStorage = insetStorage.makeOutset(1, 1);
551 insetStorage.inset(1, 1);
552 if (is_inverted(insetStorage)) {
553 /*
554 * our bounds checks assume the rects are never inverted. If insetting has
555 * created that, we assume that the area is too small to safely perform a
556 * quick-accept, so we just mark the rect as empty (so the quick-accept check
557 * will always fail.
558 */
559 insetStorage.setEmpty(); // just so we don't pass an inverted rect
560 }
561 if (rclip.isRect()) {
562 insetClip = &insetStorage;
563 }
564 outsetClip = &outsetStorage;
565 }
566 }
567
568 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(path).begin();
569 SkPathPriv::RangeIter end = SkPathPriv::Iterate(path).end();
570 SkPoint pts[4], firstPt, lastPt;
571 SkPath::Verb prevVerb;
572 SkAutoConicToQuads converter;
573
574 if (SkPaint::kButt_Cap != capStyle) {
575 prevVerb = SkPath::kDone_Verb;
576 }
577 while (iter != end) {
578 auto [pathVerb, pathPts, w] = *iter++;
579 SkPath::Verb verb = (SkPath::Verb)pathVerb;
580 SkPath::Verb nextVerb = (iter != end) ? (SkPath::Verb)iter.peekVerb() : SkPath::kDone_Verb;
581 memcpy(pts, pathPts, SkPathPriv::PtsInIter(verb) * sizeof(SkPoint));
582 switch (verb) {
583 case SkPath::kMove_Verb:
584 firstPt = lastPt = pts[0];
585 break;
586 case SkPath::kLine_Verb:
587 if (SkPaint::kButt_Cap != capStyle) {
588 extend_pts<capStyle>(prevVerb, nextVerb, pts, 2);
589 }
590 lineproc(pts, 2, clip, blitter);
591 lastPt = pts[1];
592 break;
593 case SkPath::kQuad_Verb:
594 if (SkPaint::kButt_Cap != capStyle) {
595 extend_pts<capStyle>(prevVerb, nextVerb, pts, 3);
596 }
597 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
598 lastPt = pts[2];
599 break;
600 case SkPath::kConic_Verb: {
601 if (SkPaint::kButt_Cap != capStyle) {
602 extend_pts<capStyle>(prevVerb, nextVerb, pts, 3);
603 }
604 // how close should the quads be to the original conic?
605 const SkScalar tol = SK_Scalar1 / 4;
606 const SkPoint* quadPts = converter.computeQuads(pts, *w, tol);
607 for (int i = 0; i < converter.countQuads(); ++i) {
608 int level = compute_quad_level(quadPts);
609 hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
610 quadPts += 2;
611 }
612 lastPt = pts[2];
613 break;
614 }
615 case SkPath::kCubic_Verb: {
616 if (SkPaint::kButt_Cap != capStyle) {
617 extend_pts<capStyle>(prevVerb, nextVerb, pts, 4);
618 }
619 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
620 lastPt = pts[3];
621 } break;
622 case SkPath::kClose_Verb:
623 pts[0] = lastPt;
624 pts[1] = firstPt;
625 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
626 // cap moveTo/close to match svg expectations for degenerate segments
627 extend_pts<capStyle>(prevVerb, nextVerb, pts, 2);
628 }
629 lineproc(pts, 2, clip, blitter);
630 break;
631 case SkPath::kDone_Verb:
632 break;
633 }
634 if (SkPaint::kButt_Cap != capStyle) {
635 if (prevVerb == SkPath::kMove_Verb &&
636 verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
637 firstPt = pts[0]; // the curve moved the initial point, so close to it instead
638 }
639 prevVerb = verb;
640 }
641 }
642 }
643
HairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)644 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
645 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
646 }
647
AntiHairPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)648 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
649 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
650 }
651
HairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)652 void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
653 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
654 }
655
AntiHairSquarePath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)656 void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
657 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
658 }
659
HairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)660 void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
661 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
662 }
663
AntiHairRoundPath(const SkPath & path,const SkRasterClip & clip,SkBlitter * blitter)664 void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
665 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
666 }
667
668 ///////////////////////////////////////////////////////////////////////////////
669
FrameRect(const SkRect & r,const SkPoint & strokeSize,const SkRasterClip & clip,SkBlitter * blitter)670 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
671 const SkRasterClip& clip, SkBlitter* blitter) {
672 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
673
674 if (strokeSize.fX < 0 || strokeSize.fY < 0) {
675 return;
676 }
677
678 const SkScalar dx = strokeSize.fX;
679 const SkScalar dy = strokeSize.fY;
680 SkScalar rx = SkScalarHalf(dx);
681 SkScalar ry = SkScalarHalf(dy);
682 SkRect outer, tmp;
683
684 outer.setLTRB(r.fLeft - rx, r.fTop - ry, r.fRight + rx, r.fBottom + ry);
685
686 if (r.width() <= dx || r.height() <= dy) {
687 SkScan::FillRect(outer, clip, blitter);
688 return;
689 }
690
691 tmp.setLTRB(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
692 SkScan::FillRect(tmp, clip, blitter);
693 tmp.fTop = outer.fBottom - dy;
694 tmp.fBottom = outer.fBottom;
695 SkScan::FillRect(tmp, clip, blitter);
696
697 tmp.setLTRB(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
698 SkScan::FillRect(tmp, clip, blitter);
699 tmp.fLeft = outer.fRight - dx;
700 tmp.fRight = outer.fRight;
701 SkScan::FillRect(tmp, clip, blitter);
702 }
703
HairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)704 void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
705 SkBlitter* blitter) {
706 if (clip.isBW()) {
707 HairLineRgn(pts, count, &clip.bwRgn(), blitter);
708 } else {
709 const SkRegion* clipRgn = nullptr;
710
711 SkRect r;
712 r.setBounds(pts, count);
713 r.outset(SK_ScalarHalf, SK_ScalarHalf);
714
715 SkAAClipBlitterWrapper wrap;
716 if (!clip.quickContains(r.roundOut())) {
717 wrap.init(clip, blitter);
718 blitter = wrap.getBlitter();
719 clipRgn = &wrap.getRgn();
720 }
721 HairLineRgn(pts, count, clipRgn, blitter);
722 }
723 }
724
AntiHairLine(const SkPoint pts[],int count,const SkRasterClip & clip,SkBlitter * blitter)725 void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
726 SkBlitter* blitter) {
727 if (clip.isBW()) {
728 AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
729 } else {
730 const SkRegion* clipRgn = nullptr;
731
732 SkRect r;
733 r.setBounds(pts, count);
734
735 SkAAClipBlitterWrapper wrap;
736 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
737 wrap.init(clip, blitter);
738 blitter = wrap.getBlitter();
739 clipRgn = &wrap.getRgn();
740 }
741 AntiHairLineRgn(pts, count, clipRgn, blitter);
742 }
743 }
744