Bug 1459697 Part 2: Account for the possibility that EllipseShapeInfo may not generate an interval for the entire BStart() to BEnd() range, due to rounding error in the distance field calculation.
MozReview-Commit-ID: CYeBKhDYD1F
The distance field does not calculate a true Euclidean distance, so it is
unreasonable to require that the intervals span all of the BStart() to BEnd()
float area. The final block pixel may not generate an interval at all due to
rounding errors. This change makes accomodation for the rounding errors and
adds asserts to ensure we aren't tolerating errors outside the area of the
last block pixel.
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -964,17 +964,17 @@ nsFloatManager::EllipseShapeInfo::LineEd
aBStart, aBEnd);
return mCenter.x + (aIsLineLeft ? (-mRadii.width + lineDiff) :
(mRadii.width - lineDiff));
}
// We are checking against our intervals. Make sure we have some.
if (mIntervals.IsEmpty()) {
NS_WARNING("With mShapeMargin > 0, we can't proceed without intervals.");
- return 0;
+ return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
}
// Map aBStart and aBEnd into our intervals. Our intervals are calculated
// for the lower-right quadrant (in terms of horizontal-tb writing mode).
// If aBStart and aBEnd span the center of the ellipse, then we know we
// are at the maximum displacement from the center.
bool bStartIsAboveCenter = (aBStart < mCenter.y);
bool bEndIsBelowOrAtCenter = (aBEnd >= mCenter.y);
@@ -997,22 +997,36 @@ nsFloatManager::EllipseShapeInfo::LineEd
// intervals (though it has no height), but its reflection would not be
// within the intervals unless we subtract 1.
nscoord bSmallestWithinIntervals = std::min(
bStartIsAboveCenter ? aBStart + (mCenter.y - aBStart) * 2 - 1 : aBStart,
bEndIsBelowOrAtCenter ? aBEnd : aBEnd + (mCenter.y - aBEnd) * 2 - 1);
MOZ_ASSERT(bSmallestWithinIntervals >= mCenter.y &&
bSmallestWithinIntervals < BEnd(),
- "We should have a block value within the intervals.");
+ "We should have a block value within the float area.");
size_t index = MinIntervalIndexContainingY(mIntervals,
bSmallestWithinIntervals);
- MOZ_ASSERT(index < mIntervals.Length(),
- "We should have found a matching interval for this block value.");
+ if (index >= mIntervals.Length()) {
+ // This indicates that our intervals don't cover the block value
+ // bSmallestWithinIntervals. This can happen when rounding error in the
+ // distance field calculation resulted in the last block pixel row not
+ // contributing to the float area. As long as we're within one block pixel
+ // past the last interval, this is an expected outcome.
+#ifdef DEBUG
+ nscoord onePixelPastLastInterval =
+ mIntervals[mIntervals.Length() - 1].YMost() +
+ mIntervals[mIntervals.Length() - 1].Height();
+ NS_WARNING_ASSERTION(bSmallestWithinIntervals < onePixelPastLastInterval,
+ "We should have found a matching interval for this "
+ "block value.");
+#endif
+ return aIsLineLeft ? nscoord_MAX : nscoord_MIN;
+ }
// The interval is storing the line right value. If aIsLineLeft is true,
// return the line right value reflected about the center. Since this is
// an inline measurement, it's just checking the distance to an edge, and
// not a collision with a specific pixel. For that reason, we don't need
// to subtract 1 from the reflection, as we did with the block reflection.
nscoord iLineRight = mIntervals[index].XMost();
return aIsLineLeft ? iLineRight - (iLineRight - mCenter.x) * 2