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"
26 template <class units
, class F
>
29 template <class units
>
30 struct IntMarginTyped
: public BaseMargin
<int32_t, IntMarginTyped
<units
> >,
32 static_assert(IsPixel
<units
>::value
,
33 "'units' must be a coordinate system tag");
35 typedef BaseMargin
<int32_t, IntMarginTyped
<units
> > Super
;
37 IntMarginTyped() : Super() {}
38 IntMarginTyped(int32_t aTop
, int32_t aRight
, int32_t aBottom
, int32_t aLeft
)
39 : Super(aTop
, aRight
, aBottom
, aLeft
) {}
41 // XXX When all of the code is ported, the following functions to convert
42 // to and from unknown types should be removed.
44 static IntMarginTyped
<units
> FromUnknownMargin(
45 const IntMarginTyped
<UnknownUnits
>& aMargin
) {
46 return IntMarginTyped
<units
>(aMargin
.top
, aMargin
.right
, aMargin
.bottom
,
50 IntMarginTyped
<UnknownUnits
> ToUnknownMargin() const {
51 return IntMarginTyped
<UnknownUnits
>(this->top
, this->right
, this->bottom
,
55 typedef IntMarginTyped
<UnknownUnits
> IntMargin
;
57 template <class units
, class F
= Float
>
58 struct MarginTyped
: public BaseMargin
<F
, MarginTyped
<units
, F
> >,
60 static_assert(IsPixel
<units
>::value
,
61 "'units' must be a coordinate system tag");
63 typedef BaseMargin
<F
, MarginTyped
<units
, F
> > Super
;
65 MarginTyped() : Super() {}
66 MarginTyped(F aTop
, F aRight
, F aBottom
, F aLeft
)
67 : Super(aTop
, aRight
, aBottom
, aLeft
) {}
68 explicit MarginTyped(const IntMarginTyped
<units
>& aMargin
)
69 : Super(F(aMargin
.top
), F(aMargin
.right
), F(aMargin
.bottom
),
72 bool WithinEpsilonOf(const MarginTyped
& aOther
, F aEpsilon
) const {
73 return fabs(this->left
- aOther
.left
) < aEpsilon
&&
74 fabs(this->top
- aOther
.top
) < aEpsilon
&&
75 fabs(this->right
- aOther
.right
) < aEpsilon
&&
76 fabs(this->bottom
- aOther
.bottom
) < aEpsilon
;
79 IntMarginTyped
<units
> Rounded() const {
80 return IntMarginTyped
<units
>(int32_t(std::floor(this->top
+ 0.5f
)),
81 int32_t(std::floor(this->right
+ 0.5f
)),
82 int32_t(std::floor(this->bottom
+ 0.5f
)),
83 int32_t(std::floor(this->left
+ 0.5f
)));
86 typedef MarginTyped
<UnknownUnits
> Margin
;
87 typedef MarginTyped
<UnknownUnits
, double> MarginDouble
;
89 template <class units
>
90 IntMarginTyped
<units
> RoundedToInt(const MarginTyped
<units
>& aMargin
) {
91 return aMargin
.Rounded();
94 template <class units
>
96 : public BaseRect
<int32_t, IntRectTyped
<units
>, IntPointTyped
<units
>,
97 IntSizeTyped
<units
>, IntMarginTyped
<units
> >,
99 static_assert(IsPixel
<units
>::value
,
100 "'units' must be a coordinate system tag");
102 typedef BaseRect
<int32_t, IntRectTyped
<units
>, IntPointTyped
<units
>,
103 IntSizeTyped
<units
>, IntMarginTyped
<units
> >
105 typedef IntRectTyped
<units
> Self
;
106 typedef IntParam
<int32_t> ToInt
;
108 IntRectTyped() : Super() {}
109 IntRectTyped(const IntPointTyped
<units
>& aPos
,
110 const IntSizeTyped
<units
>& aSize
)
111 : Super(aPos
, aSize
) {}
113 IntRectTyped(ToInt aX
, ToInt aY
, ToInt aWidth
, ToInt aHeight
)
114 : Super(aX
.value
, aY
.value
, aWidth
.value
, aHeight
.value
) {}
116 static IntRectTyped
<units
> RoundIn(float aX
, float aY
, float aW
, float aH
) {
117 return IntRectTyped
<units
>::RoundIn(
118 RectTyped
<units
, float>(aX
, aY
, aW
, aH
));
121 static IntRectTyped
<units
> RoundOut(float aX
, float aY
, float aW
, float aH
) {
122 return IntRectTyped
<units
>::RoundOut(
123 RectTyped
<units
, float>(aX
, aY
, aW
, aH
));
126 static IntRectTyped
<units
> Round(float aX
, float aY
, float aW
, float aH
) {
127 return IntRectTyped
<units
>::Round(RectTyped
<units
, float>(aX
, aY
, aW
, aH
));
130 static IntRectTyped
<units
> Truncate(float aX
, float aY
, float aW
, float aH
) {
131 return IntRectTyped
<units
>(IntPointTyped
<units
>::Truncate(aX
, aY
),
132 IntSizeTyped
<units
>::Truncate(aW
, aH
));
135 static IntRectTyped
<units
> RoundIn(const RectTyped
<units
, float>& aRect
) {
138 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
139 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
142 static IntRectTyped
<units
> RoundOut(const RectTyped
<units
, float>& aRect
) {
145 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
146 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
149 static IntRectTyped
<units
> Round(const RectTyped
<units
, float>& aRect
) {
152 return IntRectTyped(int32_t(tmp
.X()), int32_t(tmp
.Y()),
153 int32_t(tmp
.Width()), int32_t(tmp
.Height()));
156 static IntRectTyped
<units
> Truncate(const RectTyped
<units
, float>& aRect
) {
157 return IntRectTyped::Truncate(aRect
.X(), aRect
.Y(), aRect
.Width(),
161 // Rounding isn't meaningful on an integer rectangle.
166 // XXX When all of the code is ported, the following functions to convert
167 // to and from unknown types should be removed.
169 static IntRectTyped
<units
> FromUnknownRect(
170 const IntRectTyped
<UnknownUnits
>& rect
) {
171 return IntRectTyped
<units
>(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height());
174 IntRectTyped
<UnknownUnits
> ToUnknownRect() const {
175 return IntRectTyped
<UnknownUnits
>(this->X(), this->Y(), this->Width(),
179 bool Overflows() const {
180 CheckedInt
<int32_t> xMost
= this->X();
181 xMost
+= this->Width();
182 CheckedInt
<int32_t> yMost
= this->Y();
183 yMost
+= this->Height();
184 return !xMost
.isValid() || !yMost
.isValid();
187 // Same as Union(), but in the cases where aRect is non-empty, the union is
188 // done while guarding against overflow. If an overflow is detected, Nothing
190 [[nodiscard
]] Maybe
<Self
> SafeUnion(const Self
& aRect
) const {
191 if (this->IsEmpty()) {
192 return aRect
.Overflows() ? Nothing() : Some(aRect
);
193 } else if (aRect
.IsEmpty()) {
194 return Some(*static_cast<const Self
*>(this));
196 return this->SafeUnionEdges(aRect
);
200 // Same as UnionEdges, but guards against overflow. If an overflow is
201 // detected, Nothing is returned.
202 [[nodiscard
]] Maybe
<Self
> SafeUnionEdges(const Self
& aRect
) const {
203 if (this->Overflows() || aRect
.Overflows()) {
206 // If neither |this| nor |aRect| overflow, then their XMost/YMost values
207 // should be safe to use.
208 CheckedInt
<int32_t> newX
= std::min(this->x
, aRect
.x
);
209 CheckedInt
<int32_t> newY
= std::min(this->y
, aRect
.y
);
210 CheckedInt
<int32_t> newXMost
= std::max(this->XMost(), aRect
.XMost());
211 CheckedInt
<int32_t> newYMost
= std::max(this->YMost(), aRect
.YMost());
212 CheckedInt
<int32_t> newW
= newXMost
- newX
;
213 CheckedInt
<int32_t> newH
= newYMost
- newY
;
214 if (!newW
.isValid() || !newH
.isValid()) {
217 return Some(Self(newX
.value(), newY
.value(), newW
.value(), newH
.value()));
220 // This is here only to keep IPDL-generated code happy. DO NOT USE.
221 bool operator==(const IntRectTyped
<units
>& aRect
) const {
222 return IntRectTyped
<units
>::IsEqualEdges(aRect
);
225 void InflateToMultiple(const IntSizeTyped
<units
>& aTileSize
) {
226 if (this->IsEmpty()) {
230 int32_t yMost
= this->YMost();
231 int32_t xMost
= this->XMost();
233 this->x
= mozilla::RoundDownToMultiple(this->x
, aTileSize
.width
);
234 this->y
= mozilla::RoundDownToMultiple(this->y
, aTileSize
.height
);
235 xMost
= mozilla::RoundUpToMultiple(xMost
, aTileSize
.width
);
236 yMost
= mozilla::RoundUpToMultiple(yMost
, aTileSize
.height
);
238 this->SetWidth(xMost
- this->x
);
239 this->SetHeight(yMost
- this->y
);
242 typedef IntRectTyped
<UnknownUnits
> IntRect
;
244 template <class units
, class F
= Float
>
245 struct RectTyped
: public BaseRect
<F
, RectTyped
<units
, F
>, PointTyped
<units
, F
>,
246 SizeTyped
<units
, F
>, MarginTyped
<units
, F
> >,
248 static_assert(IsPixel
<units
>::value
,
249 "'units' must be a coordinate system tag");
251 typedef BaseRect
<F
, RectTyped
<units
, F
>, PointTyped
<units
, F
>,
252 SizeTyped
<units
, F
>, MarginTyped
<units
, F
> >
255 RectTyped() : Super() {}
256 RectTyped(const PointTyped
<units
, F
>& aPos
, const SizeTyped
<units
, F
>& aSize
)
257 : Super(aPos
, aSize
) {}
258 RectTyped(F _x
, F _y
, F _width
, F _height
) : Super(_x
, _y
, _width
, _height
) {}
259 explicit RectTyped(const IntRectTyped
<units
>& rect
)
260 : Super(F(rect
.X()), F(rect
.Y()), F(rect
.Width()), F(rect
.Height())) {}
262 void NudgeToIntegers() {
263 NudgeToInteger(&(this->x
));
264 NudgeToInteger(&(this->y
));
265 NudgeToInteger(&(this->width
));
266 NudgeToInteger(&(this->height
));
269 bool ToIntRect(IntRectTyped
<units
>* aOut
) const {
271 IntRectTyped
<units
>(int32_t(this->X()), int32_t(this->Y()),
272 int32_t(this->Width()), int32_t(this->Height()));
273 return RectTyped
<units
, F
>(F(aOut
->X()), F(aOut
->Y()), F(aOut
->Width()),
275 .IsEqualEdges(*this);
278 // XXX When all of the code is ported, the following functions to convert to
279 // and from unknown types should be removed.
281 static RectTyped
<units
, F
> FromUnknownRect(
282 const RectTyped
<UnknownUnits
, F
>& rect
) {
283 return RectTyped
<units
, F
>(rect
.X(), rect
.Y(), rect
.Width(), rect
.Height());
286 RectTyped
<UnknownUnits
, F
> ToUnknownRect() const {
287 return RectTyped
<UnknownUnits
, F
>(this->X(), this->Y(), this->Width(),
291 // This is here only to keep IPDL-generated code happy. DO NOT USE.
292 bool operator==(const RectTyped
<units
, F
>& aRect
) const {
293 return RectTyped
<units
, F
>::IsEqualEdges(aRect
);
296 bool WithinEpsilonOf(const RectTyped
& aOther
, F aEpsilon
) const {
297 return fabs(this->x
- aOther
.x
) < aEpsilon
&&
298 fabs(this->y
- aOther
.y
) < aEpsilon
&&
299 fabs(this->width
- aOther
.width
) < aEpsilon
&&
300 fabs(this->height
- aOther
.height
) < aEpsilon
;
303 typedef RectTyped
<UnknownUnits
> Rect
;
304 typedef RectTyped
<UnknownUnits
, double> RectDouble
;
306 template <class units
>
307 IntRectTyped
<units
> RoundedToInt(const RectTyped
<units
>& aRect
) {
308 RectTyped
<units
> copy(aRect
);
310 return IntRectTyped
<units
>(int32_t(copy
.X()), int32_t(copy
.Y()),
311 int32_t(copy
.Width()), int32_t(copy
.Height()));
314 template <class units
>
315 bool RectIsInt32Safe(const RectTyped
<units
>& aRect
) {
316 float min
= (float)std::numeric_limits
<std::int32_t>::min();
317 float max
= (float)std::numeric_limits
<std::int32_t>::max();
318 return aRect
.x
> min
&& aRect
.y
> min
&& aRect
.width
< max
&&
319 aRect
.height
< max
&& aRect
.XMost() < max
&& aRect
.YMost() < max
;
322 template <class units
>
323 IntRectTyped
<units
> RoundedIn(const RectTyped
<units
>& aRect
) {
324 return IntRectTyped
<units
>::RoundIn(aRect
);
327 template <class units
>
328 IntRectTyped
<units
> RoundedOut(const RectTyped
<units
>& aRect
) {
329 return IntRectTyped
<units
>::RoundOut(aRect
);
332 template <class units
>
333 IntRectTyped
<units
> TruncatedToInt(const RectTyped
<units
>& aRect
) {
334 return IntRectTyped
<units
>::Truncate(aRect
);
337 template <class units
>
338 RectTyped
<units
> IntRectToRect(const IntRectTyped
<units
>& aRect
) {
339 return RectTyped
<units
>(aRect
.X(), aRect
.Y(), aRect
.Width(), aRect
.Height());
342 // Convenience functions for intersecting and unioning two rectangles wrapped in
344 template <typename Rect
>
345 Maybe
<Rect
> IntersectMaybeRects(const Maybe
<Rect
>& a
, const Maybe
<Rect
>& b
) {
351 return Some(a
->Intersect(*b
));
354 template <typename Rect
>
355 Maybe
<Rect
> UnionMaybeRects(const Maybe
<Rect
>& a
, const Maybe
<Rect
>& b
) {
361 return Some(a
->Union(*b
));
365 struct RectCornerRadii final
{
366 Size radii
[eCornerCount
];
368 RectCornerRadii() = default;
370 explicit RectCornerRadii(Float radius
) {
371 for (const auto i
: mozilla::AllPhysicalCorners()) {
372 radii
[i
].SizeTo(radius
, radius
);
376 RectCornerRadii(Float radiusX
, Float radiusY
) {
377 for (const auto i
: mozilla::AllPhysicalCorners()) {
378 radii
[i
].SizeTo(radiusX
, radiusY
);
382 RectCornerRadii(Float tl
, Float tr
, Float br
, Float bl
) {
383 radii
[eCornerTopLeft
].SizeTo(tl
, tl
);
384 radii
[eCornerTopRight
].SizeTo(tr
, tr
);
385 radii
[eCornerBottomRight
].SizeTo(br
, br
);
386 radii
[eCornerBottomLeft
].SizeTo(bl
, bl
);
389 RectCornerRadii(const Size
& tl
, const Size
& tr
, const Size
& br
,
391 radii
[eCornerTopLeft
] = tl
;
392 radii
[eCornerTopRight
] = tr
;
393 radii
[eCornerBottomRight
] = br
;
394 radii
[eCornerBottomLeft
] = bl
;
397 const Size
& operator[](size_t aCorner
) const { return radii
[aCorner
]; }
399 Size
& operator[](size_t aCorner
) { return radii
[aCorner
]; }
401 bool operator==(const RectCornerRadii
& aOther
) const {
402 return TopLeft() == aOther
.TopLeft() && TopRight() == aOther
.TopRight() &&
403 BottomRight() == aOther
.BottomRight() &&
404 BottomLeft() == aOther
.BottomLeft();
407 bool AreRadiiSame() const {
408 return TopLeft() == TopRight() && TopLeft() == BottomRight() &&
409 TopLeft() == BottomLeft();
412 void Scale(Float aXScale
, Float aYScale
) {
413 for (const auto i
: mozilla::AllPhysicalCorners()) {
414 radii
[i
].Scale(aXScale
, aYScale
);
418 const Size
TopLeft() const { return radii
[eCornerTopLeft
]; }
419 Size
& TopLeft() { return radii
[eCornerTopLeft
]; }
421 const Size
TopRight() const { return radii
[eCornerTopRight
]; }
422 Size
& TopRight() { return radii
[eCornerTopRight
]; }
424 const Size
BottomRight() const { return radii
[eCornerBottomRight
]; }
425 Size
& BottomRight() { return radii
[eCornerBottomRight
]; }
427 const Size
BottomLeft() const { return radii
[eCornerBottomLeft
]; }
428 Size
& BottomLeft() { return radii
[eCornerBottomLeft
]; }
430 bool IsEmpty() const {
431 return TopLeft().IsEmpty() && TopRight().IsEmpty() &&
432 BottomRight().IsEmpty() && BottomLeft().IsEmpty();
436 /* A rounded rectangle abstraction.
438 * This can represent a rectangle with a different pair of radii on each corner.
440 * Note: CoreGraphics and Direct2D only support rounded rectangle with the same
441 * radii on all corners. However, supporting CSS's border-radius requires the
442 * extra flexibility. */
444 typedef mozilla::gfx::RectCornerRadii RectCornerRadii
;
446 RoundedRect(const Rect
& aRect
, const RectCornerRadii
& aCorners
)
447 : rect(aRect
), corners(aCorners
) {}
449 void Deflate(Float aTopWidth
, Float aBottomWidth
, Float aLeftWidth
,
451 // deflate the internal rect
452 rect
.SetRect(rect
.X() + aLeftWidth
, rect
.Y() + aTopWidth
,
453 std::max(0.f
, rect
.Width() - aLeftWidth
- aRightWidth
),
454 std::max(0.f
, rect
.Height() - aTopWidth
- aBottomWidth
));
456 corners
.radii
[mozilla::eCornerTopLeft
].width
= std::max(
457 0.f
, corners
.radii
[mozilla::eCornerTopLeft
].width
- aLeftWidth
);
458 corners
.radii
[mozilla::eCornerTopLeft
].height
= std::max(
459 0.f
, corners
.radii
[mozilla::eCornerTopLeft
].height
- aTopWidth
);
461 corners
.radii
[mozilla::eCornerTopRight
].width
= std::max(
462 0.f
, corners
.radii
[mozilla::eCornerTopRight
].width
- aRightWidth
);
463 corners
.radii
[mozilla::eCornerTopRight
].height
= std::max(
464 0.f
, corners
.radii
[mozilla::eCornerTopRight
].height
- aTopWidth
);
466 corners
.radii
[mozilla::eCornerBottomLeft
].width
= std::max(
467 0.f
, corners
.radii
[mozilla::eCornerBottomLeft
].width
- aLeftWidth
);
468 corners
.radii
[mozilla::eCornerBottomLeft
].height
= std::max(
469 0.f
, corners
.radii
[mozilla::eCornerBottomLeft
].height
- aBottomWidth
);
471 corners
.radii
[mozilla::eCornerBottomRight
].width
= std::max(
472 0.f
, corners
.radii
[mozilla::eCornerBottomRight
].width
- aRightWidth
);
473 corners
.radii
[mozilla::eCornerBottomRight
].height
= std::max(
474 0.f
, corners
.radii
[mozilla::eCornerBottomRight
].height
- aBottomWidth
);
477 RectCornerRadii corners
;
481 } // namespace mozilla
483 #endif /* MOZILLA_GFX_RECT_H_ */