Bug 1389152: Change LogicalRect from having nsRect as a member variable, and exposing its member variable addresses, to storing four nscoord values separately and doing some operations by creating temporary rectangles to ensure consistency. Add a method to BaseRect to get OriginAndSize at once. r?jfkthame,bas.schouten
MozReview-Commit-ID: FhmSXK7p5c6
--- a/gfx/tests/gtest/TestRect.cpp
+++ b/gfx/tests/gtest/TestRect.cpp
@@ -4,16 +4,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <limits>
#include "gtest/gtest.h"
#include "nsRect.h"
#include "gfxRect.h"
+#include "mozilla/WritingModes.h"
#ifdef XP_WIN
#include <windows.h>
#endif
template <class RectType>
static bool
TestConstructors()
{
@@ -440,17 +441,65 @@ TestSwap()
{
RectType rect(1, 2, 3, 4);
EXPECT_TRUE(rect.X() == 1 && rect.Y() == 2 && rect.Width() == 3 && rect.Height() == 4);
rect.Swap();
EXPECT_TRUE(rect.X() == 2 && rect.Y() == 1 && rect.Width() == 4 && rect.Height() == 3);
return true;
}
+static void
+TestIntersectionLogicalHelper(nscoord x1, nscoord y1, nscoord w1, nscoord h1,
+ nscoord x2, nscoord y2, nscoord w2, nscoord h2,
+ nscoord xR, nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty)
+{
+ nsRect rect1(x1, y1, w1, h1);
+ nsRect rect2(x2, y2, w2, h2);
+ nsRect rectDebug;
+ EXPECT_TRUE(isNonEmpty == rectDebug.IntersectRect(rect1, rect2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(xR, yR, wR, hR)));
+ mozilla::LogicalRect r1(mozilla::WritingMode(), rect1.X(), rect1.Y(), rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r2(mozilla::WritingMode(), rect2.X(), rect2.Y(), rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r1.IntersectRect(r1, r2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(r1.IStart(WritingMode()), r1.BStart(WritingMode()),
+ r1.ISize(WritingMode()), r1.BSize(WritingMode()))));
+
+ mozilla::LogicalRect r3(mozilla::WritingMode(), rect1.X(), rect1.Y(), rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r4(mozilla::WritingMode(), rect2.X(), rect2.Y(), rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r4.IntersectRect(r3, r4));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(r4.IStart(WritingMode()), r4.BStart(WritingMode()),
+ r4.ISize(WritingMode()), r4.BSize(WritingMode()))));
+
+ mozilla::LogicalRect r5(mozilla::WritingMode(), rect1.X(), rect1.Y(), rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r6(mozilla::WritingMode(), rect2.X(), rect2.Y(), rect2.Width(), rect2.Height());
+ mozilla::LogicalRect r7(mozilla::WritingMode(), 0, 0, 1, 1);
+ EXPECT_TRUE(isNonEmpty == r7.IntersectRect(r5, r6));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(r7.IStart(WritingMode()), r7.BStart(WritingMode()),
+ r7.ISize(WritingMode()), r7.BSize(WritingMode()))));
+}
+
+static void
+TestIntersectionLogical(nscoord x1, nscoord y1, nscoord w1, nscoord h1,
+ nscoord x2, nscoord y2, nscoord w2, nscoord h2,
+ nscoord xR, nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty)
+{
+ TestIntersectionLogicalHelper(x1, y1, w1, h1, x2, y2, w2, h2, xR, yR, wR, hR, isNonEmpty);
+ TestIntersectionLogicalHelper(x2, y2, w2, h2, x1, y1, w1, h1, xR, yR, wR, hR, isNonEmpty);
+}
+
+TEST(Gfx, Logical)
+{
+ TestIntersectionLogical(578, 0, 2650, 1152, 1036, 0, 2312, 1, 1036, 0, 2192, 1, true);
+ TestIntersectionLogical(0, 0, 1000, 1000, 500, 500, 1000, 1000, 500, 500, 500, 500, true);
+ TestIntersectionLogical(100, 200, 300, 400, 50, 250, 100, 100, 100, 250, 50, 100, true);
+ TestIntersectionLogical(0, 100, 200, 300, 300, 100, 100, 300, 300, 100, 0, 0, false);
+}
TEST(Gfx, nsRect) {
TestConstructors<nsRect>();
TestEqualityOperator<nsRect>();
TestContainment<nsRect>();
TestIntersects<nsRect>();
TestIntersection<nsRect>();
TestUnion<nsRect>();
@@ -478,8 +527,9 @@ TEST(Gfx, gfxRect) {
TestIntersects<gfxRect>();
TestIntersection<gfxRect>();
TestUnion<gfxRect>();
TestBug1135677<gfxRect>();
TestFiniteGfx();
TestSetWH<gfxRect>();
TestSwap<gfxRect>();
}
+
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -1484,123 +1484,132 @@ private:
*/
class LogicalRect {
public:
explicit LogicalRect(WritingMode aWritingMode)
:
#ifdef DEBUG
mWritingMode(aWritingMode),
#endif
- mRect(0, 0, 0, 0)
+ mIStart(0),
+ mBStart(0),
+ mISize(0),
+ mBSize(0)
{ }
LogicalRect(WritingMode aWritingMode,
nscoord aIStart, nscoord aBStart,
nscoord aISize, nscoord aBSize)
:
#ifdef DEBUG
mWritingMode(aWritingMode),
#endif
- mRect(aIStart, aBStart, aISize, aBSize)
+ mIStart(aIStart),
+ mBStart(aBStart),
+ mISize(aISize),
+ mBSize(aBSize)
{ }
LogicalRect(WritingMode aWritingMode,
const LogicalPoint& aOrigin,
const LogicalSize& aSize)
:
#ifdef DEBUG
mWritingMode(aWritingMode),
#endif
- mRect(aOrigin.mPoint, aSize.mSize)
+ mIStart(aOrigin.mPoint.x),
+ mBStart(aOrigin.mPoint.y),
+ mISize(aSize.mSize.width),
+ mBSize(aSize.mSize.height)
{
CHECK_WRITING_MODE(aOrigin.GetWritingMode());
CHECK_WRITING_MODE(aSize.GetWritingMode());
}
LogicalRect(WritingMode aWritingMode,
const nsRect& aRect,
const nsSize& aContainerSize)
#ifdef DEBUG
: mWritingMode(aWritingMode)
#endif
{
if (aWritingMode.IsVertical()) {
- mRect.y = aWritingMode.IsVerticalLR()
- ? aRect.x : aContainerSize.width - aRect.XMost();
- mRect.x = aWritingMode.IsInlineReversed()
- ? aContainerSize.height - aRect.YMost() : aRect.y;
- mRect.height = aRect.width;
- mRect.width = aRect.height;
+ mBStart = aWritingMode.IsVerticalLR()
+ ? aRect.X() : aContainerSize.width - aRect.XMost();
+ mIStart = aWritingMode.IsInlineReversed()
+ ? aContainerSize.height - aRect.YMost() : aRect.Y();
+ mBSize = aRect.Width();
+ mISize = aRect.Height();
} else {
- mRect.x = aWritingMode.IsInlineReversed()
- ? aContainerSize.width - aRect.XMost() : aRect.x;
- mRect.y = aRect.y;
- mRect.width = aRect.width;
- mRect.height = aRect.height;
+ mIStart = aWritingMode.IsInlineReversed()
+ ? aContainerSize.width - aRect.XMost() : aRect.X();
+ mBStart = aRect.Y();
+ mISize = aRect.Width();
+ mBSize = aRect.Height();
}
}
/**
* Inline- and block-dimension geometry.
*/
nscoord IStart(WritingMode aWritingMode) const // inline-start edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.X();
+ return mIStart;
}
nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.XMost();
+ return mIStart + mISize;
}
nscoord ISize(WritingMode aWritingMode) const // inline-size
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.Width();
+ return mISize;
}
nscoord BStart(WritingMode aWritingMode) const // block-start edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.Y();
+ return mBStart;
}
nscoord BEnd(WritingMode aWritingMode) const // block-end edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.YMost();
+ return mBStart + mBSize;
}
nscoord BSize(WritingMode aWritingMode) const // block-size
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.Height();
+ return mBSize;
}
/**
* Writable (reference) accessors are only available for the basic logical
* fields (Start and Size), not derivatives like End.
*/
nscoord& IStart(WritingMode aWritingMode) // inline-start edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.x;
+ return mIStart;
}
nscoord& ISize(WritingMode aWritingMode) // inline-size
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.width;
+ return mISize;
}
nscoord& BStart(WritingMode aWritingMode) // block-start edge
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.y;
+ return mBStart;
}
nscoord& BSize(WritingMode aWritingMode) // block-size
{
CHECK_WRITING_MODE(aWritingMode);
- return mRect.height;
+ return mBSize;
}
/**
* Accessors for line-relative coordinates
*/
nscoord LineLeft(WritingMode aWritingMode,
const nsSize& aContainerSize) const
{
@@ -1627,91 +1636,98 @@ public:
/**
* Physical coordinates of the rect.
*/
nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
{
CHECK_WRITING_MODE(aWritingMode);
if (aWritingMode.IsVertical()) {
return aWritingMode.IsVerticalLR() ?
- mRect.Y() : aContainerWidth - mRect.YMost();
+ mBStart : aContainerWidth - BEnd();
} else {
return aWritingMode.IsInlineReversed() ?
- aContainerWidth - mRect.XMost() : mRect.X();
+ aContainerWidth - IEnd() : mIStart;
}
}
nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
{
CHECK_WRITING_MODE(aWritingMode);
if (aWritingMode.IsVertical()) {
- return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.XMost()
- : mRect.X();
+ return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
+ : mIStart;
} else {
- return mRect.Y();
+ return mBStart;
}
}
nscoord Width(WritingMode aWritingMode) const
{
CHECK_WRITING_MODE(aWritingMode);
- return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width();
+ return aWritingMode.IsVertical() ? mBSize : mISize;
}
nscoord Height(WritingMode aWritingMode) const
{
CHECK_WRITING_MODE(aWritingMode);
- return aWritingMode.IsVertical() ? mRect.Width() : mRect.Height();
+ return aWritingMode.IsVertical() ? mISize : mBSize;
}
nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
{
CHECK_WRITING_MODE(aWritingMode);
if (aWritingMode.IsVertical()) {
return aWritingMode.IsVerticalLR() ?
- mRect.YMost() : aContainerWidth - mRect.Y();
+ BEnd() : aContainerWidth - mBStart;
} else {
return aWritingMode.IsInlineReversed() ?
- aContainerWidth - mRect.X() : mRect.XMost();
+ aContainerWidth - mIStart : IEnd();
}
}
nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
{
CHECK_WRITING_MODE(aWritingMode);
if (aWritingMode.IsVertical()) {
- return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.x
- : mRect.XMost();
+ return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
+ : IEnd();
} else {
- return mRect.YMost();
+ return mBStart;
}
}
bool IsEmpty() const
{
- return mRect.IsEmpty();
+ return mISize <= 0 || mBSize << 0;
}
bool IsAllZero() const
{
- return (mRect.x == 0 && mRect.y == 0 &&
- mRect.width == 0 && mRect.height == 0);
+ return (mIStart == 0 && mBStart == 0 &&
+ mISize == 0 && mBSize == 0);
}
bool IsZeroSize() const
{
- return (mRect.width == 0 && mRect.height == 0);
+ return (mISize == 0 && mBSize == 0);
}
- void SetEmpty() { mRect.SetEmpty(); }
+ void SetEmpty() { mISize = mBSize = 0; }
bool IsEqualEdges(const LogicalRect aOther) const
{
CHECK_WRITING_MODE(aOther.GetWritingMode());
- return mRect.IsEqualEdges(aOther.mRect);
+ bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
+ mISize == aOther.mISize && mBSize == aOther.mBSize;
+
+ // We want the same result as nsRect, so assert we get it.
+ MOZ_ASSERT(result == nsRect(mIStart, mBStart, mISize, mBSize).
+ IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
+ aOther.mISize, aOther.mBSize)));
+ return result;
}
LogicalPoint Origin(WritingMode aWritingMode) const
{
CHECK_WRITING_MODE(aWritingMode);
return LogicalPoint(aWritingMode, IStart(), BStart());
}
void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint)
@@ -1732,59 +1748,127 @@ public:
return LogicalRect(GetWritingMode(),
IStart() + aPoint.I(), BStart() + aPoint.B(),
ISize(), BSize());
}
LogicalRect& operator+=(const LogicalPoint& aPoint)
{
CHECK_WRITING_MODE(aPoint.GetWritingMode());
- mRect += aPoint.mPoint;
+ mIStart += aPoint.mPoint.x;
+ mBStart += aPoint.mPoint.y;
return *this;
}
LogicalRect operator-(const LogicalPoint& aPoint) const
{
CHECK_WRITING_MODE(aPoint.GetWritingMode());
return LogicalRect(GetWritingMode(),
IStart() - aPoint.I(), BStart() - aPoint.B(),
ISize(), BSize());
}
LogicalRect& operator-=(const LogicalPoint& aPoint)
{
CHECK_WRITING_MODE(aPoint.GetWritingMode());
- mRect -= aPoint.mPoint;
+ mIStart -= aPoint.mPoint.x;
+ mBStart -= aPoint.mPoint.y;
return *this;
}
void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
{
CHECK_WRITING_MODE(aWritingMode);
CHECK_WRITING_MODE(aDelta.GetWritingMode());
IStart() += aDelta.I();
BStart() += aDelta.B();
}
- void Inflate(nscoord aD) { mRect.Inflate(aD); }
- void Inflate(nscoord aDI, nscoord aDB) { mRect.Inflate(aDI, aDB); }
+ void Inflate(nscoord aD)
+ {
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Inflate(aD);
+#endif
+ mIStart -= aD;
+ mBStart -= aD;
+ mISize += 2 * aD;
+ mBSize += 2 * aD;
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+ }
+ void Inflate(nscoord aDI, nscoord aDB)
+ {
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Inflate(aDI, aDB);
+#endif
+ mIStart -= aDI;
+ mBStart -= aDB;
+ mISize += 2 * aDI;
+ mBSize += 2 * aDB;
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+ }
void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
{
CHECK_WRITING_MODE(aWritingMode);
CHECK_WRITING_MODE(aMargin.GetWritingMode());
- mRect.Inflate(aMargin.mMargin);
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Inflate(aMargin.mMargin);
+#endif
+ mIStart -= aMargin.mMargin.left;
+ mBStart -= aMargin.mMargin.top;
+ mISize += aMargin.mMargin.LeftRight();
+ mBSize += aMargin.mMargin.TopBottom();
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
}
- void Deflate(nscoord aD) { mRect.Deflate(aD); }
- void Deflate(nscoord aDI, nscoord aDB) { mRect.Deflate(aDI, aDB); }
+ void Deflate(nscoord aD)
+ {
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Deflate(aD);
+#endif
+ mIStart += aD;
+ mBStart += aD;
+ mISize = std::max(0, mISize - 2 * aD);
+ mBSize = std::max(0, mBSize - 2 * aD);
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+ }
+ void Deflate(nscoord aDI, nscoord aDB)
+ {
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Deflate(aDI, aDB);
+#endif
+ mIStart += aDI;
+ mBStart += aDB;
+ mISize = std::max(0, mISize - 2 * aDI);
+ mBSize = std::max(0, mBSize - 2 * aDB);
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+ }
void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
{
CHECK_WRITING_MODE(aWritingMode);
CHECK_WRITING_MODE(aMargin.GetWritingMode());
- mRect.Deflate(aMargin.mMargin);
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
+ rectDebug.Deflate(aMargin.mMargin);
+#endif
+ mIStart += aMargin.mMargin.left;
+ mBStart += aMargin.mMargin.top;
+ mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
+ mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
}
/**
* Return an nsRect containing our physical coordinates within the given
* container size.
*/
nsRect GetPhysicalRect(WritingMode aWritingMode,
const nsSize& aContainerSize) const
@@ -1818,75 +1902,102 @@ public:
/**
* Set *this to be the rectangle containing the intersection of aRect1
* and aRect2, return whether the intersection is non-empty.
*/
bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2)
{
CHECK_WRITING_MODE(aRect1.mWritingMode);
CHECK_WRITING_MODE(aRect2.mWritingMode);
- return mRect.IntersectRect(aRect1.mRect, aRect2.mRect);
+#ifdef DEBUG
+ // Compute using nsRect and assert the results match
+ nsRect rectDebug;
+ rectDebug.IntersectRect(nsRect(aRect1.mIStart, aRect1.mBStart,
+ aRect1.mISize, aRect1.mBSize),
+ nsRect(aRect2.mIStart, aRect2.mBStart,
+ aRect2.mISize, aRect2.mBSize));
+#endif
+
+ nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
+ mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
+ mISize = iEnd - mIStart;
+
+ nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
+ mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
+ mBSize = bEnd - mBStart;
+
+ if (mISize < 0 || mBSize < 0) {
+ mISize = 0;
+ mBSize = 0;
+ }
+
+ MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+ return mISize > 0 && mBSize > 0;
}
private:
LogicalRect() = delete;
#ifdef DEBUG
WritingMode GetWritingMode() const { return mWritingMode; }
#else
WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
#endif
nscoord IStart() const // inline-start edge
{
- return mRect.X();
+ return mIStart;
}
nscoord IEnd() const // inline-end edge
{
- return mRect.XMost();
+ return mIStart + mISize;
}
nscoord ISize() const // inline-size
{
- return mRect.Width();
+ return mISize;
}
nscoord BStart() const // block-start edge
{
- return mRect.Y();
+ return mBStart;
}
nscoord BEnd() const // block-end edge
{
- return mRect.YMost();
+ return mBStart + mBSize;
}
nscoord BSize() const // block-size
{
- return mRect.Height();
+ return mBSize;
}
nscoord& IStart() // inline-start edge
{
- return mRect.x;
+ return mIStart;
}
nscoord& ISize() // inline-size
{
- return mRect.width;
+ return mISize;
}
nscoord& BStart() // block-start edge
{
- return mRect.y;
+ return mBStart;
}
nscoord& BSize() // block-size
{
- return mRect.height;
+ return mBSize;
}
#ifdef DEBUG
WritingMode mWritingMode;
#endif
- nsRect mRect;
+ // Inline- and block-geometry dimension
+ nscoord mIStart; // inline-start edge
+ nscoord mBStart; // block-start edge
+ nscoord mISize; // inline-size
+ nscoord mBSize; // block-size
};
} // namespace mozilla
// Definitions of inline methods for nsStyleSides, declared in nsStyleCoord.h
// but not defined there because they need WritingMode.
inline nsStyleUnit nsStyleSides::GetUnit(mozilla::WritingMode aWM,
mozilla::LogicalSide aSide) const