[libcamera-devel,4/6] libcamera: Add geometry helper functions

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

Commit Message

David Plowman Sept. 22, 2020, 10:03 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 |  20 ++++++
 src/libcamera/geometry.cpp   | 131 +++++++++++++++++++++++++++++++++++
 2 files changed, 151 insertions(+)

Comments

Jacopo Mondi Sept. 23, 2020, 9:15 a.m. UTC | #1
Hi David,

On Tue, Sep 22, 2020 at 11:03: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 |  20 ++++++
>  src/libcamera/geometry.cpp   | 131 +++++++++++++++++++++++++++++++++++
>  2 files changed, 151 insertions(+)
>
> diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
> index 02fb63c..0447ee3 100644
> --- a/include/libcamera/geometry.h
> +++ b/include/libcamera/geometry.h
> @@ -13,6 +13,8 @@
>
>  namespace libcamera {
>
> +class Rectangle;
> +
>  class Size
>  {
>  public:
> @@ -93,8 +95,17 @@ public:
>  			std::max(height, expand.height)
>  		};
>  	}
> +
> +	Size alignedDownToAspectRatio(const Size &ratio) const;
> +	Size alignedUpToAspectRatio(const Size &ratio) const;
> +
> +	Rectangle centredTo(const Rectangle &region,
> +			    int offsetX = 0, int offsetY = 0) const;
>  };
>
> +Size operator*(const Size &size, float f);
> +Size operator/(const Size &size, float f);
> +
>  bool operator==(const Size &lhs, const Size &rhs);
>  bool operator<(const Size &lhs, const Size &rhs);
>
> @@ -176,6 +187,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 +199,10 @@ public:
>
>  	bool isNull() const { return !width && !height; }
>  	const std::string toString() const;
> +
> +	Size size() const;
> +
> +	Rectangle boundedTo(const Rectangle &boundary) const;
>  };
>
>  bool operator==(const Rectangle &lhs, const Rectangle &rhs);
> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
> index b12e1a6..0c058dd 100644
> --- a/src/libcamera/geometry.cpp
> +++ b/src/libcamera/geometry.cpp
> @@ -143,6 +143,90 @@ const std::string Size::toString() const
>   * height of this size and the \a expand size
>   */
>
> +/**
> + * \brief Align down to the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio to align down to
> + * \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::alignedDownToAspectRatio(const Size &ratio) const
> +{
> +	Size result;

result is not used, doesn't the compiler complain ?

> +
> +	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 Align up to the aspect ratio given by \a ratio
> + * \param[in] ratio The size whose aspect ratio to align up to
> + * \return A Size whose width and height are equal to the width and height
> + * of this Size aligned up to the aspect ratio of \a ratio
> + */
> +Size Size::alignedUpToAspectRatio(const Size &ratio) const
> +{
> +	Size result;
> +
> +	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 Centre a rectangle of this size within another rectangular region,
> + * with optional offsets
> + * \param[in] region The rectangular region relative to which the returned
> + * rectangle can be position
> + * \param[in] offsetX The X offset of the mid-point of the returned rectangle
> + * relative to the mid-point of the region
> + * \param[in] offsetY The Y offset of the mid-point of the returned rectangle
> + * relative to the mid-point of the region
> + *
> + * A Rectangle of this object's size is positioned within the Rectangle
> + * given by \a region. It is positioned so that its mid-point coincides
> + * with the mid-point of \a region, and is then further offset by the
> + * values \a offsetX and \a offsetY.
> + *
> + * \return A Rectangle with the horizontal and vertical sizes of
> + * this Size instance, centred with offsets within a region
> + */
> +Rectangle Size::centredTo(const Rectangle &region, int offsetX, int offsetY) const
> +{
> +	int x = (region.width - width) / 2 + region.x + offsetX;
> +	int y = (region.height - height) / 2 + region.y + offsetY;
> +
> +	return Rectangle(x, y, width, height);
> +}
> +
> +/**
> + * \brief Scale size up by the given factor

Doesn't doxygen complain for a missing \return statement ?

> + */
> +Size operator*(const Size &size, float f)
> +{
> +	return Size(size.width * f, size.height * f);
> +}
> +
> +/**
> + * \brief Scale size down by the given factor

Here too

> + */
> +Size operator/(const Size &size, float f)
> +{
> +	return Size(size.width / f, size.height / f);
> +}
> +
>  /**
>   * \brief Compare sizes for equality
>   * \return True if the two sizes are equal, false otherwise
> @@ -365,6 +449,12 @@ bool operator==(const SizeRange &lhs, const SizeRange &rhs)
>   * \param[in] height The height
>   */
>
> +/**
> + * \fn Rectangle::Rectangle(const Size &size)
> + * \brief Construct a Rectangle with zero offsets and the given size

nit: "the given \a size"

> + * \param[in] size The desired Rectangle size
> + */
> +
>  /**
>   * \var Rectangle::x
>   * \brief The horizontal coordinate of the rectangle's top-left corner
> @@ -404,6 +494,47 @@ const std::string Rectangle::toString() const
>  	return ss.str();
>  }
>
> +/**
> + * \brief Retrieve the size of this rectangle
> + * \return A Size reporting the Rectangle horizontal and vertical sizes
> + */
> +Size Rectangle::size() const
> +{
> +	return Size(width, height);
> +}
> +
> +/**
> + * \brief Bound a Rectangle so as not to exceeed another Rectangle
> + * \param[in] boundary The limit that the returned Rectangle will not exceed
> + *
> + * The Rectangle is bounded 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 bounary, 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.
> + *
> + * We note that this is not a conventional Rectangle intersection function.
> + *
> + * \return A Rectangle that does not extend beyond a boundary Rectangle
> + */
> +Rectangle Rectangle::boundedTo(const Rectangle &boundary) const
> +{
> +	Rectangle result(*this);
> +
> +	result.width = std::min(result.width, boundary.width);
> +	result.x = std::clamp<int>(result.x, boundary.x,
> +				   boundary.x + boundary.width - result.width);
> +
> +	result.height = std::min(result.height, boundary.height);
> +	result.y = std::clamp<int>(result.y, boundary.y,
> +				   boundary.y + boundary.height - result.height);
> +
> +	return result;
> +}
> +

nit aparts, very nice!
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
  j

>  /**
>   * \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 --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 02fb63c..0447ee3 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -13,6 +13,8 @@ 
 
 namespace libcamera {
 
+class Rectangle;
+
 class Size
 {
 public:
@@ -93,8 +95,17 @@  public:
 			std::max(height, expand.height)
 		};
 	}
+
+	Size alignedDownToAspectRatio(const Size &ratio) const;
+	Size alignedUpToAspectRatio(const Size &ratio) const;
+
+	Rectangle centredTo(const Rectangle &region,
+			    int offsetX = 0, int offsetY = 0) const;
 };
 
+Size operator*(const Size &size, float f);
+Size operator/(const Size &size, float f);
+
 bool operator==(const Size &lhs, const Size &rhs);
 bool operator<(const Size &lhs, const Size &rhs);
 
@@ -176,6 +187,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 +199,10 @@  public:
 
 	bool isNull() const { return !width && !height; }
 	const std::string toString() const;
+
+	Size size() const;
+
+	Rectangle boundedTo(const Rectangle &boundary) const;
 };
 
 bool operator==(const Rectangle &lhs, const Rectangle &rhs);
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index b12e1a6..0c058dd 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -143,6 +143,90 @@  const std::string Size::toString() const
  * height of this size and the \a expand size
  */
 
+/**
+ * \brief Align down to the aspect ratio given by \a ratio
+ * \param[in] ratio The size whose aspect ratio to align down to
+ * \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::alignedDownToAspectRatio(const Size &ratio) const
+{
+	Size result;
+
+	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 Align up to the aspect ratio given by \a ratio
+ * \param[in] ratio The size whose aspect ratio to align up to
+ * \return A Size whose width and height are equal to the width and height
+ * of this Size aligned up to the aspect ratio of \a ratio
+ */
+Size Size::alignedUpToAspectRatio(const Size &ratio) const
+{
+	Size result;
+
+	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 Centre a rectangle of this size within another rectangular region,
+ * with optional offsets
+ * \param[in] region The rectangular region relative to which the returned
+ * rectangle can be position
+ * \param[in] offsetX The X offset of the mid-point of the returned rectangle
+ * relative to the mid-point of the region
+ * \param[in] offsetY The Y offset of the mid-point of the returned rectangle
+ * relative to the mid-point of the region
+ *
+ * A Rectangle of this object's size is positioned within the Rectangle
+ * given by \a region. It is positioned so that its mid-point coincides
+ * with the mid-point of \a region, and is then further offset by the
+ * values \a offsetX and \a offsetY.
+ *
+ * \return A Rectangle with the horizontal and vertical sizes of
+ * this Size instance, centred with offsets within a region
+ */
+Rectangle Size::centredTo(const Rectangle &region, int offsetX, int offsetY) const
+{
+	int x = (region.width - width) / 2 + region.x + offsetX;
+	int y = (region.height - height) / 2 + region.y + offsetY;
+
+	return Rectangle(x, y, width, height);
+}
+
+/**
+ * \brief Scale size up by the given factor
+ */
+Size operator*(const Size &size, float f)
+{
+	return Size(size.width * f, size.height * f);
+}
+
+/**
+ * \brief Scale size down by the given factor
+ */
+Size operator/(const Size &size, float f)
+{
+	return Size(size.width / f, size.height / f);
+}
+
 /**
  * \brief Compare sizes for equality
  * \return True if the two sizes are equal, false otherwise
@@ -365,6 +449,12 @@  bool operator==(const SizeRange &lhs, const SizeRange &rhs)
  * \param[in] height The height
  */
 
+/**
+ * \fn Rectangle::Rectangle(const Size &size)
+ * \brief Construct a Rectangle with zero offsets and the given size
+ * \param[in] size The desired Rectangle size
+ */
+
 /**
  * \var Rectangle::x
  * \brief The horizontal coordinate of the rectangle's top-left corner
@@ -404,6 +494,47 @@  const std::string Rectangle::toString() const
 	return ss.str();
 }
 
+/**
+ * \brief Retrieve the size of this rectangle
+ * \return A Size reporting the Rectangle horizontal and vertical sizes
+ */
+Size Rectangle::size() const
+{
+	return Size(width, height);
+}
+
+/**
+ * \brief Bound a Rectangle so as not to exceeed another Rectangle
+ * \param[in] boundary The limit that the returned Rectangle will not exceed
+ *
+ * The Rectangle is bounded 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 bounary, 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.
+ *
+ * We note that this is not a conventional Rectangle intersection function.
+ *
+ * \return A Rectangle that does not extend beyond a boundary Rectangle
+ */
+Rectangle Rectangle::boundedTo(const Rectangle &boundary) const
+{
+	Rectangle result(*this);
+
+	result.width = std::min(result.width, boundary.width);
+	result.x = std::clamp<int>(result.x, boundary.x,
+				   boundary.x + boundary.width - result.width);
+
+	result.height = std::min(result.height, boundary.height);
+	result.y = std::clamp<int>(result.y, boundary.y,
+				   boundary.y + boundary.height - result.height);
+
+	return result;
+}
+
 /**
  * \brief Compare rectangles for equality
  * \return True if the two rectangles are equal, false otherwise