Quellcode-Bibliothek SkPathOpsAsWinding.cpp
Sprache: C
/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file.
*/ #include"include/core/SkPath.h" #include"include/core/SkPathBuilder.h" #include"include/core/SkPathTypes.h" #include"include/core/SkPoint.h" #include"include/core/SkRect.h" #include"include/core/SkScalar.h" #include"include/core/SkTypes.h" #include"include/pathops/SkPathOps.h" #include"include/private/base/SkMacros.h" #include"src/core/SkPathPriv.h" #include"src/pathops/SkPathOpsConic.h" #include"src/pathops/SkPathOpsCubic.h" #include"src/pathops/SkPathOpsCurve.h" #include"src/pathops/SkPathOpsPoint.h" #include"src/pathops/SkPathOpsQuad.h" #include"src/pathops/SkPathOpsTypes.h"
#include <algorithm> #include <vector>
using std::vector;
struct Contour { enumclass Direction { // SkPathDirection doesn't have 'none' state
kCCW = -1,
kNone,
kCW,
};
Contour(const SkRect& bounds, int lastStart, int verbStart)
: fBounds(bounds)
, fVerbStart(lastStart)
, fVerbEnd(verbStart) {
}
static Contour::Direction to_direction(SkScalar dy) { return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW :
Contour::Direction::kNone;
}
staticint contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) {
SkRect bounds;
bounds.setBounds(pts, kPtCount[verb] + 1); if (bounds.fTop > edge.fY) { return 0;
} if (bounds.fBottom <= edge.fY) { // check to see if y is at line end to avoid double counting return 0;
} if (bounds.fLeft >= edge.fX) { return 0;
} int winding = 0; double tVals[3];
Contour::Direction directions[3]; // must intersect horz ray with curve in case it intersects more than once int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals);
SkASSERT(between(0, count, 3)); // remove results to the right of edge for (int index = 0; index < count; ) {
SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX; if (intersectX < edge.fX) {
++index; continue;
} if (intersectX > edge.fX) {
tVals[index] = tVals[--count]; continue;
} // if intersect x equals edge x, we need to determine if pts is to the left or right of edge if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) {
++index; continue;
} // TODO : other cases need discriminating. need op angle code to figure it out // example: edge ends 45 degree diagonal going up. If pts is to the left of edge, keep. // if pts is to the right of edge, discard. With code as is, can't distiguish the two cases.
tVals[index] = tVals[--count];
} // use first derivative to determine if intersection is contributing +1 or -1 to winding for (int index = 0; index < count; ++index) {
directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY);
} for (int index = 0; index < count; ++index) { // skip intersections that end at edge and go up if (zero_or_one(tVals[index]) && Contour::Direction::kCCW != directions[index]) { continue;
}
winding += (int) directions[index];
} return winding; // note winding indicates containership, not contour direction
}
int nextEdge(Contour& contour, Edge edge) {
SkPath::Iter iter(fPath, true);
SkPoint pts[4];
SkPath::Verb verb; int verbCount = -1; int winding = 0; do {
verb = iter.next(pts); if (++verbCount < contour.fVerbStart) { continue;
} if (verbCount >= contour.fVerbEnd) { continue;
} if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { continue;
} bool horizontal = true; for (int index = 1; index <= kPtCount[verb]; ++index) { if (pts[0].fY != pts[index].fY) {
horizontal = false; break;
}
} if (horizontal) { continue;
} if (edge == Edge::kCompare) {
winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY); continue;
}
SkASSERT(edge == Edge::kInitial);
SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb)); if (minXY.fX > contour.fMinXY.fX) { continue;
} if (minXY.fX == contour.fMinXY.fX) { if (minXY.fY != contour.fMinXY.fY) { continue;
}
}
contour.fMinXY = minXY;
} while (SkPath::kDone_Verb != verb); return winding;
}
bool containerContains(Contour& contour, Contour& test) { // find outside point on lesser contour // arbitrarily, choose non-horizontal edge where point <= bounds left // note that if leftmost point is control point, may need tight bounds // to find edge with minimum-x if (SK_ScalarMax == test.fMinXY.fX) {
this->nextEdge(test, Edge::kInitial);
} // find all edges on greater equal or to the left of one on lesser
contour.fMinXY = test.fMinXY; int winding = this->nextEdge(contour, Edge::kCompare); // if edge is up, mark contour cw, otherwise, ccw // sum of greater edges direction should be cw, 0, ccw
test.fContained = winding != 0; return -1 <= winding && winding <= 1;
}
void inParent(Contour& contour, Contour& parent) { // move contour into sibling list contained by parent for (auto test : parent.fChildren) { if (test->fBounds.contains(contour.fBounds)) {
inParent(contour, *test); return;
}
} // move parent's children into contour's children if contained by contour for (auto iter = parent.fChildren.begin(); iter != parent.fChildren.end(); ) { if (contour.fBounds.contains((*iter)->fBounds)) {
contour.fChildren.push_back(*iter);
iter = parent.fChildren.erase(iter); continue;
}
++iter;
}
parent.fChildren.push_back(&contour);
}
bool checkContainerChildren(Contour* parent, Contour* child) { for (auto grandChild : child->fChildren) { if (!checkContainerChildren(child, grandChild)) { returnfalse;
}
} if (parent) { if (!containerContains(*parent, *child)) { returnfalse;
}
} returntrue;
}
bool AsWinding(const SkPath& path, SkPath* result) { if (!path.isFinite()) { returnfalse;
}
SkPathFillType fillType = path.getFillType(); if (fillType == SkPathFillType::kWinding
|| fillType == SkPathFillType::kInverseWinding ) { return set_result_path(result, path, fillType);
}
fillType = path.isInverseFillType() ? SkPathFillType::kInverseWinding :
SkPathFillType::kWinding; if (path.isEmpty() || path.isConvex()) { return set_result_path(result, path, fillType);
} // count contours
vector<Contour> contours; // one per contour
OpAsWinding winder(path);
winder.contourBounds(&contours); if (contours.size() <= 1) { return set_result_path(result, path, fillType);
} // create contour bounding box tree
Contour sorted(SkRect(), 0, 0); for (auto& contour : contours) {
winder.inParent(contour, sorted);
} // if sorted has no grandchildren, no child has to fix its children's winding if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(),
[](const Contour* contour) -> bool { return contour->fChildren.empty(); } )) { return set_result_path(result, path, fillType);
} // starting with outermost and moving inward, see if one path contains another for (auto contour : sorted.fChildren) {
winder.nextEdge(*contour, OpAsWinding::Edge::kInitial);
contour->fDirection = winder.getDirection(*contour); if (!winder.checkContainerChildren(nullptr, contour)) { returnfalse;
}
} // starting with outermost and moving inward, mark paths to reverse bool reversed = false; for (auto contour : sorted.fChildren) {
reversed |= winder.markReverse(nullptr, contour);
} if (!reversed) { return set_result_path(result, path, fillType);
}
*result = winder.reverseMarkedContours(contours, fillType); returntrue;
}
Messung V0.5
¤ 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.0.12Bemerkung:
(vorverarbeitet)
¤
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.