[libcamera-devel,v5,4/5] libcamera: Add geometry helper functions
diff mbox series

Message ID 20201023102159.26274-5-david.plowman@raspberrypi.com
State Accepted
Headers show
Series
  • Digital zoom
Related show

Commit Message

David Plowman Oct. 23, 2020, 10:21 a.m. UTC
These functions are aimed at making it easier to calculate cropping
rectangles, particularly in order to implement digital zoom.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 include/libcamera/geometry.h |  52 ++++++
 src/libcamera/geometry.cpp   | 307 +++++++++++++++++++++++++++++++++++
 2 files changed, 359 insertions(+)

Comments

Laurent Pinchart Oct. 23, 2020, 11:36 p.m. UTC | #1
Hi David,

Thank you for the patch.

On Fri, Oct 23, 2020 at 11:21:58AM +0100, David Plowman wrote:
> These functions are aimed at making it easier to calculate cropping
> rectangles, particularly in order to implement digital zoom.
> 
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  include/libcamera/geometry.h |  52 ++++++
>  src/libcamera/geometry.cpp   | 307 +++++++++++++++++++++++++++++++++++

Would you be able to write a test cases for these helpers ? They can go
in a separate patch. I can also try to help if needed.

>  2 files changed, 359 insertions(+)
> 
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 02fb63c0..3fa9e926 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -13,6 +13,30 @@
>  
>  namespace libcamera {
>  
> +class Rectangle;
> +
> +class Point
> +{
> +public:
> +	constexpr Point()
> +		: x(0), y(0)
> +	{
> +	}
> +
> +	constexpr Point(int xpos, int ypos)
> +		: x(xpos), y(ypos)
> +	{
> +	}
> +
> +	int x;
> +	int y;
> +
> +	constexpr Point operator-() const
> +	{
> +		return { -x, -y };
> +	}
> +};
> +
>  class Size
>  {
>  public:
> @@ -31,6 +55,7 @@ public:
>  
>  	bool isNull() const { return !width && !height; }
>  	const std::string toString() const;
> +	Point center() const;

The concept of the centre of a size seems a bit awkward to me. Should we
replace

	Size size = ...;
	Point p = size.center();

with

	Size size = ...;
	Point p = Rectangle(size).center();

and drop this function ?

>  
>  	Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
>  	{
> @@ -93,6 +118,17 @@ public:
>  			std::max(height, expand.height)
>  		};
>  	}
> +
> +	Size boundedToAspectRatio(const Size &ratio) const;
> +	Size expandedToAspectRatio(const Size &ratio) const;
> +
> +	Rectangle centeredTo(const Point &center) const;
> +
> +	Size operator*(float factor) const;
> +	Size operator/(float factor) const;
> +
> +	Size &operator*=(float factor);
> +	Size &operator/=(float factor);
>  };
>  
>  bool operator==(const Size &lhs, const Size &rhs);
> @@ -176,6 +212,11 @@ public:
>  	{
>  	}
>  
> +	constexpr explicit Rectangle(const Size &size)
> +		: x(0), y(0), width(size.width), height(size.height)
> +	{
> +	}
> +
>  	int x;
>  	int y;
>  	unsigned int width;
> @@ -183,6 +224,17 @@ public:
>  
>  	bool isNull() const { return !width && !height; }
>  	const std::string toString() const;
> +	Point center() const;
> +	Size size() const;
> +	Point topLeft() const;

I'd make the last two functions inline, as they're just accessors for
fields that are public.

> +
> +	Rectangle &scaleBy(const Size &numerator, const Size &denominator);
> +	Rectangle &translateBy(const Point &point);
> +
> +	Rectangle boundedTo(const Rectangle &bound) const;
> +	Rectangle enclosedIn(const Rectangle &boundary) const;
> +	Rectangle scaledBy(const Size &numerator, const Size &denominator) const;
> +	Rectangle translatedBy(const Point &point) const;
>  };
>  
>  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index b12e1a62..63a49e9a 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -17,6 +17,42 @@
>  
>  namespace libcamera {
>  
> +/**
> + * \class Point
> + * \brief Describe a point in two-dimensional space
> + *
> + * The Point structure defines a point in two-dimensional space with integer
> + * precision. The coordinates of a Point may be negative as well as positive.
> + */
> +
> +/**
> + * \fn Point::Point()
> + * \brief Construct a Point with x and y set to 0
> + */
> +
> +/**
> + * \fn Point::Point(int xpos, int ypos)
> + * \brief Construct a Point at given \a x and \a y values

s/x/xpos/ and s/y/ypos/

> + * \param[in] xpos The x-coordinate
> + * \param[in] ypos The y-coordinate
> + */
> +
> +/**
> + * \var Point::x
> + * \brief The x-coordinate of the Point
> + */
> +
> +/**
> + * \var Point::y
> + * \brief The y-coordinate of the Point
> + */
> +
> +/**
> + * \fn Point Point::operator-() const
> + * \brief Negate a Point by negating both its x and y coordinates
> + * \return The negated point
> + */
> +
>  /**
>   * \struct Size
>   * \brief Describe a two-dimensional size
> @@ -61,6 +97,15 @@ const std::string Size::toString() const
>  	return std::to_string(width) + "x" + std::to_string(height);
>  }
>  
> +/**
> + * \brief Retrieve the center point of this size
> + * \return The center Point
> + */
> +Point Size::center() const
> +{
> +	return { static_cast<int>(width / 2), static_cast<int>(height / 2) };
> +}
> +
>  /**
>   * \fn Size::alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
>   * \brief Align the size down horizontally and vertically in place
> @@ -143,6 +188,105 @@ const std::string Size::toString() const
>   * height of this size and the \a expand size
>   */
>  
> +/**
> + * \brief Bound the size down to match the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio must be matched
> + * \return A Size whose width and height are equal to the width and height
> + * of this Size aligned down to the aspect ratio of \a ratio
> + */
> +Size Size::boundedToAspectRatio(const Size &ratio) const
> +{
> +	uint64_t ratio1 = static_cast<uint64_t>(width) *
> +			  static_cast<uint64_t>(ratio.height);
> +	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
> +			  static_cast<uint64_t>(height);
> +
> +	if (ratio1 > ratio2)
> +		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
> +	else
> +		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
> +}
> +
> +/**
> + * \brief Expand the size to match the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio must be matched

I would either add here

"The behaviour of this function is undefined if either the width or the
height of the \a ratio is zero."

Should we have a special case below to return *this when !ratio.width ||
!ratio.height, to avoid crashing ? Or an ASSERT() to catch this ? I'm
considering this in the context of patch 5/5. If we decide to accept all
crop values and clamp them to what can be achieved, the call to
expandedToAspectRatio() could (I think) lead to the right result with
the crop.width && crop.height check dropped. But maybe we shouldn't rely
on how corner cases are implemented in geometry helpers, and handle them
explictly in the callers. Still, not crashing may be a good option.

> + * \return A Size whose width and height are equal to the width and height
> + * of this Size expanded up to the aspect ratio of \a ratio
> + */
> +Size Size::expandedToAspectRatio(const Size &ratio) const
> +{
> +	uint64_t ratio1 = static_cast<uint64_t>(width) *
> +			  static_cast<uint64_t>(ratio.height);
> +	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
> +			  static_cast<uint64_t>(height);
> +
> +	if (ratio1 < ratio2)
> +		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
> +	else
> +		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
> +}
> +
> +/**
> + * \brief Center a rectangle of this size at a given Point
> + * \param[in] center The center point the Rectangle is to have
> + *
> + * A Rectangle of this object's size is positioned so that its center
> + * is at the given Point.
> + *
> + * \return A Rectangle of this size, centered at the given Point.
> + */
> +Rectangle Size::centeredTo(const Point &center) const
> +{
> +	int x = center.x - width / 2;
> +	int y = center.y - height / 2;
> +
> +	return { x, y, width, height };
> +}
> +
> +/**
> + * \brief Scale size up by the given factor
> + * \param[in] factor The factor
> + * \return The scaled Size
> + */
> +Size Size::operator*(float factor) const
> +{
> +	return Size(width * factor, height * factor);
> +}
> +
> +/**
> + * \brief Scale size down by the given factor
> + * \param[in] factor The factor
> + * \return The scaled Size
> + */
> +Size Size::operator/(float factor) const
> +{
> +	return Size(width / factor, height / factor);
> +}
> +
> +/**
> + * \brief Scale this size up by the given factor in place
> + * \param[in] factor The factor
> + * \return A reference to this object
> + */
> +Size &Size::operator*=(float factor)
> +{
> +	width *= factor;
> +	height *= factor;
> +	return *this;
> +}
> +
> +/**
> + * \brief Scale this size down by the given factor in place
> + * \param[in] factor The factor
> + * \return A reference to this object
> + */
> +Size &Size::operator/=(float factor)
> +{
> +	width /= factor;
> +	height /= factor;
> +	return *this;
> +}
> +
>  /**
>   * \brief Compare sizes for equality
>   * \return True if the two sizes are equal, false otherwise
> @@ -365,6 +509,13 @@ bool operator==(const SizeRange &lhs, const SizeRange &rhs)
>   * \param[in] height The height
>   */
>  
> +/**
> + * \fn Rectangle::Rectangle(const Size &size)
> + * \brief Construct a Rectangle of \a size with its top left corner located
> + * at (0,0).

s/\.//

> + * \param[in] size The desired Rectangle size
> + */
> +
>  /**
>   * \var Rectangle::x
>   * \brief The horizontal coordinate of the rectangle's top-left corner
> @@ -404,6 +555,162 @@ const std::string Rectangle::toString() const
>  	return ss.str();
>  }
>  
> +/**
> + * \brief Retrieve the center point of this rectangle
> + * \return The center Point
> + */
> +Point Rectangle::center() const
> +{
> +	return { x + static_cast<int>(width / 2), y + static_cast<int>(height / 2) };
> +}
> +
> +/**
> + * \brief Retrieve the size of this rectangle
> + * \return The Rectangle size
> + */
> +Size Rectangle::size() const
> +{
> +	return Size(width, height);
> +}
> +
> +/**
> + * \brief Retrieve the coordinates of the top left corner of this Rectangle
> + * \return The Rectangle's top left corner
> + */
> +Point Rectangle::topLeft() const
> +{
> +	return Point(x, y);
> +}
> +
> +/**
> + * \brief Apply a non-uniform rational scaling in place to this Rectangle
> + * \param[in] numerator The numerators of the x and y scaling factors
> + * \param[in] denominator The denominators of the x and y scaling factors
> + *
> + * A non-uniform scaling is applied in place such the the resulting x
> + * coordinates are multiplied by numerator.width / denominator.width,
> + * and similarly for the y coordinates (using height in place of width).
> + *
> + * \return A reference to this object
> + */
> +Rectangle &Rectangle::scaleBy(const Size &numerator, const Size &denominator)
> +{
> +	x = static_cast<int64_t>(x) * numerator.width / denominator.width;
> +	y = static_cast<int64_t>(y) * numerator.height / denominator.height;
> +	width = static_cast<uint64_t>(width) * numerator.width / denominator.width;
> +	height = static_cast<uint64_t>(height) * numerator.height / denominator.height;
> +
> +	return *this;
> +}
> +
> +/**
> + * \brief Translate this Rectangle in place by the given Point
> + * \param[in] point The amount to translate the Rectangle by
> + *
> + * The Rectangle is translated in the x-direction by the point's x coordinate
> + * and in the y-direction by the point's y coordinate.
> + *
> + * \return A reference to this object
> + */
> +Rectangle &Rectangle::translateBy(const Point &point)
> +{
> +	x += point.x;
> +	y += point.y;
> +
> +	return *this;
> +}
> +
> +/**
> + * \brief Calculate the intersection of this Rectangle with another
> + * \param[in] bound The Rectangle that is intersected with this Rectangle
> + *
> + * This method calculates the standard intersection of two rectangles. If the
> + * rectangles do not overlap in either the x or y direction, then the size
> + * of that dimension in the result (its width or height) is set to zero. Even
> + * when one dimension is set to zero, note that the other dimension may still
> + * have a positive value if there was some overlap.
> + *
> + * \return A Rectangle that is the intersection of the input rectangles
> + */
> +Rectangle Rectangle::boundedTo(const Rectangle &bound) const
> +{
> +	int topLeftX = std::max(x, bound.x);
> +	int topLeftY = std::max(y, bound.y);
> +	int bottomRightX = std::min<int>(x + width, bound.x + bound.width);
> +	int bottomRightY = std::min<int>(y + height, bound.y + bound.height);
> +
> +	unsigned int newWidth = std::max(bottomRightX - topLeftX, 0);
> +	unsigned int newHeight = std::max(bottomRightY - topLeftY, 0);
> +
> +	return { topLeftX, topLeftY, newWidth, newHeight };
> +}
> +
> +/**
> + * \brief Enclose a Rectangle so as not to exceed another Rectangle
> + * \param[in] boundary The limit that the returned Rectangle will not exceed
> + *
> + * The Rectangle is modified so that it does not exceeed the given \a boundary.
> + * This process involves translating the Rectangle if any of its edges
> + * lie beyond \a boundary, so that those edges then lie along the boundary
> + * instead.
> + *
> + * If either width or height are larger than \a boundary, then the returned
> + * Rectangle is clipped to be no larger. But other than this, the
> + * Rectangle is not clipped or reduced in size, merely translated.
> + *
> + * Note that this is not a conventional Rectangle intersection function
> + * which is provided by Rectangle::boundedTo.

s/Rectangle::boundedTo/boundedTo()/ should be enough to please doxygen.

> + *
> + * \return A Rectangle that does not extend beyond a boundary Rectangle
> + */
> +Rectangle Rectangle::enclosedIn(const Rectangle &boundary) const
> +{
> +	/* We can't be bigger than the boundary rectangle. */
> +	Rectangle result = boundedTo(Rectangle{ x, y, boundary.size() });
> +
> +	result.x = std::clamp<int>(result.x, boundary.x,
> +				   boundary.x + boundary.width - result.width);
> +	result.y = std::clamp<int>(result.y, boundary.y,
> +				   boundary.y + boundary.height - result.height);
> +
> +	return result;
> +}
> +
> +/**
> + * \brief Apply a non-uniform rational scaling to this Rectangle
> + * \param[in] numerator The numerators of the x and y scaling factors
> + * \param[in] denominator The denominators of the x and y scaling factors
> + *
> + * A non-uniform scaling is applied such the the resulting x
> + * coordinates are multiplied by numerator.width / denominator.width,
> + * and similarly for the y coordinates (using height in place of width).
> + *
> + * \return The non-uniformly scaled Rectangle
> + */
> +Rectangle Rectangle::scaledBy(const Size &numerator, const Size &denominator) const
> +{
> +	int scaledX = static_cast<int64_t>(x) * numerator.width / denominator.width;
> +	int scaledY = static_cast<int64_t>(y) * numerator.height / denominator.height;
> +	unsigned int scaledWidth = static_cast<uint64_t>(width) * numerator.width / denominator.width;
> +	unsigned int scaledHeight = static_cast<uint64_t>(height) * numerator.height / denominator.height;
> +
> +	return { scaledX, scaledY, scaledWidth, scaledHeight };
> +}
> +
> +/**
> + * \brief Translate a Rectangle by the given amounts
> + * \param[in] point The amount to translate the Rectangle by
> + *
> + * The Rectangle is translated in the x-direction by the point's x coordinate
> + * and in the y-direction by the point's y coordinate.
> + *
> + * \return The translated Rectangle
> + */
> +Rectangle Rectangle::translatedBy(const Point &point) const
> +{
> +	return { x + point.x, y + point.y, width, height };
> +}
> +
>  /**
>   * \brief Compare rectangles for equality
>   * \return True if the two rectangles are equal, false otherwise
Jacopo Mondi Oct. 24, 2020, 5:24 p.m. UTC | #2
Hi David,

On Fri, Oct 23, 2020 at 11:21:58AM +0100, David Plowman wrote:
> These functions are aimed at making it easier to calculate cropping
> rectangles, particularly in order to implement digital zoom.
>
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>

This looks very good, just two minor documentation comments below

For the patch
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

> ---
>  include/libcamera/geometry.h |  52 ++++++
>  src/libcamera/geometry.cpp   | 307 +++++++++++++++++++++++++++++++++++
>  2 files changed, 359 insertions(+)
>
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 02fb63c0..3fa9e926 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -13,6 +13,30 @@
>
>  namespace libcamera {
>
> +class Rectangle;
> +
> +class Point
> +{
> +public:
> +	constexpr Point()
> +		: x(0), y(0)
> +	{
> +	}
> +
> +	constexpr Point(int xpos, int ypos)
> +		: x(xpos), y(ypos)
> +	{
> +	}
> +
> +	int x;
> +	int y;
> +
> +	constexpr Point operator-() const
> +	{
> +		return { -x, -y };
> +	}
> +};
> +
>  class Size
>  {
>  public:
> @@ -31,6 +55,7 @@ public:
>
>  	bool isNull() const { return !width && !height; }
>  	const std::string toString() const;
> +	Point center() const;
>
>  	Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
>  	{
> @@ -93,6 +118,17 @@ public:
>  			std::max(height, expand.height)
>  		};
>  	}
> +
> +	Size boundedToAspectRatio(const Size &ratio) const;
> +	Size expandedToAspectRatio(const Size &ratio) const;
> +
> +	Rectangle centeredTo(const Point &center) const;
> +
> +	Size operator*(float factor) const;
> +	Size operator/(float factor) const;
> +
> +	Size &operator*=(float factor);
> +	Size &operator/=(float factor);
>  };
>
>  bool operator==(const Size &lhs, const Size &rhs);
> @@ -176,6 +212,11 @@ public:
>  	{
>  	}
>
> +	constexpr explicit Rectangle(const Size &size)
> +		: x(0), y(0), width(size.width), height(size.height)
> +	{
> +	}
> +
>  	int x;
>  	int y;
>  	unsigned int width;
> @@ -183,6 +224,17 @@ public:
>
>  	bool isNull() const { return !width && !height; }
>  	const std::string toString() const;
> +	Point center() const;
> +	Size size() const;
> +	Point topLeft() const;
> +
> +	Rectangle &scaleBy(const Size &numerator, const Size &denominator);
> +	Rectangle &translateBy(const Point &point);
> +
> +	Rectangle boundedTo(const Rectangle &bound) const;
> +	Rectangle enclosedIn(const Rectangle &boundary) const;
> +	Rectangle scaledBy(const Size &numerator, const Size &denominator) const;
> +	Rectangle translatedBy(const Point &point) const;
>  };
>
>  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index b12e1a62..63a49e9a 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -17,6 +17,42 @@
>
>  namespace libcamera {
>
> +/**
> + * \class Point
> + * \brief Describe a point in two-dimensional space
> + *
> + * The Point structure defines a point in two-dimensional space with integer
> + * precision. The coordinates of a Point may be negative as well as positive.
> + */
> +
> +/**
> + * \fn Point::Point()
> + * \brief Construct a Point with x and y set to 0
> + */
> +
> +/**
> + * \fn Point::Point(int xpos, int ypos)
> + * \brief Construct a Point at given \a x and \a y values
> + * \param[in] xpos The x-coordinate
> + * \param[in] ypos The y-coordinate
> + */
> +
> +/**
> + * \var Point::x
> + * \brief The x-coordinate of the Point
> + */
> +
> +/**
> + * \var Point::y
> + * \brief The y-coordinate of the Point
> + */
> +
> +/**
> + * \fn Point Point::operator-() const
> + * \brief Negate a Point by negating both its x and y coordinates
> + * \return The negated point
> + */
> +
>  /**
>   * \struct Size
>   * \brief Describe a two-dimensional size
> @@ -61,6 +97,15 @@ const std::string Size::toString() const
>  	return std::to_string(width) + "x" + std::to_string(height);
>  }
>
> +/**
> + * \brief Retrieve the center point of this size
> + * \return The center Point
> + */
> +Point Size::center() const
> +{
> +	return { static_cast<int>(width / 2), static_cast<int>(height / 2) };
> +}
> +
>  /**
>   * \fn Size::alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
>   * \brief Align the size down horizontally and vertically in place
> @@ -143,6 +188,105 @@ const std::string Size::toString() const
>   * height of this size and the \a expand size
>   */
>
> +/**
> + * \brief Bound the size down to match the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio must be matched
> + * \return A Size whose width and height are equal to the width and height
> + * of this Size aligned down to the aspect ratio of \a ratio
> + */
> +Size Size::boundedToAspectRatio(const Size &ratio) const
> +{
> +	uint64_t ratio1 = static_cast<uint64_t>(width) *
> +			  static_cast<uint64_t>(ratio.height);
> +	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
> +			  static_cast<uint64_t>(height);
> +
> +	if (ratio1 > ratio2)
> +		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
> +	else
> +		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
> +}
> +
> +/**
> + * \brief Expand the size to match the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio must be matched
> + * \return A Size whose width and height are equal to the width and height
> + * of this Size expanded up to the aspect ratio of \a ratio
> + */
> +Size Size::expandedToAspectRatio(const Size &ratio) const
> +{
> +	uint64_t ratio1 = static_cast<uint64_t>(width) *
> +			  static_cast<uint64_t>(ratio.height);
> +	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
> +			  static_cast<uint64_t>(height);
> +
> +	if (ratio1 < ratio2)
> +		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
> +	else
> +		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
> +}
> +
> +/**
> + * \brief Center a rectangle of this size at a given Point
> + * \param[in] center The center point the Rectangle is to have
> + *
> + * A Rectangle of this object's size is positioned so that its center
> + * is at the given Point.
> + *
> + * \return A Rectangle of this size, centered at the given Point.
> + */
> +Rectangle Size::centeredTo(const Point &center) const
> +{
> +	int x = center.x - width / 2;
> +	int y = center.y - height / 2;
> +
> +	return { x, y, width, height };
> +}
> +
> +/**
> + * \brief Scale size up by the given factor
> + * \param[in] factor The factor
> + * \return The scaled Size
> + */
> +Size Size::operator*(float factor) const
> +{
> +	return Size(width * factor, height * factor);
> +}
> +
> +/**
> + * \brief Scale size down by the given factor
> + * \param[in] factor The factor
> + * \return The scaled Size
> + */
> +Size Size::operator/(float factor) const
> +{
> +	return Size(width / factor, height / factor);
> +}
> +
> +/**
> + * \brief Scale this size up by the given factor in place
> + * \param[in] factor The factor
> + * \return A reference to this object
> + */
> +Size &Size::operator*=(float factor)
> +{
> +	width *= factor;
> +	height *= factor;
> +	return *this;
> +}
> +
> +/**
> + * \brief Scale this size down by the given factor in place
> + * \param[in] factor The factor
> + * \return A reference to this object
> + */
> +Size &Size::operator/=(float factor)
> +{
> +	width /= factor;
> +	height /= factor;
> +	return *this;
> +}
> +
>  /**
>   * \brief Compare sizes for equality
>   * \return True if the two sizes are equal, false otherwise
> @@ -365,6 +509,13 @@ bool operator==(const SizeRange &lhs, const SizeRange &rhs)
>   * \param[in] height The height
>   */
>
> +/**
> + * \fn Rectangle::Rectangle(const Size &size)
> + * \brief Construct a Rectangle of \a size with its top left corner located
> + * at (0,0).
> + * \param[in] size The desired Rectangle size
> + */
> +
>  /**
>   * \var Rectangle::x
>   * \brief The horizontal coordinate of the rectangle's top-left corner
> @@ -404,6 +555,162 @@ const std::string Rectangle::toString() const
>  	return ss.str();
>  }
>
> +/**
> + * \brief Retrieve the center point of this rectangle
> + * \return The center Point
> + */
> +Point Rectangle::center() const
> +{
> +	return { x + static_cast<int>(width / 2), y + static_cast<int>(height / 2) };
> +}
> +
> +/**
> + * \brief Retrieve the size of this rectangle
> + * \return The Rectangle size
> + */
> +Size Rectangle::size() const
> +{
> +	return Size(width, height);
> +}
> +
> +/**
> + * \brief Retrieve the coordinates of the top left corner of this Rectangle
> + * \return The Rectangle's top left corner
> + */
> +Point Rectangle::topLeft() const
> +{
> +	return Point(x, y);
> +}
> +
> +/**
> + * \brief Apply a non-uniform rational scaling in place to this Rectangle
> + * \param[in] numerator The numerators of the x and y scaling factors
> + * \param[in] denominator The denominators of the x and y scaling factors
> + *
> + * A non-uniform scaling is applied in place such the the resulting x
> + * coordinates are multiplied by numerator.width / denominator.width,
> + * and similarly for the y coordinates (using height in place of width).
> + *
> + * \return A reference to this object
> + */
> +Rectangle &Rectangle::scaleBy(const Size &numerator, const Size &denominator)
> +{
> +	x = static_cast<int64_t>(x) * numerator.width / denominator.width;
> +	y = static_cast<int64_t>(y) * numerator.height / denominator.height;
> +	width = static_cast<uint64_t>(width) * numerator.width / denominator.width;
> +	height = static_cast<uint64_t>(height) * numerator.height / denominator.height;
> +
> +	return *this;
> +}
> +
> +/**
> + * \brief Translate this Rectangle in place by the given Point
> + * \param[in] point The amount to translate the Rectangle by
> + *
> + * The Rectangle is translated in the x-direction by the point's x coordinate
> + * and in the y-direction by the point's y coordinate.
> + *
> + * \return A reference to this object
> + */
> +Rectangle &Rectangle::translateBy(const Point &point)
> +{
> +	x += point.x;
> +	y += point.y;
> +
> +	return *this;
> +}
> +
> +/**
> + * \brief Calculate the intersection of this Rectangle with another
> + * \param[in] bound The Rectangle that is intersected with this Rectangle
> + *
> + * This method calculates the standard intersection of two rectangles. If the
> + * rectangles do not overlap in either the x or y direction, then the size
> + * of that dimension in the result (its width or height) is set to zero. Even
> + * when one dimension is set to zero, note that the other dimension may still
> + * have a positive value if there was some overlap.
> + *
> + * \return A Rectangle that is the intersection of the input rectangles
> + */
> +Rectangle Rectangle::boundedTo(const Rectangle &bound) const
> +{
> +	int topLeftX = std::max(x, bound.x);
> +	int topLeftY = std::max(y, bound.y);
> +	int bottomRightX = std::min<int>(x + width, bound.x + bound.width);
> +	int bottomRightY = std::min<int>(y + height, bound.y + bound.height);
> +
> +	unsigned int newWidth = std::max(bottomRightX - topLeftX, 0);
> +	unsigned int newHeight = std::max(bottomRightY - topLeftY, 0);
> +
> +	return { topLeftX, topLeftY, newWidth, newHeight };
> +}
> +
> +/**
> + * \brief Enclose a Rectangle so as not to exceed another Rectangle
> + * \param[in] boundary The limit that the returned Rectangle will not exceed
> + *
> + * The Rectangle is modified so that it does not exceeed the given \a boundary.

s/exceeed/exceed/

> + * This process involves translating the Rectangle if any of its edges
> + * lie beyond \a boundary, so that those edges then lie along the boundary
> + * instead.
> + *
> + * If either width or height are larger than \a boundary, then the returned
> + * Rectangle is clipped to be no larger. But other than this, the
> + * Rectangle is not clipped or reduced in size, merely translated.
> + *
> + * Note that this is not a conventional Rectangle intersection function
> + * which is provided by Rectangle::boundedTo.
> + *
> + * \return A Rectangle that does not extend beyond a boundary Rectangle
> + */
> +Rectangle Rectangle::enclosedIn(const Rectangle &boundary) const
> +{
> +	/* We can't be bigger than the boundary rectangle. */
> +	Rectangle result = boundedTo(Rectangle{ x, y, boundary.size() });
> +
> +	result.x = std::clamp<int>(result.x, boundary.x,
> +				   boundary.x + boundary.width - result.width);
> +	result.y = std::clamp<int>(result.y, boundary.y,
> +				   boundary.y + boundary.height - result.height);
> +
> +	return result;
> +}
> +
> +/**
> + * \brief Apply a non-uniform rational scaling to this Rectangle
> + * \param[in] numerator The numerators of the x and y scaling factors
> + * \param[in] denominator The denominators of the x and y scaling factors
> + *
> + * A non-uniform scaling is applied such the the resulting x

s/the the/the/

> + * coordinates are multiplied by numerator.width / denominator.width,
> + * and similarly for the y coordinates (using height in place of width).
> + *
> + * \return The non-uniformly scaled Rectangle
> + */
> +Rectangle Rectangle::scaledBy(const Size &numerator, const Size &denominator) const
> +{
> +	int scaledX = static_cast<int64_t>(x) * numerator.width / denominator.width;
> +	int scaledY = static_cast<int64_t>(y) * numerator.height / denominator.height;
> +	unsigned int scaledWidth = static_cast<uint64_t>(width) * numerator.width / denominator.width;
> +	unsigned int scaledHeight = static_cast<uint64_t>(height) * numerator.height / denominator.height;
> +
> +	return { scaledX, scaledY, scaledWidth, scaledHeight };
> +}
> +
> +/**
> + * \brief Translate a Rectangle by the given amounts
> + * \param[in] point The amount to translate the Rectangle by
> + *
> + * The Rectangle is translated in the x-direction by the point's x coordinate
> + * and in the y-direction by the point's y coordinate.
> + *
> + * \return The translated Rectangle
> + */
> +Rectangle Rectangle::translatedBy(const Point &point) const
> +{
> +	return { x + point.x, y + point.y, width, height };
> +}
> +
>  /**
>   * \brief Compare rectangles for equality
>   * \return True if the two rectangles are equal, false otherwise
> --
> 2.20.1
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch
diff mbox series

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 02fb63c0..3fa9e926 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -13,6 +13,30 @@ 
 
 namespace libcamera {
 
+class Rectangle;
+
+class Point
+{
+public:
+	constexpr Point()
+		: x(0), y(0)
+	{
+	}
+
+	constexpr Point(int xpos, int ypos)
+		: x(xpos), y(ypos)
+	{
+	}
+
+	int x;
+	int y;
+
+	constexpr Point operator-() const
+	{
+		return { -x, -y };
+	}
+};
+
 class Size
 {
 public:
@@ -31,6 +55,7 @@  public:
 
 	bool isNull() const { return !width && !height; }
 	const std::string toString() const;
+	Point center() const;
 
 	Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
 	{
@@ -93,6 +118,17 @@  public:
 			std::max(height, expand.height)
 		};
 	}
+
+	Size boundedToAspectRatio(const Size &ratio) const;
+	Size expandedToAspectRatio(const Size &ratio) const;
+
+	Rectangle centeredTo(const Point &center) const;
+
+	Size operator*(float factor) const;
+	Size operator/(float factor) const;
+
+	Size &operator*=(float factor);
+	Size &operator/=(float factor);
 };
 
 bool operator==(const Size &lhs, const Size &rhs);
@@ -176,6 +212,11 @@  public:
 	{
 	}
 
+	constexpr explicit Rectangle(const Size &size)
+		: x(0), y(0), width(size.width), height(size.height)
+	{
+	}
+
 	int x;
 	int y;
 	unsigned int width;
@@ -183,6 +224,17 @@  public:
 
 	bool isNull() const { return !width && !height; }
 	const std::string toString() const;
+	Point center() const;
+	Size size() const;
+	Point topLeft() const;
+
+	Rectangle &scaleBy(const Size &numerator, const Size &denominator);
+	Rectangle &translateBy(const Point &point);
+
+	Rectangle boundedTo(const Rectangle &bound) const;
+	Rectangle enclosedIn(const Rectangle &boundary) const;
+	Rectangle scaledBy(const Size &numerator, const Size &denominator) const;
+	Rectangle translatedBy(const Point &point) const;
 };
 
 bool operator==(const Rectangle &lhs, const Rectangle &rhs);
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index b12e1a62..63a49e9a 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -17,6 +17,42 @@ 
 
 namespace libcamera {
 
+/**
+ * \class Point
+ * \brief Describe a point in two-dimensional space
+ *
+ * The Point structure defines a point in two-dimensional space with integer
+ * precision. The coordinates of a Point may be negative as well as positive.
+ */
+
+/**
+ * \fn Point::Point()
+ * \brief Construct a Point with x and y set to 0
+ */
+
+/**
+ * \fn Point::Point(int xpos, int ypos)
+ * \brief Construct a Point at given \a x and \a y values
+ * \param[in] xpos The x-coordinate
+ * \param[in] ypos The y-coordinate
+ */
+
+/**
+ * \var Point::x
+ * \brief The x-coordinate of the Point
+ */
+
+/**
+ * \var Point::y
+ * \brief The y-coordinate of the Point
+ */
+
+/**
+ * \fn Point Point::operator-() const
+ * \brief Negate a Point by negating both its x and y coordinates
+ * \return The negated point
+ */
+
 /**
  * \struct Size
  * \brief Describe a two-dimensional size
@@ -61,6 +97,15 @@  const std::string Size::toString() const
 	return std::to_string(width) + "x" + std::to_string(height);
 }
 
+/**
+ * \brief Retrieve the center point of this size
+ * \return The center Point
+ */
+Point Size::center() const
+{
+	return { static_cast<int>(width / 2), static_cast<int>(height / 2) };
+}
+
 /**
  * \fn Size::alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
  * \brief Align the size down horizontally and vertically in place
@@ -143,6 +188,105 @@  const std::string Size::toString() const
  * height of this size and the \a expand size
  */
 
+/**
+ * \brief Bound the size down to match the aspect ratio given by \a ratio
+ * \param[in] ratio The size whose aspect ratio must be matched
+ * \return A Size whose width and height are equal to the width and height
+ * of this Size aligned down to the aspect ratio of \a ratio
+ */
+Size Size::boundedToAspectRatio(const Size &ratio) const
+{
+	uint64_t ratio1 = static_cast<uint64_t>(width) *
+			  static_cast<uint64_t>(ratio.height);
+	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
+			  static_cast<uint64_t>(height);
+
+	if (ratio1 > ratio2)
+		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
+	else
+		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
+}
+
+/**
+ * \brief Expand the size to match the aspect ratio given by \a ratio
+ * \param[in] ratio The size whose aspect ratio must be matched
+ * \return A Size whose width and height are equal to the width and height
+ * of this Size expanded up to the aspect ratio of \a ratio
+ */
+Size Size::expandedToAspectRatio(const Size &ratio) const
+{
+	uint64_t ratio1 = static_cast<uint64_t>(width) *
+			  static_cast<uint64_t>(ratio.height);
+	uint64_t ratio2 = static_cast<uint64_t>(ratio.width) *
+			  static_cast<uint64_t>(height);
+
+	if (ratio1 < ratio2)
+		return { static_cast<unsigned int>(ratio2 / ratio.height), height };
+	else
+		return { width, static_cast<unsigned int>(ratio1 / ratio.width) };
+}
+
+/**
+ * \brief Center a rectangle of this size at a given Point
+ * \param[in] center The center point the Rectangle is to have
+ *
+ * A Rectangle of this object's size is positioned so that its center
+ * is at the given Point.
+ *
+ * \return A Rectangle of this size, centered at the given Point.
+ */
+Rectangle Size::centeredTo(const Point &center) const
+{
+	int x = center.x - width / 2;
+	int y = center.y - height / 2;
+
+	return { x, y, width, height };
+}
+
+/**
+ * \brief Scale size up by the given factor
+ * \param[in] factor The factor
+ * \return The scaled Size
+ */
+Size Size::operator*(float factor) const
+{
+	return Size(width * factor, height * factor);
+}
+
+/**
+ * \brief Scale size down by the given factor
+ * \param[in] factor The factor
+ * \return The scaled Size
+ */
+Size Size::operator/(float factor) const
+{
+	return Size(width / factor, height / factor);
+}
+
+/**
+ * \brief Scale this size up by the given factor in place
+ * \param[in] factor The factor
+ * \return A reference to this object
+ */
+Size &Size::operator*=(float factor)
+{
+	width *= factor;
+	height *= factor;
+	return *this;
+}
+
+/**
+ * \brief Scale this size down by the given factor in place
+ * \param[in] factor The factor
+ * \return A reference to this object
+ */
+Size &Size::operator/=(float factor)
+{
+	width /= factor;
+	height /= factor;
+	return *this;
+}
+
 /**
  * \brief Compare sizes for equality
  * \return True if the two sizes are equal, false otherwise
@@ -365,6 +509,13 @@  bool operator==(const SizeRange &lhs, const SizeRange &rhs)
  * \param[in] height The height
  */
 
+/**
+ * \fn Rectangle::Rectangle(const Size &size)
+ * \brief Construct a Rectangle of \a size with its top left corner located
+ * at (0,0).
+ * \param[in] size The desired Rectangle size
+ */
+
 /**
  * \var Rectangle::x
  * \brief The horizontal coordinate of the rectangle's top-left corner
@@ -404,6 +555,162 @@  const std::string Rectangle::toString() const
 	return ss.str();
 }
 
+/**
+ * \brief Retrieve the center point of this rectangle
+ * \return The center Point
+ */
+Point Rectangle::center() const
+{
+	return { x + static_cast<int>(width / 2), y + static_cast<int>(height / 2) };
+}
+
+/**
+ * \brief Retrieve the size of this rectangle
+ * \return The Rectangle size
+ */
+Size Rectangle::size() const
+{
+	return Size(width, height);
+}
+
+/**
+ * \brief Retrieve the coordinates of the top left corner of this Rectangle
+ * \return The Rectangle's top left corner
+ */
+Point Rectangle::topLeft() const
+{
+	return Point(x, y);
+}
+
+/**
+ * \brief Apply a non-uniform rational scaling in place to this Rectangle
+ * \param[in] numerator The numerators of the x and y scaling factors
+ * \param[in] denominator The denominators of the x and y scaling factors
+ *
+ * A non-uniform scaling is applied in place such the the resulting x
+ * coordinates are multiplied by numerator.width / denominator.width,
+ * and similarly for the y coordinates (using height in place of width).
+ *
+ * \return A reference to this object
+ */
+Rectangle &Rectangle::scaleBy(const Size &numerator, const Size &denominator)
+{
+	x = static_cast<int64_t>(x) * numerator.width / denominator.width;
+	y = static_cast<int64_t>(y) * numerator.height / denominator.height;
+	width = static_cast<uint64_t>(width) * numerator.width / denominator.width;
+	height = static_cast<uint64_t>(height) * numerator.height / denominator.height;
+
+	return *this;
+}
+
+/**
+ * \brief Translate this Rectangle in place by the given Point
+ * \param[in] point The amount to translate the Rectangle by
+ *
+ * The Rectangle is translated in the x-direction by the point's x coordinate
+ * and in the y-direction by the point's y coordinate.
+ *
+ * \return A reference to this object
+ */
+Rectangle &Rectangle::translateBy(const Point &point)
+{
+	x += point.x;
+	y += point.y;
+
+	return *this;
+}
+
+/**
+ * \brief Calculate the intersection of this Rectangle with another
+ * \param[in] bound The Rectangle that is intersected with this Rectangle
+ *
+ * This method calculates the standard intersection of two rectangles. If the
+ * rectangles do not overlap in either the x or y direction, then the size
+ * of that dimension in the result (its width or height) is set to zero. Even
+ * when one dimension is set to zero, note that the other dimension may still
+ * have a positive value if there was some overlap.
+ *
+ * \return A Rectangle that is the intersection of the input rectangles
+ */
+Rectangle Rectangle::boundedTo(const Rectangle &bound) const
+{
+	int topLeftX = std::max(x, bound.x);
+	int topLeftY = std::max(y, bound.y);
+	int bottomRightX = std::min<int>(x + width, bound.x + bound.width);
+	int bottomRightY = std::min<int>(y + height, bound.y + bound.height);
+
+	unsigned int newWidth = std::max(bottomRightX - topLeftX, 0);
+	unsigned int newHeight = std::max(bottomRightY - topLeftY, 0);
+
+	return { topLeftX, topLeftY, newWidth, newHeight };
+}
+
+/**
+ * \brief Enclose a Rectangle so as not to exceed another Rectangle
+ * \param[in] boundary The limit that the returned Rectangle will not exceed
+ *
+ * The Rectangle is modified so that it does not exceeed the given \a boundary.
+ * This process involves translating the Rectangle if any of its edges
+ * lie beyond \a boundary, so that those edges then lie along the boundary
+ * instead.
+ *
+ * If either width or height are larger than \a boundary, then the returned
+ * Rectangle is clipped to be no larger. But other than this, the
+ * Rectangle is not clipped or reduced in size, merely translated.
+ *
+ * Note that this is not a conventional Rectangle intersection function
+ * which is provided by Rectangle::boundedTo.
+ *
+ * \return A Rectangle that does not extend beyond a boundary Rectangle
+ */
+Rectangle Rectangle::enclosedIn(const Rectangle &boundary) const
+{
+	/* We can't be bigger than the boundary rectangle. */
+	Rectangle result = boundedTo(Rectangle{ x, y, boundary.size() });
+
+	result.x = std::clamp<int>(result.x, boundary.x,
+				   boundary.x + boundary.width - result.width);
+	result.y = std::clamp<int>(result.y, boundary.y,
+				   boundary.y + boundary.height - result.height);
+
+	return result;
+}
+
+/**
+ * \brief Apply a non-uniform rational scaling to this Rectangle
+ * \param[in] numerator The numerators of the x and y scaling factors
+ * \param[in] denominator The denominators of the x and y scaling factors
+ *
+ * A non-uniform scaling is applied such the the resulting x
+ * coordinates are multiplied by numerator.width / denominator.width,
+ * and similarly for the y coordinates (using height in place of width).
+ *
+ * \return The non-uniformly scaled Rectangle
+ */
+Rectangle Rectangle::scaledBy(const Size &numerator, const Size &denominator) const
+{
+	int scaledX = static_cast<int64_t>(x) * numerator.width / denominator.width;
+	int scaledY = static_cast<int64_t>(y) * numerator.height / denominator.height;
+	unsigned int scaledWidth = static_cast<uint64_t>(width) * numerator.width / denominator.width;
+	unsigned int scaledHeight = static_cast<uint64_t>(height) * numerator.height / denominator.height;
+
+	return { scaledX, scaledY, scaledWidth, scaledHeight };
+}
+
+/**
+ * \brief Translate a Rectangle by the given amounts
+ * \param[in] point The amount to translate the Rectangle by
+ *
+ * The Rectangle is translated in the x-direction by the point's x coordinate
+ * and in the y-direction by the point's y coordinate.
+ *
+ * \return The translated Rectangle
+ */
+Rectangle Rectangle::translatedBy(const Point &point) const
+{
+	return { x + point.x, y + point.y, width, height };
+}
+
 /**
  * \brief Compare rectangles for equality
  * \return True if the two rectangles are equal, false otherwise