From patchwork Mon Jul 24 07:10:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18863 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 4FCB9C324E for ; Mon, 24 Jul 2023 07:11:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C99FF628C7; Mon, 24 Jul 2023 09:11:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182681; bh=5sPxj+3er3deOD8JJ1ckeFkzP2xyjqOLP3WMPssZpK4=; 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=HR+nZe2/0c3/bDLNsi47xoetDfFzMI5SFJDJ92b6gEl3p7Majky5vtaU5+vaqCfDl gr7h/UGazOyQcaE6n7au9PqbebO8KQHT5Gw95exh2oXRayvkX3Vs873DeaMpr2IaiN fsp1Zj16iGOTo4sADwmkIaC/idir3IPMFErGObAuZ4BkOT7qYAy5mfCghETnYRldr+ KCPeWya9zFrVEPHmZr/FRfXX32xCU+/6hUlup+tbNNitnlHG9mbtRel1sXJMC4aH+7 h65PBX/WIVDF/JhY5kAgToIp6796dTGUIsV/XyMGQ7C0CKf/UHZgUhldx18DT+Kwgk vYQW+CM6tsn8A== 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 27EF161E25 for ; Mon, 24 Jul 2023 09:11:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="evRgTWMk"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0AC774AD; Mon, 24 Jul 2023 09:10:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182622; bh=5sPxj+3er3deOD8JJ1ckeFkzP2xyjqOLP3WMPssZpK4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=evRgTWMkUcGt0g+I56NIU9YmhCHBozcGH+ReculRLU3gcfYe0JO4/Nngn4hgs2oZ+ aSVI0nOcV6dBgAoagja1VyFj22HK8X7aKf+OSsSbvZv7DsI9aI2KNbXPGZkUrfx7CF vOqJNyTip5r5/9rpoImUFxQ8kKa46G3vgHAqYe2A= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:52 +0200 Message-Id: <20230724071101.5256-2-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 01/10] 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 Mon Jul 24 07:10:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18864 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 31BE9BDC71 for ; Mon, 24 Jul 2023 07:11:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7E2DA628CC; Mon, 24 Jul 2023 09:11:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182683; bh=yzZAi3e2eilrv1EvD0hf800qyt3WTFM+D5evp0TGr3o=; 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=j4yaHqc377DBLBYtE8/jN6E8rEQ/V1OtpRdbGPP1fVpcPdSkf01fphU62hz/MFvDl 7cosJ1UjhbBT4RM071u3lk63j1zsvC9JRLXUu3sbEWYhXlhMt3oKbxw7HVRMDsvklQ QMip7rigd+ybSLVyWDvOHfUbFQ8nRcTwlgibqG1TpJh9NyhxDN9MLlAonOlGXNG72R nhslnpLFwZoWIVDnMturDKn3S2WTZdS6ite9BETwJoyi3X4YRd5wU8MeEJzDEK4Yys lqFbSjalu486uxrc8LUOnUsRxdgjssEG2cVGhDVoWWcVVk7cczBIZSpPuAGpwJLnDG QDOgD7G4OmRXw== 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 CC00F61E25 for ; Mon, 24 Jul 2023 09:11:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="J1V6JJdt"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1B2CF2C6; Mon, 24 Jul 2023 09:10:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182623; bh=yzZAi3e2eilrv1EvD0hf800qyt3WTFM+D5evp0TGr3o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J1V6JJdtwJVEPXH6i+kvl4wvzN7WWZO2gc+GoOv6G3+6Al3344FtkJhFCO2aAR6yK uVcJucI7Kq5e5IqIf3SvamBOwte0Yh8nj6+RtqHy1eY4DzNkunHpF8aYld+VMLSkvu 3DjOuqq2t1c8froMi1gQsjhbBBT/CuRr3xJY90qg= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:53 +0200 Message-Id: <20230724071101.5256-3-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 02/10] libcamera: camera: Introduce 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 the Orientation enumeration which describes the possible 2D transformations that can be applied to an image using two basic plane transformations. Add to the CameraConfiguration class a new member 'orientation' which is used to specify the image orientation in the memory buffers delivered to applications. The enumeration values follow the ones defined by the EXIF specification at revision 2.32, Tag 274 'orientation'. The newly introduced field 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 | 2 + include/libcamera/meson.build | 1 + include/libcamera/orientation.h | 28 ++++++++++++ src/libcamera/camera.cpp | 18 +++++++- src/libcamera/meson.build | 1 + src/libcamera/orientation.cpp | 78 +++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/orientation.h create mode 100644 src/libcamera/orientation.cpp diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 004bc89455f5..6e9342773962 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,7 @@ public: std::size_t size() const; Transform transform; + Orientation orientation; protected: CameraConfiguration(); diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 408b7acf152c..a24c50d66a82 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -12,6 +12,7 @@ libcamera_public_headers = files([ 'framebuffer_allocator.h', 'geometry.h', 'logging.h', + 'orientation.h', 'pixel_format.h', 'request.h', 'stream.h', diff --git a/include/libcamera/orientation.h b/include/libcamera/orientation.h new file mode 100644 index 000000000000..63ac4aba07ce --- /dev/null +++ b/include/libcamera/orientation.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Ideas On Board Oy + * + * orientation.h - Image orientation + */ + +#pragma once + +#include + +namespace libcamera { + +enum class Orientation { + /* EXIF tag 274 starts from '1' */ + rotate0 = 1, + rotate0Flip, + rotate180, + rotate180Flip, + rotate90Flip, + rotate270, + rotate270Flip, + rotate90, +}; + +std::ostream &operator<<(std::ostream &out, const Orientation &orientation); + +} /* namespace libcamera */ diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 0eecee766f00..d4ad4a553752 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -160,7 +160,8 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : transform(Transform::Identity), config_({}) + : transform(Transform::Identity), orientation(Orientation::rotate0), + config_({}) { } @@ -404,6 +405,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 field is set to Orientation::rotate0. + */ + /** * \var CameraConfiguration::config_ * \brief The vector of stream configurations diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index b24f82965764..d0e26f6b4141 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -34,6 +34,7 @@ libcamera_sources = files([ 'mapped_framebuffer.cpp', 'media_device.cpp', 'media_object.cpp', + 'orientation.cpp', 'pipeline_handler.cpp', 'pixel_format.cpp', 'process.cpp', diff --git a/src/libcamera/orientation.cpp b/src/libcamera/orientation.cpp new file mode 100644 index 000000000000..f2ee14dd4182 --- /dev/null +++ b/src/libcamera/orientation.cpp @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2023, Ideas On Board Oy + * + * orientation.cpp - Image orientation + */ + +#include + +#include +#include + +/** + * \file libcamera/orientation.h + * \brief Image orientation definition + */ + +namespace libcamera { + +/** + * \enum 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 plane transformations 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 then 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 then applying an + * horizontal mirroring. + */ + +/** + * \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 Orientation &orientation) +{ + constexpr std::array orientationNames = { + "", /* Orientation starts counting from 1. */ + "rotate0", "rotate0Flip", + "rotate180", "rotate180Flip", + "rotate90Flip", "rotate270", + "rotate270Flip", "rotate90", + }; + + out << orientationNames[static_cast(orientation)]; + return out; +} + +} /* namespace libcamera */ From patchwork Mon Jul 24 07:10:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18865 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 A959AC324E for ; Mon, 24 Jul 2023 07:11:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 54CBC628C4; Mon, 24 Jul 2023 09:11:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182686; bh=y9epwtLNk6aBc5bER+lj2dWaexIfG+WAF18mvhsbzmU=; 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=QbLILNwSpzQB4bIt8s7L+8ar3s491ni0OSDTLyccxYAIVJdCI5LIcsoZEkib3yDm+ CANTnKeXgAes+j0Yd0avLQRCNNltbpxnPuOXCIkmjyZkYnpkbxxYk8K7e5beuQyWoG +IpZJPXGg5xXebhBM/qhhOq4mDER11ZFYtZrRJWv04VxrY6+a/nZgJAk5dxlCMC0yA IjStVNVJDmrbe4lu0p+g9bIXR14fOVBuVAhtWS055GW+Fhs71kLP5VdxCyX0Tnm11T psHUPAhdGtwwjjzsOrEyj7W2T2SUnglThZOXJLpf2uaU6zteVc8+YWJ4bdDHRV4i6f arL9HJP4TED2A== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A2921628C3 for ; Mon, 24 Jul 2023 09:11:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="MKOClqYm"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CACD9735; Mon, 24 Jul 2023 09:10:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182625; bh=y9epwtLNk6aBc5bER+lj2dWaexIfG+WAF18mvhsbzmU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MKOClqYms3cD0j+ka0JYftAlxgXHFNzCCIUGPOEaAv77oAcnJqYcsTpvq+u6bynYG Sprmxdu5IYZ1KySXz3pqOdG8MVdLRXSJ7AlDJcuQCAkg7No+5raE/RxQSKt3BZWFpH FPjj85PAydbDjfTt+gNYzsDAcf7lhk1PaKwbJC78= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:54 +0200 Message-Id: <20230724071101.5256-4-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 03/10] 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/orientation.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: 18866 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 331CCC32AA for ; Mon, 24 Jul 2023 07:11:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DA61E628D4; Mon, 24 Jul 2023 09:11:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182686; bh=xDK91yoWnPn+8AhlpzdW8lSc0qCCbfllNaIxij+HUQg=; 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=17mpc9u3OTXDX61fc4Ixw3V9/elpfc9+ae8zQ27On5HeUKCofpv6lRHcfWBaltxey EzUjs4cersxS2TqSd0NJWuEWNUuXID+w73zG1HqXnPQaNOUdJrAMOO1n+QI3gxv4Yz Chtm8xwc/+JqEPISFWJo+gSgFm+C2cf/K+tgGnW3hiR/EYrFpw0zkoztN8FnppZrmK 5MK+RhrsBF/UpheVAsi4dHqP9+0fuCYocyULITiOBMi8sTrgSw/JzP4Rp7tfFcAJGx rJMBH/5uRH1pkJlxHGFkJ/ofVEUAAqvo3lJ/gotxkmnPoKZERz6xz5N0tASZB6aLmS OfTGS5qUiCndQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5CB5A628CB for ; Mon, 24 Jul 2023 09:11:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Cov2X0JP"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 97BB84AD; Mon, 24 Jul 2023 09:10:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182626; bh=xDK91yoWnPn+8AhlpzdW8lSc0qCCbfllNaIxij+HUQg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Cov2X0JP7XP/wHSucBCibrOUlUrCJj5WRey1Yo3HIffbJoTyF1s9Azy6ezHyf6GNb c4d8WwkOI2eKfJVH1Rv2rcvQ3eiZp96R2IqA4VbDXNgfIYVsHuJF68zJHYrqcJDd0n HCc8az/3KO3iuHE5lMZqsmXJcCN7A4So63kmHGgA= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:55 +0200 Message-Id: <20230724071101.5256-5-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 04/10] 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 Mon Jul 24 07:10:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18867 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 B3255C32AB for ; Mon, 24 Jul 2023 07:11:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5ADCD628D7; Mon, 24 Jul 2023 09:11:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182687; bh=C071+c9sKX4BUUndlfeOERBWDFRvAPyBmpu+wDpWu2E=; 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=xIS6G0fM6j0InyC12qCN/8W1SqucDRhDCVW3DjjEej5UoBMELjLW/6n54MOcfsySL d5zGivDxpvaFKJKz7mY2MJpPPsiwBvnwdCogV9XnVXHDkYgSquFAjRr2QvHvzfhG82 dt6P0gx0AqGaIlk8EivbOtOn6BkOHHhKiBkhntJa7HA8iyJo0zsCQPv6VnvYTlwRx1 aBxj44zFGt3y3DjbfXllEZBz0sdqrau66R/V9xIwejaQgadT0leNkoAdjz7VYRR1XY b+/v6qpVDKqMSFtlGflAlvlwxSXAlqSfaIxqNKO01wiO05zB9tgopxxfLGSaFfKr1J 28zXKvUMGVigw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 421D3628C9 for ; Mon, 24 Jul 2023 09:11:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ubvtD1uK"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6EDF6735; Mon, 24 Jul 2023 09:10:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182627; bh=C071+c9sKX4BUUndlfeOERBWDFRvAPyBmpu+wDpWu2E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ubvtD1uK4NzYVwPXqqeyFImWRWciBMxWENVx836Eiw/u3mH493X6uoFL7TUniHOd9 NtawJuQ0DaEfDUbAxDljFdlrh/YgBDAng+KHhS+lrA1aMLsfmHpOsgYE7MJb3zsaxl xdKaU+xhZq3phBbtr2FsEi/VR6O1orueCxhD/XPY= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:56 +0200 Message-Id: <20230724071101.5256-6-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 05/10] 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. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- include/libcamera/transform.h | 4 +++ src/libcamera/transform.cpp | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 2a6838c78369..14f1db0e8572 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -11,6 +11,8 @@ namespace libcamera { +enum class Orientation; + enum class Transform : int { Identity = 0, Rot0 = Identity, @@ -69,6 +71,8 @@ constexpr Transform operator~(Transform t) } Transform transformFromRotation(int angle, bool *success = nullptr); +Transform transformFromOrientation(const Orientation &orientation); +Orientation transformToOrientation(const Transform &transform); const char *transformToString(Transform t); diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp index 4668303d0676..c70cac3f14ee 100644 --- a/src/libcamera/transform.cpp +++ b/src/libcamera/transform.cpp @@ -7,6 +7,8 @@ #include +#include + /** * \file transform.h * \brief Enum to represent and manipulate 2D plane transforms @@ -299,6 +301,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 Orientation &orientation) +{ + switch (orientation) { + case Orientation::rotate0: + return Transform::Identity; + case Orientation::rotate0Flip: + return Transform::HFlip; + case Orientation::rotate180: + return Transform::Rot180; + case Orientation::rotate180Flip: + return Transform::VFlip; + case Orientation::rotate90Flip: + return Transform::Transpose; + case Orientation::rotate90: + return Transform::Rot90; + case Orientation::rotate270Flip: + return Transform::Rot180Transpose; + case Orientation::rotate270: + return Transform::Rot270; + } + + return Transform::Identity; +} + +/** + * \brief Return the Orientation representing \a transform + * \param[in] transform The transform to convert + * \return The Orientation corresponding to \a transform + */ +Orientation transformToOrientation(const Transform &transform) +{ + switch (transform) { + case Transform::Identity: + return Orientation::rotate0; + case Transform::HFlip: + return Orientation::rotate0Flip; + case Transform::VFlip: + return Orientation::rotate180Flip; + case Transform::Rot180: + return Orientation::rotate180; + case Transform::Transpose: + return Orientation::rotate90Flip; + case Transform::Rot270: + return Orientation::rotate270; + case Transform::Rot90: + return Orientation::rotate90; + case Transform::Rot180Transpose: + return Orientation::rotate270Flip; + } + + return Orientation::rotate0; +} + /** * \brief Return a character string describing the transform * \param[in] t The transform to be described. From patchwork Mon Jul 24 07:10:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18868 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 4C54DBDC71 for ; Mon, 24 Jul 2023 07:11:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F1125628DB; Mon, 24 Jul 2023 09:11:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182691; bh=de7+2e+c3mVF0ji8CUWyiKapTfOOMPyU2+UAbMj/jMg=; 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=MKxZr5UssduDIsP8hv9NxlqzZDLcU7ZXP+N+uM6zfe8nFqXGll4/8dNGH+gD9jEPM xHA8+zahDtf6Tukx3/fyY0/FlHp7242aLMQfC1P0tmoGhRl7k5jay8lJKqEgxOJ7Tg Ykkm5mO4QmfZ6ErkrHFZSf5KMWWICND0+wUuRnPZBEMKVsTZE+FxNmi8p0K6b3ITeE mEh5EZEacRdYTHC1dRuhPUKG6M38v35WsZjZQszdlqxbmNdgbhL69zSh7dkKJgkLEV IINcFHjteYVpBX94fIQqnMLt4jsDM3ZKIoFMT/czRqbsrs3b8yoQJzLFTtsyWAIZRH odcfdkCn2i5fw== 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 242DC628C0 for ; Mon, 24 Jul 2023 09:11:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BR8P++4y"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 345EF2C6; Mon, 24 Jul 2023 09:10:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182627; bh=de7+2e+c3mVF0ji8CUWyiKapTfOOMPyU2+UAbMj/jMg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BR8P++4ymW4U4xqOU27LxJD7XeRdOJaq9cS3NJ/IO42daq50ak8oYQHfJG7M/0hm9 5gOFjAuqev2XRMCp1mGDSApAvl7kaM/Rb1c+6PQ6FZruB/ivud8fiOslHn9ue934En FMvlEea8UjWq1DsPBc3EkFVPTvZJozDGDekNbEcE= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:57 +0200 Message-Id: <20230724071101.5256-7-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 06/10] libcamera: transform: Invert operator*() operands 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 current definition of operator*(Transform t1, Transform t0) follows the function composition notion, where t0 is applied first then t1 is applied last. In order to introduce operator*(Orientation, Transform) where a Transform is applied on top of an Orientation, invert the operand order of operator*(Transform, Transform) so that usage of operator* with both Orientation and Transform can be made associative. For example: Orientation o; Transform t = t1 * t2 Orientation o1 = o * t = o * (t1 * t2) = (o * t1) * t2 = o * t1 * t2 Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- src/libcamera/camera_sensor.cpp | 4 ++-- src/libcamera/transform.cpp | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp index 3ba364c44a40..038d8b959072 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -1051,7 +1051,7 @@ Transform CameraSensor::validateTransform(Transform *transform) const * Combine the requested transform to compensate the sensor mounting * rotation. */ - Transform combined = *transform * rotationTransform_; + Transform combined = rotationTransform_ * *transform; /* * We combine the platform and user transform, but must "adjust away" @@ -1080,7 +1080,7 @@ Transform CameraSensor::validateTransform(Transform *transform) const * 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.) + * combined = rotationTransform * transform.) */ *transform = -rotationTransform_; combined = Transform::Identity; diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp index c70cac3f14ee..d192d63d9290 100644 --- a/src/libcamera/transform.cpp +++ b/src/libcamera/transform.cpp @@ -189,14 +189,17 @@ Input image | | goes to output image | | */ /** - * \brief Compose two transforms together - * \param[in] t1 The second transform - * \param[in] t0 The first transform + * \brief Compose two transforms by applying \a t0 first then \a t1 + * \param[in] t0 The first transform to apply + * \param[in] t1 The second transform to apply + * + * Compose two transforms by applying \a t1 after \a t0. The operation + * is conceptually equivalent to the canonical notion of function composition, + * with inverse order of operands. If in the canonical function composition + * notation "f * g" equals to "f(g())", the notation for Transforms composition + * "t0 * t1" equals to "t1(t0()))" where \a t0 is applied first, then \a t1. * - * Composing transforms follows the usual mathematical convention for - * composing functions. That is, when performing `t1 * t0`, \a t0 is applied - * first, and then \a t1. - * For example, `Transpose * HFlip` performs `HFlip` first and then the + * For example, `HFlip * Transpose` performs `HFlip` first and then the * `Transpose` yielding `Rot270`, as shown below. ~~~ A-B B-A B-D @@ -206,7 +209,7 @@ Input image | | -> HFLip -> | | -> Transpose -> | | = Rot270 * Note that composition is generally non-commutative for Transforms, * and not the same as XOR-ing the underlying bit representations. */ -Transform operator*(Transform t1, Transform t0) +Transform operator*(Transform t0, Transform t1) { /* * Reorder the operations so that we imagine doing t0's transpose From patchwork Mon Jul 24 07:10:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18869 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 0F6A0C324E for ; Mon, 24 Jul 2023 07:11:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AD8E1628C0; Mon, 24 Jul 2023 09:11:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182691; bh=ROI3Ux4EEZWj62eMqFJTgCtOWTqlIBEQRw94ikVQKDc=; 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=qKNKYNwYcouIW5btBYTi8K7Ui3GxlvgmvmrbmA2ao8B/7nDxv87cyJu9dT98p+sF+ 5yghrS81t3X/QMqh+hFXgFZDUXaEO79lE1LpJDleGmLUIyywFLZcIvd7s4PKYr9v91 rkHb3U+JjmusCCueTtXS4Qaar5VKevoy2ZsnQsW9hxwHBCio2iKSnibCeoTmCCZgrp R3aEpspwYon5RqEYwxlKSaMdR+7SOAFcom8VJqhL/xdjV+H61ZikEbaQsO1Q3F6hZm jwYaOrsWogn9YafeGBcY3fLioGfLZS1RszESkIbpNQJasaLT4V9tXm99ZyHBHCPrxn dwiGmnzCClBMg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 49AD0628C2 for ; Mon, 24 Jul 2023 09:11:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Bfbpb1/8"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 17DE8735; Mon, 24 Jul 2023 09:10:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182629; bh=ROI3Ux4EEZWj62eMqFJTgCtOWTqlIBEQRw94ikVQKDc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Bfbpb1/8GBtzgDu4qaGEXh2tty/tnNXTRRb+3rs3qVgx+LnbfenLqKuxwUUJWVGPt lHlraAYmEJsekAKfokABQbaWn143PrSNlg4JfcUND5XUFa03vNXLprdbKhLGCrLU4q AfQVUqjN1XX3cGvEs5qvS5sxCwlkhB5bg/NvnxSE= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:58 +0200 Message-Id: <20230724071101.5256-8-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 07/10] 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 the combination of the two These two operations allow applications to use Transforms to manipulate the Orientation inside the CameraConfiguration, if they wish. Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- include/libcamera/transform.h | 3 +++ src/libcamera/transform.cpp | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 14f1db0e8572..847a1f94713c 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -74,6 +74,9 @@ Transform transformFromRotation(int angle, bool *success = nullptr); Transform transformFromOrientation(const Orientation &orientation); Orientation transformToOrientation(const Transform &transform); +Transform operator/(const Orientation &o1, const Orientation &o2); +Orientation operator*(const Orientation &o1, const Transform &t); + const char *transformToString(Transform t); } /* namespace libcamera */ diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp index d192d63d9290..80cd804ca530 100644 --- a/src/libcamera/transform.cpp +++ b/src/libcamera/transform.cpp @@ -362,6 +362,43 @@ Orientation transformToOrientation(const Transform &transform) return Orientation::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/(const Orientation &o1, const Orientation &o2) +{ + Transform t1 = transformFromOrientation(o1); + Transform t2 = transformFromOrientation(o2); + + return -t2 * t1; +} + +/** + * \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 + */ +Orientation operator*(const Orientation &o, const Transform &t) +{ + /* + * Apply a Transform corresponding to the base orientation first and + * then apply \a t to it. + */ + return transformToOrientation(transformFromOrientation(o) * t); +} + /** * \brief Return a character string describing the transform * \param[in] t The transform to be described. From patchwork Mon Jul 24 07:10:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18870 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 CF12BC32AA for ; Mon, 24 Jul 2023 07:11:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 59C9E628C8; Mon, 24 Jul 2023 09:11:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182692; bh=qTPUrJtjfBz/W3EoRsvo4YPyZE3kCvlmGeoor6hJi9I=; 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=2/07GHztv0fG1jwFchL3Zhu75/tdXgEi+CJHNha/OCdeqHZREgVA3NUI718a8US7T p3lpJmPihpJvdw2oclhccHunlZ3sXG8NZCx8qofAc4lLbSA08kEKNDhKr1vFGHXAYn rcdHVxYa/YOLhfHAx98yQGcMM4Lys+SD9Uh9RGpkJAOI0+IKT6J+zZKCFcSx5uppNc Ryge6ewIyeZADL2O3aIS2AUaHwE9Q1E7/uQD6a0W+hQPV0WDrNGWj4o+prF2K07aqq JQ0aRNUJ9td0xnKlY2In66EVZN7BarmJ02qHKJk8NsGnTEzAcX1gNDAL8PAHTxruw/ oeh6+n11+WfSw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4A37D628D6 for ; Mon, 24 Jul 2023 09:11:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZJUEFetb"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 496E92C6; Mon, 24 Jul 2023 09:10:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182630; bh=qTPUrJtjfBz/W3EoRsvo4YPyZE3kCvlmGeoor6hJi9I=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZJUEFetbOD6R+ISNUb9tbyQ3/ti9C030za68DK1IYDNcW6o5rZ45ulSXkG2vSB+JR mOW6hEqWDBpv409j6icgZVoIHjVeO/QXw1zfe5kLAK4DHqA1Lv0fb6hgLWTpo8QJ3W wg2nuejwULtDQzBifYSDWU61RxMyWSyzDnxZyzSI= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:10:59 +0200 Message-Id: <20230724071101.5256-9-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 08/10] 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 o2 * t = o1 Signed-off-by: Jacopo Mondi Reviewed-by: David Plowman --- test/meson.build | 1 + test/transform.cpp | 331 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 test/transform.cpp 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..4a6cf7387f02 --- /dev/null +++ b/test/transform.cpp @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023, Ideas On Board Oy + * + * transform.cpp - Transform and Orientation tests + */ + +#include + +#include +#include + +#include "test.h" + +using namespace std; +using namespace libcamera; + +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. + */ + struct RotationTestEntry { + Orientation o1; + 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, + }, + { + /* + * 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 Mon Jul 24 07:11:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18871 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 512C9C32AC for ; Mon, 24 Jul 2023 07:11:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0DD25628C4; Mon, 24 Jul 2023 09:11:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182693; bh=kDi+P08cIzhtSvtGMBiIPdqBfLDf+Q1UCjD4MsMpCmo=; 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=Vx+3R67MUsMiC4Sn76dVrELWf+lGtGqijreA1BXfNPYlz2gmRs4/mn38XBUPB2/l/ SHkJNGE0vBOpB4aBJ9a5m8PTPMOvdq03SFRB4I0gkoG93W///KCbn4W50cz/nB+qWh T2U5VWPfQKP38KBajsc4ax7OWUIx2iEuTHjpwnWUplV216ZUUtz/WXKRBv4Lv6G+3W IoEW7uz58cMVaeyT8QkNAef7yjeRnxieaIlXfKSg7shn4ibCmndewYbO9sqJuSrlGe z0sTqMCexnfJPJZYr3ol07gvSRA9AZTqBoDpoQpHjqKDrp2fkuTeUrAEfydSZtTgLi GnT+/slFmKWHA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4A106628DA for ; Mon, 24 Jul 2023 09:11:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uw0cXx1n"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3FEF6735; Mon, 24 Jul 2023 09:10:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182630; bh=kDi+P08cIzhtSvtGMBiIPdqBfLDf+Q1UCjD4MsMpCmo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uw0cXx1nYDKNvAEzZRMN0dEIBJzIZIbMGvuqIvCzYO1oggS27iY0UZbz05COZ7up/ VKMol2rgEnEhoFhY6tjpo9bB+p0aYFeZpNsem5c38Z3izsdaa3Bj3uuTqgPzKDlSNT tOykV3sOavinoCFO+/1kf6iz2PLx5w8UhMVCDXG8= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:11:00 +0200 Message-Id: <20230724071101.5256-10-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 09/10] 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 | 2 - include/libcamera/internal/camera_sensor.h | 4 +- src/libcamera/camera.cpp | 16 +--- 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, 66 insertions(+), 85 deletions(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 6e9342773962..98c72ba148ed 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -22,7 +22,6 @@ #include #include #include -#include namespace libcamera { @@ -67,7 +66,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..6c5952a8fa1a 100644 --- a/include/libcamera/internal/camera_sensor.h +++ b/include/libcamera/internal/camera_sensor.h @@ -32,6 +32,8 @@ class MediaEntity; struct CameraSensorProperties; +enum class Orientation; + class CameraSensor : protected Loggable { public: @@ -71,7 +73,7 @@ public: CameraLens *focusLens() { return focusLens_.get(); } - Transform validateTransform(Transform *transform) const; + Transform computeTransform(Orientation *orientation) const; protected: std::string logPrefix() const override; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index d4ad4a553752..a9ea821a78c2 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -160,8 +160,7 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : transform(Transform::Identity), orientation(Orientation::rotate0), - config_({}) + : orientation(Orientation::rotate0), config_({}) { } @@ -392,19 +391,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 038d8b959072..3f06ceae5659 100644 --- a/src/libcamera/camera_sensor.cpp +++ b/src/libcamera/camera_sensor.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -465,7 +466,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 +1025,64 @@ 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(Orientation *orientation) const { + 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 = rotationTransform_ * *transform; + 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 = rotationTransform * transform.) - */ - *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 38f48a5d9269..4bdb7e5f9b87 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -111,8 +111,8 @@ CameraConfiguration::Status UVCCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + if (orientation != Orientation::rotate0) { + orientation = Orientation::rotate0; status = Adjusted; } diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index 00e6f4c6a51f..bc705f01284a 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 != Orientation::rotate0) { + orientation = Orientation::rotate0; status = Adjusted; } From patchwork Mon Jul 24 07:11:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18872 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 E911AC32AD for ; Mon, 24 Jul 2023 07:11:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7AA8F628DC; Mon, 24 Jul 2023 09:11:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1690182693; bh=s4CLXqHfONxDG0QXp32leVgsLJ8uPxx3iu1DcR8O0X4=; 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=vlGNLwIWDJIROs/Z9Dw9ILVLGZjtDppZ6C37sYvPtqR0g3Pu0ByIq460oYT0IvRRJ njNDyGYZtUvU5pobybJIntJfPYwgS/Pj6JVZFkTcqCl/EK+om6tH3jqr98rQNIyN0l UYhb4MOMFRPV9uLfQMq4h2+tP2YYyA82Exyl6dcsdra3ZpTrnEroGkidwahQCdNLZo 1X+kYoM2U+s+GOJmI+orB9ppCiFwf+gGw+zHwALZJWYN3qHPismW+HFqk4ujFceOTx FroTkY/NCpjublwlrFxs1EL2kdQe232u0FXi8NXm37/bOHCqzobl4p7759khVznufJ 7UebKjjjij6Qw== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 31B6B628BF for ; Mon, 24 Jul 2023 09:11:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="CRpRjHXh"; dkim-atps=neutral Received: from uno.localdomain (mob-5-91-20-233.net.vodafone.it [5.91.20.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5CD6212AE; Mon, 24 Jul 2023 09:10:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1690182631; bh=s4CLXqHfONxDG0QXp32leVgsLJ8uPxx3iu1DcR8O0X4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CRpRjHXhfVIEWpg7ghII16fr102y0zNxxKPOYxVi0n1rblctWC/CxXxyufdy4antw ckS3y3UTufQCBlx01ToNeFr1/EWtrxxPdmfBMLy0khCApWOYO7VlB00fsQ0vGtBLPR Z/SVfQvJ+ZmFmem66PjzO/JDin4Z7OK5NZpf/cbM= To: libcamera-devel@lists.libcamera.org Date: Mon, 24 Jul 2023 09:11:01 +0200 Message-Id: <20230724071101.5256-11-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> References: <20230724071101.5256-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 10/10] 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 Reviewed-by: David Plowman --- src/apps/cam/camera_session.cpp | 17 +++++++++++++++++ src/apps/cam/main.cpp | 5 +++++ src/apps/cam/main.h | 1 + 3 files changed, 23 insertions(+) diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp index 066e397b5f47..1cf7a245aaa7 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::Orientation::rotate180; + } else if (orient == "mirror") { + config->orientation = + libcamera::Orientation::rotate0Flip; + } else if (orient == "flip") { + config->orientation = + libcamera::Orientation::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,