/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #ifndef MOZILLA_GFX_RECT_ABSOLUTE_H_ #define MOZILLA_GFX_RECT_ABSOLUTE_H_ #include #include #include "mozilla/Attributes.h" #include "Point.h" #include "Rect.h" #include "Types.h" namespace mozilla { template struct IsPixel; namespace gfx { /** * A RectAbsolute is similar to a Rect (see BaseRect.h), but represented as * (x1, y1, x2, y2) instead of (x, y, width, height). * * Unless otherwise indicated, methods on this class correspond * to methods on BaseRect. * * The API is currently very bare-bones; it may be extended as needed. * * Do not use this class directly. Subclass it, pass that subclass as the * Sub parameter, and only use that subclass. */ template struct BaseRectAbsolute { protected: T left, top, right, bottom; public: BaseRectAbsolute() : left(0), top(0), right(0), bottom(0) {} BaseRectAbsolute(T aLeft, T aTop, T aRight, T aBottom) : left(aLeft), top(aTop), right(aRight), bottom(aBottom) {} MOZ_ALWAYS_INLINE T X() const { return left; } MOZ_ALWAYS_INLINE T Y() const { return top; } MOZ_ALWAYS_INLINE T Width() const { return right - left; } MOZ_ALWAYS_INLINE T Height() const { return bottom - top; } MOZ_ALWAYS_INLINE T XMost() const { return right; } MOZ_ALWAYS_INLINE T YMost() const { return bottom; } MOZ_ALWAYS_INLINE const T& Left() const { return left; } MOZ_ALWAYS_INLINE const T& Right() const { return right; } MOZ_ALWAYS_INLINE const T& Top() const { return top; } MOZ_ALWAYS_INLINE const T& Bottom() const { return bottom; } MOZ_ALWAYS_INLINE T& Left() { return left; } MOZ_ALWAYS_INLINE T& Right() { return right; } MOZ_ALWAYS_INLINE T& Top() { return top; } MOZ_ALWAYS_INLINE T& Bottom() { return bottom; } T Area() const { return Width() * Height(); } void Inflate(T aD) { Inflate(aD, aD); } void Inflate(T aDx, T aDy) { left -= aDx; top -= aDy; right += aDx; bottom += aDy; } MOZ_ALWAYS_INLINE void SetBox(T aLeft, T aTop, T aRight, T aBottom) { left = aLeft; top = aTop; right = aRight; bottom = aBottom; } void SetLeftEdge(T aLeft) { left = aLeft; } void SetRightEdge(T aRight) { right = aRight; } void SetTopEdge(T aTop) { top = aTop; } void SetBottomEdge(T aBottom) { bottom = aBottom; } static Sub FromRect(const Rect& aRect) { if (aRect.Overflows()) { return Sub(); } return Sub(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); } [[nodiscard]] Sub Intersect(const Sub& aOther) const { Sub result; result.left = std::max(left, aOther.left); result.top = std::max(top, aOther.top); result.right = std::min(right, aOther.right); result.bottom = std::min(bottom, aOther.bottom); if (result.right < result.left || result.bottom < result.top) { result.SizeTo(0, 0); } return result; } bool IsEmpty() const { return right <= left || bottom <= top; } bool IsEqualEdges(const Sub& aOther) const { return left == aOther.left && top == aOther.top && right == aOther.right && bottom == aOther.bottom; } bool IsEqualInterior(const Sub& aRect) const { return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty()); } MOZ_ALWAYS_INLINE void MoveBy(T aDx, T aDy) { left += aDx; right += aDx; top += aDy; bottom += aDy; } MOZ_ALWAYS_INLINE void MoveBy(const Point& aPoint) { left += aPoint.x; right += aPoint.x; top += aPoint.y; bottom += aPoint.y; } MOZ_ALWAYS_INLINE void SizeTo(T aWidth, T aHeight) { right = left + aWidth; bottom = top + aHeight; } bool Contains(const Sub& aRect) const { return aRect.IsEmpty() || (left <= aRect.left && aRect.right <= right && top <= aRect.top && aRect.bottom <= bottom); } bool Contains(T aX, T aY) const { return (left <= aX && aX < right && top <= aY && aY < bottom); } bool Intersects(const Sub& aRect) const { return !IsEmpty() && !aRect.IsEmpty() && left < aRect.right && aRect.left < right && top < aRect.bottom && aRect.top < bottom; } void SetEmpty() { left = right = top = bottom = 0; } // Returns the smallest rectangle that contains both the area of both // this and aRect2. // Thus, empty input rectangles are ignored. // If both rectangles are empty, returns this. // WARNING! This is not safe against overflow, prefer using SafeUnion instead // when dealing with int-based rects. [[nodiscard]] Sub Union(const Sub& aRect) const { if (IsEmpty()) { return aRect; } else if (aRect.IsEmpty()) { return *static_cast(this); } else { return UnionEdges(aRect); } } // Returns the smallest rectangle that contains both the points (including // edges) of both aRect1 and aRect2. // Thus, empty input rectangles are allowed to affect the result. // WARNING! This is not safe against overflow, prefer using SafeUnionEdges // instead when dealing with int-based rects. [[nodiscard]] Sub UnionEdges(const Sub& aRect) const { Sub result; result.left = std::min(left, aRect.left); result.top = std::min(top, aRect.top); result.right = std::max(XMost(), aRect.XMost()); result.bottom = std::max(YMost(), aRect.YMost()); return result; } // Scale 'this' by aScale without doing any rounding. void Scale(T aScale) { Scale(aScale, aScale); } // Scale 'this' by aXScale and aYScale, without doing any rounding. void Scale(T aXScale, T aYScale) { right = XMost() * aXScale; bottom = YMost() * aYScale; left = left * aXScale; top = top * aYScale; } // Scale 'this' by aScale, converting coordinates to integers so that the // result is the smallest integer-coordinate rectangle containing the // unrounded result. Note: this can turn an empty rectangle into a non-empty // rectangle void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); } // Scale 'this' by aXScale and aYScale, converting coordinates to integers so // that the result is the smallest integer-coordinate rectangle containing the // unrounded result. // Note: this can turn an empty rectangle into a non-empty rectangle void ScaleRoundOut(double aXScale, double aYScale) { right = static_cast(ceil(double(XMost()) * aXScale)); bottom = static_cast(ceil(double(YMost()) * aYScale)); left = static_cast(floor(double(left) * aXScale)); top = static_cast(floor(double(top) * aYScale)); } // Scale 'this' by aScale, converting coordinates to integers so that the // result is the largest integer-coordinate rectangle contained by the // unrounded result. void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); } // Scale 'this' by aXScale and aYScale, converting coordinates to integers so // that the result is the largest integer-coordinate rectangle contained by // the unrounded result. void ScaleRoundIn(double aXScale, double aYScale) { right = static_cast(floor(double(XMost()) * aXScale)); bottom = static_cast(floor(double(YMost()) * aYScale)); left = static_cast(ceil(double(left) * aXScale)); top = static_cast(ceil(double(top) * aYScale)); } // Scale 'this' by 1/aScale, converting coordinates to integers so that the // result is the smallest integer-coordinate rectangle containing the // unrounded result. Note: this can turn an empty rectangle into a non-empty // rectangle void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); } // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers // so that the result is the smallest integer-coordinate rectangle containing // the unrounded result. Note: this can turn an empty rectangle into a // non-empty rectangle void ScaleInverseRoundOut(double aXScale, double aYScale) { right = static_cast(ceil(double(XMost()) / aXScale)); bottom = static_cast(ceil(double(YMost()) / aYScale)); left = static_cast(floor(double(left) / aXScale)); top = static_cast(floor(double(top) / aYScale)); } // Scale 'this' by 1/aScale, converting coordinates to integers so that the // result is the largest integer-coordinate rectangle contained by the // unrounded result. void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); } // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers // so that the result is the largest integer-coordinate rectangle contained by // the unrounded result. void ScaleInverseRoundIn(double aXScale, double aYScale) { right = static_cast(floor(double(XMost()) / aXScale)); bottom = static_cast(floor(double(YMost()) / aYScale)); left = static_cast(ceil(double(left) / aXScale)); top = static_cast(ceil(double(top) / aYScale)); } /** * Translate this rectangle to be inside aRect. If it doesn't fit inside * aRect then the dimensions that don't fit will be shrunk so that they * do fit. The resulting rect is returned. */ [[nodiscard]] Sub MoveInsideAndClamp(const Sub& aRect) const { T newLeft = std::max(aRect.left, left); T newTop = std::max(aRect.top, top); T width = std::min(aRect.Width(), Width()); T height = std::min(aRect.Height(), Height()); Sub rect(newLeft, newTop, newLeft + width, newTop + height); newLeft = std::min(rect.right, aRect.right) - width; newTop = std::min(rect.bottom, aRect.bottom) - height; rect.MoveBy(newLeft - rect.left, newTop - rect.top); return rect; } friend std::ostream& operator<<( std::ostream& stream, const BaseRectAbsolute& aRect) { return stream << "(l=" << aRect.left << ", t=" << aRect.top << ", r=" << aRect.right << ", b=" << aRect.bottom << ')'; } }; template struct IntRectAbsoluteTyped : public BaseRectAbsolute, IntPointTyped, IntRectTyped>, public Units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); typedef BaseRectAbsolute, IntPointTyped, IntRectTyped> Super; typedef IntParam ToInt; IntRectAbsoluteTyped() : Super() {} IntRectAbsoluteTyped(ToInt aLeft, ToInt aTop, ToInt aRight, ToInt aBottom) : Super(aLeft.value, aTop.value, aRight.value, aBottom.value) {} }; template struct RectAbsoluteTyped : public BaseRectAbsolute, PointTyped, RectTyped>, public Units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); typedef BaseRectAbsolute, PointTyped, RectTyped> Super; RectAbsoluteTyped() : Super() {} RectAbsoluteTyped(Float aLeft, Float aTop, Float aRight, Float aBottom) : Super(aLeft, aTop, aRight, aBottom) {} }; typedef IntRectAbsoluteTyped IntRectAbsolute; } // namespace gfx } // namespace mozilla #endif /* MOZILLA_GFX_RECT_ABSOLUTE_H_ */