From patchwork Tue Sep 22 10:03:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9702 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 58221BF01C for ; Tue, 22 Sep 2020 10:04:13 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0BDEF62FE2; Tue, 22 Sep 2020 12:04:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="MT6g2cud"; dkim-atps=neutral Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D52DE62FD4 for ; Tue, 22 Sep 2020 12:04:10 +0200 (CEST) Received: by mail-wm1-x333.google.com with SMTP id s13so2683910wmh.4 for ; Tue, 22 Sep 2020 03:04:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=F6UgUxLglMFwqKHvcjbNLJCpNLX9Vnk/4za5rmYDkjI=; b=MT6g2cudWwvq7NTFIG7LeLTt6yT51wLSmUQyWjzZQ5xOYX18EkZyoUWc7BdoeEtFdI 8eGa1ShQqLANXg+MFr+cRI+UHJeuHTP4VBLp3EKSA2xHR4u7RpFQZjq6RYwk6lScGs4z 5x4/dEkgOL1EzzEn2zt5dHpGoE1d5+hwvP/98LsRWwFuuSJvjSFU03Sl+kvyJI/QLWLG l0N2GoZmqtNyyAioKPbtzCVaIEqdKOIE/39Z9hs8igv2VqYPlhmIPflC55BlvYEwnCGq wjRkepiDmFaUH8lOWYRDCdUB8g48AhwDTYURxruPmTThEUJfOqKTUIKu9ryeUVfmSgka 8shA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=F6UgUxLglMFwqKHvcjbNLJCpNLX9Vnk/4za5rmYDkjI=; b=qtZZlljEfYxezKNED5X4dR8bpVNpKgJXiEbQAyF8OGlHTC081vnP1bxr6S1QzaqWVM M2PWuQrjQUEmql0GCa84DnoR+46atLNwPOXC39AhSq94Rqmv72rzV8wW+0dPBNLZccvg gfIpK2RhiHUAesxOtWG1EI+9ByPYgZNdYYWm1TLaGa2TypbU4QoQ7vtokHniuHYbzDSj pFKxqjuSU7oPHGw47UYY1xUcN+7vUDjnCJglef7J0vZ9xW5bn6bBg81wujBxiDhlATZR tp6DTHhvyMf2BytaiV2KskXWoqnDM7EdV+uP89+keLBw2sYtBzUSTINtULMwPmwlrb/J EL8g== X-Gm-Message-State: AOAM531xg7XkDuaWOuAz3BvHE3kOEfSILGchG5OOgD2PdAWCOIPprHS1 D9PrB1Wge4zQnQcrOSD3I8oOh2LRaHHFpw== X-Google-Smtp-Source: ABdhPJwUtFfXJ8VprskvmsrnAY0d/22lr7W82zobIyMDF+2/pbpl8SEznotOmbeUguj4TcvwEeOlJQ== X-Received: by 2002:a1c:6445:: with SMTP id y66mr185760wmb.12.1600769050205; Tue, 22 Sep 2020 03:04:10 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id g14sm26369595wrv.25.2020.09.22.03.04.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 22 Sep 2020 03:04:09 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Tue, 22 Sep 2020 11:03:58 +0100 Message-Id: <20200922100400.30766-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200922100400.30766-1-david.plowman@raspberrypi.com> References: <20200922100400.30766-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/6] libcamera: Add geometry helper functions X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" These functions are aimed at making it easier to calculate cropping rectangles, particularly in order to implement digital zoom. Signed-off-by: David Plowman Reviewed-by: Jacopo Mondi --- 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 ®ion, + 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(width) * + static_cast(ratio.height); + uint64_t ratio2 = static_cast(ratio.width) * + static_cast(height); + + if (ratio1 > ratio2) + return { static_cast(ratio2 / ratio.height), height }; + else + return { width, static_cast(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(width) * + static_cast(ratio.height); + uint64_t ratio2 = static_cast(ratio.width) * + static_cast(height); + + if (ratio1 < ratio2) + return { static_cast(ratio2 / ratio.height), height }; + else + return { width, static_cast(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 ®ion, 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(result.x, boundary.x, + boundary.x + boundary.width - result.width); + + result.height = std::min(result.height, boundary.height); + result.y = std::clamp(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