SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) {
startIndex &= 7; // keep it legal // To be backwards compatible with the old impl for building a rrect path, we // first check to see if the rrect itself can be simplified... const SkRect& bounds = rr.getBounds(); auto [asType, newIndex] = SkPathPriv::SimplifyRRect(rr, startIndex); switch (asType) { case SkPathPriv::RRectAsEnum::kRect: return SkPath::Rect(bounds, SkPathFillType::kDefault, dir, newIndex);
case SkPathPriv::RRectAsEnum::kOval: return SkPath::Oval(bounds, dir, newIndex);
case SkPathPriv::RRectAsEnum::kRRect: // fall through break;
} return MakeNullCheck(SkPathData::RRect(rr, dir, newIndex), SkPathFillType::kDefault, false);
}
for (auto [verb, pts, weight] : SkPathPriv::Iterate(*this)) { if (verb == SkPathVerb::kClose || (segmentCount > 0 && verb == SkPathVerb::kMove)) { // Closing the current contour; but since convexity is a precondition, it's the only // contour that matters.
SkASSERT(moveCnt);
segmentCount++; break;
} elseif (verb == SkPathVerb::kMove) { // A move at the start of the contour (or multiple leading moves, in which case we // keep the last one before a non-move verb).
SkASSERT(!segmentCount);
SkDEBUGCODE(++moveCnt);
firstPt = prevPt = pts[0];
} else { int pointCount = SkPathPriv::PtsInVerb((unsigned) verb);
SkASSERT(pointCount > 0);
if (!SkPathPriv::AllPointsEq({pts, (size_t)pointCount + 1})) {
SkASSERT(moveCnt); int nextPt = pointCount;
segmentCount++;
if (prevPt == pts[nextPt]) { // A pre-condition to getting here is that the path is convex, so if a // verb's start and end points are the same, it means it's the only // verb in the contour (and the only contour). While it's possible for // such a single verb to be a convex curve, we do not have any non-zero // length edges to conservatively test against without splitting or // evaluating the curve. For simplicity, just reject the rectangle. returnfalse;
} elseif (SkPathVerb::kConic == verb) {
SkConic orig;
orig.set(pts, *weight);
SkPoint quadPts[5]; int count = orig.chopIntoQuadsPOW2(quadPts, 1);
SkASSERT_RELEASE(2 == count);
const SkSpan<const SkPathVerb> vbs = path.verbs();
fVerbs = vbs.begin();
fVerbStop = vbs.end(); // For empty paths we receive an empty span, which should yield a nullptr fVerbsStop.
SkASSERT(!!fVerbs == !!fVerbStop);
if (SkPathVerb::kMove == *verbs) {
verbs += 1; // skip the initial moveto
}
while (verbs < stop) { // verbs points one beyond the current verb, decrement first.
SkPathVerb v = *verbs++; if (SkPathVerb::kMove == v) { break;
} if (SkPathVerb::kClose == v) { return true;
}
} returnfalse;
}
SkPathVerb SkPath::Iter::autoClose(SkPoint pts[2]) {
SkASSERT(pts); if (fLastPt != fMoveTo) { // A special case: if both points are NaN, SkPoint::operation== returns // false, but the iterator expects that they are treated as the same. // (consider SkPoint is a 2-dimension float point). if (SkIsNaN(fLastPt.fX) || SkIsNaN(fLastPt.fY) ||
SkIsNaN(fMoveTo.fX) || SkIsNaN(fMoveTo.fY)) { return SkPathVerb::kClose;
}
if (fVerbs == fVerbStop) { // Close the curve if requested and if there is some curve to close if (fNeedClose) { if (SkPathVerb::kLine == this->autoClose(ptsParam)) { return kLine_Verb;
}
fNeedClose = false; return kClose_Verb;
} return kDone_Verb;
}
SkRect SkPath::computeTightBounds() const { // If we're only lines, then our (quick) bounds is also tight. if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) { return this->getBounds();
}
SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) { auto convexity = path.getConvexityOrUnknown(); if (SkPathConvexity_IsConvex(convexity)) { // Note, this can return kUnknown. That is valid. If we've determined that the // path is convex, then we've already tried to compute its first-direction. If // that failed, then kUnknown is the right answer. return SkPathConvexity_ToFirstDirection(convexity);
}
// Note, this can compute a 'first' direction, even for non-convex shapes. if (auto raw = SkPathPriv::Raw(path, SkResolveConvexity::kNo)) { return ComputeFirstDirection(*raw);
} else { return SkPathFirstDirection::kUnknown;
}
}
// Make sure MakeNoCheck() didn't alias us to the standard Empty instance. We want our // pointer to be distinct from that one.
SkASSERT(gErrorSingleton != SkPathData::Empty().get());
SkScalar eval(SkScalar x, SkScalar y) const { return fA * x + fB * y + fC;
}
SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
bool normalize() { double a = fA; double b = fB; double c = fC; double dmag = sqrt(a * a + b * b); // length of initial plane normal is zero if (dmag == 0) {
fA = fB = 0;
fC = SK_Scalar1; return true;
} double dscale = sk_ieee_double_divide(1.0, dmag);
a *= dscale;
b *= dscale;
c *= dscale; // check if we're not finite, or normal is zero-length if (!SkIsFinite(a, b, c) ||
(a == 0 && b == 0)) {
fA = fB = 0;
fC = SK_Scalar1; returnfalse;
}
fA = a;
fB = b;
fC = c; return true;
}
enum Result {
kAllNegative,
kAllPositive,
kMixed
};
Result test(const SkRect& bounds) const { // check whether the diagonal aligned with the normal crosses the plane
SkPoint diagMin, diagMax; if (fA >= 0) {
diagMin.fX = bounds.fLeft;
diagMax.fX = bounds.fRight;
} else {
diagMin.fX = bounds.fRight;
diagMax.fX = bounds.fLeft;
} if (fB >= 0) {
diagMin.fY = bounds.fTop;
diagMax.fY = bounds.fBottom;
} else {
diagMin.fY = bounds.fBottom;
diagMax.fY = bounds.fTop;
}
SkScalar test = this->eval(diagMin.fX, diagMin.fY);
SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY); if (sign > 0) { // the path is either all on one side of the half-plane or the other if (test < 0) { return kAllNegative;
} else { return kAllPositive;
}
} return kMixed;
}
};
auto rotated = path.tryMakeTransform(*inv); if (!rotated) { return {};
} auto raw = SkPathPriv::Raw(*rotated, SkResolveConvexity::kNo); if (!raw) {
SkASSERT(false); // if rotated was valid, so should the raw return {};
}
SkScalar big = SK_ScalarMax;
SkRect clip = {-big, 0, big, big };
rec.fResult.setFillType(path.getFillType());
SkPath result = rec.fResult.detach(&mx); if (!result.isFinite()) { return {};
} return result;
}
// true means we have written to clippedPath bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) { if (!matrix.hasPerspective()) { returnfalse;
}
SkHalfPlane plane {
matrix[SkMatrix::kMPersp0],
matrix[SkMatrix::kMPersp1],
matrix[SkMatrix::kMPersp2] - kW0PlaneDistance
}; if (plane.normalize()) { switch (plane.test(path.getBounds())) { case SkHalfPlane::kAllPositive: returnfalse; case SkHalfPlane::kMixed: { if (auto result = clip(path, plane)) {
*clippedPath = *result;
} else {
*clippedPath = SkPath(); // clipped out (or failed)
} return true;
} default: break; // handled outside of the switch
}
} // clipped out (or failed)
*clippedPath = SkPath(); return true;
}
std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill) { if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) { return {};
}
SkPoint rectPts[5]; int rectPtCnt = 0; bool needsClose = !isSimpleFill; for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) { switch (v) { case SkPathVerb::kMove: if (0 != rectPtCnt) { return {};
}
rectPts[0] = verbPts[0];
++rectPtCnt; break; case SkPathVerb::kLine: if (5 == rectPtCnt) { return {};
}
rectPts[rectPtCnt] = verbPts[1];
++rectPtCnt; break; case SkPathVerb::kClose: if (4 == rectPtCnt) {
rectPts[4] = rectPts[0];
rectPtCnt = 5;
}
needsClose = false; break; case SkPathVerb::kQuad: case SkPathVerb::kConic: case SkPathVerb::kCubic: return {};
}
} if (needsClose) { return {};
} if (rectPtCnt < 5) { return {};
} if (rectPts[0] != rectPts[4]) { return {};
} // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge ( // and pts 1 and 2 the opposite vertical or horizontal edge). bool vec03IsVertical; if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) { // Make sure it has non-zero width and height if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) { return {};
}
vec03IsVertical = true;
} elseif (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) { // Make sure it has non-zero width and height if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) { return {};
}
vec03IsVertical = false;
} else { return {};
}
SkPathRectInfo info;
// Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit // set if it is on the bottom edge. unsigned sortFlags =
((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10); switch (sortFlags) { case0b00:
info.fRect.setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
info.fStartIndex = 0; break; case0b01:
info.fRect.setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
info.fStartIndex = 1; break; case0b10:
info.fRect.setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
info.fStartIndex = 3; break; case0b11:
info.fRect.setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
info.fStartIndex = 2; break;
} return info;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.