Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkPathBuilder.cpp
Go to the documentation of this file.
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
9
15#include "src/base/SkVx.h"
16#include "src/core/SkGeometry.h"
18#include "src/core/SkPathPriv.h"
19
20#include <algorithm>
21#include <cmath>
22#include <cstdint>
23#include <cstring>
24#include <iterator>
25#include <utility>
26
30
32 this->reset();
33 fFillType = ft;
34}
35
37 *this = src;
38}
39
42
44 fPts.clear();
45 fVerbs.clear();
46 fConicWeights.clear();
47 fFillType = SkPathFillType::kWinding;
48 fIsVolatile = false;
49
50 // these are internal state
51
52 fSegmentMask = 0;
53 fLastMovePoint = {0, 0};
54 fLastMoveIndex = -1; // illegal
55 fNeedsMoveVerb = true;
56
57 return *this;
58}
59
61 this->reset().setFillType(src.getFillType());
62
63 for (auto [verb, pts, w] : SkPathPriv::Iterate(src)) {
64 switch (verb) {
65 case SkPathVerb::kMove: this->moveTo(pts[0]); break;
66 case SkPathVerb::kLine: this->lineTo(pts[1]); break;
67 case SkPathVerb::kQuad: this->quadTo(pts[1], pts[2]); break;
68 case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], w[0]); break;
69 case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break;
70 case SkPathVerb::kClose: this->close(); break;
71 }
72 }
73 return *this;
74}
75
76void SkPathBuilder::incReserve(int extraPtCount, int extraVbCount) {
77 fPts.reserve_exact(Sk32_sat_add(fPts.size(), extraPtCount));
78 fVerbs.reserve_exact(Sk32_sat_add(fVerbs.size(), extraVbCount));
79}
80
82 SkRect bounds;
83 bounds.setBounds(fPts.begin(), fPts.size());
84 return bounds;
85}
86
87/*
88 * Some old behavior in SkPath -- should we keep it?
89 *
90 * After each edit (i.e. adding a verb)
91 this->setConvexityType(SkPathConvexity::kUnknown);
92 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
93 */
94
96 // only needed while SkPath is mutable
97 fLastMoveIndex = SkToInt(fPts.size());
98
99 fPts.push_back(pt);
100 fVerbs.push_back((uint8_t)SkPathVerb::kMove);
101
102 fLastMovePoint = pt;
103 fNeedsMoveVerb = false;
104 return *this;
105}
106
108 this->ensureMove();
109
110 fPts.push_back(pt);
111 fVerbs.push_back((uint8_t)SkPathVerb::kLine);
112
113 fSegmentMask |= kLine_SkPathSegmentMask;
114 return *this;
115}
116
118 this->ensureMove();
119
120 SkPoint* p = fPts.push_back_n(2);
121 p[0] = pt1;
122 p[1] = pt2;
123 fVerbs.push_back((uint8_t)SkPathVerb::kQuad);
124
125 fSegmentMask |= kQuad_SkPathSegmentMask;
126 return *this;
127}
128
130 this->ensureMove();
131
132 SkPoint* p = fPts.push_back_n(2);
133 p[0] = pt1;
134 p[1] = pt2;
135 fVerbs.push_back((uint8_t)SkPathVerb::kConic);
136 fConicWeights.push_back(w);
137
138 fSegmentMask |= kConic_SkPathSegmentMask;
139 return *this;
140}
141
143 this->ensureMove();
144
145 SkPoint* p = fPts.push_back_n(3);
146 p[0] = pt1;
147 p[1] = pt2;
148 p[2] = pt3;
149 fVerbs.push_back((uint8_t)SkPathVerb::kCubic);
150
151 fSegmentMask |= kCubic_SkPathSegmentMask;
152 return *this;
153}
154
156 if (!fVerbs.empty()) {
157 this->ensureMove();
158
159 fVerbs.push_back((uint8_t)SkPathVerb::kClose);
160
161 // fLastMovePoint stays where it is -- the previous moveTo
162 fNeedsMoveVerb = true;
163 }
164 return *this;
165}
166
167///////////////////////////////////////////////////////////////////////////////////////////
168
170 this->ensureMove();
171 return this->lineTo(fPts.back() + p1);
172}
173
175 this->ensureMove();
176 SkPoint base = fPts.back();
177 return this->quadTo(base + p1, base + p2);
178}
179
181 this->ensureMove();
182 SkPoint base = fPts.back();
183 return this->conicTo(base + p1, base + p2, w);
184}
185
187 this->ensureMove();
188 SkPoint base = fPts.back();
189 return this->cubicTo(base + p1, base + p2, base + p3);
190}
191
192///////////////////////////////////////////////////////////////////////////////////////////
193
194SkPath SkPathBuilder::make(sk_sp<SkPathRef> pr) const {
195 auto convexity = SkPathConvexity::kUnknown;
197
198 switch (fIsA) {
199 case kIsA_Oval:
200 pr->setIsOval(fIsACCW, fIsAStart);
201 convexity = SkPathConvexity::kConvex;
203 break;
204 case kIsA_RRect:
205 pr->setIsRRect(fIsACCW, fIsAStart);
206 convexity = SkPathConvexity::kConvex;
208 break;
209 default: break;
210 }
211
212 // Wonder if we can combine convexity and dir internally...
213 // unknown, convex_cw, convex_ccw, concave
214 // Do we ever have direction w/o convexity, or viceversa (inside path)?
215 //
216 auto path = SkPath(std::move(pr), fFillType, fIsVolatile, convexity, dir);
217
218 // This hopefully can go away in the future when Paths are immutable,
219 // but if while they are still editable, we need to correctly set this.
220 const uint8_t* start = path.fPathRef->verbsBegin();
221 const uint8_t* stop = path.fPathRef->verbsEnd();
222 if (start < stop) {
223 SkASSERT(fLastMoveIndex >= 0);
224 // peek at the last verb, to know if our last contour is closed
225 const bool isClosed = (stop[-1] == (uint8_t)SkPathVerb::kClose);
226 path.fLastMoveToIndex = isClosed ? ~fLastMoveIndex : fLastMoveIndex;
227 }
228
229 return path;
230}
231
233 return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts,
234 fVerbs,
235 fConicWeights,
236 fSegmentMask)));
237}
238
240 auto path = this->make(sk_sp<SkPathRef>(new SkPathRef(std::move(fPts),
241 std::move(fVerbs),
242 std::move(fConicWeights),
243 fSegmentMask)));
244 this->reset();
245 return path;
246}
247
248///////////////////////////////////////////////////////////////////////////////////////////////////
249
250static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
251 SkPoint* pt) {
252 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
253 // Chrome uses this path to move into and out of ovals. If not
254 // treated as a special case the moves can distort the oval's
255 // bounding box (and break the circle special case).
256 pt->set(oval.fRight, oval.centerY());
257 return true;
258 } else if (0 == oval.width() && 0 == oval.height()) {
259 // Chrome will sometimes create 0 radius round rects. Having degenerate
260 // quad segments in the path prevents the path from being recognized as
261 // a rect.
262 // TODO: optimizing the case where only one of width or height is zero
263 // should also be considered. This case, however, doesn't seem to be
264 // as common as the single point case.
265 pt->set(oval.fRight, oval.fTop);
266 return true;
267 }
268 return false;
269}
270
271// Return the unit vectors pointing at the start/stop points for the given start/sweep angles
272//
273static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
274 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
275 SkScalar startRad = SkDegreesToRadians(startAngle),
276 stopRad = SkDegreesToRadians(startAngle + sweepAngle);
277
278 startV->fY = SkScalarSinSnapToZero(startRad);
279 startV->fX = SkScalarCosSnapToZero(startRad);
280 stopV->fY = SkScalarSinSnapToZero(stopRad);
281 stopV->fX = SkScalarCosSnapToZero(stopRad);
282
283 /* If the sweep angle is nearly (but less than) 360, then due to precision
284 loss in radians-conversion and/or sin/cos, we may end up with coincident
285 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
286 of drawing a nearly complete circle (good).
287 e.g. canvas.drawArc(0, 359.99, ...)
288 -vs- canvas.drawArc(0, 359.9, ...)
289 We try to detect this edge case, and tweak the stop vector
290 */
291 if (*startV == *stopV) {
292 SkScalar sw = SkScalarAbs(sweepAngle);
293 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
294 // make a guess at a tiny angle (in radians) to tweak by
295 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
296 // not sure how much will be enough, so we use a loop
297 do {
298 stopRad -= deltaRad;
299 stopV->fY = SkScalarSinSnapToZero(stopRad);
300 stopV->fX = SkScalarCosSnapToZero(stopRad);
301 } while (*startV == *stopV);
302 }
303 }
304 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
305}
306
307/**
308 * If this returns 0, then the caller should just line-to the singlePt, else it should
309 * ignore singlePt and append the specified number of conics.
310 */
311static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
313 SkPoint* singlePt) {
314 SkMatrix matrix;
315
316 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
317 matrix.postTranslate(oval.centerX(), oval.centerY());
318
319 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
320 if (0 == count) {
321 matrix.mapXY(stop.x(), stop.y(), singlePt);
322 }
323 return count;
324}
325
326static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
327 return SkScalarNearlyEqual(a.fX, b.fX)
328 && SkScalarNearlyEqual(a.fY, b.fY);
329}
330
331SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
332 bool forceMoveTo) {
333 if (oval.width() < 0 || oval.height() < 0) {
334 return *this;
335 }
336
337 if (fVerbs.empty()) {
338 forceMoveTo = true;
339 }
340
341 SkPoint lonePt;
342 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
343 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
344 }
345
346 SkVector startV, stopV;
348 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
349
350 SkPoint singlePt;
351
352 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
353 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
354 // arcs from the same oval.
355 auto addPt = [forceMoveTo, this](const SkPoint& pt) {
356 if (forceMoveTo) {
357 this->moveTo(pt);
358 } else if (!nearly_equal(fPts.back(), pt)) {
359 this->lineTo(pt);
360 }
361 };
362
363 // At this point, we know that the arc is not a lone point, but startV == stopV
364 // indicates that the sweepAngle is too small such that angles_to_unit_vectors
365 // cannot handle it.
366 if (startV == stopV) {
367 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
368 SkScalar radiusX = oval.width() / 2;
369 SkScalar radiusY = oval.height() / 2;
370 // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
371 // is very small and radius is huge, the expected behavior here is to draw a line. But
372 // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
373 singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
374 oval.centerY() + radiusY * SkScalarSin(endAngle));
375 addPt(singlePt);
376 return *this;
377 }
378
380 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
381 if (count) {
382 this->incReserve(count * 2 + 1);
383 const SkPoint& pt = conics[0].fPts[0];
384 addPt(pt);
385 for (int i = 0; i < count; ++i) {
386 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
387 }
388 } else {
389 addPt(singlePt);
390 }
391 return *this;
392}
393
394SkPathBuilder& SkPathBuilder::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
395 if (oval.isEmpty() || 0 == sweepAngle) {
396 return *this;
397 }
398
399 const SkScalar kFullCircleAngle = SkIntToScalar(360);
400
401 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
402 // We can treat the arc as an oval if it begins at one of our legal starting positions.
403 // See SkPath::addOval() docs.
404 SkScalar startOver90 = startAngle / 90.f;
405 SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
406 SkScalar error = startOver90 - startOver90I;
407 if (SkScalarNearlyEqual(error, 0)) {
408 // Index 1 is at startAngle == 0.
409 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
410 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
411 return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
412 (unsigned) startIndex);
413 }
414 }
415 return this->arcTo(oval, startAngle, sweepAngle, true);
416}
417
419 this->ensureMove();
420
421 if (radius == 0) {
422 return this->lineTo(p1);
423 }
424
425 // need to know our prev pt so we can construct tangent vectors
426 SkPoint start = fPts.back();
427
428 // need double precision for these calcs.
429 skvx::double2 befored = normalize(skvx::double2{p1.fX - start.fX, p1.fY - start.fY});
430 skvx::double2 afterd = normalize(skvx::double2{p2.fX - p1.fX, p2.fY - p1.fY});
431 double cosh = dot(befored, afterd);
432 double sinh = cross(befored, afterd);
433
434 // If the previous point equals the first point, befored will be denormalized.
435 // If the two points equal, afterd will be denormalized.
436 // If the second point equals the first point, sinh will be zero.
437 // In all these cases, we cannot construct an arc, so we construct a line to the first point.
438 if (!isfinite(befored) || !isfinite(afterd) || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
439 return this->lineTo(p1);
440 }
441
442 // safe to convert back to floats now
443 SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
444 SkScalar xx = p1.fX - dist * befored[0];
445 SkScalar yy = p1.fY - dist * befored[1];
446
447 SkVector after = SkVector::Make(afterd[0], afterd[1]);
448 after.setLength(dist);
449 this->lineTo(xx, yy);
450 SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
451 return this->conicTo(p1, p1 + after, weight);
452}
453
454// This converts the SVG arc to conics.
455// Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
456// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
457// See also SVG implementation notes:
458// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
459// Note that arcSweep bool value is flipped from the original implementation.
461 SkPathDirection arcSweep, SkPoint endPt) {
462 this->ensureMove();
463
464 SkPoint srcPts[2] = { fPts.back(), endPt };
465
466 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
467 // joining the endpoints.
468 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
469 if (!rad.fX || !rad.fY) {
470 return this->lineTo(endPt);
471 }
472 // If the current point and target point for the arc are identical, it should be treated as a
473 // zero length path. This ensures continuity in animations.
474 if (srcPts[0] == srcPts[1]) {
475 return this->lineTo(endPt);
476 }
477 SkScalar rx = SkScalarAbs(rad.fX);
478 SkScalar ry = SkScalarAbs(rad.fY);
479 SkVector midPointDistance = srcPts[0] - srcPts[1];
480 midPointDistance *= 0.5f;
481
482 SkMatrix pointTransform;
483 pointTransform.setRotate(-angle);
484
485 SkPoint transformedMidPoint;
486 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
487 SkScalar squareRx = rx * rx;
488 SkScalar squareRy = ry * ry;
489 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
490 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
491
492 // Check if the radii are big enough to draw the arc, scale radii if not.
493 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
494 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
495 if (radiiScale > 1) {
496 radiiScale = SkScalarSqrt(radiiScale);
497 rx *= radiiScale;
498 ry *= radiiScale;
499 }
500
501 pointTransform.setScale(1 / rx, 1 / ry);
502 pointTransform.preRotate(-angle);
503
504 SkPoint unitPts[2];
505 pointTransform.mapPoints(unitPts, srcPts, (int) std::size(unitPts));
506 SkVector delta = unitPts[1] - unitPts[0];
507
508 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
509 SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
510
511 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
512 if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) { // flipped from the original implementation
513 scaleFactor = -scaleFactor;
514 }
515 delta.scale(scaleFactor);
516 SkPoint centerPoint = unitPts[0] + unitPts[1];
517 centerPoint *= 0.5f;
518 centerPoint.offset(-delta.fY, delta.fX);
519 unitPts[0] -= centerPoint;
520 unitPts[1] -= centerPoint;
521 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
522 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
523 SkScalar thetaArc = theta2 - theta1;
524 if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) { // arcSweep flipped from the original implementation
525 thetaArc += SK_ScalarPI * 2;
526 } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) { // arcSweep flipped from the original implementation
527 thetaArc -= SK_ScalarPI * 2;
528 }
529
530 // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
531 // so we do a quick check here. The precise tolerance amount is just made up.
532 // PI/million happens to fix the bug in 9272, but a larger value is probably
533 // ok too.
534 if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
535 return this->lineTo(endPt);
536 }
537
538 pointTransform.setRotate(angle);
539 pointTransform.preScale(rx, ry);
540
541 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
542 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
543 SkScalar thetaWidth = thetaArc / segments;
544 SkScalar t = SkScalarTan(0.5f * thetaWidth);
545 if (!SkIsFinite(t)) {
546 return *this;
547 }
548 SkScalar startTheta = theta1;
550 auto scalar_is_integer = [](SkScalar scalar) -> bool {
551 return scalar == SkScalarFloorToScalar(scalar);
552 };
553 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
554 scalar_is_integer(rx) && scalar_is_integer(ry) &&
555 scalar_is_integer(endPt.fX) && scalar_is_integer(endPt.fY);
556
557 for (int i = 0; i < segments; ++i) {
558 SkScalar endTheta = startTheta + thetaWidth,
559 sinEndTheta = SkScalarSinSnapToZero(endTheta),
560 cosEndTheta = SkScalarCosSnapToZero(endTheta);
561
562 unitPts[1].set(cosEndTheta, sinEndTheta);
563 unitPts[1] += centerPoint;
564 unitPts[0] = unitPts[1];
565 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
566 SkPoint mapped[2];
567 pointTransform.mapPoints(mapped, unitPts, (int) std::size(unitPts));
568 /*
569 Computing the arc width introduces rounding errors that cause arcs to start
570 outside their marks. A round rect may lose convexity as a result. If the input
571 values are on integers, place the conic on integers as well.
572 */
573 if (expectIntegers) {
574 for (SkPoint& point : mapped) {
575 point.fX = SkScalarRoundToScalar(point.fX);
576 point.fY = SkScalarRoundToScalar(point.fY);
577 }
578 }
579 this->conicTo(mapped[0], mapped[1], w);
580 startTheta = endTheta;
581 }
582
583 // The final point should match the input point (by definition); replace it to
584 // ensure that rounding errors in the above math don't cause any problems.
585 fPts.back() = endPt;
586 return *this;
587}
588
589///////////////////////////////////////////////////////////////////////////////////////////
590
591namespace {
592 template <unsigned N> class PointIterator {
593 public:
594 PointIterator(SkPathDirection dir, unsigned startIndex)
595 : fCurrent(startIndex % N)
596 , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1)
597 {}
598
599 const SkPoint& current() const {
600 SkASSERT(fCurrent < N);
601 return fPts[fCurrent];
602 }
603
604 const SkPoint& next() {
605 fCurrent = (fCurrent + fAdvance) % N;
606 return this->current();
607 }
608
609 protected:
610 SkPoint fPts[N];
611
612 private:
613 unsigned fCurrent;
614 unsigned fAdvance;
615 };
616
617 class RectPointIterator : public PointIterator<4> {
618 public:
619 RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex)
620 : PointIterator(dir, startIndex) {
621
622 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
623 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
624 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
625 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
626 }
627 };
628
629 class OvalPointIterator : public PointIterator<4> {
630 public:
631 OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex)
632 : PointIterator(dir, startIndex) {
633
634 const SkScalar cx = oval.centerX();
635 const SkScalar cy = oval.centerY();
636
637 fPts[0] = SkPoint::Make(cx, oval.fTop);
638 fPts[1] = SkPoint::Make(oval.fRight, cy);
639 fPts[2] = SkPoint::Make(cx, oval.fBottom);
640 fPts[3] = SkPoint::Make(oval.fLeft, cy);
641 }
642 };
643
644 class RRectPointIterator : public PointIterator<8> {
645 public:
646 RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex)
647 : PointIterator(dir, startIndex)
648 {
649 const SkRect& bounds = rrect.getBounds();
650 const SkScalar L = bounds.fLeft;
651 const SkScalar T = bounds.fTop;
652 const SkScalar R = bounds.fRight;
653 const SkScalar B = bounds.fBottom;
654
663 }
664 };
665} // anonymous namespace
666
667
668SkPathBuilder& SkPathBuilder::addRect(const SkRect& rect, SkPathDirection dir, unsigned index) {
669 const int kPts = 4; // moveTo + 3 lines
670 const int kVerbs = 5; // moveTo + 3 lines + close
671 this->incReserve(kPts, kVerbs);
672
673 RectPointIterator iter(rect, dir, index);
674
675 this->moveTo(iter.current());
676 this->lineTo(iter.next());
677 this->lineTo(iter.next());
678 this->lineTo(iter.next());
679 return this->close();
680}
681
682SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) {
683 const IsA prevIsA = fIsA;
684
685 const int kPts = 9; // moveTo + 4 conics(2 pts each)
686 const int kVerbs = 6; // moveTo + 4 conics + close
687 this->incReserve(kPts, kVerbs);
688
689 OvalPointIterator ovalIter(oval, dir, index);
690 RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1));
691
692 // The corner iterator pts are tracking "behind" the oval/radii pts.
693
694 this->moveTo(ovalIter.current());
695 for (unsigned i = 0; i < 4; ++i) {
696 this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2);
697 }
698 this->close();
699
700 if (prevIsA == kIsA_JustMoves) {
701 fIsA = kIsA_Oval;
702 fIsACCW = (dir == SkPathDirection::kCCW);
703 fIsAStart = index % 4;
704 }
705 return *this;
706}
707
709 const IsA prevIsA = fIsA;
710 const SkRect& bounds = rrect.getBounds();
711
712 if (rrect.isRect() || rrect.isEmpty()) {
713 // degenerate(rect) => radii points are collapsing
714 this->addRect(bounds, dir, (index + 1) / 2);
715 } else if (rrect.isOval()) {
716 // degenerate(oval) => line points are collapsing
717 this->addOval(bounds, dir, index / 2);
718 } else {
719 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
720 const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW));
721 const SkScalar weight = SK_ScalarRoot2Over2;
722
723 const int kVerbs = startsWithConic
724 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
725 : 10; // moveTo + 4x lineTo + 4x conicTo + close
726 this->incReserve(kVerbs);
727
728 RRectPointIterator rrectIter(rrect, dir, index);
729 // Corner iterator indices follow the collapsed radii model,
730 // adjusted such that the start pt is "behind" the radii start pt.
731 const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1);
732 RectPointIterator rectIter(bounds, dir, rectStartIndex);
733
734 this->moveTo(rrectIter.current());
735 if (startsWithConic) {
736 for (unsigned i = 0; i < 3; ++i) {
737 this->conicTo(rectIter.next(), rrectIter.next(), weight);
738 this->lineTo(rrectIter.next());
739 }
740 this->conicTo(rectIter.next(), rrectIter.next(), weight);
741 // final lineTo handled by close().
742 } else {
743 for (unsigned i = 0; i < 4; ++i) {
744 this->lineTo(rrectIter.next());
745 this->conicTo(rectIter.next(), rrectIter.next(), weight);
746 }
747 }
748 this->close();
749 }
750
751 if (prevIsA == kIsA_JustMoves) {
752 fIsA = kIsA_RRect;
753 fIsACCW = (dir == SkPathDirection::kCCW);
754 fIsAStart = index % 8;
755 }
756 return *this;
757}
758
760 if (r >= 0) {
761 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
762 }
763 return *this;
764}
765
766SkPathBuilder& SkPathBuilder::addPolygon(const SkPoint pts[], int count, bool isClosed) {
767 if (count <= 0) {
768 return *this;
769 }
770
771 this->moveTo(pts[0]);
772 this->polylineTo(&pts[1], count - 1);
773 if (isClosed) {
774 this->close();
775 }
776 return *this;
777}
778
780 if (count > 0) {
781 this->ensureMove();
782
783 this->incReserve(count, count);
784 memcpy(fPts.push_back_n(count), pts, count * sizeof(SkPoint));
785 memset(fVerbs.push_back_n(count), (uint8_t)SkPathVerb::kLine, count);
786 fSegmentMask |= kLine_SkPathSegmentMask;
787 }
788 return *this;
789}
790
791//////////////////////////////////////////////////////////////////////////////////////////////////
792
794 for (auto& p : fPts) {
795 p += {dx, dy};
796 }
797 return *this;
798}
799
801 SkPath::RawIter iter(src);
802 SkPoint pts[4];
803 SkPath::Verb verb;
804
805 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
806 switch (verb) {
807 case SkPath::kMove_Verb: this->moveTo (pts[0]); break;
808 case SkPath::kLine_Verb: this->lineTo (pts[1]); break;
809 case SkPath::kQuad_Verb: this->quadTo (pts[1], pts[2]); break;
810 case SkPath::kCubic_Verb: this->cubicTo(pts[1], pts[2], pts[3]); break;
811 case SkPath::kConic_Verb: this->conicTo(pts[1], pts[2], iter.conicWeight()); break;
812 case SkPath::kClose_Verb: this->close(); break;
814 }
815 }
816
817 return *this;
818}
819
820SkPathBuilder& SkPathBuilder::privateReverseAddPath(const SkPath& src) {
821
822 const uint8_t* verbsBegin = src.fPathRef->verbsBegin();
823 const uint8_t* verbs = src.fPathRef->verbsEnd();
824 const SkPoint* pts = src.fPathRef->pointsEnd();
825 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
826
827 bool needMove = true;
828 bool needClose = false;
829 while (verbs > verbsBegin) {
830 uint8_t v = *--verbs;
831 int n = SkPathPriv::PtsInVerb(v);
832
833 if (needMove) {
834 --pts;
835 this->moveTo(pts->fX, pts->fY);
836 needMove = false;
837 }
838 pts -= n;
839 switch ((SkPathVerb)v) {
841 if (needClose) {
842 this->close();
843 needClose = false;
844 }
845 needMove = true;
846 pts += 1; // so we see the point in "if (needMove)" above
847 break;
849 this->lineTo(pts[0]);
850 break;
852 this->quadTo(pts[1], pts[0]);
853 break;
855 this->conicTo(pts[1], pts[0], *--conicWeights);
856 break;
858 this->cubicTo(pts[2], pts[1], pts[0]);
859 break;
861 needClose = true;
862 break;
863 default:
864 SkDEBUGFAIL("unexpected verb");
865 }
866 }
867 return *this;
868}
SkPoint fPts[2]
int count
static float next(float f)
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
static bool SkIsFinite(T x, Pack... values)
static void normalize(int n, double *gauss)
SkRotationDirection
Definition SkGeometry.h:321
@ kCW_SkRotationDirection
Definition SkGeometry.h:322
@ kCCW_SkRotationDirection
Definition SkGeometry.h:323
static bool nearly_equal(const SkPoint &a, const SkPoint &b)
static int build_arc_conics(const SkRect &oval, const SkVector &start, const SkVector &stop, SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], SkPoint *singlePt)
static bool arc_is_lone_point(const SkRect &oval, SkScalar startAngle, SkScalar sweepAngle, SkPoint *pt)
static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, SkVector *startV, SkVector *stopV, SkRotationDirection *dir)
SkPathFirstDirection
Definition SkPathEnums.h:19
@ kCubic_SkPathSegmentMask
Definition SkPathTypes.h:45
@ kConic_SkPathSegmentMask
Definition SkPathTypes.h:44
@ kQuad_SkPathSegmentMask
Definition SkPathTypes.h:43
@ kLine_SkPathSegmentMask
Definition SkPathTypes.h:42
SkPathDirection
Definition SkPathTypes.h:34
SkPathFillType
Definition SkPathTypes.h:11
SkPathVerb
Definition SkPathTypes.h:48
@ kClose
SkPath::RawIter returns 0 points.
@ kCubic
SkPath::RawIter returns 4 points.
@ kConic
SkPath::RawIter returns 3 points + 1 weight.
@ kQuad
SkPath::RawIter returns 3 points.
@ kMove
SkPath::RawIter returns 1 point.
@ kLine
SkPath::RawIter returns 2 points.
static int build_arc_conics(const SkRect &oval, const SkVector &start, const SkVector &stop, SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], SkPoint *singlePt)
Definition SkPath.cpp:968
static bool arc_is_lone_point(const SkRect &oval, SkScalar startAngle, SkScalar sweepAngle, SkPoint *pt)
Definition SkPath.cpp:907
static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, SkVector *startV, SkVector *stopV, SkRotationDirection *dir)
Definition SkPath.cpp:930
static constexpr int32_t Sk32_sat_add(int32_t a, int32_t b)
Definition SkSafe32.h:20
#define SkDegreesToRadians(degrees)
Definition SkScalar.h:77
#define SkScalarCopySign(x, y)
Definition SkScalar.h:40
#define SkScalarFloorToScalar(x)
Definition SkScalar.h:30
static float SkScalarSinSnapToZero(SkScalar radians)
Definition SkScalar.h:115
#define SkScalarTan(radians)
Definition SkScalar.h:47
#define SkScalarATan2(y, x)
Definition SkScalar.h:50
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SkScalarSin(radians)
Definition SkScalar.h:45
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:107
#define SK_Scalar1
Definition SkScalar.h:18
#define SkScalarHalf(a)
Definition SkScalar.h:75
#define SK_ScalarHalf
Definition SkScalar.h:19
#define SkDoubleToScalar(x)
Definition SkScalar.h:64
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SkScalarRoundToScalar(x)
Definition SkScalar.h:32
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SkScalarSqrt(x)
Definition SkScalar.h:42
#define SK_ScalarRoot2Over2
Definition SkScalar.h:23
#define SkScalarAbs(x)
Definition SkScalar.h:39
static float SkScalarCosSnapToZero(SkScalar radians)
Definition SkScalar.h:120
#define SK_ScalarPI
Definition SkScalar.h:21
constexpr int SkToInt(S x)
Definition SkTo.h:29
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
#define N
Definition beziers.cpp:19
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const
Definition SkMatrix.cpp:770
SkMatrix & setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:296
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:452
SkMatrix & preRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:462
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:315
SkPathBuilder & conicTo(SkPoint pt1, SkPoint pt2, SkScalar w)
SkPathBuilder & close()
SkPathBuilder & arcTo(const SkRect &oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg, bool forceMoveTo)
SkPathBuilder & lineTo(SkPoint pt)
SkRect computeBounds() const
SkPathBuilder & rLineTo(SkPoint pt)
SkPathBuilder & operator=(const SkPath &)
SkPathBuilder & addArc(const SkRect &oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg)
SkPathBuilder & addPath(const SkPath &)
SkPathBuilder & polylineTo(const SkPoint pts[], int count)
SkPathBuilder & addRect(const SkRect &, SkPathDirection, unsigned startIndex)
SkPathBuilder & setFillType(SkPathFillType ft)
SkPathBuilder & addRRect(const SkRRect &, SkPathDirection, unsigned startIndex)
SkPathBuilder & cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3)
void incReserve(int extraPtCount, int extraVerbCount)
SkPathBuilder & addCircle(SkScalar center_x, SkScalar center_y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
SkPathBuilder & moveTo(SkPoint pt)
SkPathBuilder & reset()
SkPathBuilder & addOval(const SkRect &, SkPathDirection, unsigned startIndex)
SkPathBuilder & addPolygon(const SkPoint pts[], int count, bool isClosed)
SkPathBuilder & offset(SkScalar dx, SkScalar dy)
SkPathBuilder & rQuadTo(SkPoint pt1, SkPoint pt2)
SkPathBuilder & rCubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3)
SkPathBuilder & quadTo(SkPoint pt1, SkPoint pt2)
SkPathBuilder & rConicTo(SkPoint p1, SkPoint p2, SkScalar w)
SkPath snapshot() const
static int PtsInVerb(unsigned verb)
Definition SkPathPriv.h:322
SkScalar conicWeight() const
Definition SkPath.h:1692
Verb next(SkPoint[4])
Definition SkPath.cpp:1917
SkPathFillType getFillType() const
Definition SkPath.h:230
@ kClose_Verb
Definition SkPath.h:1463
@ kMove_Verb
Definition SkPath.h:1458
@ kConic_Verb
Definition SkPath.h:1461
@ kDone_Verb
Definition SkPath.h:1464
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
bool isOval() const
Definition SkRRect.h:85
SkVector radii(Corner corner) const
Definition SkRRect.h:271
@ kUpperLeft_Corner
index of top-left corner radii
Definition SkRRect.h:252
@ kLowerRight_Corner
index of bottom-right corner radii
Definition SkRRect.h:254
@ kUpperRight_Corner
index of top-right corner radii
Definition SkRRect.h:253
@ kLowerLeft_Corner
index of bottom-left corner radii
Definition SkRRect.h:255
bool isRect() const
Definition SkRRect.h:84
bool isEmpty() const
Definition SkRRect.h:83
const SkRect & getBounds() const
Definition SkRRect.h:279
T * push_back_n(int n)
Definition SkTArray.h:262
bool empty() const
Definition SkTArray.h:194
int size() const
Definition SkTArray.h:416
void reserve_exact(int n)
Definition SkTArray.h:176
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
const uint8_t uint32_t uint32_t GError ** error
#define R(r)
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
SkRRect rrect
Definition SkRecords.h:232
SkRect oval
Definition SkRecords.h:249
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition switches.h:145
static bool nearly_equal(SkColor4f x, SkColor4f y)
Definition p3.cpp:35
SkScalar w
#define T
SkPoint fPts[3]
Definition SkGeometry.h:336
static int BuildUnitArc(const SkVector &start, const SkVector &stop, SkRotationDirection, const SkMatrix *, SkConic conics[kMaxConicsForArc])
@ kMaxConicsForArc
Definition SkGeometry.h:411
bool setLength(float length)
Definition SkPoint.cpp:30
float fX
x-axis value
void offset(float dx, float dy)
static constexpr SkPoint Make(float x, float y)
void set(float x, float y)
float fY
y-axis value
constexpr float y() const
constexpr float x() const
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
constexpr float centerX() const
Definition SkRect.h:776
constexpr float height() const
Definition SkRect.h:769
constexpr float centerY() const
Definition SkRect.h:785
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15