[libcamera-devel] [PATCH v4 4/5] libcamera: Add geometry helper functions

David Plowman david.plowman at raspberrypi.com
Mon Oct 19 14:51:55 CEST 2020


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 at raspberrypi.com>
---
 include/libcamera/geometry.h |  53 +++++++
 src/libcamera/geometry.cpp   | 296 +++++++++++++++++++++++++++++++++++
 2 files changed, 349 insertions(+)

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 02fb63c0..5935cc63 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -13,6 +13,25 @@
 
 namespace libcamera {
 
+class Point
+{
+public:
+	constexpr Point()
+		: x(0), y(0)
+	{
+	}
+
+	constexpr Point(int xpos, int ypos)
+		: x(xpos), y(ypos)
+	{
+	}
+
+	int x;
+	int y;
+};
+
+class Rectangle;
+
 class Size
 {
 public:
@@ -32,6 +51,8 @@ public:
 	bool isNull() const { return !width && !height; }
 	const std::string toString() const;
 
+	Point center() const;
+
 	Size &alignDownTo(unsigned int hAlignment, unsigned int vAlignment)
 	{
 		width = width / hAlignment * hAlignment;
@@ -93,8 +114,19 @@ 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*(const Size &size, float factor);
+Size operator/(const Size &size, float factor);
+
+Size &operator*=(Size &size, float factor);
+Size &operator/=(Size &size, float factor);
+
 bool operator==(const Size &lhs, const Size &rhs);
 bool operator<(const Size &lhs, const Size &rhs);
 
@@ -176,6 +208,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 +220,22 @@ public:
 
 	bool isNull() const { return !width && !height; }
 	const std::string toString() const;
+
+	Point center() const;
+
+	Size size() const;
+
+	Rectangle &translateBy(int dx, int dy);
+
+	Rectangle &rescaleTo(const Size &from, const Size &to);
+
+	Rectangle translatedBy(int dx, int dy) const;
+
+	Rectangle rescaledTo(const Size &from, const Size &to) const;
+
+	Rectangle boundedTo(const Rectangle &bound) const;
+
+	Rectangle clampedTo(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 b12e1a62..75741c1e 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -17,6 +17,36 @@
 
 namespace libcamera {
 
+/**
+ * \struct 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 x, int y)
+ * \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
+ */
+
 /**
  * \struct Size
  * \brief Describe a two-dimensional size
@@ -61,6 +91,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 +182,101 @@ 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
+ * \return The scaled Size
+ */
+Size operator*(const Size &size, float factor)
+{
+	return Size(size.width * factor, size.height * factor);
+}
+
+/**
+ * \brief Scale size down by the given factor
+ * \return The scaled Size
+ */
+Size operator/(const Size &size, float factor)
+{
+	return Size(size.width / factor, size.height / factor);
+}
+
+/**
+ * \brief Scale size up by the given factor in place
+ * \return The scaled Size
+ */
+Size &operator*=(Size &size, float factor)
+{
+	size.width *= factor;
+	size.height *= factor;
+	return size;
+}
+
+/**
+ * \brief Scale size down by the given factor in place
+ * \return The scaled Size
+ */
+Size &operator/=(Size &size, float factor)
+{
+	size.width /= factor;
+	size.height /= factor;
+	return size;
+}
+
 /**
  * \brief Compare sizes for equality
  * \return True if the two sizes are equal, false otherwise
@@ -365,6 +499,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 +545,161 @@ 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 Translate this Rectangle by the given amounts in place
+ * \param[in] dx The amount to translate the Rectangle in the x direction
+ * \param[in] dy The amount to translate the Rectangle in the y direction
+ *
+ * The Rectangle is translated by the given amounts \a dx and \a dy.
+ *
+ * \return A reference to this object
+ */
+Rectangle &Rectangle::translateBy(int dx, int dy)
+{
+	x += dx;
+	y += dy;
+
+	return *this;
+}
+
+/**
+ * \brief Rescale this Rectangle in place from one coordinate frame to another
+ * \param[in] from The current coordinate frame of the Rectangle
+ * \param[in] to The new coordinate frame for the Rectangle
+ *
+ * The Rectangle can be regarded as being embedded within a coordinate frame
+ * given by \a from. It is rescaled so as to occupy the equivalent part of the
+ * new coordinate frame given by \a to.
+ *
+ * This is purely a scaling operation, and the Rectangle does not need to lie
+ * inside the coordinate frames.
+ *
+ * \return A reference to this object
+ */
+Rectangle &Rectangle::rescaleTo(const Size &from, const Size &to)
+{
+	x = static_cast<int64_t>(x) * to.width / from.width;
+	y = static_cast<int64_t>(y) * to.height / from.height;
+	width = static_cast<uint64_t>(width) * to.width / from.width;
+	height = static_cast<uint64_t>(height) * to.height / from.height;
+
+	return *this;
+}
+
+/**
+ * \brief Translate a Rectangle by the given amounts
+ * \param[in] dx The amount to translate the Rectangle in the x direction
+ * \param[in] dy The amount to translate the Rectangle in the y direction
+ *
+ * The Rectangle is translated by the given amounts \a dx and \a dy.
+ *
+ * \return The translated Rectangle
+ */
+Rectangle Rectangle::translatedBy(int dx, int dy) const
+{
+	return { x + dx, y + dy, width, height };
+}
+
+/**
+ * \brief Rescale a Rectangle from one coordinate frame to another
+ * \param[in] from The coordinate frame of the given Rectangle
+ * \param[in] to The new coordinate frame for the new Rectangle
+ *
+ * The Rectangle can be regarded as being embedded within a coordinate frame
+ * given by \a from. A new Rectangle, rescaled so as to occupy the equivalent
+ * part of the new coordinate frame given by \a to, is created and returned.
+ *
+ * This is purely a scaling operation, and the Rectangle does not need to lie
+ * inside the coordinate frames.
+ *
+ * \return The rescaled Rectangle
+ */
+Rectangle Rectangle::rescaledTo(const Size &from, const Size &to) const
+{
+	int scaledX = static_cast<int64_t>(x) * to.width / from.width;
+	int scaledY = static_cast<int64_t>(y) * to.height / from.height;
+	unsigned int scaledWidth = static_cast<uint64_t>(width) * to.width / from.width;
+	unsigned int scaledHeight = static_cast<uint64_t>(height) * to.height / from.height;
+
+	return { scaledX, scaledY, scaledWidth, scaledHeight };
+}
+
+/**
+ * \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);
+
+	int newWidth = bottomRightX - topLeftX;
+	int newHeight = bottomRightY - topLeftY;
+
+	return { topLeftX, topLeftY,
+		 static_cast<unsigned int>(std::max(newWidth, 0)),
+		 static_cast<unsigned int>(std::max(newHeight, 0)) };
+}
+
+/**
+ * \brief Clamp 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.
+ *
+ * We note that this is not a conventional Rectangle intersection function
+ * \sa Rectangle::boundedTo
+ *
+ * \return A Rectangle that does not extend beyond a boundary Rectangle
+ */
+Rectangle Rectangle::clampedTo(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 Compare rectangles for equality
  * \return True if the two rectangles are equal, false otherwise
-- 
2.20.1



More information about the libcamera-devel mailing list