1/*
2 * Copyright 2015 Google Inc.
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/SkPathBuilder.h"
9#include "include/core/SkRRect.h"
10#include "include/private/SkPathRef.h"
11#include "include/private/SkSafe32.h"
12#include "src/core/SkGeometry.h"
13// need SkDVector
14#include "src/pathops/SkPathOpsPoint.h"
15
16SkPathBuilder::SkPathBuilder() {
17 this->reset();
18}
19
20SkPathBuilder::~SkPathBuilder() {
21}
22
23SkPathBuilder& SkPathBuilder::reset() {
24 fPts.reset();
25 fVerbs.reset();
26 fConicWeights.reset();
27 fFillType = SkPathFillType::kWinding;
28 fIsVolatile = false;
29
30 // these are internal state
31
32 fSegmentMask = 0;
33 fLastMovePoint = {0, 0};
34 fNeedsMoveVerb = true;
35
36 return *this;
37}
38
39void SkPathBuilder::incReserve(int extraPtCount, int extraVbCount) {
40 fPts.setReserve( Sk32_sat_add(fPts.count(), extraPtCount));
41 fVerbs.setReserve(Sk32_sat_add(fVerbs.count(), extraVbCount));
42}
43
44/*
45 * Some old behavior in SkPath -- should we keep it?
46 *
47 * After each edit (i.e. adding a verb)
48 this->setConvexityType(SkPathConvexityType::kUnknown);
49 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
50 */
51
52SkPathBuilder& SkPathBuilder::moveTo(SkPoint pt) {
53 fPts.push_back(pt);
54 fVerbs.push_back((uint8_t)SkPathVerb::kMove);
55
56 fLastMovePoint = pt;
57 fNeedsMoveVerb = false;
58 return *this;
59}
60
61SkPathBuilder& SkPathBuilder::lineTo(SkPoint pt) {
62 this->ensureMove();
63
64 fPts.push_back(pt);
65 fVerbs.push_back((uint8_t)SkPathVerb::kLine);
66
67 fSegmentMask |= kLine_SkPathSegmentMask;
68 return *this;
69}
70
71SkPathBuilder& SkPathBuilder::quadTo(SkPoint pt1, SkPoint pt2) {
72 this->ensureMove();
73
74 SkPoint* p = fPts.append(2);
75 p[0] = pt1;
76 p[1] = pt2;
77 fVerbs.push_back((uint8_t)SkPathVerb::kQuad);
78
79 fSegmentMask |= kQuad_SkPathSegmentMask;
80 return *this;
81}
82
83SkPathBuilder& SkPathBuilder::conicTo(SkPoint pt1, SkPoint pt2, SkScalar w) {
84 this->ensureMove();
85
86 SkPoint* p = fPts.append(2);
87 p[0] = pt1;
88 p[1] = pt2;
89 fVerbs.push_back((uint8_t)SkPathVerb::kConic);
90 fConicWeights.push_back(w);
91
92 fSegmentMask |= kConic_SkPathSegmentMask;
93 return *this;
94}
95
96SkPathBuilder& SkPathBuilder::cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3) {
97 this->ensureMove();
98
99 SkPoint* p = fPts.append(3);
100 p[0] = pt1;
101 p[1] = pt2;
102 p[2] = pt3;
103 fVerbs.push_back((uint8_t)SkPathVerb::kCubic);
104
105 fSegmentMask |= kCubic_SkPathSegmentMask;
106 return *this;
107}
108
109SkPathBuilder& SkPathBuilder::close() {
110 this->ensureMove();
111
112 fVerbs.push_back((uint8_t)SkPathVerb::kClose);
113
114 // fLastMovePoint stays where it is -- the previous moveTo
115 fNeedsMoveVerb = true;
116 return *this;
117}
118
119///////////////////////////////////////////////////////////////////////////////////////////
120
121SkPathBuilder& SkPathBuilder::rLineTo(SkPoint p1) {
122 this->ensureMove();
123 return this->lineTo(fPts.back() + p1);
124}
125
126SkPathBuilder& SkPathBuilder::rQuadTo(SkPoint p1, SkPoint p2) {
127 this->ensureMove();
128 SkPoint base = fPts.back();
129 return this->quadTo(base + p1, base + p2);
130}
131
132SkPathBuilder& SkPathBuilder::rConicTo(SkPoint p1, SkPoint p2, SkScalar w) {
133 this->ensureMove();
134 SkPoint base = fPts.back();
135 return this->conicTo(base + p1, base + p2, w);
136}
137
138SkPathBuilder& SkPathBuilder::rCubicTo(SkPoint p1, SkPoint p2, SkPoint p3) {
139 this->ensureMove();
140 SkPoint base = fPts.back();
141 return this->cubicTo(base + p1, base + p2, base + p3);
142}
143
144///////////////////////////////////////////////////////////////////////////////////////////
145
146SkPath SkPathBuilder::make(sk_sp<SkPathRef> pr) const {
147 switch (fIsA) {
148 case kIsA_Oval: pr->setIsOval( true, fIsACCW, fIsAStart); break;
149 case kIsA_RRect: pr->setIsRRect(true, fIsACCW, fIsAStart); break;
150 default: break;
151 }
152 return SkPath(std::move(pr), fFillType, fIsVolatile);
153}
154
155SkPath SkPathBuilder::snapshot() {
156 return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts,
157 fVerbs,
158 fConicWeights,
159 fSegmentMask)));
160}
161
162SkPath SkPathBuilder::detach() {
163 auto path = this->make(sk_sp<SkPathRef>(new SkPathRef(std::move(fPts),
164 std::move(fVerbs),
165 std::move(fConicWeights),
166 fSegmentMask)));
167 this->reset();
168 return path;
169}
170
171///////////////////////////////////////////////////////////////////////////////////////////////////
172
173static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
174 SkPoint* pt) {
175 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
176 // Chrome uses this path to move into and out of ovals. If not
177 // treated as a special case the moves can distort the oval's
178 // bounding box (and break the circle special case).
179 pt->set(oval.fRight, oval.centerY());
180 return true;
181 } else if (0 == oval.width() && 0 == oval.height()) {
182 // Chrome will sometimes create 0 radius round rects. Having degenerate
183 // quad segments in the path prevents the path from being recognized as
184 // a rect.
185 // TODO: optimizing the case where only one of width or height is zero
186 // should also be considered. This case, however, doesn't seem to be
187 // as common as the single point case.
188 pt->set(oval.fRight, oval.fTop);
189 return true;
190 }
191 return false;
192}
193
194// Return the unit vectors pointing at the start/stop points for the given start/sweep angles
195//
196static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
197 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
198 SkScalar startRad = SkDegreesToRadians(startAngle),
199 stopRad = SkDegreesToRadians(startAngle + sweepAngle);
200
201 startV->fY = SkScalarSinSnapToZero(startRad);
202 startV->fX = SkScalarCosSnapToZero(startRad);
203 stopV->fY = SkScalarSinSnapToZero(stopRad);
204 stopV->fX = SkScalarCosSnapToZero(stopRad);
205
206 /* If the sweep angle is nearly (but less than) 360, then due to precision
207 loss in radians-conversion and/or sin/cos, we may end up with coincident
208 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
209 of drawing a nearly complete circle (good).
210 e.g. canvas.drawArc(0, 359.99, ...)
211 -vs- canvas.drawArc(0, 359.9, ...)
212 We try to detect this edge case, and tweak the stop vector
213 */
214 if (*startV == *stopV) {
215 SkScalar sw = SkScalarAbs(sweepAngle);
216 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
217 // make a guess at a tiny angle (in radians) to tweak by
218 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
219 // not sure how much will be enough, so we use a loop
220 do {
221 stopRad -= deltaRad;
222 stopV->fY = SkScalarSinSnapToZero(stopRad);
223 stopV->fX = SkScalarCosSnapToZero(stopRad);
224 } while (*startV == *stopV);
225 }
226 }
227 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
228}
229
230/**
231 * If this returns 0, then the caller should just line-to the singlePt, else it should
232 * ignore singlePt and append the specified number of conics.
233 */
234static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
235 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
236 SkPoint* singlePt) {
237 SkMatrix matrix;
238
239 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
240 matrix.postTranslate(oval.centerX(), oval.centerY());
241
242 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
243 if (0 == count) {
244 matrix.mapXY(stop.x(), stop.y(), singlePt);
245 }
246 return count;
247}
248
249static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
250 return SkScalarNearlyEqual(a.fX, b.fX)
251 && SkScalarNearlyEqual(a.fY, b.fY);
252}
253
254SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
255 bool forceMoveTo) {
256 if (oval.width() < 0 || oval.height() < 0) {
257 return *this;
258 }
259
260 if (fVerbs.count() == 0) {
261 forceMoveTo = true;
262 }
263
264 SkPoint lonePt;
265 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
266 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
267 }
268
269 SkVector startV, stopV;
270 SkRotationDirection dir;
271 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
272
273 SkPoint singlePt;
274
275 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
276 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
277 // arcs from the same oval.
278 auto addPt = [forceMoveTo, this](const SkPoint& pt) {
279 if (forceMoveTo) {
280 this->moveTo(pt);
281 } else if (!nearly_equal(fPts.back(), pt)) {
282 this->lineTo(pt);
283 }
284 };
285
286 // At this point, we know that the arc is not a lone point, but startV == stopV
287 // indicates that the sweepAngle is too small such that angles_to_unit_vectors
288 // cannot handle it.
289 if (startV == stopV) {
290 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
291 SkScalar radiusX = oval.width() / 2;
292 SkScalar radiusY = oval.height() / 2;
293 // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
294 // is very small and radius is huge, the expected behavior here is to draw a line. But
295 // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
296 singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
297 oval.centerY() + radiusY * SkScalarSin(endAngle));
298 addPt(singlePt);
299 return *this;
300 }
301
302 SkConic conics[SkConic::kMaxConicsForArc];
303 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
304 if (count) {
305 this->incReserve(count * 2 + 1);
306 const SkPoint& pt = conics[0].fPts[0];
307 addPt(pt);
308 for (int i = 0; i < count; ++i) {
309 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
310 }
311 } else {
312 addPt(singlePt);
313 }
314 return *this;
315}
316
317SkPathBuilder& SkPathBuilder::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
318 if (oval.isEmpty() || 0 == sweepAngle) {
319 return *this;
320 }
321
322 const SkScalar kFullCircleAngle = SkIntToScalar(360);
323
324 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
325 // We can treat the arc as an oval if it begins at one of our legal starting positions.
326 // See SkPath::addOval() docs.
327 SkScalar startOver90 = startAngle / 90.f;
328 SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
329 SkScalar error = startOver90 - startOver90I;
330 if (SkScalarNearlyEqual(error, 0)) {
331 // Index 1 is at startAngle == 0.
332 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
333 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
334 return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
335 (unsigned) startIndex);
336 }
337 }
338 return this->arcTo(oval, startAngle, sweepAngle, true);
339}
340
341SkPathBuilder& SkPathBuilder::arcTo(SkPoint p1, SkPoint p2, SkScalar radius) {
342 this->ensureMove();
343
344 if (radius == 0) {
345 return this->lineTo(p1);
346 }
347
348 // need to know our prev pt so we can construct tangent vectors
349 SkPoint start = fPts.back();
350
351 // need double precision for these calcs.
352 SkDVector befored, afterd;
353 befored.set({p1.fX - start.fX, p1.fY - start.fY}).normalize();
354 afterd.set({p2.fX - p1.fX, p2.fY - p1.fY}).normalize();
355 double cosh = befored.dot(afterd);
356 double sinh = befored.cross(afterd);
357
358 if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
359 return this->lineTo(p1);
360 }
361
362 // safe to convert back to floats now
363 SkVector before = befored.asSkVector();
364 SkVector after = afterd.asSkVector();
365 SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
366 SkScalar xx = p1.fX - dist * before.fX;
367 SkScalar yy = p1.fY - dist * before.fY;
368 after.setLength(dist);
369 this->lineTo(xx, yy);
370 SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
371 return this->conicTo(p1, p1 + after, weight);
372}
373
374// This converts the SVG arc to conics.
375// Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
376// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
377// See also SVG implementation notes:
378// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
379// Note that arcSweep bool value is flipped from the original implementation.
380SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder::ArcSize arcLarge,
381 SkPathDirection arcSweep, SkPoint endPt) {
382 this->ensureMove();
383
384 SkPoint srcPts[2] = { fPts.back(), endPt };
385
386 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
387 // joining the endpoints.
388 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
389 if (!rad.fX || !rad.fY) {
390 return this->lineTo(endPt);
391 }
392 // If the current point and target point for the arc are identical, it should be treated as a
393 // zero length path. This ensures continuity in animations.
394 if (srcPts[0] == srcPts[1]) {
395 return this->lineTo(endPt);
396 }
397 SkScalar rx = SkScalarAbs(rad.fX);
398 SkScalar ry = SkScalarAbs(rad.fY);
399 SkVector midPointDistance = srcPts[0] - srcPts[1];
400 midPointDistance *= 0.5f;
401
402 SkMatrix pointTransform;
403 pointTransform.setRotate(-angle);
404
405 SkPoint transformedMidPoint;
406 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
407 SkScalar squareRx = rx * rx;
408 SkScalar squareRy = ry * ry;
409 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
410 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
411
412 // Check if the radii are big enough to draw the arc, scale radii if not.
413 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
414 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
415 if (radiiScale > 1) {
416 radiiScale = SkScalarSqrt(radiiScale);
417 rx *= radiiScale;
418 ry *= radiiScale;
419 }
420
421 pointTransform.setScale(1 / rx, 1 / ry);
422 pointTransform.preRotate(-angle);
423
424 SkPoint unitPts[2];
425 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
426 SkVector delta = unitPts[1] - unitPts[0];
427
428 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
429 SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
430
431 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
432 if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) { // flipped from the original implementation
433 scaleFactor = -scaleFactor;
434 }
435 delta.scale(scaleFactor);
436 SkPoint centerPoint = unitPts[0] + unitPts[1];
437 centerPoint *= 0.5f;
438 centerPoint.offset(-delta.fY, delta.fX);
439 unitPts[0] -= centerPoint;
440 unitPts[1] -= centerPoint;
441 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
442 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
443 SkScalar thetaArc = theta2 - theta1;
444 if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) { // arcSweep flipped from the original implementation
445 thetaArc += SK_ScalarPI * 2;
446 } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) { // arcSweep flipped from the original implementation
447 thetaArc -= SK_ScalarPI * 2;
448 }
449
450 // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
451 // so we do a quick check here. The precise tolerance amount is just made up.
452 // PI/million happens to fix the bug in 9272, but a larger value is probably
453 // ok too.
454 if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
455 return this->lineTo(endPt);
456 }
457
458 pointTransform.setRotate(angle);
459 pointTransform.preScale(rx, ry);
460
461 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
462 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
463 SkScalar thetaWidth = thetaArc / segments;
464 SkScalar t = SkScalarTan(0.5f * thetaWidth);
465 if (!SkScalarIsFinite(t)) {
466 return *this;
467 }
468 SkScalar startTheta = theta1;
469 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
470 auto scalar_is_integer = [](SkScalar scalar) -> bool {
471 return scalar == SkScalarFloorToScalar(scalar);
472 };
473 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
474 scalar_is_integer(rx) && scalar_is_integer(ry) &&
475 scalar_is_integer(endPt.fX) && scalar_is_integer(endPt.fY);
476
477 for (int i = 0; i < segments; ++i) {
478 SkScalar endTheta = startTheta + thetaWidth,
479 sinEndTheta = SkScalarSinSnapToZero(endTheta),
480 cosEndTheta = SkScalarCosSnapToZero(endTheta);
481
482 unitPts[1].set(cosEndTheta, sinEndTheta);
483 unitPts[1] += centerPoint;
484 unitPts[0] = unitPts[1];
485 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
486 SkPoint mapped[2];
487 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
488 /*
489 Computing the arc width introduces rounding errors that cause arcs to start
490 outside their marks. A round rect may lose convexity as a result. If the input
491 values are on integers, place the conic on integers as well.
492 */
493 if (expectIntegers) {
494 for (SkPoint& point : mapped) {
495 point.fX = SkScalarRoundToScalar(point.fX);
496 point.fY = SkScalarRoundToScalar(point.fY);
497 }
498 }
499 this->conicTo(mapped[0], mapped[1], w);
500 startTheta = endTheta;
501 }
502
503#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT
504 // The final point should match the input point (by definition); replace it to
505 // ensure that rounding errors in the above math don't cause any problems.
506 fPts.back() = endPt;
507#endif
508 return *this;
509}
510
511///////////////////////////////////////////////////////////////////////////////////////////
512
513namespace {
514 template <unsigned N> class PointIterator {
515 public:
516 PointIterator(SkPathDirection dir, unsigned startIndex)
517 : fCurrent(startIndex % N)
518 , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1)
519 {}
520
521 const SkPoint& current() const {
522 SkASSERT(fCurrent < N);
523 return fPts[fCurrent];
524 }
525
526 const SkPoint& next() {
527 fCurrent = (fCurrent + fAdvance) % N;
528 return this->current();
529 }
530
531 protected:
532 SkPoint fPts[N];
533
534 private:
535 unsigned fCurrent;
536 unsigned fAdvance;
537 };
538
539 class RectPointIterator : public PointIterator<4> {
540 public:
541 RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex)
542 : PointIterator(dir, startIndex) {
543
544 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
545 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
546 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
547 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
548 }
549 };
550
551 class OvalPointIterator : public PointIterator<4> {
552 public:
553 OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex)
554 : PointIterator(dir, startIndex) {
555
556 const SkScalar cx = oval.centerX();
557 const SkScalar cy = oval.centerY();
558
559 fPts[0] = SkPoint::Make(cx, oval.fTop);
560 fPts[1] = SkPoint::Make(oval.fRight, cy);
561 fPts[2] = SkPoint::Make(cx, oval.fBottom);
562 fPts[3] = SkPoint::Make(oval.fLeft, cy);
563 }
564 };
565
566 class RRectPointIterator : public PointIterator<8> {
567 public:
568 RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex)
569 : PointIterator(dir, startIndex)
570 {
571 const SkRect& bounds = rrect.getBounds();
572 const SkScalar L = bounds.fLeft;
573 const SkScalar T = bounds.fTop;
574 const SkScalar R = bounds.fRight;
575 const SkScalar B = bounds.fBottom;
576
577 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
578 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
579 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
580 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
581 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
582 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
583 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
584 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
585 }
586 };
587} // anonymous namespace
588
589
590SkPathBuilder& SkPathBuilder::addRect(const SkRect& rect, SkPathDirection dir, unsigned index) {
591 const int kPts = 4; // moveTo + 3 lines
592 const int kVerbs = 5; // moveTo + 3 lines + close
593 this->incReserve(kPts, kVerbs);
594
595 RectPointIterator iter(rect, dir, index);
596
597 this->moveTo(iter.current());
598 this->lineTo(iter.next());
599 this->lineTo(iter.next());
600 this->lineTo(iter.next());
601 return this->close();
602}
603
604SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) {
605 const IsA prevIsA = fIsA;
606
607 const int kPts = 9; // moveTo + 4 conics(2 pts each)
608 const int kVerbs = 6; // moveTo + 4 conics + close
609 this->incReserve(kPts, kVerbs);
610
611 OvalPointIterator ovalIter(oval, dir, index);
612 RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1));
613
614 // The corner iterator pts are tracking "behind" the oval/radii pts.
615
616 this->moveTo(ovalIter.current());
617 for (unsigned i = 0; i < 4; ++i) {
618 this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2);
619 }
620 this->close();
621
622 if (prevIsA == kIsA_JustMoves) {
623 fIsA = kIsA_Oval;
624 fIsACCW = (dir == SkPathDirection::kCCW);
625 fIsAStart = index % 4;
626 }
627 return *this;
628}
629
630SkPathBuilder& SkPathBuilder::addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned index) {
631 const IsA prevIsA = fIsA;
632 const SkRect& bounds = rrect.getBounds();
633
634 if (rrect.isRect() || rrect.isEmpty()) {
635 // degenerate(rect) => radii points are collapsing
636 this->addRect(bounds, dir, (index + 1) / 2);
637 } else if (rrect.isOval()) {
638 // degenerate(oval) => line points are collapsing
639 this->addOval(bounds, dir, index / 2);
640 } else {
641 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
642 const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW));
643 const SkScalar weight = SK_ScalarRoot2Over2;
644
645 const int kVerbs = startsWithConic
646 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
647 : 10; // moveTo + 4x lineTo + 4x conicTo + close
648 this->incReserve(kVerbs);
649
650 RRectPointIterator rrectIter(rrect, dir, index);
651 // Corner iterator indices follow the collapsed radii model,
652 // adjusted such that the start pt is "behind" the radii start pt.
653 const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1);
654 RectPointIterator rectIter(bounds, dir, rectStartIndex);
655
656 this->moveTo(rrectIter.current());
657 if (startsWithConic) {
658 for (unsigned i = 0; i < 3; ++i) {
659 this->conicTo(rectIter.next(), rrectIter.next(), weight);
660 this->lineTo(rrectIter.next());
661 }
662 this->conicTo(rectIter.next(), rrectIter.next(), weight);
663 // final lineTo handled by close().
664 } else {
665 for (unsigned i = 0; i < 4; ++i) {
666 this->lineTo(rrectIter.next());
667 this->conicTo(rectIter.next(), rrectIter.next(), weight);
668 }
669 }
670 this->close();
671 }
672
673 if (prevIsA == kIsA_JustMoves) {
674 fIsA = kIsA_RRect;
675 fIsACCW = (dir == SkPathDirection::kCCW);
676 fIsAStart = index % 8;
677 }
678 return *this;
679}
680
681SkPathBuilder& SkPathBuilder::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
682 if (r >= 0) {
683 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
684 }
685 return *this;
686}
687
688SkPathBuilder& SkPathBuilder::addPolygon(const SkPoint pts[], int count, bool isClosed) {
689 if (count <= 0) {
690 return *this;
691 }
692
693 this->incReserve(count, count + isClosed);
694
695 this->moveTo(pts[0]);
696 if (count > 1) {
697 count -= 1;
698 memcpy(fPts.append(count), &pts[1], count * sizeof(SkPoint));
699 memset(fVerbs.append(count), (uint8_t)SkPathVerb::kLine, count);
700 fSegmentMask |= kLine_SkPathSegmentMask;
701 }
702 if (isClosed) {
703 this->close();
704 }
705 return *this;
706}
707