Message ID | 20201023102159.26274-5-david.plowman@raspberrypi.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
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 ¢er) 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 ¢er) 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
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 ¢er) 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 ¢er) 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
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 ¢er) 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 ¢er) 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
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(+)