Coverage Report

Created: 2021-08-22 09:07

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