diff options
Diffstat (limited to 'gfx/skia/skia/src/pathops/SkOpEdgeBuilder.cpp')
-rw-r--r-- | gfx/skia/skia/src/pathops/SkOpEdgeBuilder.cpp | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/pathops/SkOpEdgeBuilder.cpp b/gfx/skia/skia/src/pathops/SkOpEdgeBuilder.cpp new file mode 100644 index 0000000000..7078f2d67c --- /dev/null +++ b/gfx/skia/skia/src/pathops/SkOpEdgeBuilder.cpp @@ -0,0 +1,360 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "src/pathops/SkOpEdgeBuilder.h" + +#include "include/core/SkPath.h" +#include "include/core/SkPoint.h" +#include "include/core/SkTypes.h" +#include "src/base/SkTSort.h" +#include "src/core/SkGeometry.h" +#include "src/core/SkPathPriv.h" +#include "src/pathops/SkPathOpsCubic.h" +#include "src/pathops/SkPathOpsPoint.h" +#include "src/pathops/SkReduceOrder.h" + +#include <algorithm> +#include <array> + +void SkOpEdgeBuilder::init() { + fOperand = false; + fXorMask[0] = fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask + : kWinding_PathOpsMask; + fUnparseable = false; + fSecondHalf = preFetch(); +} + +// very tiny points cause numerical instability : don't allow them +static SkPoint force_small_to_zero(const SkPoint& pt) { + SkPoint ret = pt; + if (SkScalarAbs(ret.fX) < FLT_EPSILON_ORDERABLE_ERR) { + ret.fX = 0; + } + if (SkScalarAbs(ret.fY) < FLT_EPSILON_ORDERABLE_ERR) { + ret.fY = 0; + } + return ret; +} + +static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) { + if (SkPath::kMove_Verb == verb) { + return false; + } + for (int index = 0; index <= SkPathOpsVerbToPoints(verb); ++index) { + curve[index] = force_small_to_zero(curve[index]); + } + return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]); +} + +void SkOpEdgeBuilder::addOperand(const SkPath& path) { + SkASSERT(!fPathVerbs.empty() && fPathVerbs.back() == SkPath::kDone_Verb); + fPathVerbs.pop_back(); + fPath = &path; + fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask + : kWinding_PathOpsMask; + preFetch(); +} + +bool SkOpEdgeBuilder::finish() { + fOperand = false; + if (fUnparseable || !walk()) { + return false; + } + complete(); + SkOpContour* contour = fContourBuilder.contour(); + if (contour && !contour->count()) { + fContoursHead->remove(contour); + } + return true; +} + +void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curveStart) { + if (!SkDPoint::ApproximatelyEqual(curveEnd, curveStart)) { + *fPathVerbs.append() = SkPath::kLine_Verb; + *fPathPts.append() = curveStart; + } else { + int verbCount = fPathVerbs.size(); + int ptsCount = fPathPts.size(); + if (SkPath::kLine_Verb == fPathVerbs[verbCount - 1] + && fPathPts[ptsCount - 2] == curveStart) { + fPathVerbs.pop_back(); + fPathPts.pop_back(); + } else { + fPathPts[ptsCount - 1] = curveStart; + } + } + *fPathVerbs.append() = SkPath::kClose_Verb; +} + +int SkOpEdgeBuilder::preFetch() { + if (!fPath->isFinite()) { + fUnparseable = true; + return 0; + } + SkPoint curveStart; + SkPoint curve[4]; + bool lastCurve = false; + for (auto [pathVerb, pts, w] : SkPathPriv::Iterate(*fPath)) { + auto verb = static_cast<SkPath::Verb>(pathVerb); + switch (verb) { + case SkPath::kMove_Verb: + if (!fAllowOpenContours && lastCurve) { + closeContour(curve[0], curveStart); + } + *fPathVerbs.append() = verb; + curve[0] = force_small_to_zero(pts[0]); + *fPathPts.append() = curve[0]; + curveStart = curve[0]; + lastCurve = false; + continue; + case SkPath::kLine_Verb: + curve[1] = force_small_to_zero(pts[1]); + if (SkDPoint::ApproximatelyEqual(curve[0], curve[1])) { + uint8_t lastVerb = fPathVerbs.back(); + if (lastVerb != SkPath::kLine_Verb && lastVerb != SkPath::kMove_Verb) { + fPathPts.back() = curve[0] = curve[1]; + } + continue; // skip degenerate points + } + break; + case SkPath::kQuad_Verb: + curve[1] = force_small_to_zero(pts[1]); + curve[2] = force_small_to_zero(pts[2]); + verb = SkReduceOrder::Quad(curve, curve); + if (verb == SkPath::kMove_Verb) { + continue; // skip degenerate points + } + break; + case SkPath::kConic_Verb: + curve[1] = force_small_to_zero(pts[1]); + curve[2] = force_small_to_zero(pts[2]); + verb = SkReduceOrder::Quad(curve, curve); + if (SkPath::kQuad_Verb == verb && 1 != *w) { + verb = SkPath::kConic_Verb; + } else if (verb == SkPath::kMove_Verb) { + continue; // skip degenerate points + } + break; + case SkPath::kCubic_Verb: + curve[1] = force_small_to_zero(pts[1]); + curve[2] = force_small_to_zero(pts[2]); + curve[3] = force_small_to_zero(pts[3]); + verb = SkReduceOrder::Cubic(curve, curve); + if (verb == SkPath::kMove_Verb) { + continue; // skip degenerate points + } + break; + case SkPath::kClose_Verb: + closeContour(curve[0], curveStart); + lastCurve = false; + continue; + case SkPath::kDone_Verb: + continue; + } + *fPathVerbs.append() = verb; + int ptCount = SkPathOpsVerbToPoints(verb); + fPathPts.append(ptCount, &curve[1]); + if (verb == SkPath::kConic_Verb) { + *fWeights.append() = *w; + } + curve[0] = curve[ptCount]; + lastCurve = true; + } + if (!fAllowOpenContours && lastCurve) { + closeContour(curve[0], curveStart); + } + *fPathVerbs.append() = SkPath::kDone_Verb; + return fPathVerbs.size() - 1; +} + +bool SkOpEdgeBuilder::close() { + complete(); + return true; +} + +bool SkOpEdgeBuilder::walk() { + uint8_t* verbPtr = fPathVerbs.begin(); + uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; + SkPoint* pointsPtr = fPathPts.begin(); + SkScalar* weightPtr = fWeights.begin(); + SkPath::Verb verb; + SkOpContour* contour = fContourBuilder.contour(); + int moveToPtrBump = 0; + while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { + if (verbPtr == endOfFirstHalf) { + fOperand = true; + } + verbPtr++; + switch (verb) { + case SkPath::kMove_Verb: + if (contour && contour->count()) { + if (fAllowOpenContours) { + complete(); + } else if (!close()) { + return false; + } + } + if (!contour) { + fContourBuilder.setContour(contour = fContoursHead->appendContour()); + } + contour->init(fGlobalState, fOperand, + fXorMask[fOperand] == kEvenOdd_PathOpsMask); + pointsPtr += moveToPtrBump; + moveToPtrBump = 1; + continue; + case SkPath::kLine_Verb: + fContourBuilder.addLine(pointsPtr); + break; + case SkPath::kQuad_Verb: + { + SkVector vec1 = pointsPtr[1] - pointsPtr[0]; + SkVector vec2 = pointsPtr[2] - pointsPtr[1]; + if (vec1.dot(vec2) < 0) { + SkPoint pair[5]; + if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { + goto addOneQuad; + } + if (!SkScalarsAreFinite(&pair[0].fX, std::size(pair) * 2)) { + return false; + } + for (unsigned index = 0; index < std::size(pair); ++index) { + pair[index] = force_small_to_zero(pair[index]); + } + SkPoint cStorage[2][2]; + SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); + SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); + SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; + SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; + if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { + fContourBuilder.addCurve(v1, curve1); + fContourBuilder.addCurve(v2, curve2); + break; + } + } + } + addOneQuad: + fContourBuilder.addQuad(pointsPtr); + break; + case SkPath::kConic_Verb: { + SkVector vec1 = pointsPtr[1] - pointsPtr[0]; + SkVector vec2 = pointsPtr[2] - pointsPtr[1]; + SkScalar weight = *weightPtr++; + if (vec1.dot(vec2) < 0) { + // FIXME: max curvature for conics hasn't been implemented; use placeholder + SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); + if (0 < maxCurvature && maxCurvature < 1) { + SkConic conic(pointsPtr, weight); + SkConic pair[2]; + if (!conic.chopAt(maxCurvature, pair)) { + // if result can't be computed, use original + fContourBuilder.addConic(pointsPtr, weight); + break; + } + SkPoint cStorage[2][3]; + SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); + SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); + SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; + SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; + if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { + fContourBuilder.addCurve(v1, curve1, pair[0].fW); + fContourBuilder.addCurve(v2, curve2, pair[1].fW); + break; + } + } + } + fContourBuilder.addConic(pointsPtr, weight); + } break; + case SkPath::kCubic_Verb: + { + // Split complex cubics (such as self-intersecting curves or + // ones with difficult curvature) in two before proceeding. + // This can be required for intersection to succeed. + SkScalar splitT[3]; + int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT); + if (!breaks) { + fContourBuilder.addCubic(pointsPtr); + break; + } + SkASSERT(breaks <= (int) std::size(splitT)); + struct Splitsville { + double fT[2]; + SkPoint fPts[4]; + SkPoint fReduced[4]; + SkPath::Verb fVerb; + bool fCanAdd; + } splits[4]; + SkASSERT(std::size(splits) == std::size(splitT) + 1); + SkTQSort(splitT, splitT + breaks); + for (int index = 0; index <= breaks; ++index) { + Splitsville* split = &splits[index]; + split->fT[0] = index ? splitT[index - 1] : 0; + split->fT[1] = index < breaks ? splitT[index] : 1; + SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]); + if (!part.toFloatPoints(split->fPts)) { + return false; + } + split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); + SkPoint* curve = SkPath::kCubic_Verb == split->fVerb + ? split->fPts : split->fReduced; + split->fCanAdd = can_add_curve(split->fVerb, curve); + } + for (int index = 0; index <= breaks; ++index) { + Splitsville* split = &splits[index]; + if (!split->fCanAdd) { + continue; + } + int prior = index; + while (prior > 0 && !splits[prior - 1].fCanAdd) { + --prior; + } + if (prior < index) { + split->fT[0] = splits[prior].fT[0]; + split->fPts[0] = splits[prior].fPts[0]; + } + int next = index; + int breakLimit = std::min(breaks, (int) std::size(splits) - 1); + while (next < breakLimit && !splits[next + 1].fCanAdd) { + ++next; + } + if (next > index) { + split->fT[1] = splits[next].fT[1]; + split->fPts[3] = splits[next].fPts[3]; + } + if (prior < index || next > index) { + split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); + } + SkPoint* curve = SkPath::kCubic_Verb == split->fVerb + ? split->fPts : split->fReduced; + if (!can_add_curve(split->fVerb, curve)) { + return false; + } + fContourBuilder.addCurve(split->fVerb, curve); + } + } + break; + case SkPath::kClose_Verb: + SkASSERT(contour); + if (!close()) { + return false; + } + contour = nullptr; + continue; + default: + SkDEBUGFAIL("bad verb"); + return false; + } + SkASSERT(contour); + if (contour->count()) { + contour->debugValidate(); + } + pointsPtr += SkPathOpsVerbToPoints(verb); + } + fContourBuilder.flush(); + if (contour && contour->count() &&!fAllowOpenContours && !close()) { + return false; + } + return true; +} |