blob: 65bd093f56eafc7dac2cd51f9d32f5c2879cb2ea [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_CONTOURED_RECT_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_CONTOURED_RECT_H_
#include <array>
#include <optional>
#include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "ui/gfx/geometry/insets_f.h"
#include "ui/gfx/geometry/outsets_f.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rrect_f.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace gfx {
class QuadF;
}
namespace blink {
class Path;
// A ContouredRect is a rect with corners that can have arbitrary
// radius and curvature, as in not necessarily rounded.
// It is a superset of FloatRoundedRect, with the added information needed
// to render corner shapes correctly.
class PLATFORM_EXPORT ContouredRect {
DISALLOW_NEW();
public:
// A corner curvature is the exponent of a superellipse quadrant.
// e.g. 2 is round, 1 is bevel/angled, infinity is defunct/straight.
// This is equivalent to 2^s, where s is the superellipse parameter.
// See https://drafts.csswg.org/css-borders-4/#superellipse-parameter
class CornerCurvature {
public:
static constexpr float kRound = 2;
static constexpr float kBevel = 1;
static constexpr float kScoop = 0.5;
static constexpr float kStraight = 1000;
static constexpr float kNotch = 1 / kStraight;
constexpr CornerCurvature() = default;
constexpr CornerCurvature(float top_left,
float top_right,
float bottom_right,
float bottom_left)
: top_left_(top_left),
top_right_(top_right),
bottom_right_(bottom_right),
bottom_left_(bottom_left) {
DCHECK_GE(top_left, 0);
DCHECK_GE(top_right, 0);
DCHECK_GE(bottom_right, 0);
DCHECK_GE(bottom_left, 0);
}
constexpr bool IsRound() const {
return (top_left_ == kRound) && IsUniform();
}
constexpr bool IsConvex() const {
return top_left_ >= kBevel && top_right_ >= kBevel &&
bottom_right_ >= kBevel && bottom_left_ >= kBevel;
}
constexpr bool IsHyperellipse() const {
return top_left_ >= kRound && top_right_ >= kRound &&
bottom_right_ >= kRound && bottom_left_ >= kRound;
}
constexpr bool IsUniform() const {
return top_left_ == top_right_ && top_left_ == bottom_right_ &&
top_left_ == bottom_left_;
}
constexpr float TopLeft() const { return top_left_; }
constexpr float TopRight() const { return top_right_; }
constexpr float BottomRight() const { return bottom_right_; }
constexpr float BottomLeft() const { return bottom_left_; }
constexpr bool operator==(const CornerCurvature&) const = default;
String ToString() const;
private:
float top_left_ = kRound;
float top_right_ = kRound;
float bottom_right_ = kRound;
float bottom_left_ = kRound;
};
// A Corner is a axis-aligned quad, with the points ordered (start, outer,
// end, center), and a curvature. It is used as a convenient way to perform
// corner-related computations without having to worry about the
// transpositions of the different corners.
class PLATFORM_EXPORT Corner {
public:
constexpr Corner(const gfx::RectF& rect, size_t rotation, float curvature)
: curvature_(curvature) {
vertices_ = {rect.origin(), rect.top_right(), rect.bottom_right(),
rect.bottom_left()};
std::rotate(vertices_.begin(), vertices_.begin() + rotation,
vertices_.end());
}
constexpr Corner(const std::array<gfx::PointF, 4>& vertices,
float curvature)
: vertices_(vertices), curvature_(ClampCurvature(curvature)) {}
constexpr const gfx::PointF& Start() const { return vertices_.at(0); }
constexpr const gfx::PointF& Outer() const { return vertices_.at(1); }
constexpr const gfx::PointF& End() const { return vertices_.at(2); }
constexpr const gfx::PointF& Center() const { return vertices_.at(3); }
static constexpr float ClampCurvature(float curvature) {
return std::clamp(curvature, CornerCurvature::kNotch,
CornerCurvature::kStraight);
}
constexpr float Curvature() const { return curvature_; }
constexpr bool IsStraight() const {
return Curvature() == CornerCurvature::kStraight;
}
constexpr bool IsBevel() const {
return curvature_ == CornerCurvature::kBevel;
}
constexpr bool IsRound() const {
return curvature_ == CornerCurvature::kRound;
}
constexpr bool IsNotch() const {
return curvature_ == CornerCurvature::kNotch;
}
constexpr bool IsConcave() const { return curvature_ < 1; }
constexpr bool IsHyperellipse() const { return curvature_ >= 2; }
constexpr bool IsZero() const { return Start() == End(); }
constexpr bool IsEmpty() const {
return v1().Length() == 0 || v2().Length() == 0;
}
constexpr bool operator==(const Corner&) const = default;
// Invert the curvature
constexpr Corner Inverse() const {
return Corner({Start(), Center(), End(), Outer()}, 1 / Curvature());
}
// Change the direction (clockwise/counter-counterclockwise)
constexpr Corner Reverse() const {
return Corner({End(), Outer(), Start(), Center()}, Curvature());
}
constexpr gfx::RectF BoundingBox() const {
return gfx::BoundingRect(Start(), End());
}
constexpr bool Intersects(const Corner& other) const {
return BoundingBox().Intersects(other.BoundingBox());
}
constexpr gfx::Vector2dF v1() const { return Outer() - Start(); }
constexpr gfx::Vector2dF v2() const { return End() - Outer(); }
constexpr gfx::Vector2dF v3() const { return Center() - End(); }
constexpr gfx::Vector2dF v4() const { return Start() - Center(); }
constexpr float DiagonalLength() const {
return (End() - Start()).Length();
}
constexpr gfx::PointF HalfCorner() const {
const float normalized_half_corner = HalfCornerForCurvature(curvature_);
return MapPoint(
gfx::Vector2dF(normalized_half_corner, normalized_half_corner));
}
static constexpr float HalfCornerForCurvature(float curvature) {
return std::pow(0.5, 1 / ClampCurvature(curvature));
}
static float CurvatureForHalfCorner(float half_corner);
constexpr gfx::PointF MapPoint(
const gfx::Vector2dF& normalized_point) const {
return Center() + gfx::ScaleVector2d(v1(), normalized_point.x()) +
gfx::ScaleVector2d(v4(), normalized_point.y());
}
gfx::PointF QuadraticControlPoint() const;
Corner AlignedToOrigin(const Corner& origin,
float thickness_start,
float thickness_end) const;
String ToString() const;
private:
std::array<gfx::PointF, 4> vertices_;
float curvature_;
};
constexpr ContouredRect() = default;
constexpr explicit ContouredRect(const FloatRoundedRect& rect)
: rect_(rect) {}
constexpr ContouredRect(const FloatRoundedRect& rect,
const CornerCurvature& curvature)
: rect_(rect), corner_curvature_(curvature) {}
bool operator==(const ContouredRect&) const = default;
constexpr const gfx::RectF& BoundingRect() const { return rect_.Rect(); }
constexpr const CornerCurvature& GetCornerCurvature() const {
return corner_curvature_;
}
constexpr bool HasRoundCurvature() const {
return corner_curvature_.IsRound() || !IsRounded();
}
constexpr bool IsConvex() const {
return !IsRounded() || corner_curvature_.IsConvex();
}
const FloatRoundedRect::Radii& GetRadii() const { return rect_.GetRadii(); }
void SetRadii(const FloatRoundedRect::Radii& radii) { rect_.SetRadii(radii); }
bool IsRounded() const {
return rect_.IsRounded() || (origin_rect_ && origin_rect_->IsRounded());
}
const FloatRoundedRect& AsRoundedRect() const { return rect_; }
const gfx::RectF& Rect() const { return rect_.Rect(); }
constexpr bool IsEmpty() const { return rect_.IsEmpty(); }
void SetCornerCurvature(const CornerCurvature& curvature) {
corner_curvature_ = curvature;
}
void Move(const gfx::Vector2dF& offset) { rect_.Move(offset); }
void Inset(const gfx::InsetsF& insets) { rect_.Inset(insets); }
void Inset(float inset) { rect_.Inset(inset); }
void OutsetWithCornerCorrection(float outset) {
OutsetWithCornerCorrection(gfx::OutsetsF(outset));
}
void OutsetForShapeMargin(float outset) {
rect_.OutsetForShapeMargin(outset);
}
bool XInterceptsAtY(float y,
float& min_x_intercept,
float& max_x_intercept) const;
void Outset(const gfx::OutsetsF& outsets) { rect_.Outset(outsets); }
void OutsetWithCornerCorrection(const gfx::OutsetsF&);
void ConstrainRadii() { rect_.ConstrainRadii(); }
bool IntersectsQuad(const gfx::QuadF&) const;
// Whether the radii are constrained in the size of rect().
bool IsRenderable() const { return rect_.IsRenderable(); }
String ToString() const;
Path GetPath() const;
const FloatRoundedRect& GetOriginRect() const {
return origin_rect_ ? *origin_rect_ : rect_;
}
void SetOriginRect(const FloatRoundedRect& rect) { origin_rect_ = rect; }
constexpr bool IsInnerRect() const {
return origin_rect_ && *origin_rect_ != rect_;
}
constexpr Corner TopRightCorner() const {
return IsInnerRect() ? TopRightCornerInternal().AlignedToOrigin(
ContouredRect(*origin_rect_, corner_curvature_)
.TopRightCornerInternal(),
rect_.Rect().y() - origin_rect_->Rect().y(),
origin_rect_->Rect().right() - Rect().right())
: TopRightCornerInternal();
}
constexpr Corner BottomRightCorner() const {
return IsInnerRect() ? BottomRightCornerInternal().AlignedToOrigin(
ContouredRect(*origin_rect_, corner_curvature_)
.BottomRightCornerInternal(),
origin_rect_->Rect().right() - Rect().right(),
origin_rect_->Rect().bottom() - Rect().bottom())
: BottomRightCornerInternal();
}
constexpr Corner BottomLeftCorner() const {
return IsInnerRect()
? BottomLeftCornerInternal().AlignedToOrigin(
ContouredRect(*origin_rect_, corner_curvature_)
.BottomLeftCornerInternal(),
origin_rect_->Rect().bottom() - rect_.Rect().bottom(),
Rect().x() - origin_rect_->Rect().x())
: BottomLeftCornerInternal();
}
constexpr Corner TopLeftCorner() const {
return IsInnerRect() ? TopLeftCornerInternal().AlignedToOrigin(
ContouredRect(*origin_rect_, corner_curvature_)
.TopLeftCornerInternal(),
Rect().x() - origin_rect_->Rect().x(),
rect_.Rect().y() - origin_rect_->Rect().y())
: TopLeftCornerInternal();
}
private:
constexpr Corner TopRightCornerInternal() const {
return Corner(rect_.TopRightCorner(), 0, corner_curvature_.TopRight());
}
constexpr Corner BottomRightCornerInternal() const {
return Corner(rect_.BottomRightCorner(), 1,
corner_curvature_.BottomRight());
}
constexpr Corner BottomLeftCornerInternal() const {
return Corner(rect_.BottomLeftCorner(), 2, corner_curvature_.BottomLeft());
}
constexpr Corner TopLeftCornerInternal() const {
return Corner(rect_.TopLeftCorner(), 3, corner_curvature_.TopLeft());
}
FloatRoundedRect rect_;
CornerCurvature corner_curvature_;
std::optional<FloatRoundedRect> origin_rect_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_CONTOURED_RECT_H_