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/. */
7 #ifndef MOZILLA_GFX_RECT_H_
8 #define MOZILLA_GFX_RECT_H_
11 #include "BaseMargin.h"
12 #include "NumericTools.h"
15 #include "mozilla/Maybe.h"
27 template <class Units
, class F
>
30 template <class Units
>
31 struct MOZ_EMPTY_BASES IntMarginTyped
32 : public BaseMargin
<int32_t, IntMarginTyped
<Units
>, IntCoordTyped
<Units
> >,
34 static_assert(IsPixel
<Units
>::value
,
35 "'Units' must be a coordinate system tag");
37 typedef IntCoordTyped
<Units
> Coord
;
38 typedef BaseMargin
<int32_t, IntMarginTyped
<Units
>, Coord
> Super
;
40 IntMarginTyped() : Super() {
41 static_assert(sizeof(IntMarginTyped
) == sizeof(int32_t) * 4,
42 "Would be unfortunate otherwise!");
44 IntMarginTyped(Coord aTop
, Coord aRight
, Coord aBottom
, Coord aLeft
)
45 : Super(aTop
, aRight
, aBottom
, aLeft
) {}
47 // XXX When all of the code is ported, the following functions to convert
48 // to and from unknown types should be removed.
50 static IntMarginTyped
<Units
> FromUnknownMargin(
51 const IntMarginTyped
<UnknownUnits
>& aMargin
) {
52 return IntMarginTyped
<Units
>(aMargin
.top
.value
, aMargin
.right
.value
,
53 aMargin
.bottom
.value
, aMargin
.left
.value
);
56 IntMarginTyped
<UnknownUnits
> ToUnknownMargin() const {
57 return IntMarginTyped
<UnknownUnits
>(this->top
, this->right
, this->bottom
,
61 typedef IntMarginTyped
<UnknownUnits
> IntMargin
;
63 template <class Units
, class F
= Float
>
65 : public BaseMargin
<F
, MarginTyped
<Units
, F
>, CoordTyped
<Units
, F
> >,
67 static_assert(IsPixel
<Units
>::value
,
68 "'Units' must be a coordinate system tag");
70 typedef CoordTyped
<Units
, F
> Coord
;
71 typedef BaseMargin
<F
, MarginTyped
<Units
, F
>, Coord
> Super
;
73 MarginTyped() : Super() {}
74 MarginTyped(Coord aTop
, Coord aRight
, Coord aBottom
, Coord aLeft
)
75 : Super(aTop
, aRight
, aBottom
, aLeft
) {}
76 explicit MarginTyped(const IntMarginTyped
<Units
>& aMargin
)
77 : Super(F(aMargin
.top
), F(aMargin
.right
), F(aMargin
.bottom
),
80 bool WithinEpsilonOf(const MarginTyped
& aOther
, F aEpsilon
) const {
81 return fabs(this->left
- aOther
.left
) < aEpsilon
&&
82 fabs(this->top
- aOther
.top
) < aEpsilon
&&
83 fabs(this->right
- aOther
.right
) < aEpsilon
&&
84 fabs(this->bottom
- aOther
.bottom
) < aEpsilon
;
87 IntMarginTyped
<Units
> Rounded() const {
88 return IntMarginTyped
<Units
>(int32_t(std::floor(this->top
+ 0.5f
)),
89 int32_t(std::floor(this->right
+ 0.5f
)),
90 int32_t(std::floor(this->bottom
+ 0.5f
)),
91 int32_t(std::floor(this->left
+ 0.5f
)));
94 typedef MarginTyped
<UnknownUnits
> Margin
;
95 typedef MarginTyped
<UnknownUnits
, double> MarginDouble
;
97 template <class Units
>
98 IntMarginTyped
<Units
> RoundedToInt(const MarginTyped
<Units
>& aMargin
) {
99 return aMargin
.Rounded();
102 template <class Units
>
103 struct MOZ_EMPTY_BASES IntRectTyped
104 : public BaseRect
<int32_t, IntRectTyped
<Units
>, IntPointTyped
<Units
>,
105 IntSizeTyped
<Units
>, IntMarginTyped
<Units
> >,
107 static_assert(IsPixel
<Units
>::value
,
108 "'Units' must be a coordinate system tag");
110 typedef BaseRect
<int32_t, IntRectTyped
<Units
>, IntPointTyped
<Units
>,
111 IntSizeTyped
<Units
>, IntMarginTyped
<Units
> >
113 typedef IntRectTyped
<Units
> Self
;
114 typedef IntParam
<int32_t> ToInt
;
116 IntRectTyped() : Super() {
117 static_assert(sizeof(IntRectTyped
) == sizeof(int32_t) * 4,
118 "Would be unfortunate otherwise!");
120 IntRectTyped(const IntPointTyped
<Units
>& aPos
,
121 const IntSizeTyped
<Units
>& aSize
)
122 : Super(aPos
, aSize
) {}
124 IntRectTyped(ToInt aX
, ToInt aY
, ToInt aWidth
, ToInt aHeight
)
125 : Super(aX
.value
, aY
.value
, aWidth
.value
, aHeight
.value
) {}
127 static IntRectTyped
<Units
> RoundIn(float aX
, float aY
, float aW
, float aH
) {
128 return IntRectTyped
<Units
>::RoundIn(
129 RectTyped
<Units
, float>(aX
, aY
, aW
, aH
));
132 static IntRectTyped
<Units
> RoundOut(float aX
, float aY
, float aW
, float aH
) {
133 return IntRectTyped
<Units
>::RoundOut(
134 RectTyped
<Units
, float>(aX
, aY
, aW
, aH
));
137 static IntRectTyped
<Units
> Round(float aX
, float aY
, float aW
, float aH
) {
138 return IntRectTyped
<Units
>::Round(RectTyped
<Units
, float>(aX
, aY
, aW
, aH
));
141 static IntRectTyped
<Units
> Truncate(float aX
, float aY
, float aW
, float aH
) {
142 return IntRectTyped
<Units
>(IntPointTyped
<Units
>::Truncate(aX
, aY
),
143 IntSizeTyped
<Units
>::Truncate(aW
, aH
));
146 static IntRectTyped
<Units
> RoundIn(const RectTyped
<Units
, float>& aRect
) {
149 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
150 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
153 static IntRectTyped
<Units
> RoundOut(const RectTyped
<Units
, float>& aRect
) {
156 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
157 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
160 static IntRectTyped
<Units
> Round(const RectTyped
<Units
, float>& aRect
) {
163 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
164 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
167 static IntRectTyped
<Units
> Truncate(const RectTyped
<Units
, float>& aRect
) {
168 return IntRectTyped::Truncate(aRect
.X(), aRect
.Y(), aRect
.Width(),
172 // Rounding isn't meaningful on an integer rectangle.
177 // XXX When all of the code is ported, the following functions to convert
178 // to and from unknown types should be removed.
180 static IntRectTyped
<Units
> FromUnknownRect(
181 const IntRectTyped
<UnknownUnits
>& rect
) {
182 return IntRectTyped
<Units
>(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height());
185 IntRectTyped
<UnknownUnits
> ToUnknownRect() const {
186 return IntRectTyped
<UnknownUnits
>(this->X(), this->Y(), this->Width(),
190 bool Overflows() const {
191 CheckedInt
<int32_t> xMost
= this->X();
192 xMost
+= this->Width();
193 CheckedInt
<int32_t> yMost
= this->Y();
194 yMost
+= this->Height();
195 return !xMost
.isValid() || !yMost
.isValid();
198 // Same as Union(), but in the cases where aRect is non-empty, the union is
199 // done while guarding against overflow. If an overflow is detected, Nothing
201 [[nodiscard
]] Maybe
<Self
> SafeUnion(const Self
& aRect
) const {
202 if (this->IsEmpty()) {
203 return aRect
.Overflows() ? Nothing() : Some(aRect
);
204 } else if (aRect
.IsEmpty()) {
205 return Some(*static_cast<const Self
*>(this));
207 return this->SafeUnionEdges(aRect
);
211 // Same as UnionEdges, but guards against overflow. If an overflow is
212 // detected, Nothing is returned.
213 [[nodiscard
]] Maybe
<Self
> SafeUnionEdges(const Self
& aRect
) const {
214 if (this->Overflows() || aRect
.Overflows()) {
217 // If neither |this| nor |aRect| overflow, then their XMost/YMost values
218 // should be safe to use.
219 CheckedInt
<int32_t> newX
= std::min(this->x
, aRect
.x
);
220 CheckedInt
<int32_t> newY
= std::min(this->y
, aRect
.y
);
221 CheckedInt
<int32_t> newXMost
= std::max(this->XMost(), aRect
.XMost());
222 CheckedInt
<int32_t> newYMost
= std::max(this->YMost(), aRect
.YMost());
223 CheckedInt
<int32_t> newW
= newXMost
- newX
;
224 CheckedInt
<int32_t> newH
= newYMost
- newY
;
225 if (!newW
.isValid() || !newH
.isValid()) {
228 return Some(Self(newX
.value(), newY
.value(), newW
.value(), newH
.value()));
231 // This is here only to keep IPDL-generated code happy. DO NOT USE.
232 bool operator==(const IntRectTyped
<Units
>& aRect
) const {
233 return IntRectTyped
<Units
>::IsEqualEdges(aRect
);
236 void InflateToMultiple(const IntSizeTyped
<Units
>& aTileSize
) {
237 if (this->IsEmpty()) {
241 int32_t yMost
= this->YMost();
242 int32_t xMost
= this->XMost();
244 this->x
= mozilla::RoundDownToMultiple(this->x
, aTileSize
.width
);
245 this->y
= mozilla::RoundDownToMultiple(this->y
, aTileSize
.height
);
246 xMost
= mozilla::RoundUpToMultiple(xMost
, aTileSize
.width
);
247 yMost
= mozilla::RoundUpToMultiple(yMost
, aTileSize
.height
);
249 this->SetWidth(xMost
- this->x
);
250 this->SetHeight(yMost
- this->y
);
253 typedef IntRectTyped
<UnknownUnits
> IntRect
;
255 template <class Units
, class F
= Float
>
256 struct MOZ_EMPTY_BASES RectTyped
257 : public BaseRect
<F
, RectTyped
<Units
, F
>, PointTyped
<Units
, F
>,
258 SizeTyped
<Units
, F
>, MarginTyped
<Units
, F
> >,
260 static_assert(IsPixel
<Units
>::value
,
261 "'Units' must be a coordinate system tag");
263 typedef BaseRect
<F
, RectTyped
<Units
, F
>, PointTyped
<Units
, F
>,
264 SizeTyped
<Units
, F
>, MarginTyped
<Units
, F
> >
267 RectTyped() : Super() {
268 static_assert(sizeof(RectTyped
) == sizeof(F
) * 4,
269 "Would be unfortunate otherwise!");
271 RectTyped(const PointTyped
<Units
, F
>& aPos
, const SizeTyped
<Units
, F
>& aSize
)
272 : Super(aPos
, aSize
) {}
273 RectTyped(F _x
, F _y
, F _width
, F _height
) : Super(_x
, _y
, _width
, _height
) {}
274 explicit RectTyped(const IntRectTyped
<Units
>& rect
)
275 : Super(F(rect
.X()), F(rect
.Y()), F(rect
.Width()), F(rect
.Height())) {}
277 void NudgeToIntegers() {
278 NudgeToInteger(&(this->x
));
279 NudgeToInteger(&(this->y
));
280 NudgeToInteger(&(this->width
));
281 NudgeToInteger(&(this->height
));
284 bool ToIntRect(IntRectTyped
<Units
>* aOut
) const {
286 IntRectTyped
<Units
>(int32_t(this->X()), int32_t(this->Y()),
287 int32_t(this->Width()), int32_t(this->Height()));
288 return RectTyped
<Units
, F
>(F(aOut
->X()), F(aOut
->Y()), F(aOut
->Width()),
290 .IsEqualEdges(*this);
293 // XXX When all of the code is ported, the following functions to convert to
294 // and from unknown types should be removed.
296 static RectTyped
<Units
, F
> FromUnknownRect(
297 const RectTyped
<UnknownUnits
, F
>& rect
) {
298 return RectTyped
<Units
, F
>(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height());
301 RectTyped
<UnknownUnits
, F
> ToUnknownRect() const {
302 return RectTyped
<UnknownUnits
, F
>(this->X(), this->Y(), this->Width(),
306 // This is here only to keep IPDL-generated code happy. DO NOT USE.
307 bool operator==(const RectTyped
<Units
, F
>& aRect
) const {
308 return RectTyped
<Units
, F
>::IsEqualEdges(aRect
);
311 bool WithinEpsilonOf(const RectTyped
& aOther
, F aEpsilon
) const {
312 return fabs(this->x
- aOther
.x
) < aEpsilon
&&
313 fabs(this->y
- aOther
.y
) < aEpsilon
&&
314 fabs(this->width
- aOther
.width
) < aEpsilon
&&
315 fabs(this->height
- aOther
.height
) < aEpsilon
;
318 typedef RectTyped
<UnknownUnits
> Rect
;
319 typedef RectTyped
<UnknownUnits
, double> RectDouble
;
321 template <class Units
>
322 IntRectTyped
<Units
> RoundedToInt(const RectTyped
<Units
>& aRect
) {
323 RectTyped
<Units
> copy(aRect
);
325 return IntRectTyped
<Units
>(int32_t(copy
.X()), int32_t(copy
.Y()),
326 int32_t(copy
.Width()), int32_t(copy
.Height()));
329 template <class Units
>
330 bool RectIsInt32Safe(const RectTyped
<Units
>& aRect
) {
331 float min
= (float)std::numeric_limits
<std::int32_t>::min();
332 float max
= (float)std::numeric_limits
<std::int32_t>::max();
333 return aRect
.x
> min
&& aRect
.y
> min
&& aRect
.width
< max
&&
334 aRect
.height
< max
&& aRect
.XMost() < max
&& aRect
.YMost() < max
;
337 template <class Units
>
338 IntRectTyped
<Units
> RoundedIn(const RectTyped
<Units
>& aRect
) {
339 return IntRectTyped
<Units
>::RoundIn(aRect
);
342 template <class Units
>
343 IntRectTyped
<Units
> RoundedOut(const RectTyped
<Units
>& aRect
) {
344 return IntRectTyped
<Units
>::RoundOut(aRect
);
347 template <class Units
>
348 IntRectTyped
<Units
> TruncatedToInt(const RectTyped
<Units
>& aRect
) {
349 return IntRectTyped
<Units
>::Truncate(aRect
);
352 template <class Units
>
353 RectTyped
<Units
> IntRectToRect(const IntRectTyped
<Units
>& aRect
) {
354 return RectTyped
<Units
>(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height());
357 // Convenience functions for intersecting and unioning two rectangles wrapped in
359 template <typename Rect
>
360 Maybe
<Rect
> IntersectMaybeRects(const Maybe
<Rect
>& a
, const Maybe
<Rect
>& b
) {
366 return Some(a
->Intersect(*b
));
369 template <typename Rect
>
370 Maybe
<Rect
> UnionMaybeRects(const Maybe
<Rect
>& a
, const Maybe
<Rect
>& b
) {
376 return Some(a
->Union(*b
));
380 struct RectCornerRadii final
{
381 Size radii
[eCornerCount
];
383 RectCornerRadii() = default;
385 explicit RectCornerRadii(Float radius
) {
386 for (const auto i
: mozilla::AllPhysicalCorners()) {
387 radii
[i
].SizeTo(radius
, radius
);
391 RectCornerRadii(Float radiusX
, Float radiusY
) {
392 for (const auto i
: mozilla::AllPhysicalCorners()) {
393 radii
[i
].SizeTo(radiusX
, radiusY
);
397 RectCornerRadii(Float tl
, Float tr
, Float br
, Float bl
) {
398 radii
[eCornerTopLeft
].SizeTo(tl
, tl
);
399 radii
[eCornerTopRight
].SizeTo(tr
, tr
);
400 radii
[eCornerBottomRight
].SizeTo(br
, br
);
401 radii
[eCornerBottomLeft
].SizeTo(bl
, bl
);
404 RectCornerRadii(const Size
& tl
, const Size
& tr
, const Size
& br
,
406 radii
[eCornerTopLeft
] = tl
;
407 radii
[eCornerTopRight
] = tr
;
408 radii
[eCornerBottomRight
] = br
;
409 radii
[eCornerBottomLeft
] = bl
;
412 const Size
& operator[](size_t aCorner
) const { return radii
[aCorner
]; }
414 Size
& operator[](size_t aCorner
) { return radii
[aCorner
]; }
416 bool operator==(const RectCornerRadii
& aOther
) const {
417 return TopLeft() == aOther
.TopLeft() && TopRight() == aOther
.TopRight() &&
418 BottomRight() == aOther
.BottomRight() &&
419 BottomLeft() == aOther
.BottomLeft();
422 bool AreRadiiSame() const {
423 return TopLeft() == TopRight() && TopLeft() == BottomRight() &&
424 TopLeft() == BottomLeft();
427 void Scale(Float aXScale
, Float aYScale
) {
428 for (const auto i
: mozilla::AllPhysicalCorners()) {
429 radii
[i
].Scale(aXScale
, aYScale
);
433 const Size
TopLeft() const { return radii
[eCornerTopLeft
]; }
434 Size
& TopLeft() { return radii
[eCornerTopLeft
]; }
436 const Size
TopRight() const { return radii
[eCornerTopRight
]; }
437 Size
& TopRight() { return radii
[eCornerTopRight
]; }
439 const Size
BottomRight() const { return radii
[eCornerBottomRight
]; }
440 Size
& BottomRight() { return radii
[eCornerBottomRight
]; }
442 const Size
BottomLeft() const { return radii
[eCornerBottomLeft
]; }
443 Size
& BottomLeft() { return radii
[eCornerBottomLeft
]; }
445 bool IsEmpty() const {
446 return TopLeft().IsEmpty() && TopRight().IsEmpty() &&
447 BottomRight().IsEmpty() && BottomLeft().IsEmpty();
451 /* A rounded rectangle abstraction.
453 * This can represent a rectangle with a different pair of radii on each corner.
455 * Note: CoreGraphics and Direct2D only support rounded rectangle with the same
456 * radii on all corners. However, supporting CSS's border-radius requires the
457 * extra flexibility. */
459 typedef mozilla::gfx::RectCornerRadii RectCornerRadii
;
461 RoundedRect(const Rect
& aRect
, const RectCornerRadii
& aCorners
)
462 : rect(aRect
), corners(aCorners
) {}
464 void Deflate(Float aTopWidth
, Float aBottomWidth
, Float aLeftWidth
,
466 // deflate the internal rect
467 rect
.SetRect(rect
.X() + aLeftWidth
, rect
.Y() + aTopWidth
,
468 std::max(0.f
, rect
.Width() - aLeftWidth
- aRightWidth
),
469 std::max(0.f
, rect
.Height() - aTopWidth
- aBottomWidth
));
471 corners
.radii
[mozilla::eCornerTopLeft
].width
= std::max(
472 0.f
, corners
.radii
[mozilla::eCornerTopLeft
].width
- aLeftWidth
);
473 corners
.radii
[mozilla::eCornerTopLeft
].height
= std::max(
474 0.f
, corners
.radii
[mozilla::eCornerTopLeft
].height
- aTopWidth
);
476 corners
.radii
[mozilla::eCornerTopRight
].width
= std::max(
477 0.f
, corners
.radii
[mozilla::eCornerTopRight
].width
- aRightWidth
);
478 corners
.radii
[mozilla::eCornerTopRight
].height
= std::max(
479 0.f
, corners
.radii
[mozilla::eCornerTopRight
].height
- aTopWidth
);
481 corners
.radii
[mozilla::eCornerBottomLeft
].width
= std::max(
482 0.f
, corners
.radii
[mozilla::eCornerBottomLeft
].width
- aLeftWidth
);
483 corners
.radii
[mozilla::eCornerBottomLeft
].height
= std::max(
484 0.f
, corners
.radii
[mozilla::eCornerBottomLeft
].height
- aBottomWidth
);
486 corners
.radii
[mozilla::eCornerBottomRight
].width
= std::max(
487 0.f
, corners
.radii
[mozilla::eCornerBottomRight
].width
- aRightWidth
);
488 corners
.radii
[mozilla::eCornerBottomRight
].height
= std::max(
489 0.f
, corners
.radii
[mozilla::eCornerBottomRight
].height
- aBottomWidth
);
492 RectCornerRadii corners
;
496 } // namespace mozilla
498 #endif /* MOZILLA_GFX_RECT_H_ */