Patch Detail
Show a patch.
GET /api/1.1/patches/10217/?format=api
{ "id": 10217, "url": "https://patchwork.libcamera.org/api/1.1/patches/10217/?format=api", "web_url": "https://patchwork.libcamera.org/patch/10217/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20201023102159.26274-5-david.plowman@raspberrypi.com>", "date": "2020-10-23T10:21:58", "name": "[libcamera-devel,v5,4/5] libcamera: Add geometry helper functions", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "a57a9eb8687ae281f6a512cf34e24434a94284c0", "submitter": { "id": 42, "url": "https://patchwork.libcamera.org/api/1.1/people/42/?format=api", "name": "David Plowman", "email": "david.plowman@raspberrypi.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/10217/mbox/", "series": [ { "id": 1409, "url": "https://patchwork.libcamera.org/api/1.1/series/1409/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1409", "date": "2020-10-23T10:21:54", "name": "Digital zoom", "version": 5, "mbox": "https://patchwork.libcamera.org/series/1409/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/10217/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/10217/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A4F96C3D3C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 23 Oct 2020 10:22:11 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6ED50619A3;\n\tFri, 23 Oct 2020 12:22:11 +0200 (CEST)", "from mail-wr1-x431.google.com (mail-wr1-x431.google.com\n\t[IPv6:2a00:1450:4864:20::431])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 79C8B615D3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 23 Oct 2020 12:22:07 +0200 (CEST)", "by mail-wr1-x431.google.com with SMTP id g12so1179622wrp.10\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 23 Oct 2020 03:22:07 -0700 (PDT)", "from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72])\n\tby smtp.gmail.com with ESMTPSA id\n\tr128sm2477831wma.20.2020.10.23.03.22.05\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 23 Oct 2020 03:22:06 -0700 (PDT)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"TwOjIVY/\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=IvJiNICrhZMootT3r4tEqLKNGICxjfRfS2QtfMEuoIU=;\n\tb=TwOjIVY/5Yhh9jx0avZ2QtLnoADkL3F+cKco7qcpAr2TueYjRTzrtHAe//fORnTKjU\n\tKX3mVQ9azMdFK+6rRhSwURxN0xzFDGS2bkAAaFCXfQlKu9AXcCn+ANCJKgQX2puyXnhv\n\tNFdk11bclBKH4nDuB2CKRGCj1UY+oWjtQeQrLCgYFFq3k0n/qAjuc8htfiUSMlOXYzZK\n\t3T9TM+ZLrKYgXO8XNhTQqPiqz6yJdl5PD5TLsxaxSsU7HVGpNXBG+cjT9pjMcR4zAN2u\n\tuEALNFdzSyQd9Z91TJjPkQ8EaW25qTGcMQR9NB4VQxJUTUmdwevmpbHw+heulfDkymn3\n\tXR9w==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=IvJiNICrhZMootT3r4tEqLKNGICxjfRfS2QtfMEuoIU=;\n\tb=axvLK+gkOlLeSUIMIzKHMwIFHr+J73YQAPon25v2g/zAjvmTWwV2oy5B5U2gJyEt+k\n\tRAEy7aK+6bmTyLvw+fiobmAyNOzmU8XsqdvwzvS2/j//U2F8IlYR0KbBgM+q8I42laHp\n\t8Z0Xcz2TnwiI82kCb0MYWmJQHmVh+six4ivBssczKW2YAK+odpNybnsAUvWvvAeATy7S\n\tRUCUrrFb4zI6unfofkgS/uhfRpGIEzlIO4oYU/Z2uCSJznCdj2kkJYmrBmHRRhhWQypR\n\tHVQp3+MICFlBDhcwnD0cfQgFhcwVuWonLo43E+MfrG3Dgd8WORybTptSQIuB+HGWYpm9\n\tRKtw==", "X-Gm-Message-State": "AOAM532a7xc6Ntnfg1CNO6/tpV9/77YQOCgbmrmvAs/BekEh9o7AAw+M\n\t9PIBSR0UirUvjOxt//iDnaGL1x2pwCN4Bw==", "X-Google-Smtp-Source": "ABdhPJxTu01nzEmNvjUVVj0xcuJxxz6J/rnNy3jG7urx9sebqWcfI6YH9CLyxCytZpVtWNg9ORAVNA==", "X-Received": "by 2002:a5d:49cc:: with SMTP id\n\tt12mr1964624wrs.342.1603448526690; \n\tFri, 23 Oct 2020 03:22:06 -0700 (PDT)", "From": "David Plowman <david.plowman@raspberrypi.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 23 Oct 2020 11:21:58 +0100", "Message-Id": "<20201023102159.26274-5-david.plowman@raspberrypi.com>", "X-Mailer": "git-send-email 2.20.1", "In-Reply-To": "<20201023102159.26274-1-david.plowman@raspberrypi.com>", "References": "<20201023102159.26274-1-david.plowman@raspberrypi.com>", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [PATCH v5 4/5] libcamera: Add geometry helper\n\tfunctions", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "These functions are aimed at making it easier to calculate cropping\nrectangles, particularly in order to implement digital zoom.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n include/libcamera/geometry.h | 52 ++++++\n src/libcamera/geometry.cpp | 307 +++++++++++++++++++++++++++++++++++\n 2 files changed, 359 insertions(+)", "diff": "diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h\nindex 02fb63c0..3fa9e926 100644\n--- a/include/libcamera/geometry.h\n+++ b/include/libcamera/geometry.h\n@@ -13,6 +13,30 @@\n \n namespace libcamera {\n \n+class Rectangle;\n+\n+class Point\n+{\n+public:\n+\tconstexpr Point()\n+\t\t: x(0), y(0)\n+\t{\n+\t}\n+\n+\tconstexpr Point(int xpos, int ypos)\n+\t\t: x(xpos), y(ypos)\n+\t{\n+\t}\n+\n+\tint x;\n+\tint y;\n+\n+\tconstexpr Point operator-() const\n+\t{\n+\t\treturn { -x, -y };\n+\t}\n+};\n+\n class Size\n {\n public:\n@@ -31,6 +55,7 @@ public:\n \n \tbool isNull() const { return !width && !height; }\n \tconst std::string toString() const;\n+\tPoint center() const;\n \n \tSize &alignDownTo(unsigned int hAlignment, unsigned int vAlignment)\n \t{\n@@ -93,6 +118,17 @@ public:\n \t\t\tstd::max(height, expand.height)\n \t\t};\n \t}\n+\n+\tSize boundedToAspectRatio(const Size &ratio) const;\n+\tSize expandedToAspectRatio(const Size &ratio) const;\n+\n+\tRectangle centeredTo(const Point ¢er) const;\n+\n+\tSize operator*(float factor) const;\n+\tSize operator/(float factor) const;\n+\n+\tSize &operator*=(float factor);\n+\tSize &operator/=(float factor);\n };\n \n bool operator==(const Size &lhs, const Size &rhs);\n@@ -176,6 +212,11 @@ public:\n \t{\n \t}\n \n+\tconstexpr explicit Rectangle(const Size &size)\n+\t\t: x(0), y(0), width(size.width), height(size.height)\n+\t{\n+\t}\n+\n \tint x;\n \tint y;\n \tunsigned int width;\n@@ -183,6 +224,17 @@ public:\n \n \tbool isNull() const { return !width && !height; }\n \tconst std::string toString() const;\n+\tPoint center() const;\n+\tSize size() const;\n+\tPoint topLeft() const;\n+\n+\tRectangle &scaleBy(const Size &numerator, const Size &denominator);\n+\tRectangle &translateBy(const Point &point);\n+\n+\tRectangle boundedTo(const Rectangle &bound) const;\n+\tRectangle enclosedIn(const Rectangle &boundary) const;\n+\tRectangle scaledBy(const Size &numerator, const Size &denominator) const;\n+\tRectangle translatedBy(const Point &point) const;\n };\n \n bool operator==(const Rectangle &lhs, const Rectangle &rhs);\ndiff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp\nindex b12e1a62..63a49e9a 100644\n--- a/src/libcamera/geometry.cpp\n+++ b/src/libcamera/geometry.cpp\n@@ -17,6 +17,42 @@\n \n namespace libcamera {\n \n+/**\n+ * \\class Point\n+ * \\brief Describe a point in two-dimensional space\n+ *\n+ * The Point structure defines a point in two-dimensional space with integer\n+ * precision. The coordinates of a Point may be negative as well as positive.\n+ */\n+\n+/**\n+ * \\fn Point::Point()\n+ * \\brief Construct a Point with x and y set to 0\n+ */\n+\n+/**\n+ * \\fn Point::Point(int xpos, int ypos)\n+ * \\brief Construct a Point at given \\a x and \\a y values\n+ * \\param[in] xpos The x-coordinate\n+ * \\param[in] ypos The y-coordinate\n+ */\n+\n+/**\n+ * \\var Point::x\n+ * \\brief The x-coordinate of the Point\n+ */\n+\n+/**\n+ * \\var Point::y\n+ * \\brief The y-coordinate of the Point\n+ */\n+\n+/**\n+ * \\fn Point Point::operator-() const\n+ * \\brief Negate a Point by negating both its x and y coordinates\n+ * \\return The negated point\n+ */\n+\n /**\n * \\struct Size\n * \\brief Describe a two-dimensional size\n@@ -61,6 +97,15 @@ const std::string Size::toString() const\n \treturn std::to_string(width) + \"x\" + std::to_string(height);\n }\n \n+/**\n+ * \\brief Retrieve the center point of this size\n+ * \\return The center Point\n+ */\n+Point Size::center() const\n+{\n+\treturn { static_cast<int>(width / 2), static_cast<int>(height / 2) };\n+}\n+\n /**\n * \\fn Size::alignDownTo(unsigned int hAlignment, unsigned int vAlignment)\n * \\brief Align the size down horizontally and vertically in place\n@@ -143,6 +188,105 @@ const std::string Size::toString() const\n * height of this size and the \\a expand size\n */\n \n+/**\n+ * \\brief Bound the size down to match the aspect ratio given by \\a ratio\n+ * \\param[in] ratio The size whose aspect ratio must be matched\n+ * \\return A Size whose width and height are equal to the width and height\n+ * of this Size aligned down to the aspect ratio of \\a ratio\n+ */\n+Size Size::boundedToAspectRatio(const Size &ratio) const\n+{\n+\tuint64_t ratio1 = static_cast<uint64_t>(width) *\n+\t\t\t static_cast<uint64_t>(ratio.height);\n+\tuint64_t ratio2 = static_cast<uint64_t>(ratio.width) *\n+\t\t\t static_cast<uint64_t>(height);\n+\n+\tif (ratio1 > ratio2)\n+\t\treturn { static_cast<unsigned int>(ratio2 / ratio.height), height };\n+\telse\n+\t\treturn { width, static_cast<unsigned int>(ratio1 / ratio.width) };\n+}\n+\n+/**\n+ * \\brief Expand the size to match the aspect ratio given by \\a ratio\n+ * \\param[in] ratio The size whose aspect ratio must be matched\n+ * \\return A Size whose width and height are equal to the width and height\n+ * of this Size expanded up to the aspect ratio of \\a ratio\n+ */\n+Size Size::expandedToAspectRatio(const Size &ratio) const\n+{\n+\tuint64_t ratio1 = static_cast<uint64_t>(width) *\n+\t\t\t static_cast<uint64_t>(ratio.height);\n+\tuint64_t ratio2 = static_cast<uint64_t>(ratio.width) *\n+\t\t\t static_cast<uint64_t>(height);\n+\n+\tif (ratio1 < ratio2)\n+\t\treturn { static_cast<unsigned int>(ratio2 / ratio.height), height };\n+\telse\n+\t\treturn { width, static_cast<unsigned int>(ratio1 / ratio.width) };\n+}\n+\n+/**\n+ * \\brief Center a rectangle of this size at a given Point\n+ * \\param[in] center The center point the Rectangle is to have\n+ *\n+ * A Rectangle of this object's size is positioned so that its center\n+ * is at the given Point.\n+ *\n+ * \\return A Rectangle of this size, centered at the given Point.\n+ */\n+Rectangle Size::centeredTo(const Point ¢er) const\n+{\n+\tint x = center.x - width / 2;\n+\tint y = center.y - height / 2;\n+\n+\treturn { x, y, width, height };\n+}\n+\n+/**\n+ * \\brief Scale size up by the given factor\n+ * \\param[in] factor The factor\n+ * \\return The scaled Size\n+ */\n+Size Size::operator*(float factor) const\n+{\n+\treturn Size(width * factor, height * factor);\n+}\n+\n+/**\n+ * \\brief Scale size down by the given factor\n+ * \\param[in] factor The factor\n+ * \\return The scaled Size\n+ */\n+Size Size::operator/(float factor) const\n+{\n+\treturn Size(width / factor, height / factor);\n+}\n+\n+/**\n+ * \\brief Scale this size up by the given factor in place\n+ * \\param[in] factor The factor\n+ * \\return A reference to this object\n+ */\n+Size &Size::operator*=(float factor)\n+{\n+\twidth *= factor;\n+\theight *= factor;\n+\treturn *this;\n+}\n+\n+/**\n+ * \\brief Scale this size down by the given factor in place\n+ * \\param[in] factor The factor\n+ * \\return A reference to this object\n+ */\n+Size &Size::operator/=(float factor)\n+{\n+\twidth /= factor;\n+\theight /= factor;\n+\treturn *this;\n+}\n+\n /**\n * \\brief Compare sizes for equality\n * \\return True if the two sizes are equal, false otherwise\n@@ -365,6 +509,13 @@ bool operator==(const SizeRange &lhs, const SizeRange &rhs)\n * \\param[in] height The height\n */\n \n+/**\n+ * \\fn Rectangle::Rectangle(const Size &size)\n+ * \\brief Construct a Rectangle of \\a size with its top left corner located\n+ * at (0,0).\n+ * \\param[in] size The desired Rectangle size\n+ */\n+\n /**\n * \\var Rectangle::x\n * \\brief The horizontal coordinate of the rectangle's top-left corner\n@@ -404,6 +555,162 @@ const std::string Rectangle::toString() const\n \treturn ss.str();\n }\n \n+/**\n+ * \\brief Retrieve the center point of this rectangle\n+ * \\return The center Point\n+ */\n+Point Rectangle::center() const\n+{\n+\treturn { x + static_cast<int>(width / 2), y + static_cast<int>(height / 2) };\n+}\n+\n+/**\n+ * \\brief Retrieve the size of this rectangle\n+ * \\return The Rectangle size\n+ */\n+Size Rectangle::size() const\n+{\n+\treturn Size(width, height);\n+}\n+\n+/**\n+ * \\brief Retrieve the coordinates of the top left corner of this Rectangle\n+ * \\return The Rectangle's top left corner\n+ */\n+Point Rectangle::topLeft() const\n+{\n+\treturn Point(x, y);\n+}\n+\n+/**\n+ * \\brief Apply a non-uniform rational scaling in place to this Rectangle\n+ * \\param[in] numerator The numerators of the x and y scaling factors\n+ * \\param[in] denominator The denominators of the x and y scaling factors\n+ *\n+ * A non-uniform scaling is applied in place such the the resulting x\n+ * coordinates are multiplied by numerator.width / denominator.width,\n+ * and similarly for the y coordinates (using height in place of width).\n+ *\n+ * \\return A reference to this object\n+ */\n+Rectangle &Rectangle::scaleBy(const Size &numerator, const Size &denominator)\n+{\n+\tx = static_cast<int64_t>(x) * numerator.width / denominator.width;\n+\ty = static_cast<int64_t>(y) * numerator.height / denominator.height;\n+\twidth = static_cast<uint64_t>(width) * numerator.width / denominator.width;\n+\theight = static_cast<uint64_t>(height) * numerator.height / denominator.height;\n+\n+\treturn *this;\n+}\n+\n+/**\n+ * \\brief Translate this Rectangle in place by the given Point\n+ * \\param[in] point The amount to translate the Rectangle by\n+ *\n+ * The Rectangle is translated in the x-direction by the point's x coordinate\n+ * and in the y-direction by the point's y coordinate.\n+ *\n+ * \\return A reference to this object\n+ */\n+Rectangle &Rectangle::translateBy(const Point &point)\n+{\n+\tx += point.x;\n+\ty += point.y;\n+\n+\treturn *this;\n+}\n+\n+/**\n+ * \\brief Calculate the intersection of this Rectangle with another\n+ * \\param[in] bound The Rectangle that is intersected with this Rectangle\n+ *\n+ * This method calculates the standard intersection of two rectangles. If the\n+ * rectangles do not overlap in either the x or y direction, then the size\n+ * of that dimension in the result (its width or height) is set to zero. Even\n+ * when one dimension is set to zero, note that the other dimension may still\n+ * have a positive value if there was some overlap.\n+ *\n+ * \\return A Rectangle that is the intersection of the input rectangles\n+ */\n+Rectangle Rectangle::boundedTo(const Rectangle &bound) const\n+{\n+\tint topLeftX = std::max(x, bound.x);\n+\tint topLeftY = std::max(y, bound.y);\n+\tint bottomRightX = std::min<int>(x + width, bound.x + bound.width);\n+\tint bottomRightY = std::min<int>(y + height, bound.y + bound.height);\n+\n+\tunsigned int newWidth = std::max(bottomRightX - topLeftX, 0);\n+\tunsigned int newHeight = std::max(bottomRightY - topLeftY, 0);\n+\n+\treturn { topLeftX, topLeftY, newWidth, newHeight };\n+}\n+\n+/**\n+ * \\brief Enclose a Rectangle so as not to exceed another Rectangle\n+ * \\param[in] boundary The limit that the returned Rectangle will not exceed\n+ *\n+ * The Rectangle is modified so that it does not exceeed the given \\a boundary.\n+ * This process involves translating the Rectangle if any of its edges\n+ * lie beyond \\a boundary, so that those edges then lie along the boundary\n+ * instead.\n+ *\n+ * If either width or height are larger than \\a boundary, then the returned\n+ * Rectangle is clipped to be no larger. But other than this, the\n+ * Rectangle is not clipped or reduced in size, merely translated.\n+ *\n+ * Note that this is not a conventional Rectangle intersection function\n+ * which is provided by Rectangle::boundedTo.\n+ *\n+ * \\return A Rectangle that does not extend beyond a boundary Rectangle\n+ */\n+Rectangle Rectangle::enclosedIn(const Rectangle &boundary) const\n+{\n+\t/* We can't be bigger than the boundary rectangle. */\n+\tRectangle result = boundedTo(Rectangle{ x, y, boundary.size() });\n+\n+\tresult.x = std::clamp<int>(result.x, boundary.x,\n+\t\t\t\t boundary.x + boundary.width - result.width);\n+\tresult.y = std::clamp<int>(result.y, boundary.y,\n+\t\t\t\t boundary.y + boundary.height - result.height);\n+\n+\treturn result;\n+}\n+\n+/**\n+ * \\brief Apply a non-uniform rational scaling to this Rectangle\n+ * \\param[in] numerator The numerators of the x and y scaling factors\n+ * \\param[in] denominator The denominators of the x and y scaling factors\n+ *\n+ * A non-uniform scaling is applied such the the resulting x\n+ * coordinates are multiplied by numerator.width / denominator.width,\n+ * and similarly for the y coordinates (using height in place of width).\n+ *\n+ * \\return The non-uniformly scaled Rectangle\n+ */\n+Rectangle Rectangle::scaledBy(const Size &numerator, const Size &denominator) const\n+{\n+\tint scaledX = static_cast<int64_t>(x) * numerator.width / denominator.width;\n+\tint scaledY = static_cast<int64_t>(y) * numerator.height / denominator.height;\n+\tunsigned int scaledWidth = static_cast<uint64_t>(width) * numerator.width / denominator.width;\n+\tunsigned int scaledHeight = static_cast<uint64_t>(height) * numerator.height / denominator.height;\n+\n+\treturn { scaledX, scaledY, scaledWidth, scaledHeight };\n+}\n+\n+/**\n+ * \\brief Translate a Rectangle by the given amounts\n+ * \\param[in] point The amount to translate the Rectangle by\n+ *\n+ * The Rectangle is translated in the x-direction by the point's x coordinate\n+ * and in the y-direction by the point's y coordinate.\n+ *\n+ * \\return The translated Rectangle\n+ */\n+Rectangle Rectangle::translatedBy(const Point &point) const\n+{\n+\treturn { x + point.x, y + point.y, width, height };\n+}\n+\n /**\n * \\brief Compare rectangles for equality\n * \\return True if the two rectangles are equal, false otherwise\n", "prefixes": [ "libcamera-devel", "v5", "4/5" ] }