int SkIntersections::insert(double one, double two, const SkDPoint& pt) { if (fIsCoincident[0] == 3 && between(fT[0][0], one, fT[0][1])) { // For now, don't allow a mix of coincident and non-coincident intersections return -1; } SkASSERT(fUsed <= 1 || fT[0][0] <= fT[0][1]); int index; for (index = 0; index < fUsed; ++index) { double oldOne = fT[0][index]; double oldTwo = fT[1][index]; if (one == oldOne && two == oldTwo) { return -1; } if (more_roughly_equal(oldOne, one) && more_roughly_equal(oldTwo, two)) { if ((precisely_zero(one) && !precisely_zero(oldOne)) || (precisely_equal(one, 1) && !precisely_equal(oldOne, 1)) || (precisely_zero(two) && !precisely_zero(oldTwo)) || (precisely_equal(two, 1) && !precisely_equal(oldTwo, 1))) { SkASSERT(one >= 0 && one <= 1); SkASSERT(two >= 0 && two <= 1); fT[0][index] = one; fT[1][index] = two; fPt[index] = pt; } return -1; } #if ONE_OFF_DEBUG if (pt.roughlyEqual(fPt[index])) { SkDebugf("%s t=%1.9g pts roughly equal\n", __FUNCTION__, one); } #endif if (fT[0][index] > one) { break; } } if (fUsed >= fMax) { SkASSERT(0); // FIXME : this error, if it is to be handled at runtime in release, must // be propagated all the way back down to the caller, and return failure. fUsed = 0; return 0; } int remaining = fUsed - index; if (remaining > 0) { memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining); memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining); memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining); int clearMask = ~((1 << index) - 1); fIsCoincident[0] += fIsCoincident[0] & clearMask; fIsCoincident[1] += fIsCoincident[1] & clearMask; } fPt[index] = pt; SkASSERT(one >= 0 && one <= 1); SkASSERT(two >= 0 && two <= 1); fT[0][index] = one; fT[1][index] = two; ++fUsed; return index; }
void SkOpSegment::debugValidate() const { #if DEBUG_VALIDATE int count = fTs.count(); SK_ALWAYSBREAK(count >= 2); SK_ALWAYSBREAK(fTs[0].fT == 0); SK_ALWAYSBREAK(fTs[count - 1].fT == 1); int done = 0; double t = -1; const SkOpSpan* last = NULL; bool tinyTFound = false; bool hasLoop = false; for (int i = 0; i < count; ++i) { const SkOpSpan& span = fTs[i]; SK_ALWAYSBREAK(t <= span.fT); t = span.fT; int otherIndex = span.fOtherIndex; const SkOpSegment* other = span.fOther; SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb); const SkOpSpan& otherSpan = other->fTs[otherIndex]; SK_ALWAYSBREAK(otherSpan.fPt == span.fPt); SK_ALWAYSBREAK(otherSpan.fOtherT == t); SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); done += span.fDone; if (last) { SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther); bool tsEqual = last->fT == span.fT; bool tsPreciselyEqual = precisely_equal(last->fT, span.fT); SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual); bool pointsEqual = last->fPt == span.fPt; bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt); #if 0 // bufferOverflow test triggers this SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual); #endif // SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound); SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop); SK_ALWAYSBREAK(!last->fTiny || pointsEqual); SK_ALWAYSBREAK(!last->fTiny || last->fDone); SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual); SK_ALWAYSBREAK(!last->fSmall || last->fDone); // SK_ALWAYSBREAK(!last->fSmall || last->fTiny); // SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone); if (last->fTiny) { tinyTFound |= !tsPreciselyEqual; } else { tinyTFound = false; } } last = &span; hasLoop |= last->fLoop; } SK_ALWAYSBREAK(done == fDoneSpans); // if (fAngles.count() ) { // fAngles.begin()->debugValidateLoop(); // } #endif }
int SkOpContour::alignT(bool swap, int tIndex, SkIntersections* ts) const { double tVal = (*ts)[swap][tIndex]; if (tVal != 0 && precisely_zero(tVal)) { ts->set(swap, tIndex, 0); return 0; } if (tVal != 1 && precisely_equal(tVal, 1)) { ts->set(swap, tIndex, 1); return 1; } return -1; }
void SkOpAngle::setSpans() { double startT = (*fSpans)[fStart].fT; double endT = (*fSpans)[fEnd].fT; switch (fVerb) { case SkPath::kLine_Verb: { SkDLine l = SkDLine::SubDivide(fPts, startT, endT); // OPTIMIZATION: for pure line compares, we never need fTangent1.c fTangent1.lineEndPoints(l); fSide = 0; } break; case SkPath::kQuad_Verb: { SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); quad = SkDQuad::SubDivide(fPts, startT, endT); fTangent1.quadEndPoints(quad, 0, 1); if (dx() == 0 && dy() == 0) { fTangent1.quadEndPoints(quad); } fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only } break; case SkPath::kCubic_Verb: { // int nextC = 2; fCurvePart = SkDCubic::SubDivide(fPts, startT, endT); fTangent1.cubicEndPoints(fCurvePart, 0, 1); if (dx() == 0 && dy() == 0) { fTangent1.cubicEndPoints(fCurvePart, 0, 2); // nextC = 3; if (dx() == 0 && dy() == 0) { fTangent1.cubicEndPoints(fCurvePart, 0, 3); } } // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only // if (nextC == 2 && approximately_zero(fSide)) { // fSide = -fTangent1.pointDistance(fCurvePart[3]); // } double testTs[4]; // OPTIMIZATION: keep inflections precomputed with cubic segment? int testCount = SkDCubic::FindInflections(fPts, testTs); double limitT = endT; int index; for (index = 0; index < testCount; ++index) { if (!between(startT, testTs[index], limitT)) { testTs[index] = -1; } } testTs[testCount++] = startT; testTs[testCount++] = endT; SkTQSort<double>(testTs, &testTs[testCount - 1]); double bestSide = 0; int testCases = (testCount << 1) - 1; index = 0; while (testTs[index] < 0) { ++index; } index <<= 1; for (; index < testCases; ++index) { int testIndex = index >> 1; double testT = testTs[testIndex]; if (index & 1) { testT = (testT + testTs[testIndex + 1]) / 2; } // OPTIMIZE: could avoid call for t == startT, endT SkDPoint pt = dcubic_xy_at_t(fPts, testT); double testSide = fTangent1.pointDistance(pt); if (fabs(bestSide) < fabs(testSide)) { bestSide = testSide; } } fSide = -bestSide; // compare sign only } break; default: SkASSERT(0); } fUnsortable = dx() == 0 && dy() == 0; if (fUnsortable) { return; } SkASSERT(fStart != fEnd); int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro? for (int index = fStart; index != fEnd; index += step) { #if 1 const SkOpSpan& thisSpan = (*fSpans)[index]; const SkOpSpan& nextSpan = (*fSpans)[index + step]; if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { continue; } fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd; #if DEBUG_UNSORTABLE if (fUnsortable) { SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT); SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT); SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); } #endif return; #else if ((*fSpans)[index].fUnsortableStart) { fUnsortable = true; return; } #endif } #if 1 #if DEBUG_UNSORTABLE SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT); SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT); SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); #endif fUnsortable = true; #endif }