RectAbsolute.h (11653B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_RECT_ABSOLUTE_H_ 8 #define MOZILLA_GFX_RECT_ABSOLUTE_H_ 9 10 #include <algorithm> 11 #include <cstdint> 12 13 #include "mozilla/Attributes.h" 14 #include "Point.h" 15 #include "Rect.h" 16 #include "Types.h" 17 18 namespace mozilla { 19 20 template <typename> 21 struct IsPixel; 22 23 namespace gfx { 24 25 /** 26 * A RectAbsolute is similar to a Rect (see BaseRect.h), but represented as 27 * (x1, y1, x2, y2) instead of (x, y, width, height). 28 * 29 * Unless otherwise indicated, methods on this class correspond 30 * to methods on BaseRect. 31 * 32 * The API is currently very bare-bones; it may be extended as needed. 33 * 34 * Do not use this class directly. Subclass it, pass that subclass as the 35 * Sub parameter, and only use that subclass. 36 */ 37 template <class T, class Sub, class Point, class Rect> 38 struct BaseRectAbsolute { 39 protected: 40 T left, top, right, bottom; 41 42 public: 43 BaseRectAbsolute() : left(0), top(0), right(0), bottom(0) {} 44 BaseRectAbsolute(T aLeft, T aTop, T aRight, T aBottom) 45 : left(aLeft), top(aTop), right(aRight), bottom(aBottom) {} 46 47 MOZ_ALWAYS_INLINE T X() const { return left; } 48 MOZ_ALWAYS_INLINE T Y() const { return top; } 49 MOZ_ALWAYS_INLINE T Width() const { return right - left; } 50 MOZ_ALWAYS_INLINE T Height() const { return bottom - top; } 51 MOZ_ALWAYS_INLINE T XMost() const { return right; } 52 MOZ_ALWAYS_INLINE T YMost() const { return bottom; } 53 MOZ_ALWAYS_INLINE const T& Left() const { return left; } 54 MOZ_ALWAYS_INLINE const T& Right() const { return right; } 55 MOZ_ALWAYS_INLINE const T& Top() const { return top; } 56 MOZ_ALWAYS_INLINE const T& Bottom() const { return bottom; } 57 MOZ_ALWAYS_INLINE T& Left() { return left; } 58 MOZ_ALWAYS_INLINE T& Right() { return right; } 59 MOZ_ALWAYS_INLINE T& Top() { return top; } 60 MOZ_ALWAYS_INLINE T& Bottom() { return bottom; } 61 T Area() const { return Width() * Height(); } 62 63 void Inflate(T aD) { Inflate(aD, aD); } 64 void Inflate(T aDx, T aDy) { 65 left -= aDx; 66 top -= aDy; 67 right += aDx; 68 bottom += aDy; 69 } 70 71 MOZ_ALWAYS_INLINE void SetBox(T aLeft, T aTop, T aRight, T aBottom) { 72 left = aLeft; 73 top = aTop; 74 right = aRight; 75 bottom = aBottom; 76 } 77 void SetLeftEdge(T aLeft) { left = aLeft; } 78 void SetRightEdge(T aRight) { right = aRight; } 79 void SetTopEdge(T aTop) { top = aTop; } 80 void SetBottomEdge(T aBottom) { bottom = aBottom; } 81 82 static Sub FromRect(const Rect& aRect) { 83 if (aRect.Overflows()) { 84 return Sub(); 85 } 86 return Sub(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); 87 } 88 89 [[nodiscard]] Sub Intersect(const Sub& aOther) const { 90 Sub result; 91 result.left = std::max<T>(left, aOther.left); 92 result.top = std::max<T>(top, aOther.top); 93 result.right = std::min<T>(right, aOther.right); 94 result.bottom = std::min<T>(bottom, aOther.bottom); 95 if (result.right < result.left || result.bottom < result.top) { 96 result.SizeTo(0, 0); 97 } 98 return result; 99 } 100 101 bool IsEmpty() const { return right <= left || bottom <= top; } 102 103 bool IsEqualEdges(const Sub& aOther) const { 104 return left == aOther.left && top == aOther.top && right == aOther.right && 105 bottom == aOther.bottom; 106 } 107 108 bool IsEqualInterior(const Sub& aRect) const { 109 return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty()); 110 } 111 112 MOZ_ALWAYS_INLINE void MoveBy(T aDx, T aDy) { 113 left += aDx; 114 right += aDx; 115 top += aDy; 116 bottom += aDy; 117 } 118 MOZ_ALWAYS_INLINE void MoveBy(const Point& aPoint) { 119 left += aPoint.x; 120 right += aPoint.x; 121 top += aPoint.y; 122 bottom += aPoint.y; 123 } 124 MOZ_ALWAYS_INLINE void SizeTo(T aWidth, T aHeight) { 125 right = left + aWidth; 126 bottom = top + aHeight; 127 } 128 129 bool Contains(const Sub& aRect) const { 130 return aRect.IsEmpty() || (left <= aRect.left && aRect.right <= right && 131 top <= aRect.top && aRect.bottom <= bottom); 132 } 133 bool Contains(T aX, T aY) const { 134 return (left <= aX && aX < right && top <= aY && aY < bottom); 135 } 136 137 bool Intersects(const Sub& aRect) const { 138 return !IsEmpty() && !aRect.IsEmpty() && left < aRect.right && 139 aRect.left < right && top < aRect.bottom && aRect.top < bottom; 140 } 141 142 void SetEmpty() { left = right = top = bottom = 0; } 143 144 // Returns the smallest rectangle that contains both the area of both 145 // this and aRect. Thus, empty input rectangles are ignored. 146 // Note: if both rectangles are empty, returns aRect. 147 // WARNING! This is not safe against overflow, prefer using SafeUnion instead 148 // when dealing with int-based rects. 149 [[nodiscard]] Sub Union(const Sub& aRect) const { 150 if (IsEmpty()) { 151 return aRect; 152 } else if (aRect.IsEmpty()) { 153 return *static_cast<const Sub*>(this); 154 } else { 155 return UnionEdges(aRect); 156 } 157 } 158 // Returns the smallest rectangle that contains both the points (including 159 // edges) of both aRect1 and aRect2. 160 // Thus, empty input rectangles are allowed to affect the result. 161 // WARNING! This is not safe against overflow, prefer using SafeUnionEdges 162 // instead when dealing with int-based rects. 163 [[nodiscard]] Sub UnionEdges(const Sub& aRect) const { 164 Sub result; 165 result.left = std::min(left, aRect.left); 166 result.top = std::min(top, aRect.top); 167 result.right = std::max(XMost(), aRect.XMost()); 168 result.bottom = std::max(YMost(), aRect.YMost()); 169 return result; 170 } 171 172 // Scale 'this' by aScale without doing any rounding. 173 void Scale(T aScale) { Scale(aScale, aScale); } 174 // Scale 'this' by aXScale and aYScale, without doing any rounding. 175 void Scale(T aXScale, T aYScale) { 176 right = XMost() * aXScale; 177 bottom = YMost() * aYScale; 178 left = left * aXScale; 179 top = top * aYScale; 180 } 181 // Scale 'this' by aScale, converting coordinates to integers so that the 182 // result is the smallest integer-coordinate rectangle containing the 183 // unrounded result. Note: this can turn an empty rectangle into a non-empty 184 // rectangle 185 void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); } 186 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so 187 // that the result is the smallest integer-coordinate rectangle containing the 188 // unrounded result. 189 // Note: this can turn an empty rectangle into a non-empty rectangle 190 void ScaleRoundOut(double aXScale, double aYScale) { 191 right = static_cast<T>(ceil(double(XMost()) * aXScale)); 192 bottom = static_cast<T>(ceil(double(YMost()) * aYScale)); 193 left = static_cast<T>(floor(double(left) * aXScale)); 194 top = static_cast<T>(floor(double(top) * aYScale)); 195 } 196 // Scale 'this' by aScale, converting coordinates to integers so that the 197 // result is the largest integer-coordinate rectangle contained by the 198 // unrounded result. 199 void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); } 200 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so 201 // that the result is the largest integer-coordinate rectangle contained by 202 // the unrounded result. 203 void ScaleRoundIn(double aXScale, double aYScale) { 204 right = static_cast<T>(floor(double(XMost()) * aXScale)); 205 bottom = static_cast<T>(floor(double(YMost()) * aYScale)); 206 left = static_cast<T>(ceil(double(left) * aXScale)); 207 top = static_cast<T>(ceil(double(top) * aYScale)); 208 } 209 // Scale 'this' by 1/aScale, converting coordinates to integers so that the 210 // result is the smallest integer-coordinate rectangle containing the 211 // unrounded result. Note: this can turn an empty rectangle into a non-empty 212 // rectangle 213 void ScaleInverseRoundOut(double aScale) { 214 ScaleInverseRoundOut(aScale, aScale); 215 } 216 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers 217 // so that the result is the smallest integer-coordinate rectangle containing 218 // the unrounded result. Note: this can turn an empty rectangle into a 219 // non-empty rectangle 220 void ScaleInverseRoundOut(double aXScale, double aYScale) { 221 right = static_cast<T>(ceil(double(XMost()) / aXScale)); 222 bottom = static_cast<T>(ceil(double(YMost()) / aYScale)); 223 left = static_cast<T>(floor(double(left) / aXScale)); 224 top = static_cast<T>(floor(double(top) / aYScale)); 225 } 226 // Scale 'this' by 1/aScale, converting coordinates to integers so that the 227 // result is the largest integer-coordinate rectangle contained by the 228 // unrounded result. 229 void ScaleInverseRoundIn(double aScale) { 230 ScaleInverseRoundIn(aScale, aScale); 231 } 232 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers 233 // so that the result is the largest integer-coordinate rectangle contained by 234 // the unrounded result. 235 void ScaleInverseRoundIn(double aXScale, double aYScale) { 236 right = static_cast<T>(floor(double(XMost()) / aXScale)); 237 bottom = static_cast<T>(floor(double(YMost()) / aYScale)); 238 left = static_cast<T>(ceil(double(left) / aXScale)); 239 top = static_cast<T>(ceil(double(top) / aYScale)); 240 } 241 242 /** 243 * Translate this rectangle to be inside aRect. If it doesn't fit inside 244 * aRect then the dimensions that don't fit will be shrunk so that they 245 * do fit. The resulting rect is returned. 246 */ 247 [[nodiscard]] Sub MoveInsideAndClamp(const Sub& aRect) const { 248 T newLeft = std::max(aRect.left, left); 249 T newTop = std::max(aRect.top, top); 250 T width = std::min(aRect.Width(), Width()); 251 T height = std::min(aRect.Height(), Height()); 252 Sub rect(newLeft, newTop, newLeft + width, newTop + height); 253 newLeft = std::min(rect.right, aRect.right) - width; 254 newTop = std::min(rect.bottom, aRect.bottom) - height; 255 rect.MoveBy(newLeft - rect.left, newTop - rect.top); 256 return rect; 257 } 258 259 friend std::ostream& operator<<( 260 std::ostream& stream, 261 const BaseRectAbsolute<T, Sub, Point, Rect>& aRect) { 262 return stream << "(l=" << aRect.left << ", t=" << aRect.top 263 << ", r=" << aRect.right << ", b=" << aRect.bottom << ')'; 264 } 265 }; 266 267 template <class Units> 268 struct IntRectAbsoluteTyped 269 : public BaseRectAbsolute<int32_t, IntRectAbsoluteTyped<Units>, 270 IntPointTyped<Units>, IntRectTyped<Units>>, 271 public Units { 272 static_assert(IsPixel<Units>::value, 273 "'units' must be a coordinate system tag"); 274 typedef BaseRectAbsolute<int32_t, IntRectAbsoluteTyped<Units>, 275 IntPointTyped<Units>, IntRectTyped<Units>> 276 Super; 277 typedef IntParam<int32_t> ToInt; 278 279 IntRectAbsoluteTyped() : Super() {} 280 IntRectAbsoluteTyped(ToInt aLeft, ToInt aTop, ToInt aRight, ToInt aBottom) 281 : Super(aLeft.value, aTop.value, aRight.value, aBottom.value) {} 282 }; 283 284 template <class Units> 285 struct RectAbsoluteTyped 286 : public BaseRectAbsolute<Float, RectAbsoluteTyped<Units>, 287 PointTyped<Units>, RectTyped<Units>>, 288 public Units { 289 static_assert(IsPixel<Units>::value, 290 "'units' must be a coordinate system tag"); 291 typedef BaseRectAbsolute<Float, RectAbsoluteTyped<Units>, PointTyped<Units>, 292 RectTyped<Units>> 293 Super; 294 295 RectAbsoluteTyped() : Super() {} 296 RectAbsoluteTyped(Float aLeft, Float aTop, Float aRight, Float aBottom) 297 : Super(aLeft, aTop, aRight, aBottom) {} 298 }; 299 300 typedef IntRectAbsoluteTyped<UnknownUnits> IntRectAbsolute; 301 302 } // namespace gfx 303 } // namespace mozilla 304 305 #endif /* MOZILLA_GFX_RECT_ABSOLUTE_H_ */