From patchwork Mon Sep 7 16:44:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9514 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 C9606BDB1C for ; Mon, 7 Sep 2020 16:45:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 932B462BBA; Mon, 7 Sep 2020 18:45:00 +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="K6SyI6rP"; dkim-atps=neutral Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4ACE062BBA for ; Mon, 7 Sep 2020 18:44:58 +0200 (CEST) Received: by mail-wr1-x42f.google.com with SMTP id z4so16450223wrr.4 for ; Mon, 07 Sep 2020 09:44:58 -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=O61rTSPSnTdYCOC7EfprGKw/KQFBenZCvM1enIUSkgs=; b=K6SyI6rPmy3qLPoD3QNZxE0lz4hZ9G+2KZEVYueSLA6p/j/C/a0HV7F2xnFNJyskMw SCNj+fEzbXW5vuMqEJE5Z96eSaK+uJ1aCsnaNGSB19oMCes3SxVixdOULgKUrhzjSwv2 1PVMsMFSNnBbSjZX0tBYzMbzRJgqaW/xCpKGynX+SKBjDwwouwixT6muxe5XXRJmsy25 bQBcqme9i6myDjSG8Uc5aByEAblYn/VMtvLCMRx9pRCPtMcrdproKrBmhou9X2XEAYzV j+yiqp1cBxjITqj8vXVaJESAf32ZM652ZSqqiO3HjZG9CqaAHBfXpU/DAs0/ZFPgy/wt AWiA== 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=O61rTSPSnTdYCOC7EfprGKw/KQFBenZCvM1enIUSkgs=; b=hVzskMEbDSwR8it006ZQesOxnrZ41pnSBAXghaM6JiWHWZpNfxB56Bgz4H5Pcf3Vbq Ht+KOKIl5NInlDZ4H2uq4tlpGOna5VQFwd+G+2hOsO75FTt5nAY2Q6Z13otq/T70V34h 1lsMI9ENJ22aIGus+kKjVXWVcAteYL4jrw7C/tlopZcNvOryo4O1gkuasZXgWzdFrU6D 7G77fqx6n0+svXhO3Sp6Oyr69fCilIZAclzG07Cy4rwEp4//BhzBGiEILSAm0CaYYMUO B0WrAfieqoQJ1YTR5XOqythcdXaFwsMY+iMuS44VNysIzqHDSXLY9UeIFjQO4m2uU4rL lgWg== X-Gm-Message-State: AOAM532lxz/7Oc8QGARLsRWpmRqmK5Yl4fwci3a4WvTuP9MlM0p7rXbR +moI1aDJaj3VIk1hWUfUO5o7q1KP0mWOwg== X-Google-Smtp-Source: ABdhPJwlMXoZSrwC01EFwkbxTQB2snDH41eXBppWiulz3jpr8cqLB/1ag3cPIopn9JfHKAWSK5beYQ== X-Received: by 2002:adf:ec90:: with SMTP id z16mr21613682wrn.145.1599497097680; Mon, 07 Sep 2020 09:44:57 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id v3sm27707033wmh.6.2020.09.07.09.44.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Sep 2020 09:44:57 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Mon, 7 Sep 2020 17:44:49 +0100 Message-Id: <20200907164450.13082-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200907164450.13082-1-david.plowman@raspberrypi.com> References: <20200907164450.13082-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 3/4] 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. --- include/libcamera/geometry.h | 18 +++++ src/libcamera/geometry.cpp | 129 +++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h index 02fb63c..8f6a9a0 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,16 @@ public: std::max(height, expand.height) }; } + + Size aspectRatioDown(const Size &ar) const; + Size aspectRatioUp(const Size &ar) const; + Rectangle centre(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 +186,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 +198,9 @@ public: bool isNull() const { return !width && !height; } const std::string toString() const; + + Size size() const; + Rectangle clamp(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..3e26167 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -143,6 +143,89 @@ const std::string Size::toString() const * height of this size and the \a expand size */ +/** + * \brief Clip the given size to have a given aspect ratio + * \param[in] ar The aspect ratio the result is to have + */ +Size Size::aspectRatioDown(const Size &ar) const +{ + Size result; + + uint64_t ratio1 = static_cast(width) * + static_cast(ar.height); + uint64_t ratio2 = static_cast(ar.width) * + static_cast(height); + + if (ratio1 > ratio2) + result = Size(ratio2 / ar.height, height); + else + result = Size(width, ratio1 / ar.width); + + return result; +} + +/** + * \brief Expand the given size to have a given aspect ratio + * \param[in] ar The aspect ratio the result is to have + */ +Size Size::aspectRatioUp(const Size &ar) const +{ + Size result; + + uint64_t ratio1 = static_cast(width) * + static_cast(ar.height); + uint64_t ratio2 = static_cast(ar.width) * + static_cast(height); + + if (ratio1 < ratio2) + result = Size(ratio2 / ar.height, height); + else + result = Size(width, ratio1 / ar.width); + + return result; +} + +/** + * \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 offset within a region + */ +Rectangle Size::centre(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 +448,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 the zero offsets and size + * \param[in] size The size + */ + /** * \var Rectangle::x * \brief The horizontal coordinate of the rectangle's top-left corner @@ -404,6 +493,46 @@ const std::string Rectangle::toString() const return ss.str(); } +/** + * \brief Return the size of this rectangle + */ +Size Rectangle::size() const +{ + return Size(width, height); +} + +/** + * \brief Clamp a rectangle so as not to exceeed another rectangle + * \param[in] boundary the limit that the returned rectangle will not exceed + * + * The rectangle is clamped 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::clamp(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