--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -554,28 +554,30 @@ public:
static UniquePtr<ShapeInfo> CreateShapeBox(
nsIFrame* const aFrame,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize);
static UniquePtr<ShapeInfo> CreateBasicShape(
const UniquePtr<StyleBasicShape>& aBasicShape,
+ nscoord aShapeMargin,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize);
static UniquePtr<ShapeInfo> CreateInset(
const UniquePtr<StyleBasicShape>& aBasicShape,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize);
static UniquePtr<ShapeInfo> CreateCircleOrEllipse(
const UniquePtr<StyleBasicShape>& aBasicShape,
+ nscoord aShapeMargin,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize);
static UniquePtr<ShapeInfo> CreatePolygon(
const UniquePtr<StyleBasicShape>& aBasicShape,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
@@ -1059,22 +1061,23 @@ nsFloatManager::ImageShapeInfo::ImageSha
// min and max store the start and end of the float area for the row
// or column represented by this iteration of the outer loop.
int32_t min = -1;
int32_t max = -1;
for (int32_t inner = 0; inner < innerLimit; ++inner) {
const int32_t col = aWM.IsVertical() ? outer : inner;
const int32_t row = aWM.IsVertical() ? inner : outer;
+ const int32_t index = col + row * aStride;
// Determine if the alpha pixel at this row and column has a value
// greater than the threshold. If it does, update our min and max values
// to track the edges of the float area for this row or column.
// https://drafts.csswg.org/css-shapes-1/#valdef-shape-image-threshold-number
- const uint8_t alpha = aAlphaPixels[col + row * aStride];
+ const uint8_t alpha = aAlphaPixels[index];
if (alpha > threshold) {
if (min == -1) {
min = inner;
}
MOZ_ASSERT(max < inner);
max = inner;
}
}
@@ -1138,170 +1141,154 @@ nsFloatManager::ImageShapeInfo::ImageSha
const CSSIntPoint dfOffset =
CSSPixel::FromAppUnitsRounded(aContentRect.TopLeft());
const CSSIntRect margin = CSSPixel::FromAppUnitsRounded(aMarginRect);
const int32_t wEx = margin.width + 4;
const int32_t hEx = margin.width + 4;
const int32_t dfSize = wEx * hEx;
auto df = MakeUnique<dfType[]>(dfSize);
+ const int32_t outerLimit = aWM.IsVertical() ? wEx : hEx;
+ const int32_t innerLimit = aWM.IsVertical() ? hEx : wEx;
+
// First pass setting difference field, top-to-bottom, three cases:
// 1) Expanded region pixel: set to MAX_MARGIN_5X.
// 2) Image pixel with alpha greater than threshold: set to 0.
// 3) Other pixel: set to minimum backward-looking neighborhood
// distance value, computed with 5-7-11 chamfer.
- for (int32_t index = 0; index < dfSize; ++index) {
- const int32_t col = aWM.IsVertical() ? index / hEx : index % wEx;
- const int32_t row = aWM.IsVertical() ? index % hEx : index / wEx;
- // Handle our three cases, in order.
- if (col < 2 ||
- col >= wEx - 2 ||
- row < 2 ||
- row >= hEx - 2) {
- // Case 1: Expanded pixel.
- df[index] = MAX_MARGIN_5X;
- } else if (col >= (dfOffset.x + 2) &&
- col < (dfOffset.x + 2 + w) &&
- row >= (dfOffset.y + 2) &&
- row < (dfOffset.y + 2 + h) &&
- aAlphaPixels[col - (dfOffset.x + 2) +
- (row - (dfOffset.y + 2)) * aStride] > threshold) {
- // Case 2: Image pixel that is opaque.
- df[index] = 0;
- } else {
- // Case 3: Other pixel.
+ // Scan the pixels in a double loop. For horizontal writing modes, we do
+ // this row by row, from top to bottom. For vertical writing modes, we do
+ // column by column, from left to right. We define the two loops
+ // generically, then figure out the rows and cols within the inner loop.
+ for (int32_t outer = 0; outer < outerLimit; ++outer) {
+ for (int32_t inner = 0; inner < innerLimit; ++inner) {
+ const int32_t col = aWM.IsVertical() ? outer : inner;
+ const int32_t row = aWM.IsVertical() ? inner : outer;
+ const int32_t index = col + row * wEx;
- // Backward-looking neighborhood distance from target pixel X
- // with chamfer 5-7-11 looks like:
- //
- // +--+--+--+--+--+
- // | |11| |11| |
- // +--+--+--+--+--+
- // |11| 7| 5| 7|11|
- // +--+--+--+--+--+
- // | | 5| X| | |
- // +--+--+--+--+--+
- //
- // X should be set to the minimum of MAX_MARGIN_5X and the
- // values of all of the numbered neighbors summed with the
- // value in that chamfer cell.
- df[index] = std::min<dfType>(MAX_MARGIN_5X,
- std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
- std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
- std::min<dfType>(df[index - wEx - 2] + 11,
- std::min<dfType>(df[index - wEx - 1] + 7,
- std::min<dfType>(df[index - wEx] + 5,
- std::min<dfType>(df[index - wEx + 1] + 7,
- std::min<dfType>(df[index - wEx + 2] + 11,
- df[index - 1] + 5))))))));
+ // Handle our three cases, in order.
+ if (col < 2 ||
+ col >= wEx - 2 ||
+ row < 2 ||
+ row >= hEx - 2) {
+ // Case 1: Expanded pixel.
+ df[index] = MAX_MARGIN_5X;
+ } else if (col >= (dfOffset.x + 2) &&
+ col < (dfOffset.x + 2 + w) &&
+ row >= (dfOffset.y + 2) &&
+ row < (dfOffset.y + 2 + h) &&
+ aAlphaPixels[col - (dfOffset.x + 2) +
+ (row - (dfOffset.y + 2)) * aStride] > threshold) {
+ // Case 2: Image pixel that is opaque.
+ df[index] = 0;
+ } else {
+ // Case 3: Other pixel.
+
+ // Backward-looking neighborhood distance from target pixel X
+ // with chamfer 5-7-11 looks like:
+ //
+ // +--+--+--+--+--+
+ // | |11| |11| |
+ // +--+--+--+--+--+
+ // |11| 7| 5| 7|11|
+ // +--+--+--+--+--+
+ // | | 5| X| | |
+ // +--+--+--+--+--+
+ //
+ // X should be set to the minimum of MAX_MARGIN_5X and the
+ // values of all of the numbered neighbors summed with the
+ // value in that chamfer cell.
+ df[index] = std::min<dfType>(MAX_MARGIN_5X,
+ std::min<dfType>(df[index - (wEx * 2) - 1] + 11,
+ std::min<dfType>(df[index - (wEx * 2) + 1] + 11,
+ std::min<dfType>(df[index - wEx - 2] + 11,
+ std::min<dfType>(df[index - wEx - 1] + 7,
+ std::min<dfType>(df[index - wEx] + 5,
+ std::min<dfType>(df[index - wEx + 1] + 7,
+ std::min<dfType>(df[index - wEx + 2] + 11,
+ df[index - 1] + 5))))))));
+ }
}
}
// Okay, time for the second pass. This pass is bottom-to-top.
// All of our opaque pixels have been set to 0, and all of our
// expanded pixels have been set to MAX_MARGIN_5X. Other pixels
// have been set to some value between those two (inclusive) but
// this hasn't yet taken into account the neighbors below them.
// This time we reverse iterate so we can apply the backward-
// looking chamfer.
- // This time, only two cases:
- // 1) Expanded region pixel: skip.
- // 2) Other pixel: set to minimum backward-looking neighborhood
- // distance value, computed with 5-7-11 chamfer,
- // then check against the usedMargin5X threshold.
+ // This time, we constrain our outer and inner loop to ignore
+ // the expanded region pixels. For each pixel we iterate, we
+ // set the df value to the minimum backward-looking neighborhood
+ // distance value, computed with 5-7-11 chamfer. We also check
+ // each df value against the usedMargin5X threshold, and use that
+ // to set the min and max values for that block axis value.
// At the end of each row (or column in vertical writing modes),
// if any of the other pixels had a value less than usedMargin5X,
// we create an interval.
-
- // Set min and max in preparation for the first row.
- min = dfSize;
- max = -1;
-
- for (int32_t index = dfSize - 1; index >= 0; --index) {
- const int32_t col = aWM.IsVertical() ? index / hEx : index % wEx;
- const int32_t row = aWM.IsVertical() ? index % hEx : index / wEx;
- const int32_t curr = aWM.IsVertical() ? row : col;
+ for (int32_t outer = outerLimit - 3; outer >= 2; --outer) {
+ // Set min and max in preparation for this row or column.
+ int32_t min = outerLimit;
+ int32_t max = -1;
- // Check our two cases, in order.
- // For expanded pixels, we can skip whole rows and columns of
- // iteration, depending on the writing mode.
- if (col < 2 || col >= wEx - 2) {
- if (aWM.IsVertical()) {
- index -= (hEx - 1);
- }
- continue;
- }
- if (row < 2 || row >= hEx - 2) {
- if (!aWM.IsVertical()) {
- index -= (wEx - 1);
- }
- continue;
- }
-
- // If we've gotten this far, we're in Case 2: Other pixel.
+ for (int32_t inner = innerLimit - 3; inner >= 2; --inner) {
+ const int32_t col = aWM.IsVertical() ? outer : inner;
+ const int32_t row = aWM.IsVertical() ? inner : outer;
+ const int32_t index = col + row * wEx;
- // Only apply the chamfer calculation if the df value is not
- // already 0, since the chamfer can only reduce the value.
- if (df[index]) {
- // Forward-looking neighborhood distance from target pixel X
- // with chamfer 5-7-11 looks like:
- //
- // +--+--+--+--+--+
- // | | | X| 5| |
- // +--+--+--+--+--+
- // |11| 7| 5| 7|11|
- // +--+--+--+--+--+
- // | |11| |11| |
- // +--+--+--+--+--+
- //
- // X should be set to the minimum of its current value and
- // the values of all of the numbered neighbors summed with
- // the value in that chamfer cell.
- df[index] = std::min<dfType>(df[index],
- std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
- std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
- std::min<dfType>(df[index + wEx + 2] + 11,
- std::min<dfType>(df[index + wEx + 1] + 7,
- std::min<dfType>(df[index + wEx] + 5,
- std::min<dfType>(df[index + wEx - 1] + 7,
- std::min<dfType>(df[index + wEx - 2] + 11,
- df[index + 1] + 5))))))));
- }
+ // Only apply the chamfer calculation if the df value is not
+ // already 0, since the chamfer can only reduce the value.
+ if (df[index]) {
+ // Forward-looking neighborhood distance from target pixel X
+ // with chamfer 5-7-11 looks like:
+ //
+ // +--+--+--+--+--+
+ // | | | X| 5| |
+ // +--+--+--+--+--+
+ // |11| 7| 5| 7|11|
+ // +--+--+--+--+--+
+ // | |11| |11| |
+ // +--+--+--+--+--+
+ //
+ // X should be set to the minimum of its current value and
+ // the values of all of the numbered neighbors summed with
+ // the value in that chamfer cell.
+ df[index] = std::min<dfType>(df[index],
+ std::min<dfType>(df[index + (wEx * 2) + 1] + 11,
+ std::min<dfType>(df[index + (wEx * 2) - 1] + 11,
+ std::min<dfType>(df[index + wEx + 2] + 11,
+ std::min<dfType>(df[index + wEx + 1] + 7,
+ std::min<dfType>(df[index + wEx] + 5,
+ std::min<dfType>(df[index + wEx - 1] + 7,
+ std::min<dfType>(df[index + wEx - 2] + 11,
+ df[index + 1] + 5))))))));
+ }
- // Finally, we can check the df value and see if its less than
- // or equal to the usedMargin5X value.
- if (df[index] <= usedMargin5X) {
- if (max == -1) {
- max = curr;
- }
- if (min > curr) {
- min = curr;
+ // Finally, we can check the df value and see if its less than
+ // or equal to the usedMargin5X value.
+ if (df[index] <= usedMargin5X) {
+ if (max == -1) {
+ max = inner;
+ }
+ MOZ_ASSERT(min > inner);
+ min = inner;
}
}
- // At the end of a row (or column if vertical).
- if (curr == 2) {
- // If we found something, create an interval.
- if (max != -1) {
- // Supply a zero offset for this interval, because our
- // col and row are calculated from the margin rect.
- // We likewise need to subtract 2 from both col and row
- // because those indices are relative to the expanded
- // pixel area.
- CreateInterval(min, max, col - 2, row - 2, nsPoint(),
- aWM, aContainerSize);
- }
-
- // Reset min and max for the next row or column.
- min = dfSize;
- max = -1;
- }
+ // Supply a zero offset for this interval, because our
+ // col and row are calculated from the margin rect.
+ // We likewise need to subtract 2 from min, max, and outer
+ // because those indices are relative to the expanded
+ // pixel area.
+ CreateInterval(min - 2, max - 2, outer - 2, nsPoint(),
+ aWM, aContainerSize);
}
if (!aWM.IsVerticalRL()) {
// Because we assembled our intervals on the bottom-up pass,
// they are reversed for most writing modes. Reverse them to
// keep the array sorted on the block direction.
mIntervals.Reverse();
}
@@ -1517,20 +1504,24 @@ nsFloatManager::FloatInfo::FloatInfo(nsI
ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
mShapeInfo = ShapeInfo::CreateShapeBox(mFrame, shapeBoxRect, aWM,
aContainerSize);
break;
}
case StyleShapeSourceType::Shape: {
const UniquePtr<StyleBasicShape>& basicShape = shapeOutside.GetBasicShape();
+ nscoord shapeMargin =
+ ::ResolveToDefiniteSize(mFrame->StyleDisplay()->mShapeMargin,
+ aContainerSize.width);
// Initialize <shape-box>'s reference rect.
LogicalRect shapeBoxRect =
ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
- mShapeInfo = ShapeInfo::CreateBasicShape(basicShape, shapeBoxRect, aWM,
+ mShapeInfo = ShapeInfo::CreateBasicShape(basicShape, shapeMargin,
+ shapeBoxRect, aWM,
aContainerSize);
break;
}
}
MOZ_ASSERT(mShapeInfo,
"All shape-outside values except none should have mShapeInfo!");
@@ -1691,26 +1682,28 @@ nsFloatManager::ShapeInfo::CreateShapeBo
return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
ConvertToFloatLogical(physicalRadii,
aWM));
}
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
nsFloatManager::ShapeInfo::CreateBasicShape(
const UniquePtr<StyleBasicShape>& aBasicShape,
+ nscoord aShapeMargin,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize)
{
switch (aBasicShape->GetShapeType()) {
case StyleBasicShapeType::Polygon:
return CreatePolygon(aBasicShape, aShapeBoxRect, aWM, aContainerSize);
case StyleBasicShapeType::Circle:
case StyleBasicShapeType::Ellipse:
- return CreateCircleOrEllipse(aBasicShape, aShapeBoxRect, aWM,
+ return CreateCircleOrEllipse(aBasicShape, aShapeMargin,
+ aShapeBoxRect, aWM,
aContainerSize);
case StyleBasicShapeType::Inset:
return CreateInset(aBasicShape, aShapeBoxRect, aWM, aContainerSize);
}
return nullptr;
}
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
@@ -1743,16 +1736,17 @@ nsFloatManager::ShapeInfo::CreateInset(
return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
ConvertToFloatLogical(physicalRadii,
aWM));
}
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
const UniquePtr<StyleBasicShape>& aBasicShape,
+ nscoord aShapeMargin,
const LogicalRect& aShapeBoxRect,
WritingMode aWM,
const nsSize& aContainerSize)
{
// Use physical coordinates to compute the center of circle() or ellipse()
// since the <position> keywords such as 'left', 'top', etc. are physical.
// https://drafts.csswg.org/css-shapes-1/#funcdef-ellipse
nsRect physicalShapeBoxRect =
@@ -1763,24 +1757,25 @@ nsFloatManager::ShapeInfo::CreateCircleO
ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
// Compute the circle or ellipse radii.
nsSize radii;
StyleBasicShapeType type = aBasicShape->GetShapeType();
if (type == StyleBasicShapeType::Circle) {
nscoord radius = ShapeUtils::ComputeCircleRadius(aBasicShape, physicalCenter,
physicalShapeBoxRect);
- radii = nsSize(radius, radius);
+ radii = nsSize(radius + aShapeMargin, radius + aShapeMargin);
} else {
MOZ_ASSERT(type == StyleBasicShapeType::Ellipse);
nsSize physicalRadii =
ShapeUtils::ComputeEllipseRadii(aBasicShape, physicalCenter,
physicalShapeBoxRect);
LogicalSize logicalRadii(aWM, physicalRadii);
- radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
+ radii = nsSize(logicalRadii.ISize(aWM) + aShapeMargin,
+ logicalRadii.BSize(aWM) + aShapeMargin);
}
return MakeUnique<EllipseShapeInfo>(logicalCenter, radii);
}
/* static */ UniquePtr<nsFloatManager::ShapeInfo>
nsFloatManager::ShapeInfo::CreatePolygon(
const UniquePtr<StyleBasicShape>& aBasicShape,