bool TightBounds(const SkPath& path, SkRect* result) { SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune SkOpContour contour; SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr)); // turn path into list of segments SkScalar scaleFactor = ScaleFactor(path); SkPath scaledPath; const SkPath* workingPath; if (scaleFactor > SK_Scalar1) { ScalePath(path, 1.f / scaleFactor, &scaledPath); workingPath = &scaledPath; } else { workingPath = &path; } SkOpEdgeBuilder builder(*workingPath, &contour, &globalState); if (!builder.finish()) { return false; } if (!SortContourList(&contourList, false, false)) { result->setEmpty(); return true; } SkOpContour* current = contourList; SkPathOpsBounds bounds = current->bounds(); while ((current = current->next())) { bounds.add(current->bounds()); } *result = bounds; return true; }
bool TightBounds(const SkPath& path, SkRect* result) { SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune SkOpContour contour; SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); SkOpGlobalState globalState(nullptr, contourList SkDEBUGPARAMS(nullptr)); // turn path into list of segments SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState); if (!builder.finish(&allocator)) { return false; } if (!SortContourList(&contourList, false, false)) { result->setEmpty(); return true; } SkOpContour* current = contourList; SkPathOpsBounds bounds = current->bounds(); while ((current = current->next())) { bounds.add(current->bounds()); } *result = bounds; return true; }
static SkOpSegment* findTopSegment(const SkTArray<SkOpContour*, true>& contourList, int* index, int* endIndex, SkPoint* topLeft, bool* unsortable, bool* done, bool firstPass) { SkOpSegment* result; const SkOpSegment* lastTopStart = NULL; int lastIndex = -1, lastEndIndex = -1; do { SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax}; int contourCount = contourList.count(); SkOpSegment* topStart = NULL; *done = true; for (int cIndex = 0; cIndex < contourCount; ++cIndex) { SkOpContour* contour = contourList[cIndex]; if (contour->done()) { continue; } const SkPathOpsBounds& bounds = contour->bounds(); if (bounds.fBottom < topLeft->fY) { *done = false; continue; } if (bounds.fBottom == topLeft->fY && bounds.fRight < topLeft->fX) { *done = false; continue; } contour->topSortableSegment(*topLeft, &bestXY, &topStart); if (!contour->done()) { *done = false; } } if (!topStart) { return NULL; } *topLeft = bestXY; result = topStart->findTop(index, endIndex, unsortable, firstPass); if (!result) { if (lastTopStart == topStart && lastIndex == *index && lastEndIndex == *endIndex) { *done = true; return NULL; } lastTopStart = topStart; lastIndex = *index; lastEndIndex = *endIndex; } } while (!result); return result; }
static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* bestHit, SkScalar* bestDx, bool* tryAgain, double* midPtr, bool opp) { const int index = *indexPtr; const int endIndex = *endIndexPtr; const double mid = *midPtr; const SkOpSegment* current = *currentPtr; double tAtMid = current->tAtMid(index, endIndex, mid); SkPoint basePt = current->ptAtT(tAtMid); int contourCount = contourList.count(); SkScalar bestY = SK_ScalarMin; SkOpSegment* bestSeg = NULL; int bestTIndex = 0; bool bestOpp; bool hitSomething = false; for (int cTest = 0; cTest < contourCount; ++cTest) { SkOpContour* contour = contourList[cTest]; bool testOpp = contour->operand() ^ current->operand() ^ opp; if (basePt.fY < contour->bounds().fTop) { continue; } if (bestY > contour->bounds().fBottom) { continue; } int segmentCount = contour->segments().count(); for (int test = 0; test < segmentCount; ++test) { SkOpSegment* testSeg = &contour->segments()[test]; SkScalar testY = bestY; double testHit; int testTIndex = testSeg->crossedSpanY(basePt, &testY, &testHit, &hitSomething, tAtMid, testOpp, testSeg == current); if (testTIndex < 0) { if (testTIndex == SK_MinS32) { hitSomething = true; bestSeg = NULL; goto abortContours; // vertical encountered, return and try different point } continue; } if (testSeg == current && current->betweenTs(index, testHit, endIndex)) { double baseT = current->t(index); double endT = current->t(endIndex); double newMid = (testHit - baseT) / (endT - baseT); #if DEBUG_WINDING double midT = current->tAtMid(index, endIndex, mid); SkPoint midXY = current->xyAtT(midT); double newMidT = current->tAtMid(index, endIndex, newMid); SkPoint newXY = current->xyAtT(newMidT); SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)" " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, current->debugID(), mid, newMid, baseT, current->xAtT(index), current->yAtT(index), baseT + mid * (endT - baseT), midXY.fX, midXY.fY, baseT + newMid * (endT - baseT), newXY.fX, newXY.fY, endT, current->xAtT(endIndex), current->yAtT(endIndex)); #endif *midPtr = newMid * 2; // calling loop with divide by 2 before continuing return SK_MinS32; } bestSeg = testSeg; *bestHit = testHit; bestOpp = testOpp; bestTIndex = testTIndex; bestY = testY; } } abortContours: int result; if (!bestSeg) { result = hitSomething ? SK_MinS32 : 0; } else { if (bestSeg->windSum(bestTIndex) == SK_MinS32) { *currentPtr = bestSeg; *indexPtr = bestTIndex; *endIndexPtr = bestSeg->nextSpan(bestTIndex, 1); SkASSERT(*indexPtr != *endIndexPtr && *indexPtr >= 0 && *endIndexPtr >= 0); *tryAgain = true; return 0; } result = bestSeg->windingAtT(*bestHit, bestTIndex, bestOpp, bestDx); SkASSERT(result == SK_MinS32 || *bestDx); } double baseT = current->t(index); double endT = current->t(endIndex); *bestHit = baseT + mid * (endT - baseT); return result; }