Bug 1265342 Part 6b: Implement shape-margin for shape-outside: inset, for general case of shape-margin > 0. r=dholbert

MozReview-Commit-ID: 4pjALPSIBhI

--HG--
extra : rebase_source : 017c887e4f8d6f2ca0c2293fcb910b0daec1e06d
This commit is contained in:
Brad Werth 2018-04-17 11:39:50 -07:00
parent df2128a244
commit 24f86cb132

View File

@ -1035,6 +1035,23 @@ public:
void Translate(nscoord aLineLeft, nscoord aBlockStart) override
{
mRect.MoveBy(aLineLeft, aBlockStart);
if (mShapeMargin > 0) {
MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
"If we have positive shape-margin, we should have corners.");
mLogicalTopLeftCorner->Translate(aLineLeft, aBlockStart);
mLogicalTopRightCorner->Translate(aLineLeft, aBlockStart);
mLogicalBottomLeftCorner->Translate(aLineLeft, aBlockStart);
mLogicalBottomRightCorner->Translate(aLineLeft, aBlockStart);
}
}
static bool EachCornerHasBalancedRadii(const nscoord* aRadii) {
return (aRadii[eCornerTopLeftX] == aRadii[eCornerTopLeftY] &&
aRadii[eCornerTopRightX] == aRadii[eCornerTopRightY] &&
aRadii[eCornerBottomLeftX] == aRadii[eCornerBottomLeftY] &&
aRadii[eCornerBottomRightX] == aRadii[eCornerBottomRightY]);
}
private:
@ -1046,8 +1063,21 @@ private:
// nullptr.
const UniquePtr<nscoord[]> mRadii;
// A shape-margin value extends the boundaries of the float area.
// A shape-margin value extends the boundaries of the float area. When our
// first constructor is used, it is for the creation of rounded boxes that
// can ignore shape-margin -- either because it was specified as zero or
// because the box shape and radii can be inflated to account for it. When
// our second constructor is used, we store the shape-margin value here.
const nscoord mShapeMargin;
// If our second constructor is called (which implies mShapeMargin > 0),
// we will construct EllipseShapeInfo objects for each corner. We use the
// float logical naming here, where LogicalTopLeftCorner means the BStart
// LineLeft corner, and similarly for the other corners.
UniquePtr<EllipseShapeInfo> mLogicalTopLeftCorner;
UniquePtr<EllipseShapeInfo> mLogicalTopRightCorner;
UniquePtr<EllipseShapeInfo> mLogicalBottomLeftCorner;
UniquePtr<EllipseShapeInfo> mLogicalBottomRightCorner;
};
nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
@ -1058,41 +1088,113 @@ nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
, mRadii(Move(aRadii))
, mShapeMargin(aShapeMargin)
{
MOZ_ASSERT(mShapeMargin > 0 && !EachCornerHasBalancedRadii(mRadii.get()),
"Slow constructor should only be used for for shape-margin > 0 "
"and radii with elliptical corners.");
// Before we inflate mRect by mShapeMargin, construct each of our corners.
// If we do it in this order, it's a bit simpler to calculate the center
// of each of the corners.
mLogicalTopLeftCorner = MakeUnique<EllipseShapeInfo>(
nsPoint(mRect.X() + mRadii[eCornerTopLeftX],
mRect.Y() + mRadii[eCornerTopLeftY]),
nsSize(mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY]),
mShapeMargin, aAppUnitsPerDevPixel);
mLogicalTopRightCorner = MakeUnique<EllipseShapeInfo>(
nsPoint(mRect.XMost() - mRadii[eCornerTopRightX],
mRect.Y() + mRadii[eCornerTopRightY]),
nsSize(mRadii[eCornerTopRightX], mRadii[eCornerTopRightY]),
mShapeMargin, aAppUnitsPerDevPixel);
mLogicalBottomLeftCorner = MakeUnique<EllipseShapeInfo>(
nsPoint(mRect.X() + mRadii[eCornerBottomLeftX],
mRect.YMost() - mRadii[eCornerBottomLeftY]),
nsSize(mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY]),
mShapeMargin, aAppUnitsPerDevPixel);
mLogicalBottomRightCorner = MakeUnique<EllipseShapeInfo>(
nsPoint(mRect.XMost() - mRadii[eCornerBottomRightX],
mRect.YMost() - mRadii[eCornerBottomRightY]),
nsSize(mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY]),
mShapeMargin, aAppUnitsPerDevPixel);
// Now we inflate our mRect by mShapeMargin.
mRect.Inflate(mShapeMargin);
}
nscoord
nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
const nscoord aBEnd) const
{
if (!mRadii) {
return mRect.x;
if (mShapeMargin == 0) {
if (!mRadii) {
return mRect.x;
}
nscoord lineLeftDiff =
ComputeEllipseLineInterceptDiff(
mRect.y, mRect.YMost(),
mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
aBStart, aBEnd);
return mRect.x + lineLeftDiff;
}
nscoord lineLeftDiff =
ComputeEllipseLineInterceptDiff(
mRect.y, mRect.YMost(),
mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
aBStart, aBEnd);
return mRect.x + lineLeftDiff;
MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalBottomLeftCorner,
"If we have positive shape-margin, we should have corners.");
// Determine if aBEnd is within our top corner.
if (aBEnd < mLogicalTopLeftCorner->BEnd()) {
return mLogicalTopLeftCorner->LineLeft(aBStart, aBEnd);
}
// Determine if aBStart is within our bottom corner.
if (aBStart >= mLogicalBottomLeftCorner->BStart()) {
return mLogicalBottomLeftCorner->LineLeft(aBStart, aBEnd);
}
// Either aBStart or aBEnd or both are within the flat part of our left
// edge. Because we've already inflated our mRect to encompass our
// mShapeMargin, we can just return the edge.
return mRect.X();
}
nscoord
nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
const nscoord aBEnd) const
{
if (!mRadii) {
return mRect.XMost();
if (mShapeMargin == 0) {
if (!mRadii) {
return mRect.XMost();
}
nscoord lineRightDiff =
ComputeEllipseLineInterceptDiff(
mRect.y, mRect.YMost(),
mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
aBStart, aBEnd);
return mRect.XMost() - lineRightDiff;
}
nscoord lineRightDiff =
ComputeEllipseLineInterceptDiff(
mRect.y, mRect.YMost(),
mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
aBStart, aBEnd);
return mRect.XMost() - lineRightDiff;
MOZ_ASSERT(mLogicalTopRightCorner && mLogicalBottomRightCorner,
"If we have positive shape-margin, we should have corners.");
// Determine if aBEnd is within our top corner.
if (aBEnd < mLogicalTopRightCorner->BEnd()) {
return mLogicalTopRightCorner->LineRight(aBStart, aBEnd);
}
// Determine if aBStart is within our bottom corner.
if (aBStart >= mLogicalBottomRightCorner->BStart()) {
return mLogicalBottomRightCorner->LineRight(aBStart, aBEnd);
}
// Either aBStart or aBEnd or both are within the flat part of our right
// edge. Because we've already inflated our mRect to encompass our
// mShapeMargin, we can just return the edge.
return mRect.XMost();
}
/////////////////////////////////////////////////////////////////////////////
@ -2141,12 +2243,9 @@ nsFloatManager::ShapeInfo::CreateInset(
Move(logicalRadii));
}
// If we have radii, and each pair is equal, we can inflate both
// logicalInsetRect and all the radii and use the fast constructor.
if (physicalRadii[0] == physicalRadii[1] &&
physicalRadii[2] == physicalRadii[3] &&
physicalRadii[4] == physicalRadii[5] &&
physicalRadii[6] == physicalRadii[7]) {
// If we have radii, and they have balanced/equal corners, we can inflate
// both logicalInsetRect and all the radii and use the fast constructor.
if (RoundedBoxShapeInfo::EachCornerHasBalancedRadii(physicalRadii)) {
logicalInsetRect.Inflate(aShapeMargin);
for (nscoord& r : physicalRadii) {
r += aShapeMargin;
@ -2156,7 +2255,7 @@ nsFloatManager::ShapeInfo::CreateInset(
aWM));
}
// With positive shape-margin and unequal radii pairs, we have to use the
// With positive shape-margin and elliptical radii, we have to use the
// slow constructor.
nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();