Bug 1311244 Part 6 - Add ShapeInfo and move <shape-box> impl to BoxShapeInfo. r=dbaron

Create ShapeInfo as a base class for implementing all the shapes. In this
design, we only need to create the correct subclass in FloatInfo's
constructor whenever shape-outside is used rather than manually branching on
StyleShapeSourceType and StyleBasicShape in all the methods like
LineRight(), LineLeft(), etc.

The concrete subclass of ShapeInfo could focus on implementing how its shape
influence the flow area by overriding the needed methods in ShapeInfo.

Move ComputeEllipseLineInterceptDiff() and XInterceptAtY() under the scope
of ShapeInfo so that they could be used by BoxShapeInfo and all the other
ShpapeInfo subclasses yet to come.

MozReview-Commit-ID: ETVc5FdGNha

--HG--
extra : rebase_source : 9a083ccd95fd7565112a45e30c15a91b97c7290f
This commit is contained in:
Ting-Yu Lin 2017-01-06 16:36:30 +08:00
Родитель ea61e604fa
Коммит 7696a7581c
2 изменённых файлов: 215 добавлений и 142 удалений

Просмотреть файл

@ -527,6 +527,91 @@ nsFloatManager::ClearContinues(StyleClear aBreakType) const
aBreakType == StyleClear::Right));
}
/////////////////////////////////////////////////////////////////////////////
// BoxShapeInfo
nscoord
nsFloatManager::BoxShapeInfo::LineLeft(WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const
{
nscoord radii[8];
bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
if (!hasRadii) {
return mShapeBoxRect.x;
}
// Get the physical side for line-left since border-radii are in
// the physical axis.
mozilla::Side lineLeftSide =
aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
nscoord blockStartCornerRadiusL =
radii[SideToHalfCorner(lineLeftSide, true, false)];
nscoord blockStartCornerRadiusB =
radii[SideToHalfCorner(lineLeftSide, true, true)];
nscoord blockEndCornerRadiusL =
radii[SideToHalfCorner(lineLeftSide, false, false)];
nscoord blockEndCornerRadiusB =
radii[SideToHalfCorner(lineLeftSide, false, true)];
if (aWM.IsLineInverted()) {
// This happens only when aWM is vertical-lr. Need to swap blockStart
// and blockEnd corners.
std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL);
std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB);
}
nscoord lineLeftDiff =
ComputeEllipseLineInterceptDiff(
mShapeBoxRect.y, mShapeBoxRect.YMost(),
blockStartCornerRadiusL, blockStartCornerRadiusB,
blockEndCornerRadiusL, blockEndCornerRadiusB,
aBStart, aBEnd);
return mShapeBoxRect.x + lineLeftDiff;
}
nscoord
nsFloatManager::BoxShapeInfo::LineRight(WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const
{
nscoord radii[8];
bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
if (!hasRadii) {
return mShapeBoxRect.XMost();
}
// Get the physical side for line-right since border-radii are in
// the physical axis.
mozilla::Side lineRightSide =
aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
nscoord blockStartCornerRadiusL =
radii[SideToHalfCorner(lineRightSide, false, false)];
nscoord blockStartCornerRadiusB =
radii[SideToHalfCorner(lineRightSide, false, true)];
nscoord blockEndCornerRadiusL =
radii[SideToHalfCorner(lineRightSide, true, false)];
nscoord blockEndCornerRadiusB =
radii[SideToHalfCorner(lineRightSide, true, true)];
if (aWM.IsLineInverted()) {
// This happens only when aWM is vertical-lr. Need to swap blockStart
// and blockEnd corners.
std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL);
std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB);
}
nscoord lineRightDiff =
ComputeEllipseLineInterceptDiff(
mShapeBoxRect.y, mShapeBoxRect.YMost(),
blockStartCornerRadiusL, blockStartCornerRadiusB,
blockEndCornerRadiusL, blockEndCornerRadiusB,
aBStart, aBEnd);
return mShapeBoxRect.XMost() - lineRightDiff;
}
/////////////////////////////////////////////////////////////////////////////
// FloatInfo
@ -545,31 +630,36 @@ nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
if (shapeOutside.GetType() == StyleShapeSourceType::None) {
return;
}
// Initialize <shape-box>'s reference rect.
LogicalRect rect = aMarginRect;
switch (shapeOutside.GetReferenceBox()) {
case StyleShapeOutsideShapeBox::Content:
rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM));
MOZ_FALLTHROUGH;
case StyleShapeOutsideShapeBox::Padding:
rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM));
MOZ_FALLTHROUGH;
case StyleShapeOutsideShapeBox::Border:
rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM));
break;
case StyleShapeOutsideShapeBox::Margin:
// Do nothing. rect is already a margin rect.
break;
case StyleShapeOutsideShapeBox::NoBox:
MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?");
break;
}
if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
// Initialize shape-box reference rect.
LogicalRect rect = aMarginRect;
switch (shapeOutside.GetReferenceBox()) {
case StyleShapeOutsideShapeBox::Content:
rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM));
MOZ_FALLTHROUGH;
case StyleShapeOutsideShapeBox::Padding:
rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM));
MOZ_FALLTHROUGH;
case StyleShapeOutsideShapeBox::Border:
rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM));
break;
case StyleShapeOutsideShapeBox::Margin:
// Do nothing. rect is already a margin rect.
break;
case StyleShapeOutsideShapeBox::NoBox:
MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?");
break;
}
mShapeBoxRect.emplace(rect.LineLeft(aWM, aContainerSize) + aLineLeft,
rect.BStart(aWM) + aBlockStart,
rect.ISize(aWM), rect.BSize(aWM));
nsRect shapeBoxRect(rect.LineLeft(aWM, aContainerSize) + aLineLeft,
rect.BStart(aWM) + aBlockStart,
rect.ISize(aWM), rect.BSize(aWM));
mShapeInfo = MakeUnique<BoxShapeInfo>(shapeBoxRect, mFrame);
}
}
@ -579,7 +669,7 @@ nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther)
, mLeftBEnd(Move(aOther.mLeftBEnd))
, mRightBEnd(Move(aOther.mRightBEnd))
, mRect(Move(aOther.mRect))
, mShapeBoxRect(Move(aOther.mShapeBoxRect))
, mShapeInfo(Move(aOther.mShapeInfo))
{
MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
}
@ -601,51 +691,10 @@ nsFloatManager::FloatInfo::LineLeft(WritingMode aWM,
}
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
if (shapeOutside.GetType() == StyleShapeSourceType::None) {
if (!mShapeInfo) {
return LineLeft();
}
if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
nscoord radii[8];
bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
if (!hasRadii) {
return ShapeBoxRect().x;
}
// Get the physical side for line-left since border-radii are in
// the physical axis.
mozilla::Side lineLeftSide =
aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
nscoord blockStartCornerRadiusL =
radii[SideToHalfCorner(lineLeftSide, true, false)];
nscoord blockStartCornerRadiusB =
radii[SideToHalfCorner(lineLeftSide, true, true)];
nscoord blockEndCornerRadiusL =
radii[SideToHalfCorner(lineLeftSide, false, false)];
nscoord blockEndCornerRadiusB =
radii[SideToHalfCorner(lineLeftSide, false, true)];
if (aWM.IsLineInverted()) {
// This happens only when aWM is vertical-lr. Need to swap blockStart
// and blockEnd corners.
std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL);
std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB);
}
nscoord lineLeftDiff =
ComputeEllipseLineInterceptDiff(
ShapeBoxRect().y, ShapeBoxRect().YMost(),
blockStartCornerRadiusL, blockStartCornerRadiusB,
blockEndCornerRadiusL, blockEndCornerRadiusB,
aBStart, aBEnd);
return ShapeBoxRect().x + lineLeftDiff;
}
// XXX: Other shape source types are not implemented yet.
return LineLeft();
return mShapeInfo->LineLeft(aWM, aBStart, aBEnd);
}
nscoord
@ -659,55 +708,42 @@ nsFloatManager::FloatInfo::LineRight(WritingMode aWM,
}
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
if (shapeOutside.GetType() == StyleShapeSourceType::None) {
if (!mShapeInfo) {
return LineRight();
}
return mShapeInfo->LineRight(aWM, aBStart, aBEnd);
}
if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
nscoord radii[8];
bool hasRadii = mFrame->GetShapeBoxBorderRadii(radii);
if (!hasRadii) {
return ShapeBoxRect().XMost();
}
// Get the physical side for line-right since border-radii are in
// the physical axis.
mozilla::Side lineRightSide =
aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
nscoord blockStartCornerRadiusL =
radii[SideToHalfCorner(lineRightSide, false, false)];
nscoord blockStartCornerRadiusB =
radii[SideToHalfCorner(lineRightSide, false, true)];
nscoord blockEndCornerRadiusL =
radii[SideToHalfCorner(lineRightSide, true, false)];
nscoord blockEndCornerRadiusB =
radii[SideToHalfCorner(lineRightSide, true, true)];
if (aWM.IsLineInverted()) {
// This happens only when aWM is vertical-lr. Need to swap blockStart
// and blockEnd corners.
std::swap(blockStartCornerRadiusL, blockEndCornerRadiusL);
std::swap(blockStartCornerRadiusB, blockEndCornerRadiusB);
}
nscoord lineRightDiff =
ComputeEllipseLineInterceptDiff(
ShapeBoxRect().y, ShapeBoxRect().YMost(),
blockStartCornerRadiusL, blockStartCornerRadiusB,
blockEndCornerRadiusL, blockEndCornerRadiusB,
aBStart, aBEnd);
return ShapeBoxRect().XMost() - lineRightDiff;
nscoord
nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const
{
if (aShapeType == ShapeType::Margin) {
return BStart();
}
// XXX: Other shape source types are not implemented yet.
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
if (!mShapeInfo) {
return BStart();
}
return mShapeInfo->BStart();
}
return LineRight();
nscoord
nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const
{
if (aShapeType == ShapeType::Margin) {
return BEnd();
}
MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
if (!mShapeInfo) {
return BEnd();
}
return mShapeInfo->BEnd();
}
/* static */ nscoord
nsFloatManager::FloatInfo::ComputeEllipseLineInterceptDiff(
nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff(
const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
@ -777,7 +813,7 @@ nsFloatManager::FloatInfo::ComputeEllipseLineInterceptDiff(
}
/* static */ nscoord
nsFloatManager::FloatInfo::XInterceptAtY(const nscoord aY,
nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY,
const nscoord aRadiusX,
const nscoord aRadiusY)
{

Просмотреть файл

@ -10,7 +10,7 @@
#define nsFloatManager_h_
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WritingModes.h"
#include "nsCoord.h"
#include "nsFrameList.h" // for DEBUG_FRAME_DUMP
@ -332,6 +332,70 @@ public:
private:
// ShapeInfo is an abstract class for implementing all the shapes in CSS
// Shapes Module. A subclass needs to override all the methods to adjust
// the flow area with respect to its shape.
class ShapeInfo
{
public:
virtual ~ShapeInfo() {}
virtual nscoord LineLeft(mozilla::WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const = 0;
virtual nscoord LineRight(mozilla::WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const = 0;
virtual nscoord BStart() const = 0;
virtual nscoord BEnd() const = 0;
protected:
// Compute the minimum line-axis difference between the bounding shape
// box and its rounded corner within the given band (block-axis region).
// This is used as a helper function to compute the LineRight() and
// LineLeft(). See the picture in the implementation for an example.
// RadiusL and RadiusB stand for radius on the line-axis and block-axis.
//
// Returns radius-x diff on the line-axis, or 0 if there's no rounded
// corner within the given band.
static nscoord ComputeEllipseLineInterceptDiff(
const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
const nscoord aBandBStart, const nscoord aBandBEnd);
static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX,
const nscoord aRadiusY);
};
// Implements shape-outside: <shape-box>.
class BoxShapeInfo final : public ShapeInfo
{
public:
BoxShapeInfo(const nsRect& aShapeBoxRect, nsIFrame* const aFrame)
: mShapeBoxRect(aShapeBoxRect)
, mFrame(aFrame)
{
}
nscoord LineLeft(mozilla::WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const override;
nscoord LineRight(mozilla::WritingMode aWM,
const nscoord aBStart,
const nscoord aBEnd) const override;
nscoord BStart() const override { return mShapeBoxRect.y; }
nscoord BEnd() const override { return mShapeBoxRect.YMost(); }
private:
// This is the reference box of css shape-outside if specified, which
// implements the <shape-box> value in the CSS Shapes Module Level 1.
// The coordinate space is the same as FloatInfo::mRect.
const nsRect mShapeBoxRect;
// The frame of the float.
nsIFrame* const mFrame;
};
struct FloatInfo {
nsIFrame *const mFrame;
// The lowest block-ends of left/right floats up to and including
@ -350,8 +414,6 @@ private:
nscoord BSize() const { return mRect.height; }
bool IsEmpty() const { return mRect.IsEmpty(); }
nsRect ShapeBoxRect() const { return mShapeBoxRect.valueOr(mRect); }
// aBStart and aBEnd are the starting and ending coordinate of a band.
// LineLeft() and LineRight() return the innermost line-left extent and
// line-right extent within the given band, respectively.
@ -359,32 +421,8 @@ private:
const nscoord aBStart, const nscoord aBEnd) const;
nscoord LineRight(mozilla::WritingMode aWM, ShapeType aShapeType,
const nscoord aBStart, const nscoord aBEnd) const;
nscoord BStart(ShapeType aShapeType) const
{
return aShapeType == ShapeType::Margin ? BStart() : ShapeBoxRect().y;
}
nscoord BEnd(ShapeType aShapeType) const
{
return aShapeType == ShapeType::Margin ? BEnd() : ShapeBoxRect().YMost();
}
// Compute the minimum line-axis difference between the bounding shape
// box and its rounded corner within the given band (block-axis region).
// This is used as a helper function to compute the LineRight() and
// LineLeft(). See the picture in the implementation for an example.
// RadiusL and RadiusB stand for radius on the line-axis and block-axis.
//
// Returns radius-x diff on the line-axis, or 0 if there's no rounded
// corner within the given band.
static nscoord ComputeEllipseLineInterceptDiff(
const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
const nscoord aBandBStart, const nscoord aBandBEnd);
static nscoord XInterceptAtY(const nscoord aY, const nscoord aRadiusX,
const nscoord aRadiusY);
nscoord BStart(ShapeType aShapeType) const;
nscoord BEnd(ShapeType aShapeType) const;
#ifdef NS_BUILD_REFCNT_LOGGING
FloatInfo(FloatInfo&& aOther);
@ -398,10 +436,9 @@ private:
// the line-relative axis of the frame manager and its block
// coordinates are in the frame manager's real block direction.
nsRect mRect;
// This is the reference box of css shape-outside if specified, which
// implements the <shape-box> value in the CSS Shapes Module Level 1.
// The coordinate setup is the same as mRect.
mozilla::Maybe<nsRect> mShapeBoxRect;
// Pointer to a concrete subclass of ShapeInfo or null, which means that
// there is no shape-outside.
mozilla::UniquePtr<ShapeInfo> mShapeInfo;
};
#ifdef DEBUG