From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- gfx/2d/Rect.h | 495 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 gfx/2d/Rect.h (limited to 'gfx/2d/Rect.h') diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h new file mode 100644 index 0000000000..f52437bbdc --- /dev/null +++ b/gfx/2d/Rect.h @@ -0,0 +1,495 @@ +/* -*- 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_H_ +#define MOZILLA_GFX_RECT_H_ + +#include "BaseRect.h" +#include "BaseMargin.h" +#include "NumericTools.h" +#include "Point.h" +#include "Tools.h" +#include "mozilla/Maybe.h" + +#include +#include + +namespace mozilla { + +template +struct IsPixel; + +namespace gfx { + +template +struct RectTyped; + +template +struct MOZ_EMPTY_BASES IntMarginTyped + : public BaseMargin >, + public Units { + static_assert(IsPixel::value, + "'Units' must be a coordinate system tag"); + + typedef BaseMargin > Super; + + IntMarginTyped() : Super() { + static_assert(sizeof(IntMarginTyped) == sizeof(int32_t) * 4, + "Would be unfortunate otherwise!"); + } + IntMarginTyped(int32_t aTop, int32_t aRight, int32_t aBottom, int32_t aLeft) + : Super(aTop, aRight, aBottom, aLeft) {} + + // XXX When all of the code is ported, the following functions to convert + // to and from unknown types should be removed. + + static IntMarginTyped FromUnknownMargin( + const IntMarginTyped& aMargin) { + return IntMarginTyped(aMargin.top, aMargin.right, aMargin.bottom, + aMargin.left); + } + + IntMarginTyped ToUnknownMargin() const { + return IntMarginTyped(this->top, this->right, this->bottom, + this->left); + } +}; +typedef IntMarginTyped IntMargin; + +template +struct MarginTyped : public BaseMargin >, + public Units { + static_assert(IsPixel::value, + "'Units' must be a coordinate system tag"); + + typedef BaseMargin > Super; + + MarginTyped() : Super() {} + MarginTyped(F aTop, F aRight, F aBottom, F aLeft) + : Super(aTop, aRight, aBottom, aLeft) {} + explicit MarginTyped(const IntMarginTyped& aMargin) + : Super(F(aMargin.top), F(aMargin.right), F(aMargin.bottom), + F(aMargin.left)) {} + + bool WithinEpsilonOf(const MarginTyped& aOther, F aEpsilon) const { + return fabs(this->left - aOther.left) < aEpsilon && + fabs(this->top - aOther.top) < aEpsilon && + fabs(this->right - aOther.right) < aEpsilon && + fabs(this->bottom - aOther.bottom) < aEpsilon; + } + + IntMarginTyped Rounded() const { + return IntMarginTyped(int32_t(std::floor(this->top + 0.5f)), + int32_t(std::floor(this->right + 0.5f)), + int32_t(std::floor(this->bottom + 0.5f)), + int32_t(std::floor(this->left + 0.5f))); + } +}; +typedef MarginTyped Margin; +typedef MarginTyped MarginDouble; + +template +IntMarginTyped RoundedToInt(const MarginTyped& aMargin) { + return aMargin.Rounded(); +} + +template +struct MOZ_EMPTY_BASES IntRectTyped + : public BaseRect, IntPointTyped, + IntSizeTyped, IntMarginTyped >, + public Units { + static_assert(IsPixel::value, + "'Units' must be a coordinate system tag"); + + typedef BaseRect, IntPointTyped, + IntSizeTyped, IntMarginTyped > + Super; + typedef IntRectTyped Self; + typedef IntParam ToInt; + + IntRectTyped() : Super() { + static_assert(sizeof(IntRectTyped) == sizeof(int32_t) * 4, + "Would be unfortunate otherwise!"); + } + IntRectTyped(const IntPointTyped& aPos, + const IntSizeTyped& aSize) + : Super(aPos, aSize) {} + + IntRectTyped(ToInt aX, ToInt aY, ToInt aWidth, ToInt aHeight) + : Super(aX.value, aY.value, aWidth.value, aHeight.value) {} + + static IntRectTyped RoundIn(float aX, float aY, float aW, float aH) { + return IntRectTyped::RoundIn( + RectTyped(aX, aY, aW, aH)); + } + + static IntRectTyped RoundOut(float aX, float aY, float aW, float aH) { + return IntRectTyped::RoundOut( + RectTyped(aX, aY, aW, aH)); + } + + static IntRectTyped Round(float aX, float aY, float aW, float aH) { + return IntRectTyped::Round(RectTyped(aX, aY, aW, aH)); + } + + static IntRectTyped Truncate(float aX, float aY, float aW, float aH) { + return IntRectTyped(IntPointTyped::Truncate(aX, aY), + IntSizeTyped::Truncate(aW, aH)); + } + + static IntRectTyped RoundIn(const RectTyped& aRect) { + auto tmp(aRect); + tmp.RoundIn(); + return IntRectTyped(int32_t(tmp.X()), int32_t(tmp.Y()), + int32_t(tmp.Width()), int32_t(tmp.Height())); + } + + static IntRectTyped RoundOut(const RectTyped& aRect) { + auto tmp(aRect); + tmp.RoundOut(); + return IntRectTyped(int32_t(tmp.X()), int32_t(tmp.Y()), + int32_t(tmp.Width()), int32_t(tmp.Height())); + } + + static IntRectTyped Round(const RectTyped& aRect) { + auto tmp(aRect); + tmp.Round(); + return IntRectTyped(int32_t(tmp.X()), int32_t(tmp.Y()), + int32_t(tmp.Width()), int32_t(tmp.Height())); + } + + static IntRectTyped Truncate(const RectTyped& aRect) { + return IntRectTyped::Truncate(aRect.X(), aRect.Y(), aRect.Width(), + aRect.Height()); + } + + // Rounding isn't meaningful on an integer rectangle. + void Round() {} + void RoundIn() {} + void RoundOut() {} + + // XXX When all of the code is ported, the following functions to convert + // to and from unknown types should be removed. + + static IntRectTyped FromUnknownRect( + const IntRectTyped& rect) { + return IntRectTyped(rect.X(), rect.Y(), rect.Width(), rect.Height()); + } + + IntRectTyped ToUnknownRect() const { + return IntRectTyped(this->X(), this->Y(), this->Width(), + this->Height()); + } + + bool Overflows() const { + CheckedInt xMost = this->X(); + xMost += this->Width(); + CheckedInt yMost = this->Y(); + yMost += this->Height(); + return !xMost.isValid() || !yMost.isValid(); + } + + // Same as Union(), but in the cases where aRect is non-empty, the union is + // done while guarding against overflow. If an overflow is detected, Nothing + // is returned. + [[nodiscard]] Maybe SafeUnion(const Self& aRect) const { + if (this->IsEmpty()) { + return aRect.Overflows() ? Nothing() : Some(aRect); + } else if (aRect.IsEmpty()) { + return Some(*static_cast(this)); + } else { + return this->SafeUnionEdges(aRect); + } + } + + // Same as UnionEdges, but guards against overflow. If an overflow is + // detected, Nothing is returned. + [[nodiscard]] Maybe SafeUnionEdges(const Self& aRect) const { + if (this->Overflows() || aRect.Overflows()) { + return Nothing(); + } + // If neither |this| nor |aRect| overflow, then their XMost/YMost values + // should be safe to use. + CheckedInt newX = std::min(this->x, aRect.x); + CheckedInt newY = std::min(this->y, aRect.y); + CheckedInt newXMost = std::max(this->XMost(), aRect.XMost()); + CheckedInt newYMost = std::max(this->YMost(), aRect.YMost()); + CheckedInt newW = newXMost - newX; + CheckedInt newH = newYMost - newY; + if (!newW.isValid() || !newH.isValid()) { + return Nothing(); + } + return Some(Self(newX.value(), newY.value(), newW.value(), newH.value())); + } + + // This is here only to keep IPDL-generated code happy. DO NOT USE. + bool operator==(const IntRectTyped& aRect) const { + return IntRectTyped::IsEqualEdges(aRect); + } + + void InflateToMultiple(const IntSizeTyped& aTileSize) { + if (this->IsEmpty()) { + return; + } + + int32_t yMost = this->YMost(); + int32_t xMost = this->XMost(); + + this->x = mozilla::RoundDownToMultiple(this->x, aTileSize.width); + this->y = mozilla::RoundDownToMultiple(this->y, aTileSize.height); + xMost = mozilla::RoundUpToMultiple(xMost, aTileSize.width); + yMost = mozilla::RoundUpToMultiple(yMost, aTileSize.height); + + this->SetWidth(xMost - this->x); + this->SetHeight(yMost - this->y); + } +}; +typedef IntRectTyped IntRect; + +template +struct MOZ_EMPTY_BASES RectTyped + : public BaseRect, PointTyped, + SizeTyped, MarginTyped >, + public Units { + static_assert(IsPixel::value, + "'Units' must be a coordinate system tag"); + + typedef BaseRect, PointTyped, + SizeTyped, MarginTyped > + Super; + + RectTyped() : Super() { + static_assert(sizeof(RectTyped) == sizeof(F) * 4, + "Would be unfortunate otherwise!"); + } + RectTyped(const PointTyped& aPos, const SizeTyped& aSize) + : Super(aPos, aSize) {} + RectTyped(F _x, F _y, F _width, F _height) : Super(_x, _y, _width, _height) {} + explicit RectTyped(const IntRectTyped& rect) + : Super(F(rect.X()), F(rect.Y()), F(rect.Width()), F(rect.Height())) {} + + void NudgeToIntegers() { + NudgeToInteger(&(this->x)); + NudgeToInteger(&(this->y)); + NudgeToInteger(&(this->width)); + NudgeToInteger(&(this->height)); + } + + bool ToIntRect(IntRectTyped* aOut) const { + *aOut = + IntRectTyped(int32_t(this->X()), int32_t(this->Y()), + int32_t(this->Width()), int32_t(this->Height())); + return RectTyped(F(aOut->X()), F(aOut->Y()), F(aOut->Width()), + F(aOut->Height())) + .IsEqualEdges(*this); + } + + // XXX When all of the code is ported, the following functions to convert to + // and from unknown types should be removed. + + static RectTyped FromUnknownRect( + const RectTyped& rect) { + return RectTyped(rect.X(), rect.Y(), rect.Width(), rect.Height()); + } + + RectTyped ToUnknownRect() const { + return RectTyped(this->X(), this->Y(), this->Width(), + this->Height()); + } + + // This is here only to keep IPDL-generated code happy. DO NOT USE. + bool operator==(const RectTyped& aRect) const { + return RectTyped::IsEqualEdges(aRect); + } + + bool WithinEpsilonOf(const RectTyped& aOther, F aEpsilon) const { + return fabs(this->x - aOther.x) < aEpsilon && + fabs(this->y - aOther.y) < aEpsilon && + fabs(this->width - aOther.width) < aEpsilon && + fabs(this->height - aOther.height) < aEpsilon; + } +}; +typedef RectTyped Rect; +typedef RectTyped RectDouble; + +template +IntRectTyped RoundedToInt(const RectTyped& aRect) { + RectTyped copy(aRect); + copy.Round(); + return IntRectTyped(int32_t(copy.X()), int32_t(copy.Y()), + int32_t(copy.Width()), int32_t(copy.Height())); +} + +template +bool RectIsInt32Safe(const RectTyped& aRect) { + float min = (float)std::numeric_limits::min(); + float max = (float)std::numeric_limits::max(); + return aRect.x > min && aRect.y > min && aRect.width < max && + aRect.height < max && aRect.XMost() < max && aRect.YMost() < max; +} + +template +IntRectTyped RoundedIn(const RectTyped& aRect) { + return IntRectTyped::RoundIn(aRect); +} + +template +IntRectTyped RoundedOut(const RectTyped& aRect) { + return IntRectTyped::RoundOut(aRect); +} + +template +IntRectTyped TruncatedToInt(const RectTyped& aRect) { + return IntRectTyped::Truncate(aRect); +} + +template +RectTyped IntRectToRect(const IntRectTyped& aRect) { + return RectTyped(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); +} + +// Convenience functions for intersecting and unioning two rectangles wrapped in +// Maybes. +template +Maybe IntersectMaybeRects(const Maybe& a, const Maybe& b) { + if (!a) { + return b; + } else if (!b) { + return a; + } else { + return Some(a->Intersect(*b)); + } +} +template +Maybe UnionMaybeRects(const Maybe& a, const Maybe& b) { + if (!a) { + return b; + } else if (!b) { + return a; + } else { + return Some(a->Union(*b)); + } +} + +struct RectCornerRadii final { + Size radii[eCornerCount]; + + RectCornerRadii() = default; + + explicit RectCornerRadii(Float radius) { + for (const auto i : mozilla::AllPhysicalCorners()) { + radii[i].SizeTo(radius, radius); + } + } + + RectCornerRadii(Float radiusX, Float radiusY) { + for (const auto i : mozilla::AllPhysicalCorners()) { + radii[i].SizeTo(radiusX, radiusY); + } + } + + RectCornerRadii(Float tl, Float tr, Float br, Float bl) { + radii[eCornerTopLeft].SizeTo(tl, tl); + radii[eCornerTopRight].SizeTo(tr, tr); + radii[eCornerBottomRight].SizeTo(br, br); + radii[eCornerBottomLeft].SizeTo(bl, bl); + } + + RectCornerRadii(const Size& tl, const Size& tr, const Size& br, + const Size& bl) { + radii[eCornerTopLeft] = tl; + radii[eCornerTopRight] = tr; + radii[eCornerBottomRight] = br; + radii[eCornerBottomLeft] = bl; + } + + const Size& operator[](size_t aCorner) const { return radii[aCorner]; } + + Size& operator[](size_t aCorner) { return radii[aCorner]; } + + bool operator==(const RectCornerRadii& aOther) const { + return TopLeft() == aOther.TopLeft() && TopRight() == aOther.TopRight() && + BottomRight() == aOther.BottomRight() && + BottomLeft() == aOther.BottomLeft(); + } + + bool AreRadiiSame() const { + return TopLeft() == TopRight() && TopLeft() == BottomRight() && + TopLeft() == BottomLeft(); + } + + void Scale(Float aXScale, Float aYScale) { + for (const auto i : mozilla::AllPhysicalCorners()) { + radii[i].Scale(aXScale, aYScale); + } + } + + const Size TopLeft() const { return radii[eCornerTopLeft]; } + Size& TopLeft() { return radii[eCornerTopLeft]; } + + const Size TopRight() const { return radii[eCornerTopRight]; } + Size& TopRight() { return radii[eCornerTopRight]; } + + const Size BottomRight() const { return radii[eCornerBottomRight]; } + Size& BottomRight() { return radii[eCornerBottomRight]; } + + const Size BottomLeft() const { return radii[eCornerBottomLeft]; } + Size& BottomLeft() { return radii[eCornerBottomLeft]; } + + bool IsEmpty() const { + return TopLeft().IsEmpty() && TopRight().IsEmpty() && + BottomRight().IsEmpty() && BottomLeft().IsEmpty(); + } +}; + +/* A rounded rectangle abstraction. + * + * This can represent a rectangle with a different pair of radii on each corner. + * + * Note: CoreGraphics and Direct2D only support rounded rectangle with the same + * radii on all corners. However, supporting CSS's border-radius requires the + * extra flexibility. */ +struct RoundedRect { + typedef mozilla::gfx::RectCornerRadii RectCornerRadii; + + RoundedRect(const Rect& aRect, const RectCornerRadii& aCorners) + : rect(aRect), corners(aCorners) {} + + void Deflate(Float aTopWidth, Float aBottomWidth, Float aLeftWidth, + Float aRightWidth) { + // deflate the internal rect + rect.SetRect(rect.X() + aLeftWidth, rect.Y() + aTopWidth, + std::max(0.f, rect.Width() - aLeftWidth - aRightWidth), + std::max(0.f, rect.Height() - aTopWidth - aBottomWidth)); + + corners.radii[mozilla::eCornerTopLeft].width = std::max( + 0.f, corners.radii[mozilla::eCornerTopLeft].width - aLeftWidth); + corners.radii[mozilla::eCornerTopLeft].height = std::max( + 0.f, corners.radii[mozilla::eCornerTopLeft].height - aTopWidth); + + corners.radii[mozilla::eCornerTopRight].width = std::max( + 0.f, corners.radii[mozilla::eCornerTopRight].width - aRightWidth); + corners.radii[mozilla::eCornerTopRight].height = std::max( + 0.f, corners.radii[mozilla::eCornerTopRight].height - aTopWidth); + + corners.radii[mozilla::eCornerBottomLeft].width = std::max( + 0.f, corners.radii[mozilla::eCornerBottomLeft].width - aLeftWidth); + corners.radii[mozilla::eCornerBottomLeft].height = std::max( + 0.f, corners.radii[mozilla::eCornerBottomLeft].height - aBottomWidth); + + corners.radii[mozilla::eCornerBottomRight].width = std::max( + 0.f, corners.radii[mozilla::eCornerBottomRight].width - aRightWidth); + corners.radii[mozilla::eCornerBottomRight].height = std::max( + 0.f, corners.radii[mozilla::eCornerBottomRight].height - aBottomWidth); + } + Rect rect; + RectCornerRadii corners; +}; + +} // namespace gfx +} // namespace mozilla + +#endif /* MOZILLA_GFX_RECT_H_ */ -- cgit v1.2.3