Flutter Engine
The Flutter Engine
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"
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"
27#include "src/core/SkPathPriv.h"
29#include "src/core/SkScan.h"
30
31#include <algorithm>
32#include <array>
33#include <cstdint>
34#include <cstring>
35
36static 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
46static 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
57static bool canConvertFDot6ToFixed(SkFDot6 x) {
58 const int maxDot6 = SK_MaxS32 >> (16 - 6);
59 return SkAbs32(x) <= maxDot6;
60}
61#endif
62
63void 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.
177void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, SkBlitter* blitter) {
179 SkBlitterClipper clipper;
180 // Create the enclosing bounds of the hairrect. i.e. we will stroke the interior of r.
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
235
236static 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
257static void hair_quad(const SkPoint pts[3], const SkRegion* clip,
258 SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
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
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
295static 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)
301static bool geometric_overlap(const SkRect& a, const SkRect& 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)
309static 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
315static 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);
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
330static 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
336static 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
360static 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
365static 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
373
374static 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
379static 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
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
428static 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);
433 if (!geometric_overlap(*outsetClip, bounds)) {
434 return;
435 } else if (geometric_contains(*insetClip, bounds)) {
436 clip = nullptr;
437 }
438 }
439
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
453static 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)
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. */
472template <SkPaint::Cap capStyle>
473void 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
520template <SkPaint::Cap capStyle>
521void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
522 SkScan::HairRgnProc lineproc) {
523 if (path.isEmpty()) {
524 return;
525 }
526
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
583 SkPoint pts[4], firstPt, lastPt;
584 SkPath::Verb prevVerb;
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) {
597 firstPt = lastPt = pts[0];
598 break;
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;
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;
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;
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
657void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
658 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
659}
660
662 hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
663}
664
666 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
667}
668
670 hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
671}
672
674 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
675}
676
678 hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
679}
680
681///////////////////////////////////////////////////////////////////////////////
682
683void 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;
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
717void 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);
727
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
738void 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
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}
int count
Definition: FontMgrTest.cpp:50
#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:3892
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
Definition: SkRasterClip.h:175
SkBlitter * getBlitter()
Definition: SkRasterClip.h:179
SkBlitter * apply(SkBlitter *blitter, const SkRegion *clip, const SkIRect *bounds=nullptr)
Definition: SkBlitter.cpp:626
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.
Definition: SkBlitter.cpp:133
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
Definition: SkPath.h:59
const SkRect & getBounds() const
Definition: SkPath.cpp:430
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition: SkPath.cpp:516
const SkRegion & bwRgn() const
Definition: SkRasterClip.h:44
bool quickContains(const SkIRect &rect) const
Definition: SkRasterClip.h:76
bool isRect() const
Definition: SkRasterClip.h:52
bool isBW() const
Definition: SkRasterClip.h:42
bool quickReject(const SkIRect &rect) const
Definition: SkRasterClip.h:85
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
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
string converter
Definition: cacheimages.py:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
SIT bool all(const Vec< 1, T > &x)
Definition: SkVx.h:582
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
Definition: SkRect.h:32
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
Definition: SkPoint_impl.h:193
float fX
x-axis value
Definition: SkPoint_impl.h:164
static float DotProduct(const SkVector &a, const SkVector &b)
Definition: SkPoint_impl.h:518
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
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
Definition: SkVx.h:83
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