73 {
74 inline static constexpr int kMaxDepth = 75;
75 int fCubicDepths[kMaxDepth + 1];
76
77 DepthHistogram() { memset(fCubicDepths, 0, sizeof(fCubicDepths)); }
78
79 ~DepthHistogram() {
80 SkDebugf(
"# times recursion terminated per depth:\n");
81 for (int i = 0; i <= kMaxDepth; i++) {
82 SkDebugf(
" depth %d: %d\n", i, fCubicDepths[i]);
83 }
84 }
85
86 inline void incDepth(int depth) {
87 SkASSERT(depth >= 0 && depth <= kMaxDepth);
88 fCubicDepths[depth]++;
89 }
90 } sCubicDepthHistogram;
91
92#define DEBUG_CUBIC_RECURSION_TRACK_DEPTH(depth) sCubicDepthHistogram.incDepth(depth)
93#else
94#define DEBUG_CUBIC_RECURSION_TRACK_DEPTH(depth) (void)(depth)
95#endif
96
99}
100
106 return false;
107 }
109 unitNormal->
scale(radius, normal);
110 return true;
111}
112
117 return false;
118 }
120 unitNormal->
scale(radius, normal);
121 return true;
122}
123
124
125
136
137
144 }
145
148 return false;
149 }
153 return true;
154 }
155
158 return false;
159 }
163 return true;
164 }
165};
166
168public:
172 bool canIgnoreCenter);
173
176
182 void close(
bool isLine) { this->finishContour(
true, isLine); }
183
185 this->finishContour(false, isLine);
187 }
188
190
192 return fInner.isZeroLengthSincePoint(0) &&
193 fOuter.isZeroLengthSincePoint(fFirstOuterPtIndexInContour);
194 }
195
196private:
202
203 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
206 int fFirstOuterPtIndexInContour;
207 int fSegmentCount;
208 bool fPrevIsLine;
209 bool fCanIgnoreCenter;
210
213
214 SkPath fInner, fOuter, fCusper;
215
216 enum StrokeType {
217 kOuter_StrokeType = 1,
218 kInner_StrokeType = -1
219 } fStrokeType;
220
221 enum ResultType {
222 kSplit_ResultType,
223 kDegenerate_ResultType,
224 kQuad_ResultType,
225 };
226
227 enum ReductionType {
228 kPoint_ReductionType,
229 kLine_ReductionType,
230 kQuad_ReductionType,
231 kDegenerate_ReductionType,
232 kDegenerate2_ReductionType,
233 kDegenerate3_ReductionType,
234 };
235
236 enum IntersectRayType {
237 kCtrlPt_RayType,
238 kResultType_RayType,
239 };
240
241 int fRecursionDepth;
242 bool fFoundTangents;
243 bool fJoinCompleted;
244
246 static ReductionType CheckConicLinear(
const SkConic& ,
SkPoint* reduction);
247 static ReductionType CheckCubicLinear(
const SkPoint cubic[4],
SkPoint reduction[3],
249 static ReductionType CheckQuadLinear(
const SkPoint quad[3],
SkPoint* reduction);
265 bool ptInQuadBounds(
const SkPoint quad[3],
const SkPoint& pt)
const;
269 void setConicEndNormal(
const SkConic& ,
272 void setCubicEndNormal(
const SkPoint cubic[4],
275 void setQuadEndNormal(
const SkPoint quad[3],
280 ResultType strokeCloseEnough(
const SkPoint stroke[3],
const SkPoint ray[2],
283
284 void finishContour(
bool close,
bool isLine);
286 bool isLine);
289
291};
292
293
294
296 SkVector* unitNormal,
bool currIsLine) {
298
301
304 return false;
305 }
306
307
308
310 unitNormal->
set(1, 0);
311 }
312
313 if (fSegmentCount == 0) {
315 fFirstUnitNormal = *unitNormal;
317
318 fOuter.
moveTo(fFirstOuterPt.
fX, fFirstOuterPt.
fY);
320 } else {
321 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
322 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
323 }
324 fPrevIsLine = currIsLine;
325 return true;
326}
327
328void SkPathStroker::postJoinTo(
const SkPoint& currPt,
const SkVector& normal,
330 fJoinCompleted = true;
331 fPrevPt = currPt;
332 fPrevUnitNormal = unitNormal;
334 fSegmentCount += 1;
335}
336
337void SkPathStroker::finishContour(bool close, bool currIsLine) {
338 if (fSegmentCount > 0) {
340
342 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
343 fFirstUnitNormal, fRadius, fInvMiterLimit,
344 fPrevIsLine, currIsLine);
346
347 if (fCanIgnoreCenter) {
348
349
352 }
353 } else {
354
357 fOuter.reversePathTo(fInner);
359 }
360 } else {
361
363 fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
364 currIsLine ? &fInner : nullptr);
365 fOuter.reversePathTo(fInner);
366
367 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
368 fPrevIsLine ? &fInner : nullptr);
370 }
374 }
375 }
376
377
379 fSegmentCount = -1;
380 fFirstOuterPtIndexInContour = fOuter.
countPoints();
381}
382
383
384
388 bool canIgnoreCenter)
389 : fRadius(radius)
390 , fResScale(resScale)
391 , fCanIgnoreCenter(canIgnoreCenter) {
392
393
394
395
396 fInvMiterLimit = 0;
397
401 } else {
403 }
404 }
407 fSegmentCount = -1;
408 fFirstOuterPtIndexInContour = 0;
409 fPrevIsLine = false;
410
411
412
413
414
415
416
421
422
424 fInvResScaleSquared = fInvResScale * fInvResScale;
425 fRecursionDepth = 0;
426}
427
429 if (fSegmentCount > 0) {
430 this->finishContour(false, false);
431 }
432 fSegmentCount = 0;
433 fFirstPt = fPrevPt = pt;
434 fJoinCompleted = false;
435}
436
437void SkPathStroker::line_to(
const SkPoint& currPt,
const SkVector& normal) {
440}
441
446 while ((verb =
copy.next(pts))) {
447 switch (verb) {
449 return false;
451 if (pts[0] == pts[1]) {
452 continue;
453 }
454 return true;
457 if (pts[0] == pts[1] && pts[0] == pts[2]) {
458 continue;
459 }
460 return true;
462 if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) {
463 continue;
464 }
465 return true;
468 return false;
469 }
470 }
471 return false;
472}
473
477 return;
478 }
480 return;
481 }
483
484 if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) {
485 return;
486 }
487 this->line_to(currPt, normal);
488 this->postJoinTo(currPt, normal, unitNormal);
489}
490
491void SkPathStroker::setQuadEndNormal(
const SkPoint quad[3],
const SkVector& normalAB,
494 *normalBC = normalAB;
495 *unitNormalBC = unitNormalAB;
496 }
497}
498
499void SkPathStroker::setConicEndNormal(
const SkConic& conic,
const SkVector& normalAB,
501 setQuadEndNormal(
conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC);
502}
503
504void SkPathStroker::setCubicEndNormal(
const SkPoint cubic[4],
const SkVector& normalAB,
508
511
512 if (degenerateAB && degenerateCD) {
513 goto DEGENERATE_NORMAL;
514 }
515
516 if (degenerateAB) {
519 }
520 if (degenerateCD) {
523 }
524 if (degenerateAB || degenerateCD) {
525DEGENERATE_NORMAL:
526 *normalCD = normalAB;
527 *unitNormalCD = unitNormalAB;
528 return;
529 }
531}
532
535 fStrokeType = strokeType;
536 fFoundTangents = false;
537 quadPts->
init(tStart, tEnd);
538}
539
540
547 if (t >= 0 && t <= 1) {
549 hit.
fX = lineStart.
fX * (1 - t) + lineEnd.
fX * t;
550 hit.fY = lineStart.
fY * (1 - t) + lineEnd.
fY * t;
552 } else {
554 }
555}
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
584 for (int index = 0; index < 3; ++index) {
585 for (int inner = index + 1; inner < 4; ++inner) {
588 if (ptMax < testMax) {
589 outer1 = index;
590 outer2 = inner;
591 ptMax = testMax;
592 }
593 }
594 }
595 SkASSERT(outer1 >= 0 && outer1 <= 2);
596 SkASSERT(outer2 >= 1 && outer2 <= 3);
598 int mid1 = (1 + (2 >> outer2)) >> outer1;
600 SkASSERT(outer1 != mid1 && outer2 != mid1);
601 int mid2 = outer1 ^ outer2 ^ mid1;
603 SkASSERT(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
604 SkASSERT(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
605 SkScalar lineSlop = ptMax * ptMax * 0.00001f;
606 return pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= lineSlop
607 &&
pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= lineSlop;
608}
609
610
611
612
613
614
615
616
621 for (int index = 0; index < 2; ++index) {
622 for (int inner = index + 1; inner < 3; ++inner) {
623 SkVector testDiff = quad[inner] - quad[index];
625 if (ptMax < testMax) {
626 outer1 = index;
627 outer2 = inner;
628 ptMax = testMax;
629 }
630 }
631 }
632 SkASSERT(outer1 >= 0 && outer1 <= 1);
633 SkASSERT(outer2 >= 1 && outer2 <= 2);
635 int mid = outer1 ^ outer2 ^ 3;
636 const float kCurvatureSlop = 0.000005f;
637 SkScalar lineSlop = ptMax * ptMax * kCurvatureSlop;
638 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop;
639}
640
643}
644
645SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(
const SkPoint cubic[4],
650 if (degenerateAB & degenerateBC & degenerateCD) {
651 return kPoint_ReductionType;
652 }
653 if (degenerateAB + degenerateBC + degenerateCD == 2) {
654 return kLine_ReductionType;
655 }
657 *tangentPtPtr = degenerateAB ? &
cubic[2] : &
cubic[1];
658 return kQuad_ReductionType;
659 }
662 int rCount = 0;
663
664 for (
int index = 0; index <
count; ++index) {
666 if (0 >= t || t >= 1) {
667 continue;
668 }
669 SkEvalCubicAt(cubic, t, &reduction[rCount],
nullptr,
nullptr);
670 if (reduction[rCount] != cubic[0] && reduction[rCount] != cubic[3]) {
671 ++rCount;
672 }
673 }
674 if (rCount == 0) {
675 return kLine_ReductionType;
676 }
677 static_assert(kQuad_ReductionType + 1 == kDegenerate_ReductionType, "enum_out_of_whack");
678 static_assert(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, "enum_out_of_whack");
679 static_assert(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, "enum_out_of_whack");
680
681 return (ReductionType) (kQuad_ReductionType + rCount);
682}
683
684SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(
const SkConic& conic,
688 if (degenerateAB & degenerateBC) {
689 return kPoint_ReductionType;
690 }
691 if (degenerateAB | degenerateBC) {
692 return kLine_ReductionType;
693 }
695 return kQuad_ReductionType;
696 }
697
698
701 return kLine_ReductionType;
702 }
703 conic.evalAt(t, reduction,
nullptr);
704 return kDegenerate_ReductionType;
705}
706
707SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(
const SkPoint quad[3],
711 if (degenerateAB & degenerateBC) {
712 return kPoint_ReductionType;
713 }
714 if (degenerateAB | degenerateBC) {
715 return kLine_ReductionType;
716 }
718 return kQuad_ReductionType;
719 }
721 if (0 == t || 1 == t) {
722 return kLine_ReductionType;
723 }
725 return kDegenerate_ReductionType;
726}
727
731 ReductionType reductionType = CheckConicLinear(conic, &reduction);
732 if (kPoint_ReductionType == reductionType) {
733
734
735
737 return;
738 }
739 if (kLine_ReductionType == reductionType) {
741 return;
742 }
743 if (kDegenerate_ReductionType == reductionType) {
748 fJoiner = saveJoiner;
749 return;
750 }
751 SkASSERT(kQuad_ReductionType == reductionType);
752 SkVector normalAB, unitAB, normalBC, unitBC;
753 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
755 return;
756 }
758 this->init(kOuter_StrokeType, &quadPts, 0, 1);
759 (void) this->conicStroke(conic, &quadPts);
760 this->init(kInner_StrokeType, &quadPts, 0, 1);
761 (void) this->conicStroke(conic, &quadPts);
762 this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC);
763 this->postJoinTo(pt2, normalBC, unitBC);
764}
765
767 const SkPoint quad[3] = { fPrevPt, pt1, pt2 };
769 ReductionType reductionType = CheckQuadLinear(quad, &reduction);
770 if (kPoint_ReductionType == reductionType) {
771
772
773
775 return;
776 }
777 if (kLine_ReductionType == reductionType) {
779 return;
780 }
781 if (kDegenerate_ReductionType == reductionType) {
786 fJoiner = saveJoiner;
787 return;
788 }
789 SkASSERT(kQuad_ReductionType == reductionType);
790 SkVector normalAB, unitAB, normalBC, unitBC;
791 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
793 return;
794 }
796 this->init(kOuter_StrokeType, &quadPts, 0, 1);
797 (void) this->quadStroke(quad, &quadPts);
798 this->init(kInner_StrokeType, &quadPts, 0, 1);
799 (void) this->quadStroke(quad, &quadPts);
800 this->setQuadEndNormal(quad, normalAB, unitAB, &normalBC, &unitBC);
801
802 this->postJoinTo(pt2, normalBC, unitBC);
803}
804
805
806
810 dxy->
set(fRadius, 0);
811 }
813 onPt->
fX = tPt.
fX + axisFlip * dxy->
fY;
814 onPt->
fY = tPt.
fY - axisFlip * dxy->
fX;
815 if (tangent) {
816 tangent->
fX = onPt->
fX + dxy->
fX;
817 tangent->
fY = onPt->
fY + dxy->
fY;
818 }
819}
820
821
822
826 conic.evalAt(t, tPt, &dxy);
827 if (dxy.
fX == 0 && dxy.
fY == 0) {
829 }
830 this->setRayPts(*tPt, &dxy, onPt, tangent);
831}
832
833
837 this->conicPerpRay(conic, quadPts->
fStartT, &conicStartPt, &quadPts->
fQuad[0],
840 }
843 this->conicPerpRay(conic, quadPts->
fEndT, &conicEndPt, &quadPts->
fQuad[2],
846 }
847}
848
849
850
856 if (dxy.
fX == 0 && dxy.
fY == 0) {
862 } else {
863
864
866 dxy = chopped[3] - chopped[2];
867 if (dxy.
fX == 0 && dxy.
fY == 0) {
868 dxy = chopped[3] - chopped[1];
869 cPts = chopped;
870 }
871 }
872 if (dxy.
fX == 0 && dxy.
fY == 0) {
873 dxy = cPts[3] - cPts[0];
874 }
875 }
876 setRayPts(*tPt, &dxy, onPt, tangent);
877}
878
879
883 this->cubicPerpRay(cubic, quadPts->
fStartT, &cubicStartPt, &quadPts->
fQuad[0],
886 }
889 this->cubicPerpRay(cubic, quadPts->
fEndT, &cubicEndPt, &quadPts->
fQuad[2],
892 }
893}
894
898 this->cubicPerpRay(cubic, quadPts->
fMidT, &cubicMidPt, mid,
nullptr);
899}
900
901
906 if (dxy.
fX == 0 && dxy.
fY == 0) {
907 dxy = quad[2] - quad[0];
908 }
909 setRayPts(*tPt, &dxy, onPt, tangent);
910}
911
912
913
914
915SkPathStroker::ResultType SkPathStroker::intersectRay(
SkQuadConstruct* quadPts,
921
922
923
924
925
926
930 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
"denom == 0");
931 }
936 if ((numerA >= 0) == (numerB >= 0)) {
937
938
941 if (std::max(dist1, dist2) <= fInvResScaleSquared) {
943 "std::max(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1, dist2);
944 }
946 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB);
947 }
948
949
950 numerA /= denom;
951 bool validDivide = numerA > numerA - 1;
952 if (validDivide) {
953 if (kCtrlPt_RayType == intersectRayType) {
955
956
959 }
961 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB);
962 }
964
966 "SkScalarNearlyZero(denom=%g)", denom);
967}
968
969
970SkPathStroker::ResultType SkPathStroker::tangentsMeet(
const SkPoint cubic[4],
972 this->cubicQuadEnds(cubic, quadPts);
974}
975
976
980 for (int n = 0; n < 3; ++n) {
981 r[n] = (quad[n].
fY -
line[0].fY) * vec.
fX - (quad[n].fX - line[0].fX) * vec.
fY;
982 }
989}
990
991
992bool SkPathStroker::ptInQuadBounds(
const SkPoint quad[3],
const SkPoint& pt)
const {
993 SkScalar xMin = std::min(std::min(quad[0].fX, quad[1].fX), quad[2].fX);
994 if (pt.
fX + fInvResScale < xMin) {
995 return false;
996 }
997 SkScalar xMax = std::max(std::max(quad[0].fX, quad[1].fX), quad[2].fX);
998 if (pt.
fX - fInvResScale > xMax) {
999 return false;
1000 }
1001 SkScalar yMin = std::min(std::min(quad[0].fY, quad[1].fY), quad[2].fY);
1002 if (pt.
fY + fInvResScale < yMin) {
1003 return false;
1004 }
1005 SkScalar yMax = std::max(std::max(quad[0].fY, quad[1].fY), quad[2].fY);
1006 if (pt.
fY - fInvResScale > yMax) {
1007 return false;
1008 }
1009 return true;
1010}
1011
1014}
1015
1017 SkVector smaller = quad[1] - quad[0];
1018 SkVector larger = quad[1] - quad[2];
1021 if (smallerLen > largerLen) {
1022 using std::swap;
1023 swap(smaller, larger);
1024 largerLen = smallerLen;
1025 }
1027 return false;
1028 }
1031}
1032
1033SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(
const SkPoint stroke[3],
1036
1040 "sharp_angle (1) =%g,%g, %g,%g, %g,%g",
1044 }
1046 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScale=%g)",
1047 ray[0].fX, ray[0].fY, strokeMid.
fX, strokeMid.
fY, fInvResScale);
1048 }
1049
1050
1051 if (!ptInQuadBounds(stroke, ray[0])) {
1053 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)",
1054 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY,
1055 ray[0].fX, ray[0].fY);
1056 }
1057
1060 if (rootCount != 1) {
1062 "rootCount=%d != 1", rootCount);
1063 }
1069 "sharp_angle (2) =%g,%g, %g,%g, %g,%g",
1073 }
1075 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)",
1076 ray[0].fX, ray[0].fY, quadPt.
fX, quadPt.
fY,
error);
1077 }
1078
1079 return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
"%s",
"fall through");
1080}
1081
1082SkPathStroker::ResultType SkPathStroker::compareQuadCubic(
const SkPoint cubic[4],
1084
1085 this->cubicQuadEnds(cubic, quadPts);
1086 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
1088 if (resultType != kQuad_ResultType) {
1089 return resultType;
1090 }
1091
1093 this->cubicPerpRay(cubic, quadPts->
fMidT, &ray[1], &ray[0],
nullptr);
1094 return this->strokeCloseEnough(quadPts->
fQuad, ray, quadPts
1096}
1097
1098SkPathStroker::ResultType SkPathStroker::compareQuadConic(
const SkConic& conic,
1100
1101 this->conicQuadEnds(conic, quadPts);
1102 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
1104 if (resultType != kQuad_ResultType) {
1105 return resultType;
1106 }
1107
1109 this->conicPerpRay(conic, quadPts->
fMidT, &ray[1], &ray[0],
nullptr);
1110 return this->strokeCloseEnough(quadPts->
fQuad, ray, quadPts
1112}
1113
1114SkPathStroker::ResultType SkPathStroker::compareQuadQuad(
const SkPoint quad[3],
1116
1119 this->quadPerpRay(quad, quadPts->
fStartT, &quadStartPt, &quadPts->
fQuad[0],
1122 }
1125 this->quadPerpRay(quad, quadPts->
fEndT, &quadEndPt, &quadPts->
fQuad[2],
1128 }
1129 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
1131 if (resultType != kQuad_ResultType) {
1132 return resultType;
1133 }
1134
1136 this->quadPerpRay(quad, quadPts->
fMidT, &ray[1], &ray[0],
nullptr);
1137 return this->strokeCloseEnough(quadPts->
fQuad, ray, quadPts
1139}
1140
1141void SkPathStroker::addDegenerateLine(
const SkQuadConstruct* quadPts) {
1143 SkPath*
path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1144 path->lineTo(quad[2].fX, quad[2].fY);
1145}
1146
1149 this->cubicQuadMid(cubic, quadPts, &strokeMid);
1151 return dist < fInvResScaleSquared;
1152}
1153
1155 if (!fFoundTangents) {
1156 ResultType resultType = this->tangentsMeet(cubic, quadPts);
1157 if (kQuad_ResultType != resultType) {
1158 if ((kDegenerate_ResultType == resultType
1160 fInvResScale)) && cubicMidOnLine(cubic, quadPts)) {
1161 addDegenerateLine(quadPts);
1163 return true;
1164 }
1165 } else {
1166 fFoundTangents = true;
1167 }
1168 }
1169 if (fFoundTangents) {
1170 ResultType resultType = this->compareQuadCubic(cubic, quadPts);
1171 if (kQuad_ResultType == resultType) {
1172 SkPath*
path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1174 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1176 return true;
1177 }
1178 if (kDegenerate_ResultType == resultType) {
1180 addDegenerateLine(quadPts);
1182 return true;
1183 }
1184 }
1185 }
1188 return false;
1189 }
1190#if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
1191 SkDEBUGCODE(gMaxRecursion[fFoundTangents] = std::max(gMaxRecursion[fFoundTangents],
1192 fRecursionDepth + 1));
1193#endif
1196
1197 addDegenerateLine(quadPts);
1198 return true;
1199 }
1202 addDegenerateLine(quadPts);
1204 --fRecursionDepth;
1205 return true;
1206 }
1207 if (!this->cubicStroke(cubic, &half)) {
1208 return false;
1209 }
1211 addDegenerateLine(quadPts);
1213 --fRecursionDepth;
1214 return true;
1215 }
1216 if (!this->cubicStroke(cubic, &half)) {
1217 return false;
1218 }
1219 --fRecursionDepth;
1220 return true;
1221}
1222
1224 ResultType resultType = this->compareQuadConic(conic, quadPts);
1225 if (kQuad_ResultType == resultType) {
1227 SkPath*
path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1228 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1229 return true;
1230 }
1231 if (kDegenerate_ResultType == resultType) {
1232 addDegenerateLine(quadPts);
1233 return true;
1234 }
1235#if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
1237 fRecursionDepth + 1));
1238#endif
1240
1241 addDegenerateLine(quadPts);
1242 return true;
1243 }
1246 if (!this->conicStroke(conic, &half)) {
1247 return false;
1248 }
1250 if (!this->conicStroke(conic, &half)) {
1251 return false;
1252 }
1253 --fRecursionDepth;
1254 return true;
1255}
1256
1258 ResultType resultType = this->compareQuadQuad(quad, quadPts);
1259 if (kQuad_ResultType == resultType) {
1261 SkPath*
path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
1262 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY);
1263 return true;
1264 }
1265 if (kDegenerate_ResultType == resultType) {
1266 addDegenerateLine(quadPts);
1267 return true;
1268 }
1269#if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
1271 fRecursionDepth + 1));
1272#endif
1274
1275 addDegenerateLine(quadPts);
1276 return true;
1277 }
1280 if (!this->quadStroke(quad, &half)) {
1281 return false;
1282 }
1284 if (!this->quadStroke(quad, &half)) {
1285 return false;
1286 }
1287 --fRecursionDepth;
1288 return true;
1289}
1290
1296 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt);
1297 if (kPoint_ReductionType == reductionType) {
1298
1299
1300
1302 return;
1303 }
1304 if (kLine_ReductionType == reductionType) {
1306 return;
1307 }
1308 if (kDegenerate_ReductionType <= reductionType && kDegenerate3_ReductionType >= reductionType) {
1309 this->
lineTo(reduction[0]);
1312 if (kDegenerate2_ReductionType <= reductionType) {
1313 this->
lineTo(reduction[1]);
1314 }
1315 if (kDegenerate3_ReductionType == reductionType) {
1316 this->
lineTo(reduction[2]);
1317 }
1319 fJoiner = saveJoiner;
1320 return;
1321 }
1322 SkASSERT(kQuad_ReductionType == reductionType);
1323 SkVector normalAB, unitAB, normalCD, unitCD;
1324 if (!this->preJoinTo(*tangentPt, &normalAB, &unitAB, false)) {
1326 return;
1327 }
1331 for (
int index = 0; index <=
count; ++index) {
1334 this->init(kOuter_StrokeType, &quadPts, lastT, nextT);
1335 (void) this->cubicStroke(cubic, &quadPts);
1336 this->init(kInner_StrokeType, &quadPts, lastT, nextT);
1337 (void) this->cubicStroke(cubic, &quadPts);
1338 lastT = nextT;
1339 }
1341 if (cusp > 0) {
1345 }
1346
1347
1348 this->setCubicEndNormal(cubic, normalAB, unitAB, &normalCD, &unitCD);
1349
1350 this->postJoinTo(pt3, normalCD, unitCD);
1351}
1352
1353
1354
1355
1357
1361 fResScale = 1;
1364 fDoFill = false;
1365}
1366
1368 fWidth =
p.getStrokeWidth();
1369 fMiterLimit =
p.getStrokeMiter();
1370 fResScale = 1;
1371 fCap = (uint8_t)
p.getStrokeCap();
1372 fJoin = (uint8_t)
p.getStrokeJoin();
1374}
1375
1378 fMiterLimit =
p.getStrokeMiter();
1379 fResScale = 1;
1380 fCap = (uint8_t)
p.getStrokeCap();
1381 fJoin = (uint8_t)
p.getStrokeJoin();
1383}
1384
1388}
1389
1392 fMiterLimit = miterLimit;
1393}
1394
1398}
1399
1403}
1404
1405
1406
1407
1408
1410public:
1412 if (&src == *dst) {
1414 fSwapWithSrc = true;
1415 } else {
1416 (*dst)->reset();
1417 fSwapWithSrc = false;
1418 }
1419 }
1420
1422 if (fSwapWithSrc) {
1424 }
1425 }
1426
1427private:
1430 bool fSwapWithSrc;
1431};
1432
1435
1437
1439
1440 if (radius <= 0) {
1441 return;
1442 }
1443
1444
1445 {
1447 bool isClosed = false;
1449 if (
src.isRect(&rect, &isClosed, &dir) && isClosed) {
1451
1452 if (
src.isInverseFillType()) {
1454 dst->toggleInverseFillType();
1455 }
1456 return;
1457 }
1458 }
1459
1460
1461
1463 src.isLastContourClosed() &&
src.isConvex();
1464
1466 fResScale, ignoreCenter);
1469
1470 for (;;) {
1472 switch (iter.
next(pts)) {
1474 stroker.moveTo(pts[0]);
1475 break;
1477 stroker.lineTo(pts[1], &iter);
1479 break;
1481 stroker.quadTo(pts[1], pts[2]);
1483 break;
1485 stroker.conicTo(pts[1], pts[2], iter.
conicWeight());
1487 } break;
1489 stroker.cubicTo(pts[1], pts[2], pts[3]);
1491 break;
1494
1495
1496
1497 if (stroker.hasOnlyMoveTo()) {
1498 stroker.lineTo(stroker.moveToPt());
1499 goto ZERO_LENGTH;
1500 }
1501
1502
1503
1504 if (stroker.isCurrentContourEmpty()) {
1505 ZERO_LENGTH:
1507 break;
1508 }
1509 }
1511 break;
1513 goto DONE;
1514 }
1515 }
1516DONE:
1518
1519 if (fDoFill && !ignoreCenter) {
1521 dst->reverseAddPath(src);
1522 } else {
1524 }
1525 } else {
1526
1527
1528
1529
1530#if 0
1531
1533 SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 };
1535 paint.setStrokeWidth(7);
1537 canvas->
drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
paint);
1538 }
1539#endif
1540#if 0
1541 if (2 ==
src.countPoints()) {
1542 dst->setIsConvex(
true);
1543 }
1544#endif
1545 }
1546
1547
1548 if (
src.isInverseFillType()) {
1550 dst->toggleInverseFillType();
1551 }
1552}
1553
1556 return gOpposite[(
int)dir];
1557}
1558
1561
1571 } else {
1580 }
1581 path->addPoly(pts, 8,
true);
1582}
1583
1588
1590 if (radius <= 0) {
1591 return;
1592 }
1593
1596 if ((rw < 0) ^ (rh < 0)) {
1598 }
1601
1604
1606 r.
outset(radius, radius);
1607
1611 }
1612
1613 switch (join) {
1615 dst->addRect(r, dir);
1616 break;
1619 break;
1621 dst->addRoundRect(r, radius, radius, dir);
1622 break;
1623 default:
1624 break;
1625 }
1626
1627 if (fWidth < std::min(rw, rh) && !fDoFill) {
1629 r.
inset(radius, radius);
1631 }
1632}
#define SkPaintDefaults_MiterLimit
#define SkAssertResult(cond)
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
static constexpr float sk_ieee_float_divide(float numer, float denom)
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
SkScalar SkFindCubicCusp(const SkPoint src[4])
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3])
int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2])
#define SK_INIT_TO_AVOID_WARNING
void swap(sk_sp< T > &a, sk_sp< T > &b)
#define SkScalarInvert(x)
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
#define SK_ScalarNearlyZero
static bool sharp_angle(const SkPoint quad[3])
static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkScalar roots[2])
#define DEBUG_CUBIC_RECURSION_TRACK_DEPTH(depth)
static bool cubic_in_line(const SkPoint cubic[4])
static bool degenerate_vector(const SkVector &v)
static bool set_normal_unitnormal(const SkPoint &before, const SkPoint &after, SkScalar scale, SkScalar radius, SkVector *normal, SkVector *unitNormal)
static SkScalar pt_to_line(const SkPoint &pt, const SkPoint &lineStart, const SkPoint &lineEnd)
static bool has_valid_tangent(const SkPath::Iter *iter)
#define STROKER_RESULT(resultType, depth, quadPts, format,...)
static bool quad_in_line(const SkPoint quad[3])
static bool conic_in_line(const SkConic &conic)
static const int kRecursiveLimits[]
static bool points_within_dist(const SkPoint &nearPt, const SkPoint &farPt, SkScalar limit)
static SkPathDirection reverse_direction(SkPathDirection dir)
static void addBevel(SkPath *path, const SkRect &r, const SkRect &outer, SkPathDirection dir)
#define STROKER_DEBUG_PARAMS(...)
constexpr uint8_t SkToU8(S x)
Type::kYUV Type::kRGBA() int(0.7 *637)
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
@ kDefault_Cap
equivalent to kButt_Cap
@ kButt_Cap
no stroke extension
static constexpr int kCapCount
@ kStrokeAndFill_Style
sets to stroke and fill geometry
static constexpr int kJoinCount
@ kDefault_Join
equivalent to kMiter_Join
@ kMiter_Join
extends to miter limit
@ kBevel_Join
connects outside edges
static SkPathFirstDirection ComputeFirstDirection(const SkPath &)
bool hasOnlyMoveTo() const
void done(SkPath *dst, bool isLine)
SkPathStroker(const SkPath &src, SkScalar radius, SkScalar miterLimit, SkPaint::Cap, SkPaint::Join, SkScalar resScale, bool canIgnoreCenter)
SkScalar getResScale() const
void quadTo(const SkPoint &, const SkPoint &)
void conicTo(const SkPoint &, const SkPoint &, SkScalar weight)
bool isCurrentContourEmpty() const
void cubicTo(const SkPoint &, const SkPoint &, const SkPoint &)
void moveTo(const SkPoint &)
void lineTo(const SkPoint &, const SkPath::Iter *iter=nullptr)
Verb next(SkPoint pts[4])
SkScalar conicWeight() const
SkPath & addCircle(SkScalar x, SkScalar y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
SkPath & moveTo(SkScalar x, SkScalar y)
bool getLastPt(SkPoint *lastPt) const
SkPath & lineTo(SkScalar x, SkScalar y)
SkPath & addPath(const SkPath &src, SkScalar dx, SkScalar dy, AddPathMode mode=kAppend_AddPathMode)
SkPath & setIsVolatile(bool isVolatile)
const SkRect & getBounds() const
void incReserve(int extraPtCount, int extraVerbCount=0, int extraConicCount=0)
static void RotateCCW(const SkPoint &src, SkPoint *dst)
static bool CanNormalize(SkScalar dx, SkScalar dy)
static SkScalar LengthSqd(const SkPoint &pt)
static bool EqualsWithinTolerance(const SkPoint &p1, const SkPoint &p2)
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
SkPaint::Cap getCap() const
void strokePath(const SkPath &path, SkPath *) const
void setCap(SkPaint::Cap)
void setJoin(SkPaint::Join)
void strokeRect(const SkRect &rect, SkPath *result, SkPathDirection=SkPathDirection::kCW) const
SkPaint::Join getJoin() const
void setMiterLimit(SkScalar)
void(* JoinProc)(SkPath *outer, SkPath *inner, const SkVector &beforeUnitNormal, const SkPoint &pivot, const SkVector &afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool prevIsLine, bool currIsLine)
static CapProc CapFactory(SkPaint::Cap)
static JoinProc JoinFactory(SkPaint::Join)
void(* CapProc)(SkPath *path, const SkPoint &pivot, const SkVector &normal, const SkPoint &stop, SkPath *otherPath)
const uint8_t uint32_t uint32_t GError ** error
sk_sp< SkBlender > blender SkRect rect
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
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
SINT T dot(const Vec< N, T > &a, const Vec< N, T > &b)
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
bool setLength(float length)
bool setNormalize(float x, float y)
float dot(const SkVector &vec) const
void set(float x, float y)
float cross(const SkVector &vec) const
void scale(float scale, SkPoint *dst) const
bool initWithEnd(SkQuadConstruct *parent)
bool initWithStart(SkQuadConstruct *parent)
bool init(SkScalar start, SkScalar end)
SkScalar fBottom
larger y-axis bounds
void inset(float dx, float dy)
SkScalar fLeft
smaller x-axis bounds
void outset(float dx, float dy)
SkScalar fRight
larger x-axis bounds
bool contains(SkScalar x, SkScalar y) const
constexpr float height() const
constexpr float width() const
SkScalar fTop
smaller y-axis bounds