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