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