blob: 40ac86727bd8bdd971ad5eca5d9a849d0a1a62bb [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_IMPELLER_GEOMETRY_ROUND_SUPERELLIPSE_H_
#define FLUTTER_IMPELLER_GEOMETRY_ROUND_SUPERELLIPSE_H_
#include "flutter/impeller/geometry/path_source.h"
#include "flutter/impeller/geometry/point.h"
#include "flutter/impeller/geometry/rect.h"
#include "flutter/impeller/geometry/rounding_radii.h"
#include "flutter/impeller/geometry/size.h"
namespace impeller {
struct RoundRect;
struct RoundSuperellipse {
RoundSuperellipse() = default;
inline static RoundSuperellipse MakeRect(const Rect& rect) {
return MakeRectRadii(rect, RoundingRadii());
}
inline static RoundSuperellipse MakeOval(const Rect& rect) {
return MakeRectRadii(rect, RoundingRadii::MakeRadii(rect.GetSize() * 0.5f));
}
inline static RoundSuperellipse MakeRectRadius(const Rect& rect,
Scalar radius) {
return MakeRectRadii(rect, RoundingRadii::MakeRadius(radius));
}
inline static RoundSuperellipse MakeRectXY(const Rect& rect,
Scalar x_radius,
Scalar y_radius) {
return MakeRectRadii(rect,
RoundingRadii::MakeRadii(Size(x_radius, y_radius)));
}
inline static RoundSuperellipse MakeRectXY(const Rect& rect,
Size corner_radii) {
return MakeRectRadii(rect, RoundingRadii::MakeRadii(corner_radii));
}
static RoundSuperellipse MakeRectRadii(const Rect& rect,
const RoundingRadii& radii);
constexpr const Rect& GetBounds() const { return bounds_; }
constexpr const RoundingRadii& GetRadii() const { return radii_; }
[[nodiscard]] constexpr bool IsFinite() const {
return bounds_.IsFinite() && //
radii_.top_left.IsFinite() && //
radii_.top_right.IsFinite() && //
radii_.bottom_left.IsFinite() && //
radii_.bottom_right.IsFinite();
}
[[nodiscard]] constexpr bool IsEmpty() const { return bounds_.IsEmpty(); }
[[nodiscard]] constexpr bool IsRect() const {
return !bounds_.IsEmpty() && radii_.AreAllCornersEmpty();
}
[[nodiscard]] constexpr bool IsOval() const {
return !bounds_.IsEmpty() && radii_.AreAllCornersSame() &&
ScalarNearlyEqual(radii_.top_left.width,
bounds_.GetWidth() * 0.5f) &&
ScalarNearlyEqual(radii_.top_left.height,
bounds_.GetHeight() * 0.5f);
}
/// @brief Returns true iff the provided point |p| is inside the
/// half-open interior of this rectangle.
///
/// For purposes of containment, a rectangle contains points
/// along the top and left edges but not points along the
/// right and bottom edges so that a point is only ever
/// considered inside one of two abutting rectangles.
[[nodiscard]] bool Contains(const Point& p) const;
/// @brief Returns a new round rectangle translated by the given offset.
[[nodiscard]] inline RoundSuperellipse Shift(Scalar dx, Scalar dy) const {
// Just in case, use the factory rather than the internal constructor
// as shifting the rectangle may increase/decrease its bit precision
// so we should re-validate the radii to the newly located rectangle.
return MakeRectRadii(bounds_.Shift(dx, dy), radii_);
}
/// @brief Returns a round rectangle with expanded edges. Negative expansion
/// results in shrinking.
[[nodiscard]] inline RoundSuperellipse Expand(Scalar left,
Scalar top,
Scalar right,
Scalar bottom) const {
// Use the factory rather than the internal constructor as the changing
// size of the rectangle requires that we re-validate the radii to the
// newly sized rectangle.
return MakeRectRadii(bounds_.Expand(left, top, right, bottom), radii_);
}
/// @brief Returns a round rectangle with expanded edges. Negative expansion
/// results in shrinking.
[[nodiscard]] inline RoundSuperellipse Expand(Scalar horizontal,
Scalar vertical) const {
// Use the factory rather than the internal constructor as the changing
// size of the rectangle requires that we re-validate the radii to the
// newly sized rectangle.
return MakeRectRadii(bounds_.Expand(horizontal, vertical), radii_);
}
/// @brief Returns a round rectangle with expanded edges. Negative expansion
/// results in shrinking.
[[nodiscard]] inline RoundSuperellipse Expand(Scalar amount) const {
// Use the factory rather than the internal constructor as the changing
// size of the rectangle requires that we re-validate the radii to the
// newly sized rectangle.
return MakeRectRadii(bounds_.Expand(amount), radii_);
}
[[nodiscard]] constexpr bool operator==(const RoundSuperellipse& rr) const {
return bounds_ == rr.bounds_ && radii_ == rr.radii_;
}
// Approximates a rounded superellipse with a round rectangle to the
// best practical accuracy.
//
// This is used for Skia backends, which does not support rounded
// superellipses directly, so rendering rounded superellipses
// falls back to RRect.
[[nodiscard]] RoundRect ToApproximateRoundRect() const;
private:
constexpr RoundSuperellipse(const Rect& bounds, const RoundingRadii& radii)
: bounds_(bounds), radii_(radii) {}
Rect bounds_;
RoundingRadii radii_;
};
class RoundSuperellipsePathSource : public PathSource {
public:
explicit RoundSuperellipsePathSource(
const RoundSuperellipse& round_superellipse);
~RoundSuperellipsePathSource();
const RoundSuperellipse& GetRoundSuperellipse() const {
return round_superellipse_;
}
// |PathSource|
FillType GetFillType() const override;
// |PathSource|
Rect GetBounds() const override;
// |PathSource|
bool IsConvex() const override;
// |PathSource|
void Dispatch(PathReceiver& receiver) const override;
private:
const RoundSuperellipse round_superellipse_;
};
} // namespace impeller
namespace std {
inline std::ostream& operator<<(std::ostream& out,
const impeller::RoundSuperellipse& rr) {
out << "(" //
<< "rect: " << rr.GetBounds() << ", " //
<< "radii: " << rr.GetRadii();
out << ")";
return out;
}
} // namespace std
#endif // FLUTTER_IMPELLER_GEOMETRY_ROUND_SUPERELLIPSE_H_