From patchwork Fri Jul 14 14:15:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18823 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 272B6C3243 for ; Fri, 14 Jul 2023 14:16:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8B768628C3; Fri, 14 Jul 2023 16:16:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344167; bh=5gIotdgCLA8wBY2A9zTP1a9SwI9UnfgiJ8repAogkSo=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Bt1Jm2pqNlFa9F8prKQlxP1ie3sQr3W33gJ+LdbhvmgqLydGFNVls1D3utJ+uxnNL wrLlBOf2QbqkfpCCr3NSXwWusTMNy0ic5HnF7UV79viatIJGQIZ9SYK5sDOmvmmdwj d4jm1jjaMiOgghP3VY9Od3VL0jR23LkZC+6oG3mCGnMDvC7psn4a+Q1dASAwCs9Pq1 TZyb9+e31dOFiMzZll4ohP7eE9EzNzgMO9Lf5n30T/ITrCUxdUi+aVd/qa4V3bgISn EP8oCFaKYufkYjhHMhUbbAQXewlY3Hy5lwOA+Mo79Y5O2Ic0Dwj8w+mdEZ/gm38nr2 UUEe9Pix5XdVQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9C28460382 for ; Fri, 14 Jul 2023 16:16:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bjQDKjr8"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C85377F3; Fri, 14 Jul 2023 16:15:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344114; bh=5gIotdgCLA8wBY2A9zTP1a9SwI9UnfgiJ8repAogkSo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bjQDKjr8fFHe0NTS+H8mQ9Vp46CEDd0u826CuM5bDQL+Rc4RqnFOgF8VQ7fjKBbEW Soj9NenyertFUDN33hBIdd08gYWh5hWPqucUfgROH+JurKLPNz9JRheBM/HB7uHAsl 473AZSbXgetf9RoJLj5adw8OwvIjfConE8JmhDEQ= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:41 +0200 Message-Id: <20230714141549.11085-2-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/9] libcamera: camera_sensor: Cache rotationTransform_ 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The rotationTransform_ depends on a V4L2 control whose value does not change for the whole lifetime of the camera. Instead of re-calculating it everytime the camera is configured, cache it at properties initialization time. Signed-off-by: Jacopo Mondi Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Reviewed-by: David Plowman --- include/libcamera/internal/camera_sensor.h | 1 + src/libcamera/camera_sensor.cpp | 54 +++++++++++++--------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h index 02c77ab037da..02b4b4d25e6d 100644 --- a/include/libcamera/internal/camera_sensor.h +++ b/include/libcamera/internal/camera_sensor.h @@ -107,6 +107,7 @@ private: Rectangle activeArea_; const BayerFormat *bayerFormat_; bool supportFlips_; + Transform rotationTransform_; ControlList properties_; diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp index f3a5aa37149f..9a033459742f 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -461,18 +461,36 @@ int CameraSensor::initProperties() const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION); if (rotationControl != controls.end()) { + propertyValue = rotationControl->second.def().get(); + /* - * validateTransform() compensates for the mounting rotation. - * However, as a camera sensor can only compensate rotations - * by applying H/VFlips, only rotation of 180 degrees are - * automatically compensated. The other valid rotations (Rot90 - * and Rot270) require transposition, which the camera sensor - * cannot perform, so leave them untouched. + * Cache the Transform associated with the camera mounting + * rotation for later use in validateTransform(). + */ + bool success; + rotationTransform_ = transformFromRotation(propertyValue, &success); + if (!success) { + LOG(CameraSensor, Warning) + << "Invalid rotation of " << propertyValue + << " degrees - ignoring"; + rotationTransform_ = Transform::Identity; + } + + /* + * Adjust property::Rotation as validateTransform() compensates + * for the mounting rotation. However, as a camera sensor can + * only compensate rotations by applying H/VFlips, only rotation + * of 180 degrees are automatically compensated. The other valid + * rotations (Rot90 and Rot270) require transposition, which the + * camera sensor cannot perform, so leave them untouched. */ - propertyValue = rotationControl->second.def().get(); if (propertyValue == 180 && supportFlips_) propertyValue = 0; properties_.set(properties::Rotation, propertyValue); + } else { + LOG(CameraSensor, Warning) + << "Rotation control not available, default to 0 degrees"; + rotationTransform_ = Transform::Identity; } properties_.set(properties::PixelArraySize, pixelArraySize_); @@ -1038,21 +1056,11 @@ void CameraSensor::updateControlInfo() */ Transform CameraSensor::validateTransform(Transform *transform) const { - /* Adjust the requested transform to compensate the sensor mounting rotation. */ - const ControlInfoMap &controls = subdev_->controls(); - int rotation = 0; - - const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION); - if (rotationControl != controls.end()) - rotation = rotationControl->second.def().get(); - - bool success; - Transform rotationTransform = transformFromRotation(rotation, &success); - if (!success) - LOG(CameraSensor, Warning) << "Invalid rotation of " << rotation - << " degrees - ignoring"; - - Transform combined = *transform * rotationTransform; + /* + * Combine the requested transform to compensate the sensor mounting + * rotation. + */ + Transform combined = *transform * rotationTransform_; /* * We combine the platform and user transform, but must "adjust away" @@ -1083,7 +1091,7 @@ Transform CameraSensor::validateTransform(Transform *transform) const * rise to this is the inverse of the rotation. (Recall that * combined = transform * rotationTransform.) */ - *transform = -rotationTransform; + *transform = -rotationTransform_; combined = Transform::Identity; } From patchwork Fri Jul 14 14:15:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18824 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 1F57EBDC71 for ; Fri, 14 Jul 2023 14:16:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 09D66628C4; Fri, 14 Jul 2023 16:16:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344168; bh=Q9IWFqB6mXxY5M7epcsmJeDXvpQKthD6mOLYgResINc=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=pP+pI85Q5wyW6WXz0oU9kDJPTwiHJo4wU1aZgV+74++O5GGodOc9U4zfVMUV13VYL wCKg39YDeNEDCmZYRAe86ThdwFZtgxvz3wFl//8A6A2NgpBv4BSsp5zO4DqJxXPoGS mbPhnA5/QhG4Dn1+e7GPYVZHnZfSyJfXMowT/TqTUUTmZUQnGnKsSJr+/BynUxJiFT qEL6q/olMPpHq1IhbvvqLUo3+xy+FdImz1Lr3ax7/eASlMy6+0bBdPCsb7Jx1/M/wN c8Dcq1SvNNzpbn8ZWN4jO+8afZNKj2+TeUtJILRu+OKDNMg0Y4bAXqGmWCXYveAjnG S3TF4yt2MZQhw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 84319628C0 for ; Fri, 14 Jul 2023 16:16:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="s1Kc/QDh"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1D72657E; Fri, 14 Jul 2023 16:15:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344115; bh=Q9IWFqB6mXxY5M7epcsmJeDXvpQKthD6mOLYgResINc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=s1Kc/QDh1TPOfoglBigLyQ2rReV8AxbcCEWmGbj79LM6Nfkts8t/zW2kYuBPrHaQ8 WXtIoqTzhOwef4+9WiSlqyZRiOba0ugwVmqfTFLpxlFAYKoag8A+YY7FdfJF0GMsDy 2QI+CjEyoHKoTS2jngJwK0W+3yRu/DnPtUXv+7ko= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:42 +0200 Message-Id: <20230714141549.11085-3-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/9] libcamera: camera: Introduce CameraConfiguration::orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Introduce CameraConfiguration::Orientation in the CameraConfiguration class. The Orienation enumeration describes the possible 2D transformations that can be applied to an image using two basic plane transformations. The enumeration values follow the ones defined by the EXIF specification at revision 2.32, Tag 274 'orientation'. The newly introduced filed is meant to replace CameraConfiguration::transform which is not removed yet not to break compilation. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- include/libcamera/camera.h | 17 +++++++++ src/libcamera/camera.cpp | 76 +++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 004bc89455f5..8132ef2506a4 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -39,6 +40,18 @@ public: Invalid, }; + enum Orientation { + /* EXIF tag 274 starts from '1' */ + rotate0 = 1, + rotate0Flip, + rotate180, + rotate180Flip, + rotate90Flip, + rotate270, + rotate270Flip, + rotate90, + }; + using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; @@ -67,6 +80,7 @@ public: std::size_t size() const; Transform transform; + Orientation orientation; protected: CameraConfiguration(); @@ -83,6 +97,9 @@ protected: std::vector config_; }; +std::ostream &operator<<(std::ostream &out, + const CameraConfiguration::Orientation &orientation); + class Camera final : public Object, public std::enable_shared_from_this, public Extensible { diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 0eecee766f00..78aa11e72dd4 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -145,6 +145,44 @@ LOG_DECLARE_CATEGORY(Camera) * The configuration is invalid and can't be adjusted automatically */ +/** + * \enum CameraConfiguration::Orientation + * \brief The image orientation in a memory buffer + * + * The Orientation enumeration describes the orientation of the images + * produced by the camera pipeline as they get received by the application + * inside memory buffers. + * + * All the possible 2D planes transformation of an image are expressed as the + * combination of two basic operations: + * + * 'a': rotate the image 90 degrees clockwise + * 'b': flip the image horizontally (mirroring) + * + * plus an identity operator 'e'. + * + * The composition of operations 'a' and 'b' according to the canonical function + * composition notion 'b * a' (where 'a' is applied first, then 'b' is applied + * next) gives origin to a symmetric group of order 8, or dihedral group. + * + * See https://en.wikipedia.org/wiki/Dihedral_group#/media/File:Dih4_cycle_graph.svg + * as an example of the 2D plane transformation and how they originate from + * the composition of the 'a' and 'b' operations. + * + * The image orientation expressed using the Orientation enumeration can be + * then inferred by applying a multiple of a 90 degrees rotation from the origin + * and the applying any horizontal mirroring to a base image assume to be + * naturally oriented (no rotation and no mirroring applied). + * + * The enumeration numerical values follow the ones defined by the EXIF + * Specification version 2.32, Tag 274 "Orientation", while the names of the + * enumerated values report the rotation and mirroring operations performed. + * + * In example Orientation::rotate90Flip describes the image transformation + * obtained by rotating 90 degrees clockwise first and the applying an + * horizontal mirroring. + */ + /** * \typedef CameraConfiguration::iterator * \brief Iterator for the stream configurations in the camera configuration @@ -160,7 +198,7 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : transform(Transform::Identity), config_({}) + : transform(Transform::Identity), orientation(rotate0), config_({}) { } @@ -317,6 +355,27 @@ std::size_t CameraConfiguration::size() const return config_.size(); } +/** + * \brief Prints human-friendly names for Orientation items + * \param[in] out The output stream + * \param[in] orientation The Orientation item + * \return The output stream \a out + */ +std::ostream &operator<<(std::ostream &out, + const CameraConfiguration::Orientation &orientation) +{ + constexpr std::array orientationNames = { + "", /* Orientation starts counting from 1. */ + "rotate0", "rotate0Flip", + "rotate180", "rotate180Flip", + "rotate90Flip", "rotate270", + "rotate270Flip", "rotate90", + }; + + out << orientationNames[orientation]; + return out; +} + /** * \enum CameraConfiguration::ColorSpaceFlag * \brief Specify the behaviour of validateColorSpaces @@ -404,6 +463,21 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF * may adjust this field at its discretion if the selection is not supported. */ +/** + * \var CameraConfiguration::orientation + * \brief The desired orientation of the images produced by the camera + * + * The orientation field is a user-specified 2D plane transformation that + * specifies how the application wants the camera images to be rotated in + * the memory buffers. + * + * If the application requested orientation cannot be obtained the validate() + * function will Adjust this field to the actual orientation of the images + * as produced by the camera pipeline. + * + * By default the orientation filed is set to Orientation::rotate0. + */ + /** * \var CameraConfiguration::config_ * \brief The vector of stream configurations From patchwork Fri Jul 14 14:15:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18825 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 07C65C3243 for ; Fri, 14 Jul 2023 14:16:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AA577628BC; Fri, 14 Jul 2023 16:16:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344171; bh=IKUZ5/s5Pk6RRZa2yVeKiJS9TU/p3xOR9TwD7Ng2QKI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=QfEGjd78pqSRkrLXZ+v5alz2g+8bgpXWDAJSWs9pABzHItfxUCeZecR17ygAHEpXE N5EyVulAOfacd+7Pj5lowuLLtHZuVG070wIGNJhIsOqJB4xsW6uKuUW3NSgAXe1DdL b9Pyd1sEDQchgLO5XE1K8rtLFnovI9byyO47SVS6nzR6mGZNKgnoZUAjKdnVZc3bHE KTv+1Zm6nIioAXkSX3S3Q3QvVKwfclWl+fokibs3P2hxWqAl1y5mFp4qSqUH5QrK8+ q/HiEOGfRlzYeNXXLeHfWRbyjTtFa5x+WeDrRa6z8FyY4YiKHPViN/xHrYZA5zhzVZ VvA6JimjcbzLw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E4B16628BC for ; Fri, 14 Jul 2023 16:16:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZNG5c72e"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 028AB7F3; Fri, 14 Jul 2023 16:15:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344119; bh=IKUZ5/s5Pk6RRZa2yVeKiJS9TU/p3xOR9TwD7Ng2QKI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZNG5c72eu1IMhevMvbK/1sPzQ0Ux8rl/dl08tLO+yOO3uk01XcXbTUnkAC176VGCD dAq05nylXd9A+JzLOAaaCG+2v0dSsVY27uRICzKxQMx52HTFwIkPnCH/sWIQZsJ9D1 SzMpVe0B0ollRacfwqKn5rUcKKxOlYl1dnNqaydo= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:43 +0200 Message-Id: <20230714141549.11085-4-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/9] Documentation: Add figures to document Orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add figures in Documentation/rotation/ to document the plane transformations defined by the Orientation enumeration. Signed-off-by: Jacopo Mondi --- Documentation/Doxyfile.in | 2 + Documentation/rotation/flip-rotate-0.eps | 170 ++++++++++++++++++ Documentation/rotation/flip-rotate-0.png | Bin 0 -> 16488 bytes Documentation/rotation/flip-rotate-180.eps | 191 +++++++++++++++++++++ Documentation/rotation/flip-rotate-180.png | Bin 0 -> 22198 bytes Documentation/rotation/flip-rotate-270.eps | 118 +++++++++++++ Documentation/rotation/flip-rotate-270.png | Bin 0 -> 9108 bytes Documentation/rotation/flip-rotate-90.eps | 118 +++++++++++++ Documentation/rotation/flip-rotate-90.png | Bin 0 -> 10918 bytes Documentation/rotation/rotate-0.eps | 169 ++++++++++++++++++ Documentation/rotation/rotate-0.png | Bin 0 -> 9086 bytes Documentation/rotation/rotate-180.eps | 189 ++++++++++++++++++++ Documentation/rotation/rotate-180.png | Bin 0 -> 22182 bytes Documentation/rotation/rotate-270.eps | 118 +++++++++++++ Documentation/rotation/rotate-270.png | Bin 0 -> 9334 bytes Documentation/rotation/rotate-90.eps | 118 +++++++++++++ Documentation/rotation/rotate-90.png | Bin 0 -> 9163 bytes src/libcamera/camera.cpp | 25 +++ 18 files changed, 1218 insertions(+) create mode 100644 Documentation/rotation/flip-rotate-0.eps create mode 100644 Documentation/rotation/flip-rotate-0.png create mode 100644 Documentation/rotation/flip-rotate-180.eps create mode 100644 Documentation/rotation/flip-rotate-180.png create mode 100644 Documentation/rotation/flip-rotate-270.eps create mode 100644 Documentation/rotation/flip-rotate-270.png create mode 100644 Documentation/rotation/flip-rotate-90.eps create mode 100644 Documentation/rotation/flip-rotate-90.png create mode 100644 Documentation/rotation/rotate-0.eps create mode 100644 Documentation/rotation/rotate-0.png create mode 100644 Documentation/rotation/rotate-180.eps create mode 100644 Documentation/rotation/rotate-180.png create mode 100644 Documentation/rotation/rotate-270.eps create mode 100644 Documentation/rotation/rotate-270.png create mode 100644 Documentation/rotation/rotate-90.eps create mode 100644 Documentation/rotation/rotate-90.png diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index 697a14d1dfe2..67ee51afb6e3 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -78,6 +78,8 @@ EXPAND_ONLY_PREDEF = YES INCLUDE_PATH = "@TOP_SRCDIR@/include/libcamera" INCLUDE_FILE_PATTERNS = *.h +IMAGE_PATH = "@TOP_SRCDIR@/Documentation" + PREDEFINED = __DOXYGEN__ \ __cplusplus \ __attribute__(x)= \ diff --git a/Documentation/rotation/flip-rotate-0.eps b/Documentation/rotation/flip-rotate-0.eps new file mode 100644 index 000000000000..3abf587d10e0 --- /dev/null +++ b/Documentation/rotation/flip-rotate-0.eps @@ -0,0 +1,170 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.17.8 (https://cairographics.org) +%%CreationDate: Mon Jun 19 14:15:10 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 349 301 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 349 301 +%%EndPageSetup +q 0 1 349 300 rectclip +1 0 0 -1 0 301 cm q +Q q +0 0 348.801 300 re W n +q +0 0 349 300 re W n +[ 348.800011 0 0 -300 0 300 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceGray setcolorspace +<< + /ImageType 1 + /Width 436 + /Height 375 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 436 0 0 -375 0 375 ] +>> +cairo_image + Gb"/lH'B#^*QJC]5o)cU+Nh+KLf'#%#Z3ln'SD9J)P_(1E"ej`NCL7;__oH$eQf/#H$T:YM + ;akP!]chgd=@tR.C_!F'GQP:,,k#3'i>"bSPWV@?+%Ap427eulL(,:If@]*VQ/c;rL)O0l2 + Gj+4qB'S4]msM\K:QFg@)!-'ETjP3J&+9VSk)&Hg32`m9SR%;/` + *BHAT%_5MgujP\hH1IfCqafId_iiNW'$.)R^K.M)Pi()pF&Jb + K*N*ZMJfq\#T>PIr*BOraR2A1[uCNhH2;]O./'CB5MWpBa_7;`U@Ho)Km^b)6m:mI)2.C*f + IC\%rgN+ansc6UC%s6=5?APT@m?4+,fL5r'fD-1%+D?E@Z'qXUVlV[_\nt]0Eq*BR^DL?Q& + Hh?8q9G-2XVo>7bhUoTiTMPMV`>Xgmmd.kojZ&1NaU\8n<1>hloJb4f=N;V + )Aq\280"h-H%6^7d>d1uXRAR+:J$CU&(j:(ZIP&jNV65%$h2d#bkE_V'rS=`YH+We9NFW.` + 0-(/JpV;r:^TT4sKQaBIQ&V^X;H)"a5_U2p]QA%e3,]Ii(D9F + E2+dF5a]6Y'9gk]pB_TH8k0_%8i85Q;eY/m++T'AbTeU/@*SgXk,DnCcWO#1*]/s_PZ8'ni + 2o8d>JP)Xk!>Nj&P%h#5@Oef^sT]eBVrZ&fci%-1@FWiN2&[b2%qPC!Jo0lgHFGY#=]ci`H + *(UE+3!3cK8iV%Fir:=Y,L0'(=acIJjf0Dup45#"]L]NPe91r3Fg__Ka/aWXen)VUR + i]j!#U8*8+aPLj](97t`I)&d#GhNm.3Tnk1Pe*rKr'S"UJ?'*d!UFh9)e="*^a)>!TO@D_' + 9He6U>qck@GMXSK&!X)jPd-9okNGriYB,#C5]/VS(DYoZibc+UI4^JM,Z8_/!?FPG^UX[(^ + -XM_$uk2*TBgNCY[Nc'ts.m2nu,]h"h"*I\Lk#9ATi<<;#e#L\JMFG*nYY@8W=3"L::4e<`/Y\U_)V73;71f)4Ztj^f<=UtZ(t:q(Ahhr)gnD<@#%4d%\;.;j9rKc]Ke_V^+Deu& + -TW&p'J30-2%G$>ni$?1%.%UYWYde-_\Rs+9nni.:coBV@%QQ!jfh,[Vj"R;#6"*IPp,?L!tnIcJl1LF + E["M]Ep7(R6>[BX)4^g.nu2$P]q:7qL+]C"uh)s*oHLih7UVd2q002M+R*#2_s5!_npfDHO + ;eKp1hkr(c:\iDef$k84c$A#c[C4Dnhh=>;RjX#eT8@K&>"mRG5*=,DPc0MKsQPe9th,]U= + FjK1X?]sY$2A?[)ZAQmt[gfd&E.@gVW[A@kM"u6G`JA8 + aJsYXWR[fhMf=2jGl+!3W6Jcf+N_N#(a + A7B[[!-BB\-@PjFe_$Z@oE8@K$dP6,k.B.(2]/(/9gB9)M+[5!ep3e@%[eA96QCX;*l.R13&TFBd^rrk$=DqJ)'#]a/Cj6<0Es + '9R/OCoQkP:a_XPRTu4^=U%u3?$KlD7_5_+l3)T$[(4e\pFm8CDB$5)Uba6^TD)N:4k[-Ja]tXr$;::Y;koYN5F4N'^Vqm,Jk1+#Eo"hs>G + ^"nMk/hsD@8TdnZjt_Om\F='^<'CbEMo!3i^7Y61=O53QY@!9G$Hkb$(Uq#!L7tq[N8glnl + j_6:AC\c=!IG(.QE)u&77q)RdVX[crTb60j.>Z/`.dQ_R42-)`.^ak%CS\V5CX8lLd#Z + O$A_7Ii%.C0XjA,:>";+>e:!ND5bB)JH/+6u> + _Yuur3\Y!<89a.J\$BX8Yl+F/2Xc4?f[&#FXssJh,Mb91&Sq\96_35DW_'KC=N8[d8@K$rT + PiE-A!+sf&(At0WDC[7F(32J728q#B'>s'*[gf,uSTg"NBk\@tAp\Ktpi,e]@V + &>N&?'*cVrLR-15L&,5Qp@m.a)-D6((-PD'/n&\M75J-WE!dDC[7J!d-@`#O2qtX + J=AL$!@ieToejUpacJdBkc?C#adAJsa@JXA#J=.An7%ElmLdVXV\9*a_p5t&<)X@TT)8,-/ + b5[>!R4ePV=)+M2STXXj38OTBOQu\.->N$m5r;6XG]R9NF\o"+Fr4FaB<^`b<#!E+Slr#9p + DC[hiW;h.8OPJ**Pn`Jk_:FE-SOF=iPD3++UtZ71bKoKOH')tpLumf4D^aF&*YfRW[_YG8j + l\M"e4@%`Q3VuPn`8fpQ0E,T:NTWDUtZ+.eX)n0A_f3CF&A'^DAsuW5Hb88h,[Vh"R8P$cR + XQ;[j=G8Y)9I]]E0;*]f1*ie$LSU5>MNHDQARY?17VaL5$D)mNfNR=!D,LGr)q,+OZe`MuD + t^\XM16E"V>0#dO;Thj@LD&a<&N(PAK;d?!OQJk1*[&=/qAM269*Ir)JO&:k,:74^Z1!YOZ + \j9akcJI!;,QCjfAJo^%P&a<$orBMAIh]@.D?5phME@ka,E#,Ot + Cm:]R^P2(0/5YD6'f\!>[>cr"9K32gk,>^c-CdZC=`!#t3%d;)Xhu,;_O2!.3P4,*CYbYiSK,A#rj^CS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM)) + sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))s + DSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sD + SS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sDSS:uMM))sE> + .;/U7Qt(>uc=[(,'Xp-lbW&]<+Z=:?*&s.4Ze3&rE?',0n1G[X_t[H'36VG@h-\2+B:pboj + rSL(*cU6_E983\jrSL(*cU6_E983\jrSL(*cU6_E983\jrSL(*cU6_E983\jrSL(*cU6_E9 + 83\jrSL(*cU6_E983\jrSL(*cU6_E983\jrSL(*cU6_E983\jrSL(*cU6_E983\`]='4"8j + I5>2bE]9[3XQ9;Y`Lj1q&eE(1&W3,k`\(7^rJW(e"s-'%0T#Q"CS_HGm6R0IV"#0o2IV?*3 + LJMRe+B`MdVi/@bN)Sd4.39.fB#&m$9c8]d?!M#:'1Cfph!,#j#N!!]2^_n"Lj1q&eE(1&W + 3,k_qS<8@Y%\.55B4TqZ"(rOf)&YEo^mPCG7L%-tE2EZ+Ob#:uS7qt9ADiCKB,\Q')*$ZlR + 0IV"#0o2IV?*3LJMRe+B`MdVi/@bN)Sd4.39.fB#+9M_q1WR53(k=GfomcokW=6U>(8M)*m + **P?G4B#f.V?C;L^Z!*oZJ7JV09Oo4nm)]h5+cS0a58[OcV)qqjPO#2h%XKX3%Hf +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/flip-rotate-0.png b/Documentation/rotation/flip-rotate-0.png new file mode 100644 index 0000000000000000000000000000000000000000..878ea43ed4fcf75f8e0b0c17b13aab31855b51eb GIT binary patch literal 16488 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 literal 0 HcmV?d00001 diff --git a/Documentation/rotation/flip-rotate-180.eps b/Documentation/rotation/flip-rotate-180.eps new file mode 100644 index 000000000000..eb3cbeb21404 --- /dev/null +++ b/Documentation/rotation/flip-rotate-180.eps @@ -0,0 +1,191 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.17.8 (https://cairographics.org) +%%CreationDate: Mon Jun 19 14:16:22 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 349 301 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 349 301 +%%EndPageSetup +q 0 1 349 300 rectclip +1 0 0 -1 0 301 cm q +Q q +0 0 348.801 300 re W n +q +0 0 349 300 re W n +[ 348.800011 0 0 -300 0 300 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceGray setcolorspace +<< + /ImageType 1 + /Width 436 + /Height 375 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 436 0 0 -375 0 375 ] +>> +cairo_image + Gb"/lH(5T!J]F[8Nh2Fp(_.Dq3CRfs"=)!aG_!l36:6t)ba:+2F1D`'@B5#cR0g?A + cY=Wk^$\@'2N'sNJSQbA@R@SqjaFZ]6_(qF\c!Re^L_&?ZjudrdFf54tT_*QWO-clJ(b2!f + >t;G/4nbA*0084VFW)ru&p?ij62X.1_'?4X#3.HK?K7lmr?CEft[cB2Q=YR;upF]p:^_m0uk^bN1^_m0uk^bN1^ + _m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_ + m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m + 0uk^bN1^_m0uk^bN1^_m0uk^bN1^_m0uk^bN1^_n:!c7Z&8l_Nr=E#(uu1J?iICN]3hP[$1 + @KsN=G@?F:lK?E+'(![EpcJXYQb]NPP=\cipc[`>I3c!J1CN@9c>FDKM/L-emD9Ye3itRYr + X;YWF1X:P-rdZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\r + dZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\rdZg(J2@1T1]Dn\rdZg(0Y]D^@f + q)EXM5,p['pr>S(;SJhH@n>Q],=tW'tmLa5==@o5j9O['NKeB:7VEGB\6]F)o;Y1-Y]7=lg + sr.@[EH#)W/H]i16lRQ5,ga66h70FCO& + 3@fF*c0Pu54AcN"+lJ:sE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE + 2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2 + E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E + ]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaE2E]^ADiCaZ()hPD.c6-j2S2 + E%;-<:Z]\\1[$(sRQK<2IN;%J0/L@6pa>\!4KYiM)f!;%HVhLX&@uN_!/4Gge=A`43J[6U] + @t1"f-GL@9`dm9"f'OKrm!R8RYN]6fMBr=o.;:kdP!*rh0)*4>?Wk3]Q6]CX)4,^$EC0Po* + A=^2a6n,1T*8kdn1qT;O2S*lUYq(qi/BnYN'H6Z5N%Jbi/BnYN'H6Z5N%Jbi/BnYN'H6Z5N + %Jbi/BnYN'H6Z5N%Jbbi[,"J69$pk$rpcImq^ektBPa+;S>.bmQSE]ij5S/J)Pbp*\-ugIm + 1E/>_]"h"7GTc'"?W2t$iSDtKaP^@pIid#\S=PZeLZ"4J*FX^'KTh"SpZQTo-ooLSZF"*t; + Tgt6sg[/ahZ@4efg8q(6)1S+l$#$It_*`N^M/@$%WPlnA@/N9c5puX`?H:qU!Ya!tTIJ7&U + :>U5AJu$LlZ1sEiq6aq5XV[G#N@9'SnDf-aiE.)aopM?S/B[#U"?`K/!7*nfCENi.UA/CCp(AdFpXVV_7Tp + AuhA=jN&brc:":Ze"-6'g>TaT8k;NR7O`W]mtVbQ3bFe,&&<8-L$32\(uT=i'=Zf;hYtIf& + O`bG:>6[i)A.Z2Q4!d"C9Skdo+`X1\+HNN%[,`.l,(dTiU"MSEXRP'Xbget"Hg(CS5Fhk3! + U&DRM.WKcNhKqgT!_%Y&B+uo='-/EqZ%^NjY7N4_/L92,H6Z\Cc+(-^a8qM\sT/=WH`r0gC + (S=\olaLZ0Uq7n\o@JHCQFoQf&I?\VY)iij:9Tpi73,nOIf#b?%a@F7/q(8B(MbIn^#k[E8 + ^mI/M(_e$#ed,p?6J&`cKVn!L^,0+AArW\r\QIgaY.]4j[,oL[]qf#)Bq+H<; + dX&\>9#2/XXm<[W/U#h40H:`TS_9`Xh1m`+^m-lt$QlnOtaZXjm0thM7^M2:6C!p]IS$U_, + )oi.,[!oQkQh4_9D<>_AK.KE;c+:mWRNB/X%_Dc%e97N_nXV;\*8%0Ue[+h78h-JaU[B&GOG6rq + :V$u96!`JlR<0C!d/"3'\jV@BF;mM6;"kAk>U0Z6Uq:hU8[fl<)ZLX8)Je$6#jScg,j@ + 5LJ?%5p$O(\9,,iu+DguVE,"]6U#gNqs'P'd$h'<,GC>-C-P@2gU#T_G3P7)1>e/A'9Ld/h + R]/\cFgBa6\G:lpOTM>O"UmjNlf83S\3RV0.oEk_D&hK5ak1Ji#S`Pn",L&ZqTIpClB$:JF + Umj*W=oKGRYO;nbP4C0MeG'rkNFSH"Zc,+8FEVk,rRT>(Ap7VQ=-qV>AL/""P&?T<(D(4S0 + rR/]*7V)P@Bq\6'(OLj)-LW@d`i:!,15'SSDj_540ueDs%H?ro;;R5PAY!bY8PtHlcOC + )P8@gJ,=DQoGYkH$Jc4iR8`R]NU#-F2\[%od'*(r\4[l`[.n_;s7._JsQo[fR+r9oUk8PH] + /`pnK)fkh5\ms.]_K=]iER1[pp3U'9Mak\[pk\Q[@RCHFo5;t/!X`RN[G4X-:uaI4;^C0FAkT_7ES_p$Rnr,s3#DCsFNspgf=r,V^66<@:`#OAoD='[H(XF06aTO'#38ONPD.3r(/h)Y5:Y#f2=[Dc]'puIPX1#>4,ipX!a#/s:*e>S"j;lk.XuoHq?O?&l@ck9K5g`q;^Hfeq:)IN3Hq40qM9q`BE + k;a6K_49rDf\uB\O&tN<&lcf#HdFg)@jb!@p32e>I[Fr2Y?,r@-_mi7KWH#$USR"hSm%HcA + jh?JOTqF$M@btJ9q'j-WdS5]H@2?8We]M;L#'G>]6(L#;bN12;VT/:JI*P\i(;JnCCViBnV + 5=Xc"SX8Opdr&rOZoG@TQYM0jd`f'dkHu'5MpHA4NP$'\uCf=JsSDn^0J6d&ojsF):Vr*U: + =QLEL(]F,<;5ZlV-o9Z6H5'ch?te'C,nU\4X?3ENbKWOss`Zj`-b6W&)Kfo/nAUe:_aV:qg + 1,&^JZlr6>i9=.!XtGt$.EQCF\>)X.Ic1hpZ4VIe6!["=XI,>bUSlj9BaaV + #,MNdWCQSnbcX6FpYQ4NMS('.(>gEC/>_/FB=t3iONhOB9R#"8^$h_iU$,(&_kXHQ)pWRR< + 63_Bl-ItkrVVC9U$_$h8PW(uR<5@s+:I3)7=]RF6*XZP[mH4p + @>t$$pApLIc,a,KlW1AdgnJA:&E@,NeDgY@tR<5A*+:I1SU,_@Q*3HR0CIMn1]-q#-Z2Mmc_#qHf4>HQ?KRpU7 + C.eX8I;6f/LsDX3&o@VhLOD&U]Y@IA_n9SiURjOGdRt(HZ+'q3P4fF[A`i-/HsghHQ;;XNhAA87Vk<8-E-7/gG1n)P4%Oq"[Nu7*CfX'aEk7!_7aba\g?1 + [@I@qkh>G7D\:#R-TMCVk%o&Oh4$&N\X%c07IXsD2g(]7^Zd:d4D*OX`:`#79V`#>`%]"9S + Sp-ql%9\#KmIt:7n*2!1ZK;th*Z+YJ**XE.`41>ClF4"B+[EMeS;W + 4OEtkAV,O(3uq)2-:,s*GS95MOD1AI6nfmkiV)r!eXQQ\qEF@[FfP0-/gYA@p)\ + r94lUXOT*@qXU.,]G[d18q8$0VV+LVknUc*M)=QGp[PY"a-[#T^E9^REsctj5OO.QbmA;=d + 4lEZ\&S@ons+@^/3*D8CALsAEf_.8#PJ%'(Ok$=h0CsD7We\D"g+dde&3Pa:)OCIr2`5-9Z%Tfsi*=3?3b(K9$Fk$j*,> + cVRG8Fu)9((h81HjM^aI1EV +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/flip-rotate-180.png b/Documentation/rotation/flip-rotate-180.png new file mode 100644 index 0000000000000000000000000000000000000000..d58c6b67f47610439693b60e3cfeb825ca2e9f6f GIT binary patch literal 22198 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≪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 iulEAW&T^F0t^O|&+iRV>Zxc5F0000> +cairo_image + Gb"0H5n*f:$ii$^VAUG"PUs?pkfcBVGMl&.VmY"-C$i9N>!&jWekU"Y[F(): + 2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y + [F():2?c8e(SO'[!#V7Xe"$!&jWe + kU"Y[F():2?c8e(SO'[!#V7Xe"$! + &jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$< + CT"Shg1fr]G$6u0S^pPOVmY"-C$i9N>!&jWekU"Y[F():2?c8e(SO'[!#V7X + e"$!&jWekU"Y[F():2?c8e(SO'[! + #V7Xe"$!&jWekU"Y[F():2?c8e(S + O'[!#V7Xe"$!&jWekU"Y[F():2?c + 8e(SO'[!#V7Xe"$!&jWekU"Y[F() + :2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8ZZLTr.)5-4~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/flip-rotate-270.png b/Documentation/rotation/flip-rotate-270.png new file mode 100644 index 0000000000000000000000000000000000000000..05015a92db72f129a51b21315545d4696873676b GIT binary patch literal 9108 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 +cairo_image + Gb"0H5n*f:$ii$^VAUG"PUs?pkfcBVGMl&.VmY"-C$i9N>!&jWekU"Y[F(): + 2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y + [F():2?c8e(SO'[!#V7Xe"$!&jWe + kU"Y[F():2?c8e(SO'[!#V7Xe"$! + &jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$< + CT"Shg1fr]G$6u0S^pPOVmY"-C$i9N>!&jWekU"Y[F():2?c8e(SO'[!#V7X + e"$!&jWekU"Y[F():2?c8e(SO'[! + #V7Xe"$!&jWekU"Y[F():2?c8e(S + O'[!#V7Xe"$!&jWekU"Y[F():2?c + 8e(SO'[!#V7Xe"$!&jWekU"Y[F() + :2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8ZZLTr.)5-4~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/flip-rotate-90.png b/Documentation/rotation/flip-rotate-90.png new file mode 100644 index 0000000000000000000000000000000000000000..0a12e42c491901db6b04940fb76b62f8b244f1dd GIT binary patch literal 10918 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 E0gCQ?YybcN literal 0 HcmV?d00001 diff --git a/Documentation/rotation/rotate-0.eps b/Documentation/rotation/rotate-0.eps new file mode 100644 index 000000000000..d58f1b832925 --- /dev/null +++ b/Documentation/rotation/rotate-0.eps @@ -0,0 +1,169 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.17.8 (https://cairographics.org) +%%CreationDate: Mon Jun 19 14:17:25 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 349 301 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 349 301 +%%EndPageSetup +q 0 1 349 300 rectclip +1 0 0 -1 0 301 cm q +Q q +0 0 348.801 300 re W n +q +0 0 349 300 re W n +[ 348.800011 0 0 -300 0 300 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceGray setcolorspace +<< + /ImageType 1 + /Width 436 + /Height 375 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 436 0 0 -375 0 375 ] +>> +cairo_image + Gb"/lH&r`j*QJDLL^VAaJUla#WXo=G%m[8tU-M+G1e-)Ai$Oa%,LNUTE/bdBRVOclV7+N7\ + '.'>F-Ie:@MI0U'Gh?-M2Gi-#bjln2$1A1:3^^?okSkdB&C8sFnbcUIm6pIF8Y/@o@`!bl2 + L,:Ct=o-1I^Q]Vc;NX*XXU^U#O?iB1q#98j;=]hMuqPabPYXh-i._QLg+JLj3DhuIa7hm46rN + 7:%RJ5in`fmdaUq^c5s1]IoIIm^R)6G[ZEQqg_dJ)&DY*ircbs)q;SQ`cgEkOB<'.lLCVYd + m!1fR2+hN+Us@P`XuH*ZHL-U*;"VSj#D4,ijjI'+R. + S,5;2tRqT/\1lE4;93bO:-,tcWFP:$]7#gq[HLV/F + I6S:Qs>'+lMoO[ad9'I]]YA5 + ?9Sh4dub&3@F!1j*o^05I[OaP6G;mFU,7n!3hn/%Jm(>SX`SQ"hBp1\%l9]A.Y2HLZUeQMb + 4#&3@F11c;MEf=^Pm60rG'*8"1sOaI%(^UJGJ:Sb>-,YaQlXKD#3D``6jZIcR_GYS+VZ3bD + >BDiYA\Z@hV2qI'Qm^dT(M5CKAd.aRSeD!-.\burj)LCOL3O6c@'i%RI]/"T-@rU7=b0+sH + rdsE`maeZ^@R=nVQ;srO9J64LF'F8XjjlL&+`V70rQVp%a33Q9--6D$bZ*u++=&"dZb6fS% + 'JmQ6ka;"RqjeT+n:>gX"X/5J&nC0(%&JCcH+1n;`+'o^L-m*'BJ]`dgc/$`J_PAs"iJ)hO + 19F9j($bVddrl)OI'FaqpDq`QerJ3%C@,b;GfmOSDViPUha:AX_\7P'Y1cK;7G^o2XP/hP> + &D?%+%G4P7/kW[PWrK_Dl]W8?("MOfX5`]HI_;4!_Gm9VO)TuQ3C7JV)Gjo-*%8B1s`+`[( + tfMV&KZQ3X_0))TR26C3J*RR%7H)o=1Oh-X!S_T\j/tXH4X3GDX0t:%i^hF15s%lC3KQ6fJ + U3rY3WC[MYMKFQjIir?L]Q*aP;1=crToQt^%qXWC5cR01GDUU:?sJ/`.)2M*ZIX>Eke1d)L + ZJrjSE5B$Z`2b0VE*)7r&dh_eQJ.j#5;S0UFV&jKZ*.eI&(PD#cm>bFT6K + 5J43M'nh^-Ioi;RE7a88mCS=84c@bjYQX\Cn#Y"Z&_fiW(fhmM;KT@1u>b?3`/)ss%$@hb,j"0i/$$d6fO0H4VgZ(.A + er<\GUYM51TDC.qbe&k2?g<==5mM^0"'BU2d_KH"jR.G@td%gj@J?3!Mm+%Z)QH-1MqnI*O + Q$?bBbA'"+r@Au`1q/@KeQ#Fq0Cp^R+=31Y&QmoSaSDr7B.6#"M,16K0#/.Z,G]Ru"9+01QY-UEWhanZRk=9J\nOEef!>XadQ + DGFN4U1p#U/`@.uQ3;o[jaiC/`oZcu[e_,)-3ct/E^Jbm'9eg:H!QI!B/Gaii2B1=86d5.4 + j1A5:9A?V$R$!1*./q"^W&F&16Ah%gU):IGW"`A@rPC0T`XK7C@\O'bCTdC`:DrH_/]FJUI + 4%9fnRH,P%A7K3PKX%1,eX2+ce*HSn&oU2dZL+++rli-!J@:+n64QML-mTaJC6mQG6T\;Jf + rSGtuj"o8r_^@d&)i<.8otc>Y(5a3c(#Oo4p%gB3B*MV#3mlBQ%HE^!tjj,gc53)C`.RcDZ + Wb1Me?7Pq+N?lTfo9T4:HOTJkuD@sLQaJ,mJM9>nu"biT#0t9"u7Q7>\n:edeQZNQVq1gR= + (4+OJK1j(lG>n7dFQqt2g*/.ia3C0oG/J%i^Ge/`Sd\&&e^Ohqd%m(HVa: + 7_79c.OSBeM^oQSAAp*Sp1Rd9Z5U*Wk#`3m9TBatb.X8tkW,OsH/seB-B&C=Xb1Q9E_,6mn + iY1,VM75,FrRNMWbQ/[n"GG%J!UB!2PJ;-KB(>Mh`8IS54!a5G[jW)_n3pbY@$q@YGHZ-A, + X,KQ91B>M$?nksD-k^K3AEKC*"@b>koF7Q;T]0r;hBoNn_Lnr*3Z_O<&b[,?f>:P4aAbA.W + e4/PQSs\hY"TC`k)kclaRT@pW)3.0?B^tHm/"pEK7pQKDST;j5@`]C?IXtO>u&"fi*J4M%> + Uq,lW%bg;kU&G[SNc2N7;e[2@1?VCVpA7>rD['ZT(0n:eg%6t7#/^-L\2B@apYMQ"4,d^D^**:Qm!I(k@S"/>?L=7,#"M="+`ZBCfb&k3V + Fb&%P;4Dth2C29"r@IeKZ'APC2d$WO?G0HTaciso"IO$7LUn+-q!!rhW^WA0$'V"a?nl:]b + oPCf8.EN$:#7;GNFGCBqbHd\F5%9LquNtLPs:_M8));)&p/WH>K5ccOO$g&#G'"&VGuJ?V( + +6dU'Jc;Jfs.S[fki>-JBk,6er7H;bco_XJlN(gKC/U33"a-&0?FnR?IT=Y4*R-qXi'mmIT + 0%=B>h:/VWP'FBU@ + #3RN@6K1$1QcbS,*N-#*XZELMiiQ=>eo>e&Hqo#Zg_YSnnN\jc$@bT/Rr)K5:.=oeH#5?VQ + ,gSERBter!,bJhHN9^<%U9tI#XBh!1[c]N!qZR!,!30,6Q3m^mPDPaFLSsE2J1B1>j\`S8$o?$ + oeUpB,W`S"3pt`R0LsXJ?o5pVL^NYi%+kuC#FpY3"#_!)E8rDc2teL#0t;?ZN+sAJMT(>/- + )2Si/A(($NOfX3$SON!l13.cH(OB!-"fd1G[l>!1[c]N!qZR!,!30,6Q3m^mPDPaFLSsE2J + 1B1>j\`S8$o?$oeUpB,W`S"3pt`R0LsXJ?o7RS_a6c.cuNMm,5U"<3UR9%'F)+!M&6Rc89K + 0!M&8(WO(`<+Y]L8r-rrF%%?b\jh*Cr!BB*ESFH^,p]5%*J2;Yi)*d&<+)o4139(76nH7)3 + !GD=\1Od+W5N%JBE5iDJis2+E"4$ZBBDd99I_Zgci/Aas`Xb&h#Ft;dcM.EQr.$SP^_oApN + $!#Y%liSSSFH^,p]5%*8>WZMN$dJGBZ?Kr<'#@K6bq>f`gR,lK/6`ibT&Tp1L$Stet^fb8T + 6#4hH<)?f7+ab"E7r_S65i)&i_ZPc7FK%$(RCcB,8:N"O>5B1CN,7!b^Z2R0@OVJMVB*bQ9 + bf^mR3%Ac\Dni630#ZN/8rE+TTM=TE(I3&:fb/:]M_S='Il(;Q=@cHLhq$fF\[BBI!t"`Pf + >1NVKu!]ZCZR5o4u!Lua>bT&UK!6uj/Ae(>6!+up(1C)2VJ27M%R0.(;^mP>Nb^hPXi(O2b + k.mB>HeQt$30Q>jm*TeXopiJ%rAL0;>^(?qVYb(;\oqeNGMl0S\?h0#LMb=KfCV+GVmltE' + $$P+OEj7lG=uP%p^bXqN][,,jrs9P9poY5IrH`SgtCuu%bd;p51QFs/o_EI,`Xt0hO?N5jN + u*O9B@0RPIU$3qrH8`rQ\^+IN-QXc9(V"B+d0[#L4lN1C9S>"(rCbR0:gZJMR\l9EJkh^mP + @F-@msoi626^PJESHE+T-@8`7f433rV1,]I?*S65i)&i_ZPc7FK%$(RCcB,8:N"O>5B1CN, + 7!b^Z2R0@OVJMVB*bQ9bf^mR3%Ac\Dni630#ZN/8rE+TTM=TE(I3&:fb/:]M_S='Il(;Q=@ + cHL:Og#Y.__@4iD60DBlS9,gs=S"5j.;-F539(76nH7)3iIJ7Oj!/(g`LGF'aq8jYNj@O0r + rY(G(N'~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/rotate-0.png b/Documentation/rotation/rotate-0.png new file mode 100644 index 0000000000000000000000000000000000000000..e7e95a299cf23c0e757dbfec1907d2b075523a98 GIT binary patch literal 9086 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 literal 0 HcmV?d00001 diff --git a/Documentation/rotation/rotate-180.eps b/Documentation/rotation/rotate-180.eps new file mode 100644 index 000000000000..3ace1c22db9e --- /dev/null +++ b/Documentation/rotation/rotate-180.eps @@ -0,0 +1,189 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.17.8 (https://cairographics.org) +%%CreationDate: Mon Jun 19 14:18:15 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 349 301 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 349 301 +%%EndPageSetup +q 0 1 349 300 rectclip +1 0 0 -1 0 301 cm q +Q q +0 0 348.801 300 re W n +q +0 0 349 300 re W n +[ 348.800011 0 0 -300 0 300 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceGray setcolorspace +<< + /ImageType 1 + /Width 436 + /Height 375 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 436 0 0 -375 0 375 ] +>> +cairo_image + Gb"/lH(7g`TuSOeAL4`-8N[J=[KqdsKl?`#\B2Y-bK;aiL\sj:3Fq4i3Q^rB\)o%"n;RVm3Qi=is1]h7qtbT7AWL*)Js4/BhTmp#cMBp + Kl[p_ZFQ=usl'[:eE-b]27Y^/YC'Q+6L3fC,Cp$FLYr[&pkfB1^#"Oj5hDU!GqagnYlRg#2 + Z_n^1*`FoLhCjShFu\L`cBs/=$Cl)9B?\./"jT)X1M5R(!bWLg))H5$JMReoN#=UM^_mAr` + XJA7i/@at@g:4,E5hpK0`u&&39("aR"7%#S8eOAbeP)"c8^>1B&JULB,n])1N5g71Ci?%)* + "D,)$b-#%%L2QMuu%L#0nW9`W;Sa"(r<-irIAlJMRY'EM+[lX\MN[UIEYti@OMtS13XqXVs + !)d8#IkiUq/UGZ;tW?6%>(EmfXX546kgH8nP[6"e?>Yle=8WVo3GJ:eb('KA`J6"dbYgR-VdX5L-pVdN#YP[Uq,6i%,2mg(aj3`XJA7i/@at@ + g:4,E5hpK0`u&&39("aR"7%#S8eOAbeP)"c8^>1B&JULB,n])1N5g71Ci?%)*"D,)$b-#%% + L2QMuu%L#0nW9`W;Sa"(r<-irIAlJMRY'E<'Xq^_m=$3.Nesi/@_M*5EFJE(5C7%cE9`32= + 8,#B7ZkS5J4'"#scpc7!YO!LtkHB,!>c!7$s_1CBXlTF5BsWbp\Te1`jT1d5P4di[;bab/[ + Z3(kqhB,N]V1FFO<0S?^M6Cl(%S0\k/ium%#B'3aG:!#:Q9B=j2SN/9>k3d%\`3I/s7<.#? + a0'9M:0ujP52]*ui(Q>m9KqB.52]*ui(Q>m9KqB.52]*ui(Q>m9KqB.52]*ui(Q>m9KqB.5 + 2]*ui(Q>m9KqB.52]*ui(Q>m9KqB.52]*ui(Q>m9KqB.52]*ui(Q>m9KqB.52]*ui(Q=FE2 + GHNFU'Lm++@61(@bL242$[`cC/r@qi#d.mh3=TJ7I9";8Tm1_m](/OhmH&>"PE49'=n2rs7 + 2uS4qo6f\:,5al(BHR2,`?3M"6_-JFl!>W3X8ah)NC!M%,'9P4Q^YjWN`U&1>tN!5q0)'jE + l=[$N'-jGrJG+1;JJgZ!nL0ngP9CC%%n>SHGS:&-%D9gA?YVqB4iG]=LSJ=89?J'T^4DaFD + B*D(e3_n!ci%,/(M#ko=JMT0&-3cF;#L:i7R2Fj5*&pk!B4?CtE8XD!S;@9l_LXL#E$bcK$ + O=u,^mQ%t/0$WJ"(u(nZZ+hr%@i7UcGG,g2/jlH3"#u:eA.Ihi%,j-;ZgZAJ?s4Q9F[MK!R + AQ51Gs+u"ultqcIBXt(uQqb3)]ol@Xte(i(OTMM#ko=JMT0&-3cF;#L:i7R2Fj5*&pk!B4? + CtE8XD!S;@9l_LXL#E$bcK$O=u,^mQ%t/0$WJ"(u(nZZ+hr%@i7UcGG,g2/jms<,lN!bhaO + uVIQnn1J?^YDtT#M`UN/r$70n.qIrrh-r+mE>4%'W@Smi^^*eNK>[AS2EjGdnNsGa_`C'pU + <5;YlUjt8UXBrcX^,m1E?d:h#/h[t5HTapQg04`OV2!EDF&PYl_ANU[cK#)Qj9*]g"4#s0cK#)Qj9*]g"4#s0cK#)Qj9*]g"4#s0cK + #)Qj9*]g"4#spGKpm6McSf^'K6j%gUtgP(OUN9`1lPI@^!,D6`ka.F=?!SgYtDDoFMN<(?K + .=(lUVrf4HI%Gqk/mMg'Qmog+@R]Y3FBCT!cf=2mV,dEi=7@gZ]ok5(Uc(ogQ[k^C@;r+,g + aJ=R%f:C7QNP0@GY#@OH=:LESb)nti&%J?YQ<;P7)`9S'B66iQ#=/'@(JZ6qj/p3rZ1 + p?oGeKT<2$hWsmGcYXVmkPJcWOJgD][/p6R;YRp^$[j6MZZtW%p==D`iGU72:s1CoqGDkhl + pMkM6#$75m0,MaY=7L0/Z7i3-JkG>#p!d#+^8_C4ZWb0MS"'/34$q+d`2Bl1>eIQKQ<9TpC&qVJi;Z3%oDN)ZROK(S,p6%G;B + ILskp>s%kKc?bIOK/]kAjgHO1Vk8r_D+-&D(n6#[fP"nkpqdZQN'ub<'Z%)1=0_#<>(Um#% + VU*lfb,.Pnb!0@6-&q&*2I:3q(i"mqEPB2\1mQ`W$RVh'-$f0qW8j0(Mu:^XDJ + U&o$)p'rh27CpN?tQK]';OEZ)OoSQ.^hT2Y?ZXuQ.!ndi,9epZD#d4HIZV;^&Y'm/kP)YIn + UfBI;@aUK'7Tk9nkM@DqTRq]"]17ZWQZ?tF!Bd;fOZ/XCtVhbi5?>IG3.KcAh*;epJqTVhn + I9/NUV"N6Wrji#t2`n)XC>FTlr8mdhFRESMGZ1-!1)/?4XtBA3cg#f%5;C[!#Iifkao8h@f + hqi0_V3:O)W2]Pj1Ds)#rkd#5$hAek8rd5cddo_iN0O!bkDXUMD`G"IlDsm[%>r+1bUOKs/ + 2gA\#ju*5W[Ec7m0q/TdCD5IErYI_$j)oPX0;;Y^fJ`Z?iT9MP2@*V=BK:r_"'I6(:Z.^#7 + C@m2s,ur/hh2U2\_%+!2YY'8=m(fB$TCTsj7UGmq+Ukot`n77SV@aLgNdF3*V*V<(Ym)4W+ + >L;0kSLr9gu>`B,PmHm.CSTa5-gVsTOn_!J%(.^->@.7WP8Oa%Uq>'*^]>lI:>$r + .M]?`rdU83A59_$mL0RUaIR.=t)Q2BKp_5'18l8,_N_UT>C3XW0? + *]l:?NDAYW[Ir9KJILrJP/4\VcBd\B)'X'Pa + 8XAS5_;5p?#l@LomPh9R6I1h8DKSp`fW5`M7.V1#3T/WS6`="B>-pL1jpTgeOm3b%*j!N^7 + BN[]4UB51l]Egf1.=m);KmQ#3Yh(flLu?Nh*"b,i3Q^irH[X;.p/(SZU8FZS[Z[h6se)"]h + A2qE=L'%O]pWcFD;j]OPFpr??6E_>+cBr*`g16Z,=SD^E]CX)hpf$NXbc-kbu(K6,lnLU4@ + F`"hI-(Ns>.SM/bYDt]e%(iRp=ZS[QX6Sc.W/($CZ'5'8Gi/F`qSd(=egU]11#3WXU3*Rtg + ZZM#AQ%nNKk%Nl6jZGV>=+n`I%))-lgpbdilr]1jVbhP'Xh+;4.43g$r,o(&$\C2cg0E-2c/3* + $T'\%<8sCa(!aG]l\JikZ&K?lC'G;Rk`kip;#T3?pHaB;AENY$)8ep.D]Pjkaq&]K9YFQV!S=5"H%B5:Y*V*:"q&m&!POCtQB@d`.pWDks^.;Lo_=^t*H1_U*m&/(Qk' + =^=oC%muR:MG)aG0'M3BX`Q`7[N3SD>j3Zd;8@Ln5i2eV_n0CmFP$7>uiI/?p^f`TIDaC!8 + #T+]EpLr@D-[/LK:?SI,-",C"\*L=p,oo;'G?_Y/'ghkk-23>X[X30?W:+E3K#?/TE80gAe + ?]hjE6hV*8)4\\[0)Lp&If2i'BI#3XV]0O-C:7C\-%hs/UR3]NfWNRXc'3ff,]4U3=XW=2H + :.C$)4\^)^:)^ok=gYnG]6%D8/[r;)e/FRHp_X/o0q[!ob31_2InIF*$Ks0JHp6IT^f=E2! + ?>#u[[ER::J-:H,4X7L/]2%.E&9=]%MioJCUsrt'mb\S4cXaL"u< + ^3ULeB_"+8kqe9.)T86h_%Fg3+%`eTaoe%EuM&#Z;2A4PYE$uT2acP+b42jk`hUs>?GCJ0H + '[d-"HMSteVO#pBplT0Zn%nNm5F_71:S3<%@cJ%#WWfe$P#;`lW)mX6a4MlC^EV:ernfoB: + @s?oWEK>N,qhFI?gGLRaA"AeRc:EAGi(0AKE#8:.A08H1k(`6:7E=$sYd.$tIcHms/[_.38 + :5R^,B6]fa0[#%lV:.^`DL84:N:%.H&RbSHpTb^in?>%f=:=LFWth6.HR+_$gWY+`*5uOhN + 6Q9GJ9A3TreuaZc>u$)0qN'!KhUt#04:u]5'!<[ZK1qV.ns@!KjlAK@@+LHi:XrflaBc**= + `)c3bUg$YS9:M5rRG^\KR1NDb7U^6[#3XQ,/DInp_jh&;.ADoc8"N]=Y*eX9tgEOIl5S98< + e*jQr9K[o!I;&iUY2T"^*LpK*A[LVYdEB"8)UGL"s^W%/09GZMCK5\anQ-_% + +H.7gP5'\;)qd4C`iZDQ@e2Y7`OYE?ALBAaic2sq6<^2e<0M]!Kf@^L?t3Bst?c9;O:?VXGE'nf + t-qtYd>=N1eo4#mg8jd.S?'0-,3P@u?V3J?^Nl9VS1^.S&Y!a4U27QJ]ZX5V*Ia?<\]_iKL + ECJk.a8Yo6Q"Z)Qn'ZG2eO%Q>$iU7R3$=?nM'io`)m]52-hptXE7#6b9[Y61\58Xn&NCf^& + rF4P022p%pe*WUeY(3ZKJPeS<'=04k0.t`b787tSl?5enA9p + )s+^5"k(MhYdO\MjKOl+0u!XKe_8/hNW.Lr>Ru5_EL'`kE)6rI%j,-rK+64QN"V;$^>&;b2Ki1f-n=t]14YRgF_+gk,d]4Z: + )M79#'SA@Y1ICP&?B32"/1\IL@"d:u9iZ?k/JIJ]K3J36AE+c`Qe('j9'$h&G`0J>WLg1ZMT#'MAW!o>4$V5_l/&PWNlHQ3s#W]!duucg:H/a?5.@Zbc==`# + =J3qJq1VZp@`EU5JoRYSBft\qs6MUc-4l-B&A$n14?/N8cR7f_pS^H1ZfL-AHpXnI,b4is/ + %T-(n+[^iqL>;(251~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/rotate-180.png b/Documentation/rotation/rotate-180.png new file mode 100644 index 0000000000000000000000000000000000000000..dfa69340497ed87e137804a963b8d9f40047ba2d GIT binary patch literal 22182 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 IzWmSs1JOTWE&u=k literal 0 HcmV?d00001 diff --git a/Documentation/rotation/rotate-270.eps b/Documentation/rotation/rotate-270.eps new file mode 100644 index 000000000000..1dbebaa3db08 --- /dev/null +++ b/Documentation/rotation/rotate-270.eps @@ -0,0 +1,118 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.17.8 (https://cairographics.org) +%%CreationDate: Wed Jul 12 10:41:53 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 0 349 301 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 349 301 +%%EndPageSetup +q 0 0 349 301 rectclip +1 0 0 -1 0 301 cm q +q +0 0 349 301 re W n +% Fallback Image: x=0 y=0 w=349 h=301 res=300ppi size=5478075 +[ 349.2 0 0 -301.2 0 301.2 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceGray setcolorspace +<< + /ImageType 1 + /Width 1455 + /Height 1255 + /Interpolate false + /BitsPerComponent 1 + /Decode [ 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 1455 0 0 -1255 0 1255 ] +>> +cairo_image + Gb"0H5n*f:$ii$^VAUG"PUs?pkfcBVGMl&.VmY"-C$i9N>!&jWekU"Y[F(): + 2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y + [F():2?c8e(SO'[!#V7Xe"$!&jWe + kU"Y[F():2?c8e(SO'[!#V7Xe"$! + &jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$< + CT"Shg1fr]G$6u0S^pPOVmY"-C$i9N>!&jWekU"Y[F():2?c8e(SO'[!#V7X + e"$!&jWekU"Y[F():2?c8e(SO'[! + #V7Xe"$!&jWekU"Y[F():2?c8e(S + O'[!#V7Xe"$!&jWekU"Y[F():2?c + 8e(SO'[!#V7Xe"$!&jWekU"Y[F() + :2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8ZZLTr.)5-4~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/rotate-270.png b/Documentation/rotation/rotate-270.png new file mode 100644 index 0000000000000000000000000000000000000000..50fea6ee9b3499a39d63e21f7199bb498f14bab9 GIT binary patch literal 9334 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 +cairo_image + Gb"0H5n*f:$ii$^VAUG"PUs?pkfcBVGMl&.VmY"-C$i9N>!&jWekU"Y[F(): + 2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y + [F():2?c8e(SO'[!#V7Xe"$!&jWe + kU"Y[F():2?c8e(SO'[!#V7Xe"$! + &jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8e(SO'[!#V7Xe"$< + CT"Shg1fr]G$6u0S^pPOVmY"-C$i9N>!&jWekU"Y[F():2?c8e(SO'[!#V7X + e"$!&jWekU"Y[F():2?c8e(SO'[! + #V7Xe"$!&jWekU"Y[F():2?c8e(S + O'[!#V7Xe"$!&jWekU"Y[F():2?c + 8e(SO'[!#V7Xe"$!&jWekU"Y[F() + :2?c8e(SO'[!#V7Xe"$!&jWekU"Y[F():2?c8ZZLTr.)5-4~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/Documentation/rotation/rotate-90.png b/Documentation/rotation/rotate-90.png new file mode 100644 index 0000000000000000000000000000000000000000..0a71eac944b08e3fca24060c0580acf3ed073e95 GIT binary patch literal 9163 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 hCQF4bLEl! X-Patchwork-Id: 18826 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 0A1E6BDC71 for ; Fri, 14 Jul 2023 14:16:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8B6DA628CF; Fri, 14 Jul 2023 16:16:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344173; bh=tKbYvB2w+97zSjXZHwyUOP23EMvgWjYm2QE/W4t3N8k=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=0YckDrTEjN61NMG+80zKgba+9jroW7N3D7fwo0bnjAm99XkYfJfI0Gd/KTN96tuHx hAQJ21Ymqm6GQHu968JwgLHq62IU9DxduokPDJ5fX5bIWci+1OaOaJ2O7WHVwcQAUC bOlMo2q68kpb7+zYP7kUdubrdRcBNDvbsoBZBtBSBE9Haj1QVn29syYSdRjvid7nhM 5fUxz5v4KCtEpx2PuYkZB9u0UQxDRQg6OQsTjgf0NT35Kvai/IbxxEU/+btzv20GR8 0mD5CXPrAp5hU/LNqSQquAaq/fM5DlknIz9LlqMLAc7honebnSE1DzeU2CV1xo4UEB G0SBpXUH72Rgw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D42A0628BC for ; Fri, 14 Jul 2023 16:16:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RLvXUyFU"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6C165814; Fri, 14 Jul 2023 16:15:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344120; bh=tKbYvB2w+97zSjXZHwyUOP23EMvgWjYm2QE/W4t3N8k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RLvXUyFU2Lr4qqCcyaQ9tnCzDO7FEtFO+AxOrTQdOfWYHWsTWwGQOFDr2gjc2tv90 PolcwFqFLjO1TRej6osJJedwHP5xMDC5YhR5oEnPbyuIEh49BxI0vTqHsxAfb0aUmQ M1EhA3Wq9q3Jtd96Kbno9ZYweqqJ7jGwaIfZcpmE= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:44 +0200 Message-Id: <20230714141549.11085-5-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/9] libcamera: properties: Make 'Rotation' the mounting rotation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Specify in the documentation that properties::Rotation specifies the mounting rotation of the camera module. This avoids confusion with the image orientation which is instead expressed by CameraConfiguration::orientation. For this reason, do not compensate the Rotation property when initializing the CameraSensor class but report the value of V4L2_CID_CAMERA_SENSOR_ROTATION or 0 if the control is not available. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- src/libcamera/camera_sensor.cpp | 11 +---------- src/libcamera/property_ids.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp index 9a033459742f..3ba364c44a40 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -476,20 +476,11 @@ int CameraSensor::initProperties() rotationTransform_ = Transform::Identity; } - /* - * Adjust property::Rotation as validateTransform() compensates - * for the mounting rotation. However, as a camera sensor can - * only compensate rotations by applying H/VFlips, only rotation - * of 180 degrees are automatically compensated. The other valid - * rotations (Rot90 and Rot270) require transposition, which the - * camera sensor cannot perform, so leave them untouched. - */ - if (propertyValue == 180 && supportFlips_) - propertyValue = 0; properties_.set(properties::Rotation, propertyValue); } else { LOG(CameraSensor, Warning) << "Rotation control not available, default to 0 degrees"; + properties_.set(properties::Rotation, propertyValue); rotationTransform_ = Transform::Identity; } diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml index 5bddafc29364..fb53081c1bd2 100644 --- a/src/libcamera/property_ids.yaml +++ b/src/libcamera/property_ids.yaml @@ -29,10 +29,10 @@ controls: - Rotation: type: int32_t description: | - The camera rotation is expressed as the angular difference in degrees - between two reference systems, one relative to the camera module, and - one defined on the external world scene to be captured when projected - on the image sensor pixel array. + The camera physical mounting rotation, expressed as the angular + difference in degrees between two reference systems, one relative to the + camera module, and one defined on the external world scene to be + captured when projected on the image sensor pixel array. A camera sensor has a 2-dimensional reference system 'Rc' defined by its pixel array read-out order. The origin is set to the first pixel From patchwork Fri Jul 14 14:15:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18827 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 934AFC32AA for ; Fri, 14 Jul 2023 14:16:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3E9B7628D2; Fri, 14 Jul 2023 16:16:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344174; bh=J/w04h4LDd4HFy1aAx56ROTbTphrUdqJB5fqO1IyhZU=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Y9KKVbhCkPSo7oCds3PmBZq6Bh90Hek44gikK1Tsr/5CAXhlyh0R/1W03aUOMZjTk kOFqTbpwboFssLAT33S2LYVd+7dqimRGXrLWueFJtk7kmf/w9rADwWj8bRH3Yfl5dI okixHME0+OWwJPJPNmdkcuxpI0VsJ3LihVNHVVL5NykykCsk9BnSYPT56+20/4qSHe 3Le8wnHzKgtLFqzP0aCTAlNlgTMg0Rq+fA9kUjc/HanELnjJAqZ2jA8qHXRb5+0edm tdVdUGCOXcFapQQEW2XWKBYqfTNoGNxQ/eSnrhxMMIjrNdK3ABD2UFFULpFhllX9mJ +o9szbE5SUKpA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D0063628CB for ; Fri, 14 Jul 2023 16:16:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OtmqRZhN"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 68F6257E; Fri, 14 Jul 2023 16:15:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344121; bh=J/w04h4LDd4HFy1aAx56ROTbTphrUdqJB5fqO1IyhZU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OtmqRZhNumqImemGJ03HKIfeHWx6mzL5XSwMat5jlK/t/SZTbeoCrOYEjhUJykpZB yVY4PVdsPp+NuA9PMNv3cDxkcnYAO6bUe13/zHqlev1aEjzFJ64/FnAMRDCpkpVr9l bGX/ZRnPghZzPErdc0YLyl0rtH7Tkwp/wsYhEROE= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:45 +0200 Message-Id: <20230714141549.11085-6-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 5/9] libcamera: transform: Add functions to convert Orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add two helper functions to the transform.cpp file that allows to convert to and from an Orientation. As transform.h now needs to include camera.h remove "#include " from camera.h to avoid a cyclic inclusion loop. Adjust all files that need to include transform.h directly and didn't do so because it was implicitly included by camera.h. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- include/libcamera/camera.h | 3 +- include/libcamera/transform.h | 4 ++ src/libcamera/camera.cpp | 1 + src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 1 + src/libcamera/transform.cpp | 58 ++++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) -- 2.40.1 diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 8132ef2506a4..55359d53f2ab 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -22,7 +22,6 @@ #include #include #include -#include namespace libcamera { @@ -31,6 +30,8 @@ class FrameBufferAllocator; class PipelineHandler; class Request; +enum class Transform; + class CameraConfiguration { public: diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 2a6838c78369..573adf18715d 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -9,6 +9,8 @@ #include +#include + namespace libcamera { enum class Transform : int { @@ -69,6 +71,8 @@ constexpr Transform operator~(Transform t) } Transform transformFromRotation(int angle, bool *success = nullptr); +Transform transformFromOrientation(const CameraConfiguration::Orientation &orientation); +CameraConfiguration::Orientation transformToOrientation(const Transform &transform); const char *transformToString(Transform t); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index c8bb56d4af7f..af842e70dcb0 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "libcamera/internal/camera.h" #include "libcamera/internal/camera_controls.h" diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 38f48a5d9269..02a6117c7955 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "libcamera/internal/camera.h" #include "libcamera/internal/device_enumerator.h" diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp index 4668303d0676..03d2b52e7f38 100644 --- a/src/libcamera/transform.cpp +++ b/src/libcamera/transform.cpp @@ -299,6 +299,64 @@ Transform transformFromRotation(int angle, bool *success) return Transform::Identity; } +/** + * \brief Return the transform representing \a orientation + * \param[in] orientation The orientation to convert + * \return The transform corresponding to \a orientation + */ +Transform transformFromOrientation(const CameraConfiguration::Orientation &orientation) +{ + switch (orientation) { + case CameraConfiguration::rotate0: + return Transform::Identity; + case CameraConfiguration::rotate0Flip: + return Transform::HFlip; + case CameraConfiguration::rotate180: + return Transform::Rot180; + case CameraConfiguration::rotate180Flip: + return Transform::VFlip; + case CameraConfiguration::rotate90Flip: + return Transform::Transpose; + case CameraConfiguration::rotate90: + return Transform::Rot90; + case CameraConfiguration::rotate270Flip: + return Transform::Rot180Transpose; + case CameraConfiguration::rotate270: + return Transform::Rot270; + } + + return Transform::Identity; +} + +/** + * \brief Return the CameraConfiguration::Orientation representing \a transform + * \param[in] transform The transform to convert + * \return The Orientation corresponding to \a transform + */ +CameraConfiguration::Orientation transformToOrientation(const Transform &transform) +{ + switch (transform) { + case Transform::Identity: + return CameraConfiguration::rotate0; + case Transform::HFlip: + return CameraConfiguration::rotate0Flip; + case Transform::VFlip: + return CameraConfiguration::rotate180Flip; + case Transform::Rot180: + return CameraConfiguration::rotate180; + case Transform::Transpose: + return CameraConfiguration::rotate90Flip; + case Transform::Rot270: + return CameraConfiguration::rotate270; + case Transform::Rot90: + return CameraConfiguration::rotate90; + case Transform::Rot180Transpose: + return CameraConfiguration::rotate270Flip; + } + + return CameraConfiguration::rotate0; +} + /** * \brief Return a character string describing the transform * \param[in] t The transform to be described. From patchwork Fri Jul 14 14:15:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18828 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 54AAEC32AB for ; Fri, 14 Jul 2023 14:16:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D2346628D7; Fri, 14 Jul 2023 16:16:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344174; bh=21h9/sAEYM75LWopORzoBDULCTVzIfbDSYtOMTm5ysY=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Ssn7BKAqUYW8aA5CXiQ2tWmcnpbVSybhGMDahJRVlG66C99zxjM9W1Ht8SRAyObVN yYOzJ9L7fPwtM+NWnTch5cVcFVIDgmhppWFJiSLMfSb7d39N/Q+PuKi+8lVIH5r4iH 2/2DdsOjFGA+xkg6Gw5+nrc/N1qpkJr4+zCCDiLkhgBtlnwgHlf1hgiSGrlDetRAFy KInAwzF65ABeIEFTzl5EWiiyOnuBpEkC6myh4BXwCkHzdRXhczqOWgv+ZtfBA0yCub IPlNY1CfoKpqw2rHz8tU6WBQKfuxbqn4pLpsjiFbX8qpEZclUrmAcuV/OUexDlzaqt GJIz1cuRcZb0Q== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A951628CB for ; Fri, 14 Jul 2023 16:16:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fpAwlQBc"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3EECD7F3; Fri, 14 Jul 2023 16:15:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344121; bh=21h9/sAEYM75LWopORzoBDULCTVzIfbDSYtOMTm5ysY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fpAwlQBcf6CxBBbnxLDtn9NuwyWo11K5vudSW1mGQ4RrK/Ml6bj6t1AWIVYD0iM53 nXExyZM9pgcq4llhuf+aPv4140klM00D6uRpUB2lxOkwGgRROtDHusQiBgqEE/gfzY HwiLoFF9miZQXuRjVwj2cIEssp2xPVCyDlnkCPlE= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:46 +0200 Message-Id: <20230714141549.11085-7-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 6/9] libcamera: transform: Add operations with Orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add two operations that allows to combine Transform with Orientation. - Transform operator/(Orientation1, Orientation2) allows to easily get back the Transform that needs to be applied to Orientation2 to get Orientation1 - Orientation operator*(Orientation1, Transform) allows to apply a Transform to an Orientation and obtain back the combination of the two The two operations allow application that are willing to use Transform to operate with the Orientation part of CameraConfiguration. Signed-off-by: Jacopo Mondi --- include/libcamera/transform.h | 5 ++ src/libcamera/transform.cpp | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 573adf18715d..a7470c70a755 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -74,6 +74,11 @@ Transform transformFromRotation(int angle, bool *success = nullptr); Transform transformFromOrientation(const CameraConfiguration::Orientation &orientation); CameraConfiguration::Orientation transformToOrientation(const Transform &transform); +Transform operator/(CameraConfiguration::Orientation o1, + CameraConfiguration::Orientation o2); +CameraConfiguration::Orientation operator*(CameraConfiguration::Orientation o1, + Transform t); + const char *transformToString(Transform t); } /* namespace libcamera */ diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp index 03d2b52e7f38..61dbb1ff1dd5 100644 --- a/src/libcamera/transform.cpp +++ b/src/libcamera/transform.cpp @@ -357,6 +357,104 @@ CameraConfiguration::Orientation transformToOrientation(const Transform &transfo return CameraConfiguration::rotate0; } +/** + * \brief Return the Transform that applied to \a o2 gives \a o1 + * \param o1 The Orientation to obtain + * \param o2 The base Orientation + * + * This operation can be used to easily compute the Transform to apply to a + * base orientation \a o2 to get the desired orientation \a o1. + * + * The CameraSensor class uses this function to compute what Transform to apply + * to a camera with mounting rotation \a o2 (the base) to obtain the user + * requested orientation \a o1. + * + * \return A Transform that applied to \a o2 gives \a o1 + */ +Transform operator/(CameraConfiguration::Orientation o1, + CameraConfiguration::Orientation o2) +{ + Transform t1 = transformFromOrientation(o1); + Transform t2 = transformFromOrientation(o2); + + /* Return immediately if the two are identical. */ + if (t1 == t2) + return Transform::Identity; + + /* If t1 is identity we need to apply -t2 to get it. */ + if (t1 == Transform::Identity) + return -t2; + + /* If t2 is identity, to get t1 we still need to do... t1. */ + if (t2 == Transform::Identity) + return t1; + + Transform combined = t1 ^ t2; + Transform result = combined; + + /* + * If the base orientation is transposed we need to invert the flips on + * the combined result. + * + * Using Rot90 as an example: + * + * Rot180 / Rot90 = Rot90 + * = (H|V) ^ (T|V) = (T|H) + * = invert_flips(T|H) = (T|V) = Rot90 + * + * Rot0 / Rot90 = Rot270 + * = () ^ (T|V) = (T|V) + * = invert_flips(T|V) = (T|H) = Rot270 + * + * Rot270 / Rot90 = Rot180 + * = (T|H) ^ (T|V) = (H|V) + * = invert_flips(H|V) = (V|H) = Rot180 + * + * Rot180Transpose / Rot90 = V + * = (T|H|V) ^ (T|V) = (H) + * = invert_flips(H) = (V) + * + * Rot270 / Transpose = V + * = (T|H) ^ T = H + * = invert_flips(H) = (V) + */ + if (!!(t2 & Transform::Transpose)) { + result = combined & Transform::Transpose; + if (!!(combined & Transform::HFlip)) + result |= Transform::VFlip; + if (!!(combined & Transform::VFlip)) + result |= Transform::HFlip; + } + + return result; +} + +/** + * \brief Apply the Transform \a t on the base orientation \a o + * \param o The base orientation + * \param t The transform to apply on \a o + * \return The Orientation resulting from applying \a t on \a o + */ +CameraConfiguration::Orientation operator*(CameraConfiguration::Orientation o, + Transform t) +{ + /* Apply an Indentity always gives us back the other operand. */ + if (t == Transform::Identity) + return o; + + /* + * As operator*(Transform t2, Transform t1) composes two Tranform + * by applying t1 first then t2 according to the canonical function + * composition notion, we here first apply a Transform corresponding to + * the base orientation and the apply \a t to it. + */ + Transform t1 = transformFromOrientation(o); + if (t1 == Transform::Identity) + return transformToOrientation(t); + + return transformToOrientation(t * t1); +} + /** * \brief Return a character string describing the transform * \param[in] t The transform to be described. From patchwork Fri Jul 14 14:15:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18829 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 C7848C3243 for ; Fri, 14 Jul 2023 14:16:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 80058628CD; Fri, 14 Jul 2023 16:16:17 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344177; bh=4gzSlnqS6hWq5XJWMreHi/yEX6KFx2yYUkkjds4Pukw=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=E/WRVSjxhupRNudRghnczYe3mxinDeWHf7CdfAjKCw9IYi1gH1HZ6vNWiXnbiCOHO YK+H1TVYCWnO8JroE7hjQspSYeJT+xmmK9O0OYOZu4IYMU6BKUyem1f9OjUR557lhU Op6Ue+HTjIVzJ9mScA/V8k6w0GmtdA5huHDqXQ7q94oQtRYLFvF7zgrpZZkwcf7x/i klr7S6tnMb+4N/EhtefVKvdAxWRCNLzhGqmTWj+pXteKQcdXWC5NE1IqLnzviOPgVV 3MRdhBVyc9aW/nCeTgCBtF2dJrUrzZ0XQVDmGMlDYYIcYe/KGnmAWKTBI8b1mhjcrv +gPbMDNfteYcg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7F60E628CB for ; Fri, 14 Jul 2023 16:16:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JInbK5/f"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2BFE2814; Fri, 14 Jul 2023 16:15:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344122; bh=4gzSlnqS6hWq5XJWMreHi/yEX6KFx2yYUkkjds4Pukw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JInbK5/fbTFCflmVGtTv7oKOhj+ohQFWo+nZvpUq2p3MjZtYXVVrr29reWBUzS/WG BOPfX1UmMOTVTq65H2RANfSa9tzj837rwvWQ3TOul3rByTkfmebfhoEZ9f8MdUzE7G rumYyoGJzibfqxDB8D5NZGgUWmDQSFUTrTw22wOw= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:47 +0200 Message-Id: <20230714141549.11085-8-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 7/9] test: Add unit test for Transform and Orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a unit test for Transform and Orientation to validate the implementation of the operations between the two types. In particular, test that: o1 / o2 = t t * o2 = o1 Signed-off-by: Jacopo Mondi --- include/libcamera/transform.h | 7 + test/meson.build | 1 + test/transform.cpp | 338 ++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 test/transform.cpp diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index a7470c70a755..4efb01a3b49d 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -16,13 +16,20 @@ namespace libcamera { enum class Transform : int { Identity = 0, Rot0 = Identity, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, Rot180 = HVFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose }; diff --git a/test/meson.build b/test/meson.build index b227be818419..189e1428485a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -46,6 +46,7 @@ public_tests = [ {'name': 'public-api', 'sources': ['public-api.cpp']}, {'name': 'signal', 'sources': ['signal.cpp']}, {'name': 'span', 'sources': ['span.cpp']}, + {'name': 'transform', 'sources': ['transform.cpp']}, ] internal_tests = [ diff --git a/test/transform.cpp b/test/transform.cpp new file mode 100644 index 000000000000..928d547de57d --- /dev/null +++ b/test/transform.cpp @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023, Ideas On Board Oy + * + * transform.cpp - Transform and Orientation tests + */ + +#include + +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +using Orientation = CameraConfiguration::Orientation; + +class TransformTest : public Test +{ +protected: + int run(); +}; + +int TransformTest::run() +{ + /* + * RotationTestEntry collects two Orientation and one Transform that + * gets combined to validate that (o1 / o2 = T) and (o1 = o2 * T) + * + * o1 / o2 = t computes the Transform to apply to o2 to obtain o1 + * o2 * t = o1 combines o2 with t by applying o2 first then t + * + * The comments on the (most complex) transform show how applying to + * an image with orientation o2 the Transform t allows to obtain o1. + * + * The image with basic rotation0 is assumed to be: + * + * AB + * CD + * + * And the Transform operators are: + * + * V = vertical flip + * H = horizontal flip + * T = transpose + * + * the operator '* (T|V)' applies V first then T, according to the + * canonical function composition notion implemented by + * operator*(Transpose t1, Transpose t2); + */ + struct RotationTestEntry { + CameraConfiguration::Orientation o1; + CameraConfiguration::Orientation o2; + Transform t; + } testEntries[] = { + /* Test identities transforms first. */ + { + Orientation::rotate0, Orientation::rotate0, + Transform::Identity, + }, + { + Orientation::rotate0Flip, Orientation::rotate0Flip, + Transform::Identity, + }, + { + Orientation::rotate180, Orientation::rotate180, + Transform::Identity, + }, + { + Orientation::rotate180Flip, Orientation::rotate180Flip, + Transform::Identity, + }, + { + Orientation::rotate90, Orientation::rotate90, + Transform::Identity, + }, + { + Orientation::rotate90Flip, Orientation::rotate90Flip, + Transform::Identity, + }, + { + Orientation::rotate270, Orientation::rotate270, + Transform::Identity, + }, + { + Orientation::rotate270Flip, Orientation::rotate270Flip, + Transform::Identity, + }, + /* + * Combine 0 and 180 degrees rotation as they're the most common + * ones. + */ + { + /* + * o2 t o1 + * -------------------------- + * CD * (H|V) = BA AB + * BA CD CD + */ + Orientation::rotate0, Orientation::rotate180, + Transform::Rot180, + }, + { + Orientation::rotate180, Orientation::rotate180, + Transform::Identity + }, + { + /* + * o2 t o1 + * -------------------------- + * AB * (H|V) = CD DC + * CD AB BA + */ + Orientation::rotate180, Orientation::rotate0, + Transform::Rot180 + }, + /* Test that transpositions are handled correctly. */ + { + /* + * o2 t o1 + * -------------------------- + * AB * (T|V) = CD CA + * CD AB DB + */ + Orientation::rotate90, Orientation::rotate0, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CA * (T|H) = AC AB + * DB BD CD + */ + Orientation::rotate0, Orientation::rotate90, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * AB * (T|H) = BA BD + * CD DC AC + */ + Orientation::rotate270, Orientation::rotate0, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * BD * (T|V) = AC AB + * AC BD CD + */ + Orientation::rotate0, Orientation::rotate270, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CD * (T|H) = DC DA + * BA AB CB + */ + Orientation::rotate90, Orientation::rotate180, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * DA * (T|V) = CB CD + * CB DA BA + */ + Orientation::rotate180, Orientation::rotate90, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CD * (T|V) = BA BC + * BA CD AD + */ + Orientation::rotate270, Orientation::rotate180, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * BC * (T|H) = CB CD + * AD DA BA + */ + Orientation::rotate180, Orientation::rotate270, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * DA * (V|H) = AD BC + * CB BC AD + */ + Orientation::rotate270, Orientation::rotate90, + Transform::Rot180, + }, + /* Test that mirroring is handled correctly. */ + { + Orientation::rotate0, Orientation::rotate0Flip, + Transform::HFlip + }, + { + Orientation::rotate0Flip, Orientation::rotate0, + Transform::HFlip + }, + { + Orientation::rotate180, Orientation::rotate180Flip, + Transform::HFlip + }, + { + Orientation::rotate180Flip, Orientation::rotate180, + Transform::HFlip + }, + { + Orientation::rotate90, Orientation::rotate90Flip, + Transform::HFlip + }, + { + Orientation::rotate90Flip, Orientation::rotate90, + Transform::HFlip + }, + { + Orientation::rotate270, Orientation::rotate270Flip, + Transform::HFlip + }, + { + Orientation::rotate270Flip, Orientation::rotate270, + Transform::HFlip + }, + { + Orientation::rotate0, Orientation::rotate0Flip, + Transform::HFlip + }, + /* + * More exotic transforms which include Transpositions and + * mirroring. + */ + { + /* + * o2 t o1 + * ------------------ + * BC * (V) = AD + * AD BC + */ + Orientation::rotate90Flip, Orientation::rotate270, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * ------------------ + * CB * (T) = CD + * DA BA + */ + Orientation::rotate180, Orientation::rotate270Flip, + Transform::Transpose, + }, + { + /* + * o2 t o1 + * ------------------ + * AD * (T) = AB + * BC DC + */ + Orientation::rotate0, Orientation::rotate90Flip, + Transform::Transpose, + }, + { + /* + * o2 t o1 + * ------------------ + * AD * (V) = BC + * BC AD + */ + Orientation::rotate270, Orientation::rotate90Flip, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * ------------------ + * DA * (V) = CB + * CB DA + */ + Orientation::rotate270Flip, Orientation::rotate90, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * -------------------------- + * CB * (V|H) = BC AD + * DA AD BC + */ + Orientation::rotate90Flip, Orientation::rotate270Flip, + Transform::Rot180, + }, + }; + + for (const auto &entry : testEntries) { + Transform transform = entry.o1 / entry.o2; + cout << "Testing: " << entry.o1 << " / " << entry.o2 + << " = " << transformToString(transform) << endl; + if (transform != entry.t) { + cerr << "Failed to validate: " << entry.o1 + << " / " << entry.o2 + << " = " << transformToString(entry.t) << endl; + cerr << "Got back: " + << transformToString(transform) << endl; + return TestFail; + } + + Orientation adjusted = entry.o2 * entry.t; + if (adjusted != entry.o1) { + cerr << "Failed to validate: " << entry.o2 + << " * " << transformToString(entry.t) + << " = " << entry.o1 << endl; + cerr << "Got back: " << adjusted << endl; + return TestFail; + } + } + + return TestPass; +} + +TEST_REGISTER(TransformTest) From patchwork Fri Jul 14 14:15:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18830 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 5570FBDC71 for ; Fri, 14 Jul 2023 14:16:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 07436628D6; Fri, 14 Jul 2023 16:16:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344178; bh=bER74sB8OfY1AoapP/bIueTj7ECFUPNc4WVMThgcKrQ=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=daumTVM/s+AQ+xYpZd5vwfyoGDOoE/LnmwWukBjxTrC88G2Lii0e3Bm0IakYchI5t WXwW7RK1czmvP78habyQPy50p14rSpl34SdNRJt5oe3aXyYOLuTnB+XQYY6sXzVnIt 15QWZ9JpoXb7xhMvfXHe2S8MDCTsEETIFlYKBPljkZoa/y9nLhNbdzRFPC643K/hFY E21JXRCTsUvMCI+eJy1d2lmkuUS0DTIoewWs+pJl085J5wod0m/MzLaKNO289eRlLX ym0DkIhfQIHWxDDHvR9TSR1ee59iFQXV/xXwbEZXsftTSAthv0Wzrg2Vuwf159mlnC KD1+Tswe8rvYg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7B170628D4 for ; Fri, 14 Jul 2023 16:16:14 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DQcD0hiW"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 02C387F3; Fri, 14 Jul 2023 16:15:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344123; bh=bER74sB8OfY1AoapP/bIueTj7ECFUPNc4WVMThgcKrQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DQcD0hiWURbRkQ5i7TFguPZmds/w4nGTr09uBVEFM0AhirJAx/ZQQy4R0LxvfPFjO 106VyRf2a1gE2uYPBPHHMDpaeDILOgrkPduXBk5YyiCWfv6U//bP8SZlXc941ilfx1 Ox2VYV6B4rS7j/LOmvnl2MfaBqB01JqOAxVoDdsE= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:48 +0200 Message-Id: <20230714141549.11085-9-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 8/9] libcamera: Move to use CameraConfiguration::orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Replace the usage of CameraConfiguration::transform with the newly introduced CameraConfiguration::orientation. Rework and rename the CameraSensor::validateTransform(transform) to CameraSensor::computeTransform(orientation), that given the desired image orientation computes the Transform that pipeline handlers should apply to the sensor to obtain it. Port all pipeline handlers to use the newly introduced function. This commit breaks existing applications as it removes the public CameraConfiguration::transform in favour of CameraConfiguration::orientation. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- include/libcamera/camera.h | 1 - include/libcamera/internal/camera_sensor.h | 3 +- src/libcamera/camera.cpp | 15 +-- src/libcamera/camera_sensor.cpp | 92 +++++++++---------- src/libcamera/pipeline/ipu3/ipu3.cpp | 6 +- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 8 +- .../pipeline/rpi/common/pipeline_base.cpp | 9 +- src/libcamera/pipeline/simple/simple.cpp | 6 +- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 4 +- src/libcamera/pipeline/vimc/vimc.cpp | 4 +- 10 files changed, 65 insertions(+), 83 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 55359d53f2ab..d937ffc24f54 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -80,7 +80,6 @@ public: bool empty() const; std::size_t size() const; - Transform transform; Orientation orientation; protected: diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h index 02b4b4d25e6d..1d47a2b1a500 100644 --- a/include/libcamera/internal/camera_sensor.h +++ b/include/libcamera/internal/camera_sensor.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -71,7 +72,7 @@ public: CameraLens *focusLens() { return focusLens_.get(); } - Transform validateTransform(Transform *transform) const; + Transform computeTransform(CameraConfiguration::Orientation *orientation) const; protected: std::string logPrefix() const override; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index af842e70dcb0..3d52d1a0019a 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -224,7 +224,7 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : transform(Transform::Identity), orientation(rotate0), config_({}) + : orientation(rotate0), config_({}) { } @@ -476,19 +476,6 @@ CameraConfiguration::Status CameraConfiguration::validateColorSpaces(ColorSpaceF return status; } -/** - * \var CameraConfiguration::transform - * \brief User-specified transform to be applied to the image - * - * The transform is a user-specified 2D plane transform that will be applied - * to the camera images by the processing pipeline before being handed to - * the application. - * - * The usual 2D plane transforms are allowed here (horizontal/vertical - * flips, multiple of 90-degree rotations etc.), but the validate() function - * may adjust this field at its discretion if the selection is not supported. - */ - /** * \var CameraConfiguration::orientation * \brief The desired orientation of the images produced by the camera diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp index 3ba364c44a40..ac81ea3889f7 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -465,7 +465,7 @@ int CameraSensor::initProperties() /* * Cache the Transform associated with the camera mounting - * rotation for later use in validateTransform(). + * rotation for later use in computeTransform(). */ bool success; rotationTransform_ = transformFromRotation(propertyValue, &success); @@ -1024,69 +1024,65 @@ void CameraSensor::updateControlInfo() */ /** - * \brief Validate a transform request against the sensor capabilities - * \param[inout] transform The requested transformation, updated to match - * the sensor capabilities + * \brief Compute the Transform that gives the requested \a orientation + * \param[inout] orientation The desired image orientation * - * The input \a transform is the transform that the caller wants, and it is - * adjusted according to the capabilities of the sensor to represent the - * "nearest" transform that can actually be delivered. + * This function computes the Transform that the pipeline handler should apply + * to the CameraSensor to obtain the requested \a orientation. * - * The returned Transform is the transform applied to the sensor in order to - * produce the input \a transform, It is also validated against the sensor's - * ability to perform horizontal and vertical flips. + * The intended caller of this function is the validate() implementation of + * pipeline handlers, that pass in the application requested + * CameraConfiguration::orientation and obtain a Transform to apply to the + * camera sensor, likely at configure() time. * - * For example, if the requested \a transform is Transform::Identity and the - * sensor rotation is 180 degrees, the output transform will be - * Transform::Rot180 to correct the images so that they appear to have - * Transform::Identity, but only if the sensor can apply horizontal and vertical - * flips. + * If the requested \a orientation cannot be obtained, the \a orientation + * parameter is adjusted to report the current image orientation and + * Transform::Identity is returned. * - * \return A Transform instance that represents which transformation has been - * applied to the camera sensor + * If the requested \a orientation can be obtained, the function computes a + * Transform and does not adjust \a orientation. + * + * Pipeline handlers are expected to verify if \a orientation has been + * adjusted by this function and set the CameraConfiguration::status to + * Adjusted accordingly. + * + * \return A Transform instance that applied to the CameraSensor produces images + * with \a orientation */ -Transform CameraSensor::validateTransform(Transform *transform) const +Transform CameraSensor::computeTransform(CameraConfiguration::Orientation *orientation) const { + CameraConfiguration::Orientation mountingOrientation = + transformToOrientation(rotationTransform_); + /* - * Combine the requested transform to compensate the sensor mounting - * rotation. + * We cannot do any flips: we cannot change the native camera mounting + * orientation. */ - Transform combined = *transform * rotationTransform_; + if (!supportFlips_) { + *orientation = mountingOrientation; + return Transform::Identity; + } /* - * We combine the platform and user transform, but must "adjust away" - * any combined result that includes a transposition, as we can't do - * those. In this case, flipping only the transpose bit is helpful to - * applications - they either get the transform they requested, or have - * to do a simple transpose themselves (they don't have to worry about - * the other possible cases). + * Now compute the required transform to obtain 'orientation' starting + * from the mounting rotation. + * + * As a note: + * orientation / mountingOrientation = transform + * mountingOrientation * transform = orientation */ - if (!!(combined & Transform::Transpose)) { - /* - * Flipping the transpose bit in "transform" flips it in the - * combined result too (as it's the last thing that happens), - * which is of course clearing it. - */ - *transform ^= Transform::Transpose; - combined &= ~Transform::Transpose; - } + Transform transform = *orientation / mountingOrientation; /* - * We also check if the sensor doesn't do h/vflips at all, in which - * case we clear them, and the application will have to do everything. + * If transform contains any Transpose we cannot do it, so adjust + * 'orientation' to report the image native orientation and return Identity. */ - if (!supportFlips_ && !!combined) { - /* - * If the sensor can do no transforms, then combined must be - * changed to the identity. The only user transform that gives - * rise to this is the inverse of the rotation. (Recall that - * combined = transform * rotationTransform.) - */ - *transform = -rotationTransform_; - combined = Transform::Identity; + if (!!(transform & Transform::Transpose)) { + *orientation = mountingOrientation; + return Transform::Identity; } - return combined; + return transform; } std::string CameraSensor::logPrefix() const diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index a81c817a1255..fa4bd0bb73e2 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -187,9 +187,9 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate() * rotation and store the final combined transform that configure() will * need to apply to the sensor to save us working it out again. */ - Transform requestedTransform = transform; - combinedTransform_ = data_->cio2_.sensor()->validateTransform(&transform); - if (transform != requestedTransform) + Orientation requestedOrientation = orientation; + combinedTransform_ = data_->cio2_.sensor()->computeTransform(&orientation); + if (orientation != requestedOrientation) status = Adjusted; /* Cap the number of entries to the available streams. */ diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 6efa79f29f66..586b46d64630 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -481,9 +481,9 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate() status = Adjusted; } - Transform requestedTransform = transform; - Transform combined = sensor->validateTransform(&transform); - if (transform != requestedTransform) + Orientation requestedOrientation = orientation; + combinedTransform_ = data_->sensor_->computeTransform(&orientation); + if (orientation != requestedOrientation) status = Adjusted; /* @@ -595,8 +595,6 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate() if (sensorFormat_.size.isNull()) sensorFormat_.size = sensor->resolution(); - combinedTransform_ = combined; - return status; } diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 179a5b81a516..5d541d71defe 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -254,9 +254,9 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() * rotation and store the final combined transform that configure() will * need to apply to the sensor to save us working it out again. */ - Transform requestedTransform = transform; - combinedTransform_ = data_->sensor_->validateTransform(&transform); - if (transform != requestedTransform) + Orientation requestedOrientation = orientation; + combinedTransform_ = data_->sensor_->computeTransform(&orientation); + if (orientation != requestedOrientation) status = Adjusted; std::vector rawStreams, outStreams; @@ -1193,7 +1193,8 @@ int CameraData::configureIPA(const CameraConfiguration *config, ipa::RPi::Config } /* Always send the user transform to the IPA. */ - params.transform = static_cast(config->transform); + params.transform = + static_cast(transformFromOrientation(config->orientation)); /* Ready the IPA - it must know about the sensor resolution. */ ret = ipa_->configure(sensorInfo_, params, result); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 05ba76bce630..911051b28498 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -889,9 +889,9 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() if (config_.empty()) return Invalid; - Transform requestedTransform = transform; - combinedTransform_ = sensor->validateTransform(&transform); - if (transform != requestedTransform) + Orientation requestedOrientation = orientation; + combinedTransform_ = sensor->computeTransform(&orientation); + if (orientation != requestedOrientation) status = Adjusted; /* Cap the number of entries to the available streams. */ diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 02a6117c7955..6f9cf0dca6f5 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -112,8 +112,8 @@ CameraConfiguration::Status UVCCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + if (orientation != CameraConfiguration::rotate0) { + orientation = CameraConfiguration::rotate0; status = Adjusted; } diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index 00e6f4c6a51f..26401ab92d15 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -128,8 +128,8 @@ CameraConfiguration::Status VimcCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + if (orientation != CameraConfiguration::rotate0) { + orientation = CameraConfiguration::rotate0; status = Adjusted; } From patchwork Fri Jul 14 14:15:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18831 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 2864FC32AC for ; Fri, 14 Jul 2023 14:16:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C98D8628D7; Fri, 14 Jul 2023 16:16:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689344178; bh=mVMd6ppsn53uIDV8y15d35+PRwvT1Nk05QhA0Dl93qY=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=SzWtVC1TA0Xw8dXrcNUFh/aP9+qiD2XQ1/dAVnn6eVt0m7D29R6LXZXPgPvDxNr49 g6DQzalCDzIsuYX9pKO/Q5aXQhmk9C33r+Y/sqS4OlVPF56wz2eV8CDy8AgGdXHnof /tpvBJ6R3uEk+U9+VC47NQDdiDoMmVxj7aRzqErNnjbQepMec9eWVv/AngL4XsmOKl iz3l7yuk12jG+nyVfgk6k/amoSDMbiwb5ylOUhbuWVjFMvGu+Kz30ot71oi4oBDkWU ksuoGH/Q9MAGjSHbRfKOF1mSmMo1RUaAF+/NyGwsihp3x9uKlypvguWz8xSQbUuNfM WQEieRtxGO1bQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6886F628CB for ; Fri, 14 Jul 2023 16:16:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sHKwNOT3"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-9-92.net.vodafone.it [5.90.9.92]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EF5D5814; Fri, 14 Jul 2023 16:15:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689344124; bh=mVMd6ppsn53uIDV8y15d35+PRwvT1Nk05QhA0Dl93qY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sHKwNOT33kCi4Niz6r5yhjzwt4t1cHiPvA7OJs1V0dmQRst0rs0+hHqOhIFJAqYDY qKPP07T+rcddQbprY6nEIomK4/gR3Z4WwAMYwTb5TkW451Ikd+X6rQo+gQPTcqpV3I 1XDcr53/C8SoFwCJyxeciV1Tin0wsl2MEuxGJhHY= To: libcamera-devel@lists.libcamera.org Date: Fri, 14 Jul 2023 16:15:49 +0200 Message-Id: <20230714141549.11085-10-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> References: <20230714141549.11085-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 9/9] apps: cam: Add option to set stream orientation 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: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a '--orientation|-o' option to the cam test application to set an orientation to the image stream. Supported values are the ones obtained by applying flips to the camera sensor: - rot180: Rotate 180 degrees - flip: vertical flip - mirror: horizontal flip Signed-off-by: Jacopo Mondi --- src/apps/cam/camera_session.cpp | 17 +++++++++++++++++ src/apps/cam/main.cpp | 5 +++++ src/apps/cam/main.h | 1 + 3 files changed, 23 insertions(+) -- 2.40.1 diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp index 066e397b5f47..4ad3a141d841 100644 --- a/src/apps/cam/camera_session.cpp +++ b/src/apps/cam/camera_session.cpp @@ -65,6 +65,23 @@ CameraSession::CameraSession(CameraManager *cm, return; } + if (options_.isSet(OptOrientation)) { + std::string orient = options_[OptOrientation].toString(); + if (orient == "rot180") { + config->orientation = + libcamera::CameraConfiguration::rotate180; + } else if (orient == "mirror") { + config->orientation = + libcamera::CameraConfiguration::rotate0Flip; + } else if (orient == "flip") { + config->orientation = + libcamera::CameraConfiguration::rotate180Flip; + } else { + std::cerr << "Invalid orientation " << orient << std::endl; + return; + } + } + /* Apply configuration if explicitly requested. */ if (StreamKeyValueParser::updateConfiguration(config.get(), options_[OptStream])) { diff --git a/src/apps/cam/main.cpp b/src/apps/cam/main.cpp index 5d8a57bc14e5..8698d5eedcf8 100644 --- a/src/apps/cam/main.cpp +++ b/src/apps/cam/main.cpp @@ -133,6 +133,11 @@ int CamApp::parseOptions(int argc, char *argv[]) "Capture until interrupted by user or until frames captured", "capture", ArgumentOptional, "count", false, OptCamera); + + parser.addOption(OptOrientation, OptionString, + "The camera stream image orientation (rot180, mirror, flip)", + "orientation", ArgumentRequired, "orientation", false, + OptCamera); #ifdef HAVE_KMS parser.addOption(OptDisplay, OptionString, "Display viewfinder through DRM/KMS on specified connector", diff --git a/src/apps/cam/main.h b/src/apps/cam/main.h index 526aecece3f3..4aa959b32e13 100644 --- a/src/apps/cam/main.h +++ b/src/apps/cam/main.h @@ -17,6 +17,7 @@ enum { OptList = 'l', OptListProperties = 'p', OptMonitor = 'm', + OptOrientation = 'o', OptSDL = 'S', OptStream = 's', OptListControls = 256,