Bug 1686668 [wpt PR 27185] - Update wpt metadata, a=testonly
[gecko.git] / gfx / 2d / Rect.h
blobd16506104b910d90338b498998716b5900f66c86
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_
10 #include "BaseRect.h"
11 #include "BaseMargin.h"
12 #include "NumericTools.h"
13 #include "Point.h"
14 #include "Tools.h"
15 #include "mozilla/Maybe.h"
17 #include <cmath>
19 namespace mozilla {
21 template <typename>
22 struct IsPixel;
24 namespace gfx {
26 template <class units, class F>
27 struct RectTyped;
29 template <class units>
30 struct IntMarginTyped : public BaseMargin<int32_t, IntMarginTyped<units> >,
31 public 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,
47 aMargin.left);
50 IntMarginTyped<UnknownUnits> ToUnknownMargin() const {
51 return IntMarginTyped<UnknownUnits>(this->top, this->right, this->bottom,
52 this->left);
55 typedef IntMarginTyped<UnknownUnits> IntMargin;
57 template <class units, class F = Float>
58 struct MarginTyped : public BaseMargin<F, MarginTyped<units, F> >,
59 public units {
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),
70 F(aMargin.left)) {}
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>
95 struct IntRectTyped
96 : public BaseRect<int32_t, IntRectTyped<units>, IntPointTyped<units>,
97 IntSizeTyped<units>, IntMarginTyped<units> >,
98 public 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> >
104 Super;
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) {
136 auto tmp(aRect);
137 tmp.RoundIn();
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) {
143 auto tmp(aRect);
144 tmp.RoundOut();
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) {
150 auto tmp(aRect);
151 tmp.Round();
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(),
158 aRect.Height());
161 // Rounding isn't meaningful on an integer rectangle.
162 void Round() {}
163 void RoundIn() {}
164 void RoundOut() {}
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(),
176 this->Height());
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
189 // is returned.
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));
195 } else {
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()) {
204 return Nothing();
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()) {
215 return Nothing();
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()) {
227 return;
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> >,
247 public units {
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> >
253 Super;
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 {
270 *aOut =
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()),
274 F(aOut->Height()))
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(),
288 this->Height());
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);
309 copy.Round();
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
343 // Maybes.
344 template <typename Rect>
345 Maybe<Rect> IntersectMaybeRects(const Maybe<Rect>& a, const Maybe<Rect>& b) {
346 if (!a) {
347 return b;
348 } else if (!b) {
349 return a;
350 } else {
351 return Some(a->Intersect(*b));
354 template <typename Rect>
355 Maybe<Rect> UnionMaybeRects(const Maybe<Rect>& a, const Maybe<Rect>& b) {
356 if (!a) {
357 return b;
358 } else if (!b) {
359 return a;
360 } else {
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,
390 const Size& bl) {
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. */
443 struct RoundedRect {
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,
450 Float aRightWidth) {
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);
476 Rect rect;
477 RectCornerRadii corners;
480 } // namespace gfx
481 } // namespace mozilla
483 #endif /* MOZILLA_GFX_RECT_H_ */