From patchwork Tue Jul 18 10:52: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: 18842 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 65EE2C324D for ; Tue, 18 Jul 2023 10:52:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 00F98628C5; Tue, 18 Jul 2023 12:52:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677542; 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=cV3Pv8IcorqLMz0p3u/3V5zDpsWtmGL5pXitXMjdKqt8+V61MggVBb0dhZoO482n6 2ZfC4kIXuESjmky0kgvXT5kp65l0La6k1A2esZx+9i589/pvdjEEDyoI7B5MA/jIyz DuuQG2yLSCB6dewmcr4xHndCRDNNUNrwKhdyxEC6qD725wZCG+Tlm5bgSGkbAQ9OBt NlhvviwxuaxBNtsUIGQtRsW3MaaZkV+JWFn4XhUbI0CioNecksj+YXM5ItWg3IBFhM qlVsE9xiu6/PcDzZXUYfWwLwPxTm+z0NxDkIVn3wdEceYZgREczvPEv9HIXAO4S0yS DBRWvtiOCBB+w== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D89EF61E2B for ; Tue, 18 Jul 2023 12:52:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="jtSf2JeS"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E12948D0; Tue, 18 Jul 2023 12:51:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677486; bh=5sPxj+3er3deOD8JJ1ckeFkzP2xyjqOLP3WMPssZpK4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jtSf2JeSF/Xloa0pqkUvmUtieyv/GDb4sVpBs9VgHdDLUbinuwJ/Pid//s8tb09oq wkbGi2i3K+DD6zcQUpd1sld5Y78T3c8t9gMwT8wE+A9kjaWqJpLIjcwBnj8W1xUbsH FdEluCtjdXLOka7bnY8c5Ffo8iqXS7NABxh02Nas= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:01 +0200 Message-Id: <20230718105210.83558-2-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Tue Jul 18 10:52:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18843 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 403E4C32AA for ; Tue, 18 Jul 2023 10:52:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7BE90628C8; Tue, 18 Jul 2023 12:52:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677542; 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=gA8bjj43M8lFwaRZZP28fcH7NCI2CDuCR5pNMFuv5sg/Idcgk9ehMyq/4Mtb30o8u IeRyYkClD7zgIqzGa1uy3YqV3tF4tq3RA7jvMjVb6dymGLe2BmgYCMmcereJ4g6EmT sQQYLTwKyrSQVh+xumifYMkCTv00AULdhLXu/lLxlcy74m6UJoHD3GQCt0ULMmqWiV OXDDk6rKKXqXXeW1b9RWlEyWNysAeInn4j43gSATQp6SN5yPFH477EyV8Fl6KYUDF+ 05GTVSeQolrTnwwDDJvXPKelitBPIHCRgP9wW2G0JAmMCuOntn4EmkettGSs3uTsSo 1JHENeANY4LDw== 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 93304628BF for ; Tue, 18 Jul 2023 12:52:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ozf0OSDl"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C3D39838; Tue, 18 Jul 2023 12:51:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677487; bh=yzZAi3e2eilrv1EvD0hf800qyt3WTFM+D5evp0TGr3o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ozf0OSDlEZj3r7syPLt7jQW52afTt7vcxS1xVjxRgiYIeJOCLoDSLiGvB+kzErBhe xvlpxQlRh3wElh33I7NyUXE/V2dYfnS1bXD2m1q/lRFqG0HElPkSJQnIcyZkYguwc/ P6EdW8XaxxvDVgOFk0LGud4k2G76qpOAQiwvNEhg= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:02 +0200 Message-Id: <20230718105210.83558-3-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Tue Jul 18 10:52:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18844 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 785CABDC71 for ; Tue, 18 Jul 2023 10:52:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 286BD628CF; Tue, 18 Jul 2023 12:52:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677546; 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=ll4XXno6cqpEJx0RlDG48XD1miIhuqgnz4Ux5mWh+hrk9xgupeF7myPSR1NndkKg+ bWS/b36AKOE4Cuvkt2QoJGoa6mGsLM66zCAih1mjFMFdjRrBLiakbIuCZiLct4z2It oY7hZN+D6A/GdTmkXnxTKdaowefaZaWHpvV4dgIHtSlqXccNI0doL9ffphhjgLbHtA EQshxda4nUdR0iFX5lPCZ1SK1MEcuN0IVv9qp2RWGMwrAizYhs2Z2u1BGjg8eBfMu/ dciw2VFcs8ZPgzHkaOZEa2bj7oLynp4fVwi5IlYEVxZxVJoEzorHxopV4xUyZLB2zv m0G1LOwsRyxjQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 44B3B61E2B for ; Tue, 18 Jul 2023 12:52:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aEk1Yk60"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8075B8D0; Tue, 18 Jul 2023 12:51:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677490; bh=y9epwtLNk6aBc5bER+lj2dWaexIfG+WAF18mvhsbzmU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aEk1Yk60Nl+qkzA9UqnlW0QdTE/F1d43u4geHVWiYG2z1DbahKW9k4jKsr/fhGh3O n+IzFrVHKH2qbp4+Kq3FZxrEBeqtsvT64bk5QPMNmMhwXYWPo0KhV0drFPGDXMPdeB Vkgzw1teUNrWL3wkIPnpVXXCJRYZj/wEmZyyRrZY= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:03 +0200 Message-Id: <20230718105210.83558-4-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 zcmeIYbyS<(_AVOSrG-Lq3c-py30ge3Q?yuccZVW{7AQ106f5q<-CH0~io3PA)8fua z``O;-cgDD5-*M0VYbPV`$h+2@&zkdDbFR7Gth`oJk;B6##|8iZco2DM4FCYC7V+`I zL`Sp?$+sH<0FU9mTF>1zOucBGU7aj#?4h*oKF&~DsJD$J0N_10nWpPW*_0%6&;5oP zxs?omPu~e$^kxa;T^{or9&KN7WgTwK)CZWK`1=5+XZLrTDfizC!`76Q@;8!d)|u*O z{5O^BhE+R)GGfjTZ^?e1ehs*9Vp%_(IwtqV8z$u!uRPd4p2l*jtT^TfcwF>#nsoJ= zQ{Ue>;FfP>SMnL}QLA3%%Q?I3Z&{oRcENWGNkipHhmH>7m6gX?Tt7NSvyMsPk6Qij zyx63j7%GTbm%GpgmkC#RUuNHjVr2-Xc5v|FyZg^b;hqMr81p`%eerqwwvanFx44&+ z;EIFw@K6kD*CuH$X2p6n{p7o5-0}5pau!8$`_}=y+X&XaD`UpxfK1`bcHS%ETZ)^S zou>0IMkgs)9)XJ!^XyArlzp`7$rZ1Pq1ExlIRmAE6dj2rnF-z1w|uU5U}R?ldEUw zh6;h)5E`lewE|zGG|e|H5m#%MGe1BkvWyR@bo-TYGAur)6lFbR<)6@$=gv>igz(o* zxb<@vzITHoe>RqUU62P;WT~qivn#49NV6-htDj4ELK;zGZy2++EBC6HC2Ml?+zeid zpfUaQ?n{4eGqkcB|h3QKg*2?M~+ft_nmV7F&IiF-BulZXwTc#8u zRq$nkrmFC*s9k04-H&U6>J4MPV?j;u$5yKtRYf0)ZY&j;t8BN)1G(NP4T)!d>tA$8 zb9FZoc;|z~w_ZUQ*X=>jXx!d?fA9gjSFbpk_Qu^)r-Dz1M;rGJ8_^o)1ia_wOYH-5 z5AlZ5s|s-*X;Ey6z1?mVQor4KymNh3Q2E_OfY8OiQu*$-QaYQ*ByMC1vcXw5q`A7@c^V zL#TtMGQ9=m5Vv9j<<|)3OTo)WfE8EG}dMCoH8*U2| zo`skf^DKRJm2LHv^V)~G4vr*NzRDAGJ(kbhQ5)!AGS=WVZxuto_#e3w52Sp&Q^@Y^ zzsaEg)SV%GFpAn+T66Gx;4Ihc4ISmQ#0Kx)8?h{9Lp2cZr%&Y}fuEW|4YULbH5J@N z6Hs(K@GeXG1=qK#Yi9%2*lh~!!LUXNeSQuKD;jpZtEhBJ=^lJly0hopG(?+vQJPq8 zx}#`d6SZzFM)o+ON!U&rZ(>|Tr5g#MH&JU^l(V)+1Ph#4QPw|zvy4#35z29D*yLpG_?3>$2#3NPs)bN>EJpIn-7q=&M{^~o^ z<89eRiln&a*$4$8gYU8A&*8!v_IdXz?kHVbE1|Ma$Mbo!(dMvC)~NUL@U|;I;6}rX zToU9X3gfi`MN@I0M%eemFZ=*0tkeB=Ol|oBxS1$G$^`&bh;g>LPvuqN>teb0DrvhD zE|!o*?zV?n5_|2~?$Zqn_k(NB`l{^6j1gia-wYZs1aZ}ravoeHxGnM;6%UV?B3~5FY94@%+fP<=*d&Vo=Hlyfw#Jrd(eTt*yYTC7i9#F%xx-T+muaS{Bv7E{RnZe4Issgmz2zS zI&7lM%BOSDI3?&RvJyYY7zoW#WoI*1UIrbOa^l4_^1{`}utwj6HyscXe8a7IDMaBL z+5STr^(NO2SW8RY6YP57h4ypd`jHfy2ScPIDK`auj&9cr17HqNu*6~OmWv=kzH2rR33>O@`ZWE$6*+d+P zdpjC!UQ)g)``?5bKiav0sdS9AK$x>?FBrL7TIW9)^`GrL?Sfp-%Q`{QX~W-;re42X z`kqAi{F={~^HJ8ANF4J=Cu=ogq(qboNwbe~`YAM#?yuSu&L8t6TOLlJwBm`#xsNa8 zO>h(6$q$%etg6!_bij%!f-QZ>f`$CYNEZCjFL>3^J~^w&D7kSn$t@O8X*s*?jVTX^ z>Z&Y3u{UJO9vC_y3CvG~%=nx%c5Tt!FBDr~X1H$JJCl=vK1mdG2?o>Vxv*er;|RSc zn*!nKgzq_3&a<8DdL^!gVJs5)Qwbf~J{w2-+LA%qxQIHB@BiqPWGqR^aWu;7g$dOO z>G;Pu!i5Yi$V?mUZ(X)<)au;Bf^)5r*C9yAC!bN?%zBvu6yiRFipV_=N0zSB{=Q#+ z=;yMUbVBh%jX8f(&d?AkCiPt}GFqi@%8m3+&G(dK6{+nh`nO1tk3OVDk7dH9q$g5> zPTn{_D=_$hnU6Bl`Gpt=?##^&u(ihJ8tl!?6vkDg z6fMCsfGE~D+Q+mY{e-fs7&h=I;hTQi>n@{`o`*+EMsJ>i4~EKB>PCjL#quk!-;@X@ z#$pT?Om%OCQ*z>^Vv|KRq$ue~fQ#>oRWB}fXvdy(Xc8sN!4bN;}xE z18lw!BA*!2A3RQy4qkX*Kk?C@xcq$P3AiFeY{X1Q^9{z+1IJ7==w=G=kzr;9qpVW^ z-o>WeL{ZgC)RXiU{c3YXiM5GMNfLu9x_l@V1f|z^s7OQ!>6M^itXbrjVr~6eIc)#> zH;#-g&HAdC6u>+!w3@EUgb@L1Vy#VWa4zk)NScHImiDp!^u`bBaaa^9IosU*sqfrC z?4`_}0F7N#Gp78h-BytrY`o{Tf^oF~{8)iX4b;I~*h48R_1+u1o7z=zf!0gC!eiml zU?66Ku^an$92FW}C92hr#m14;ASAunu#+Cj@lw>dG6&y5x?G#H#k_;FPuvDqCZ^qx z2UTr6I1Bc049vB#MPR`f5>%BE*I0UWk!>Ff<|IroROl%iQJxi?@WI1FFo|_a9q<{Q zsfhFA5D_(k-o+m6PRn2**WvSdV{CMvVYYZB-9tq}_r0fh3D4!z8B9?+>d`CT)qD;h zu`Z*J;x2eJ!65S7ev6BU$55%#^li>0RL{u$a~U5$2Q~>4xwUdn81@(RqLvtj+$A=B znxwi5sYaWwpG;jVJpEeu%Hu3DzYJ~Tg;8H~x{C@>8{^CDuAcIWNH zyP=Y{Ji@j@UA3cgRC=8T#!LpNqwVa)J^2C@nqk1f3y?8ASCW!x21q*v7h#;)hwYmu zNDX~FX~M~avNx6kKZNl2Ev*YMBosg%vDv0>*@|N1NYbh)w9z7S7rB!VvzNP5VMtQc zP-;j3e8O<@Ge%K~IU58bs)>pcaTDlkM_yc9c3Jo1&V|XAnUK$f4nb*KNkTkE=ebvs zCbt(;Jwz~3QHu`YejjLLTz2*XCH1-9CEE@@VvU=_c8Q#o>RTLJd(Z~K^1ZwZ4L&6{ zN5ktB-iTn#=j15fr+GqQmroN$mPz$(0rSk^M|9n1JKHOB+uEY>2?@oR*s1Wy7 zxnfOZxorOD7!y(5Z{Dxfd(~0CPY~WBuhLQJ?9Dg7m5vmDN8;thT{=qdP03a2`J67- zT>yl~n5~`n0L|dyB5n(N0Zt!AT1GIuLO%Kwi3%Uwh#$s=fv!o{#T(Ot>gb$tKwCmc zP-7>W?5|dft9np}&d(2gW#N~Wd~uea|CB?P>cJz&B>5S$uVWe}IJu)CUUfy|D@N3p zksVKL9dVG1BI->wc7wgE2WSKj3w5lp-yqg3+}hFFwI1l5(dN&-n=3$C_=Lur1@>$? zyg|dLCzP3X0{Uq2dy%b31C3r9C{n~v3=-Mqu7`+Y{NGd3Hdb@vL-?1HWAz_*8$U=X zxh}GF-Yy3NYxN(bD`;6_O9vCHY+ZOf;%EvXc@eDkT4_H(?U}suRUMf%E}7a^Jmd&V z(5C0kTA2{6L64texAF)>Nu_+n^ir@k>K=HnYYeE_+GF?VI7IfN?tdcvGBKP22h_wv z{8Cf$d05Fh3)k62j&_*aIR+Z89ihD$OTyBE)1+;Uj^Jepg!}^oU33lO1Yk$7#4^D( z*>-XYvouL!k;*|Hon!!`Dm|%plKqLNl>1}uimq)`WZf^t0@&PH4AJQI-oZ*b{h+PB zbazsnDuMoo79AAv1G$y&@~F^z@Z`w8e)~>tx|P=dI85YSca0W*A(13mf<>vZU8w(# zKe?tru>vbTBT8F&*yl7YbkiCN!6#JXU4;fT`aY7&D>7v#67%}(pNF<zOLCGfDZZjWY z%qjaranalP*^NV$d(ZmImhp}Zhr5FlC#&=h;Xj+7+y-R{9yizo3D5g`G*9u<()mvq znU(2p%s>c~tm~A-DLM)sWnQZVljOsiN~;el?!_}dk#cA3EL6D8^5(p^dB$G)2<``v zGw5sIUC}usWGO3ai#ZbLoySx+BQpY)c6a_T2rLF-a33#9c!-{lM5Vf7VFb#dSU4+~ zxbI0U7Vb0<*_xHVxD%zr6$Abn=>oP_+~?W84TQuR z(tS_8sBAsll`61oM!tFEDWS`VYQ5A7c4kE#%RuuO)v=L~^9G6Y@iCG;W_=n}^iB6g z-y@Cw4>w?WT6SF0jRNgK!Z^%KDg$MKo6%IadHndd+^`J%&yl|tZAy7-j z)qd$S`OV8bz{mOG^fPW~+eP+bc)dG2i*yY}35rq7bv4dxBprpdQUAmEmNF51pHC;W zDJ|u)U$sJ<>|jgRPvX3F8{9tV?3SD3S&iMZ>I={sS0=&&CU zZRThaN|4&7Cj411b1g z5Vn)IRq`fSy=teqvws__S)e`lTH!wMtTvL>(!ZuB3_6PW;Arj4@Jk9`p3~I|^YLd> za>q7)#pCj}m$LqG)z-79lM%8$z9MJ1k_z6q^A-5|`T{>+r0zU@QsfqAFkW8xDlJ~8 zOPp@LE7ynpurFcusF-D|PrNxG(WU%}`c`3XW-ayVLXX)e35UW`SwicC6fh?npV7fCH>4`+_~~#_N_=Vn}kW( z!`#uYu^P+~1N2-uWDZ~5Q?V7*bB~Ub`Yf3Rb4RK{jpQAY?q_1)wlubm( zSavQB^8^P7u$|BX1MVj4s8(art2~=MpGX5y&!$HE)H@!Z96XTf^x`64w)<&qH<@x@ z!%WWGJUsyGL&7O^NO~Qom&^OObNX1;&x_mSOKF!ws6)eWeF6iQ@**__=%2ig#xp`g3y~qF z10FuOCU2BE_CdST>+VeD+1^+TVZ!QiQ(VNrR55eTZO6O^kibY(HFz+2*v^V{D1^0; zRakjgI;mHd@GXt$par2qQ~`BWwZg$wFBrN~#7Z{Jx#PIkeRdAbPs>|KKO=cVyXVKZjmrerO?d5l$kvvf6qX8{ z$edM$Z!8Ssb?S{lUb(a3jBYr1-wz8U1iTC^|{%?n?JHvRTUjHn|mS$vYctQ z@%dVj^T<>>c=t;Zs(`4bUshGxmg_OA+Yh{-pSU=`_z@mrwDO8$w0p5#hacW~Mqeg^ z?wM2_Q^Y}W`m7zufihh;g26AQdG7JP=~8|fuH+|#U*u~j#DyCstZtz!PksdPm_W~j z_N=S&xRfT3=3Ys~XX^R!C4IuR&PycUw)^~HvSs8&tyFO}R&o{V+uE%z)XkwTxHPK$ zGj#Y|SU<3u+EiL^IKR&J6ObVzDKXbx2u1Y$nnn`)+5Rl8xaFfyZIc>rPhS$U&UN%@>tcNd02#6cvj9%}sipNH;jCt;4j_>|V%nG!tO*MpvPWKRV zy94K7Ju~dGdG3#}(M6XF`oL5#RQDh4yd-sZ2s^`+dd*HDid{WF18Kp=B(coMd-Ze8 zV0L`RIkpAOLGN(1cD>neOfz4&vVwggrLg9e(DM3Pafg@Dtntv+xi8Vg!wZ5J(i|_2 zeNnBw)B5nfU-5A={g_9Yk4?KZY8k{#T?Xgy3|}m%2k>YOM{blV`qyAZiWOqu(W(5b zyCbYI9OP9NH97`&-|WR@5yaj(JGt36S$ja(={Pft^!EoVvz$)7+4_qLK6@p-)jRgR zi#-9k4`NHaLpmGFuII}2Up1r+`eHj(B6ruOQNmQ70!7pFWc3C5G+6SUb;xf?d>X~y zVV($Y8!JdS%Wu1D?*Kg+Iw^Gx=rBJn^f+qa;SSPg>R=`M^QNPesML!8n$lWkO?BZR zkZkVRvZtuKs=xs+AtN|7jsxqdK;*Z$Bww{iR)`B4UEo3c2I1KI27^huQOBA zdQ~j>+-_S;*_5wekQJaENAyGrFI!8T>8)HWisVM5{~Ytp;&2f>In?4uegS0xX+K}JVGe?MQ=JWBP$ zGbH)shEdnD=(NmC&$mgZTqAtEMTvkASAAzph9LZX>H7YOY*e4|@Ry7j(@{@$L4)M$ zMK1N>=Sfy=8)8}IeOY#fG}Cr#u~jlTl?!zj{p@!0P1a2Hdi|~4YW+ue^IT$Lmb5qG6qp?iLHiG)hkfx=n!60G^(dRT zp2dFHyo6-k zgGfpJb*OQ}=njUfgctaib;1i%1YDrcDO9%YeE!<#7zmQ%L;{EdUC-~=y z<>a?kRZ3F^Jq9r*YToUTPqBu&hJmYw?$5uaKH!bdq<>2o^%-9WoRX*%`1LFkj~#dMnZ0psBxTM?yFxELAn|4Ok6tCCGY&g zw0&AnB8q&~@G@$*zF-e2S-A6C{FC%*`b!(^aO59>vdal5-3=1Xll$d$vEvxDQbzfX z!J$$nweque)Q1QAd;810<#&OA)D74AGDxEW;`GVe264*tTv-Wh?&QE>YT;xC7FaA(MmbFLTULp z_&B)OWxQ=XdFaHjX+>NuEWsMmvVTG#?nLRV-QAtRoSa@>UL0P$98RuQoZKJ~h?9$l zlZS^LA;IqE%5@!U!p4Ww5l9xyLW_Akw0Azs3h!IGNj6fPa5A zg+c}Rcm>VbE%^k@*!e8@`Pe~x{O0UtAP}E`sX3I37sUN9P!LBqcT-1m=r1S)IEM`a z$BfTRP(Z*G#Lmyd&(F@sZOP4UW(E~x7vK^S6o6Vl`Gxp+{{=$T)dmrjruP48)h{Ru z1eBn;DK`%YWXf)C#*eVZoXdjUl-oj(9mECV7vMD)G`BQ2{|#kf4wiLtbudLtr;UTD z6_nH2(du`{FT%mk)F7gCJRDsAw5Zvex?3U?MCp`l96h}M>Cm!qfNHv%{$i6`KuC~V zkXr!6%PYXe#RvLFNC)cbhKR&pnA}_(ynMfVeoYG);S7RU(_e9l0QlXG@C7X83N>|i za@BHjvKOWMWfJW#&A+>q5$R-M>TW7+>JCMKa`Etjxdg!6AT4elFrN^Zo12x32h8;^ z@=g{umOlSa(!Y|2R^*Q5q)kgu483_s6Zh&F@sArTv{0U{muyTyQh> zgj)Qz6M^+dm$|j6qZJgfdi)u#f3@5EA3}jgz|;aUYo>@$5U^n96NGZHgSajE*m)7G znpuMQp*&oc|BmkFWa;i@>I!{kh2Rmv6(T%;b4APid!ktWUE0eU`fC;tgt2o8u>V6C z@1KNm{^>C1uQlT@k3~5D2PY!G1^#7`LFoO_hFH81Ya!>~i{YP~{aSYZFTVbqi~oxw zAfW#{$bZD|f9d)!UH=gS|B>*2qwBwP{YMP^N5cP&uK&;I!v5!R3hId11$iMJmMR+4 zr4bKW=w^y?(g5aPZyYj{$A}gzXL&t0#M9aIuMd(WbGkR85yKs#EQ7I$PK3>cS3o8; zj`)ut4n+EymiOHLqF*hQb^yi?r=_^Ch~U=nNDX3Ux)5d!nsA+;&qyEb?a=hrd*;`d zmY3JXl-EzzD>?)uPUXvVVrXa)B0WvgU#xmgDCTD4mZ@6Gz@0Osssfr8PUo?jHJybrtdR5fj@p6;}lq>G|Dt zVp~v<5iFK*Hd|T>MApF$cNnk5Ct%Qc9I6;DL-gw z9)%4}oMGk{YkP^@wO0euYgA#w$`36lICo*KAb9-%;|MDG2uV$Rs{nkbn`_>?i65?* z;B4~JvvF+V3Zh(oC!rS{BgzdYZJ_ICow%Z_5zL(N zvh7;KXb!|4zZ@3i13Ls>9s}JHNQ1Y3BmiHqT6<)?vzU&#JD84N(E8z}%Lvj+Z72yp z!_6O+L`fThREubP?Zc6`D}bDfU+mcAC&}`*`2#c8_8}xJe&qpZb0m7&nx(q2gS~$E zcmf_Ku<>}2((mVDBRFjs0>}5)Z4}~3Yqt>N5yH%l^flFZLu*odGq>TKNu#Mn$NtS) z)3+?3hK9Qyp+bay@0S~6#ceA*_7Y(jWy7P&4nDZ(sEvgpJA6WTCHnfvHsV2aY!A%8 z<$KDZlP(wneya=yzM-9D_AlXUz7w2Y^@36sG=u$wN35kVgsWgdH(JV0`&xJ zc@-?G*DO|gMB?y0#^@_ZtH`&>tX?nnrKq1J(wMSYTLLm+)mC|_E0?Qil5A!6K876hL~V^1!HQS#Bv`#Zx1vFk5;fx|@B<}}uYtT?sHST@u@&by*F8Y64` z=1=1jVxBc=Ml_sOr0rkvMs!Gos((~8E!|^6s6Rwz@46AZA-0GET^sKci7$or~UcO)-T zl*D>B_Hey!1~17D^2%^D@y6@(mHe@(K3WYlR|c`H^7Bpf)#Iy?^B{kzY2!kDCwtdywec?f|w@FiHweD_~(DFzb*4yq@AerYLsj- zz1NXlL!IqF4Bie|w71?67?H6(?fgju$ltLHlaqXQ|_MiTsOwO{{RmM7uc= zVQslM*t1N6-OjUC&eK>g_pTxS-8(^ib?ao5ctdsPF%bs%+X4RljlvziF?Xzs&EkTp z*&GDb3PZj@74+BN{rGp=7rtZdgVd97;qG!Juh~-dt_$o_s)WavROkz*$_}lBHSW`G zr*84^6;pg7@-H=Xg5JbaC6sC>73}3~M2)xq8LzpYVH*B*0Jj0jpRgfXY^J4k}^AJy9#PT(ESQ#$S%c>F9Sl&gr zLqp}s{Ag!j}ZeEz7 z(TM)FO3C1OkEi31U)Jn9ePhjN0_(n|kMre|fX2Mc_nW{^2{0?b4wXRq+cW#q$@G zI$V$Y3Vl9B%K4U8Hx0-P<7kSz-h5hDgakr64~a+d8*_Tyf)-J<4vm@vCSkVhF&c9xj~=X*_Ad)J+FYYG zn&OEHi=nrwSw9*)2$;N}wJ{uC)-QAB#GwSWndugS>mQ(qY{flNODkjngW&jW9lJ-~ zk*tW26{o_E_WQM-jEXP{KUgT$mc?L$xT<%NB4XNuyqKb@omKOr`z0CpaU+UGkY?*a z?^VTsOazmg!hdSsscfhMsm^ufMt-YwyZ1HSU}@sr{*`~T@JAjccx!_4u!|LJH6iLj z+taFg+7XV!{^X`8!DAU>**Q~4h4QTXxiB*+y?s_fjNDa+O%PKN$M8D3fqfvNOr2@f zp!YO-`;_>7Qtmpt1ghF@3ryfaywZ#YnZY7(4?LQw)3#27BE#sMeGSq0GwYtsweMQV ztebWAx4YD;LW_>j1h%bkt?`JH0-zi#>DmwAGMjbzV%%C2yZ&LbVj)efFxexyGck8% zdWKd}#0C+qgNteMqmy!|%lRSy;Y5f>d$#4Rn1$|`N0i1{>rf2jtp>+TS)1ms%Kqhs zjc9YaY+7zlvU%^1t%O6aq1u`ZT2?7u@vE%4pjT5rTPI?7P%>&7r=QxTfmhRj5l>0& zt7MA7F?^>F1A&vydgB3P_nQ2>N9rvx6(B}o&asv~n-4G^>`GxV2qK zW_#k{&(XB$>x400%`=tT_p(IEiZC{Hj;(~tKT2`VHi^Uc-JraUy04m^1qS!Ne&hX1 zr98(d*~pqTc|W3dsUXJnXqDIB)216Gno?D+ftQ9|T>*jUZmW;2Bk~~!92a|8=@ykC z)7NT4%Ypi-sra`!ZaRfNVK3toaAY-tE}9F9GXv*@UnrgVZ)4cq9Y``*&YYRsNWvw?ub$ z6KTu0Wz{^m7{DerD1qKVi6K}08Dy5X$O}5ILQhk4nGuy3Z|c8myHj%AgX{U2GJ9S{ z={TC4Lc`;!pGJwFi^n|y`bsgRf?0@GwYbS2@X@GbFeW6&UxA!D9NAqDAnkfT*1cDb(DKcn;w!p2<@X_~0aXGHGar-o^lbssV*%s#B76Io zOO|w;?K2>!7KaJzenzzlJq2umkRGQh9AEXgGk4eTWrJE%Fp>}loSlW@+9PQF10?9x zTmePBAOlDr(P8i&QP9T|bL2D@r>H?by zCwUK+hb9HLvaocVko_Y>T1Q>+Dsr((jmB9PRx%Q9R zXUfZt!AX9$utc=-I)nID=_c91pElIQC-00)?JHc7sJK|+)xh&Y#9~gZFy$bezh}9l z4Z%kc7wWlk|(1uWz58QsowL}uyhtLx)B9d#(ZHGG=h^Jch_gN$1OX zGR_(`0qXlbi#a&O(l+EWjVVIwojefLCav=H?s+M$$-rBA#OAEG2eCQRsc`)r( zkZ>1WY%&R(?prIXT#oODWklX@9ONP*IM}962$~^#<)-k~q3~VL*v;CNd7Dlu#tp#o zYZuGzyt6rMGe*;NqcE-co0SbU)>Zgh`e2`|x7SLyQ`%~^8`>u|0(mv=z0lB&2ZPT|b z6x+q8H6Pdx>ITv*<=UQ1hPeTvllltnllFea)M9Up5aW<)V&!0pxIYFL<6Fq&Xk~Go zClpp#2hDF_wNDJ-g+Dpt?wolZSHdel{a?WrpktF~Kv(t0b9-%i?s%03LtrMv7m}kt@92)0lZ` zK&FWW;A*mBy7U~VIH3~g7Zl@$$C59(JLoT<5d}}BW|fThhuR$Qxw;kz9S>x(q&K_0QKGN;d*XX_0{A3)xc76tXD3XF?FL~;3&UZy?8=QCdty+tVqsO{yA_b<(M@d$wBUI+1&PCpf97|Nx6k-1i0 z42}eJat*1tYQV%H{Go#zhy=+5S`8_<>cKXPr~pO+tndeIQ^^=PNLyu~V&6pj^tEY$ zt4k^Fr(ysqyBvl|t+{ESk3xw{ZF|<5QzAxi#a8UzPkESbelybf8!c5w6p2lJYoh%W zTdiC5)2~`6>?rP|$FPNb-1Ip^xi}2p!6JQw;~Jd!o3~QP*6*Y=J?7H4-U1s~RQ0lB za{FK+Ww;ViWtt(bvV9m5*+{_>D1evf0A##Buc+hzOg_LLS3+w5?FitH>mQ zc1VseBb*NUxN#Dq^Av@4q+l2WwpWOY;$;LYWa&v5Qshx2L*E$%QY1XfMjGGsgCsHc zNQg4Q*9W+67o<@B8g|NOF#bywG3WhS98%gAF%Pc*&_{{Px_BJ+z7RKtI(bbAN@y+N z7a$0?O3WEOP#EFh03jnR`b!`EAL9`QBV72~Y=jGcoBgi}|AC+XG4`Jdyp|OYzeEZ$ z`GoQi&+A|nOuE6!G+)Wiv%Hj`*PAiOj#1fSA7T01kxoEUgn+-3e03WS?!v7C7vVWD z6~Sb_REz|L-X=2=yJx~fuBV2ssDC^C|A!BD6%V;Azbd$9^VqMHL%!;uqmsK1B0(|> zaMNdGupXoRoB8PQe27joyuMuwPS-;D5eAs|M=T&rmf^ZzZ8f6wRNv-&@S z@gM9zV)$>-zXHHS_KO0KE*au?TwSU}3mF(cWQy@p7DkkahVTdufp91uwE~w_kM@BW zOduw>K>`M%y{j8oc&cwX3^_Uh$}xh9NFAMaZiQ(_vL`cBEv|uws;=v@;`h%gJp@{& zT~ScUYv-<(#CO3VCqZW^JTE0e>s!F^5@68#wV9Z}K()&7vt!^39Y$+{#kicM9*A+` z+6^!n*u(&D24VnHad=8e=vLn=*QT^2akfaO8H3O;5Dz!0Xy<> z5fXpK_}|PRV*D>h|4fSiI7$#{{||}(TNXvXY*P4vgTZhq2z~71H+i$S4#B64-(RlU zuwnx0t&CXbFD7I!lyNT}@WNWh4luwwuOW__uay6pbQHdd3`s{7*YHL0Bz#Dc03xFzT_I@_^1lFppjqYs 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 zcmeFZWl){X);5S+(2(G+0fM_b1P>luH?VPcmtY~ddvJHxB)GfF4jSBo1)G~Z=bZOB zGhfxrJMVl|^W#8u)!u!tUfq4IwXR;>eM9(rMQJn?0u(4HC^VUO5-Lzo(DguZLqY&* zM&EVnLO~HSd#ZhKRxxrTvv;&J16za0oIUJ8WFU9285ETJQbGEn%NAdZ#FJ5PipZfq z?}u@GjK2F8Xcfz&4uKLKYV}yM6gg7~7>7tF($6Yg8DJ9HXfk$i!y zTC`iJZ+mv%=a4D)bE=o6A=vFDtD5_)+jbk~Y|#6Z`DVWL?-~d8^Nn8>mk%2c+R^z_ zjO~Y;N0`UZcE{6u%{5m~&kZJ;R+;YhiN zdgSu+`nGo7)KE)Z+0^8h8DEq7QJ^TB=%e(Z?L`S?V=bpPcqZjy4*baLGrse&x?xVjg;yY@c7PV2e?BAR0k0H*V`A1DE(Ch_X zBd@)6+Pq)`QMa5(Oo} z>nSU{pi_LKo>(ao(9C3Ex^EyU`>4Qhc^zXLQo(BtUGTQLhMok4>R&vo@^ z)+$;@*ykx^{&17iO`rhwqHNxsdpDW996~i@&hv@h_{=@&1(F4AwqEZ?$QJMIVQF`E z^k*5VTWf6(RE&=xy_Kt8x4u=9#NEgEb$RrY`<=&GC#lLixpAMXWljBbdht#7!!a@Z z%X8E9F%i8qS6w-gWI~~#_>}oWkpY%U;=>CWYx=PK#dc$o@QY+pM>>mm#O>9_6gPU2 zV$z2GnS-m5OnjqNqK!k)=gwUchP%bz1S6F=U8)FJ#oU#K(IdB&@P$$0B}3qYx*zaN z-AFhoyO8eTG{G$&KUY8dwN56Xu=HkV-=izf-3R5J%j)?HVT=dc;CbG5;j#NWB46SL z$DO%C16P(f!rZ3tNqj!rPxbsCHXY6u+&#y!hV%*7O-{5dS2`~i1-P8&f^DkU6~qKj zh;_d`>0#r2@RJ^&#SwOqSGopYOV3~Ke7AjNZjbLB)1Ugtjp&$hJv^?rg8%DV0W}xQ z~PcsQ^5FL2=(6_a@3_YLy%%unA)rs({i)H2Y(o!={Xf>D z?8i5bly|yDHjW&m5F2Ho6k*&8iLQ^Lb$=796S6I8_T@Ghk(HYAv!{KyBl(QN@J-0( ztt)gCcZPFFLYrs^%QdoClK3!|C0{6_caN)qA<`UdkD_LLfEtUH!u@YX`>j1uQNLLH z$OX5vrF%+xo|Q_WeZ``@NW)o1h4%=ZAk(NO!H^Iq_1EMjE|l+IHM`x|5~< zO+^I$p1dXXYh-FM#^!P>TdO<#AR(`PCTx3@~xsfa1x`05qf^Y$wsMiEwQxkL1#DB zlY6%2xhk_wiTj)@BgA&@M@;GwE%PPb?hpP`C!$O3D-OSH21-&eovKu;BqWaAaC#zm zguXTzD26#GN8s++%zMzzyxR5I7Ogxg43{=?Yln@K_0e3)igXO0Ay;mhs2NjgzG2{9 zZ%?DuVO5a*6d<5w-pXHT<06!RP&T>_AIv+ruOELo`?dMfeB}m;a3_y&-Qx__);<@J zmnEzkHnIaM^cI=-C9I~b0gz9zeaR}qWvKamN|h39Q3P44Cu;?;JZ$X$oEEr%Dl5F2WLcGOuj z*whWEX$ibdF6(N$xiTDwi`HF5T}(j?PTVKJK_=pRmGjd#_e)MOUT4KydrV&Ea z^_NT+()mh>mg_kM!8i1Z3c|6dQDg(n!&XMQAChX@#7XT5Q)@V_KWbOWxZg5*2Va=Q zM$M=V=ky!&-i6w0;%>vW*Pf^QbKix#!Os$(>__K!p}Suw^gOxbaxf>JW4zdIs@ma3 zyiXEBT20M#w2@aBpYss2#^ONnPd;BVu;ZcnX&{96q=Ft{dSF8szfTx`!DPZ)ORpkU zcc7RAN4fEZJSuGX%3J5nh-PA7$w)zummgWKCTHh zJjUC`+fbpkOnwPRoOc|_bw6bxrg5qh^#M>RASyNjYKUfH427~>O9|=VcRU`Z+`Z#> zt?$OkK5jH4robSR2PwgS!0v09zfh~Szq#ykr(OI!e!^H=Jq)hYQ*yawBxV=w!>`hZrlPi@kis4{vRiDG!H zcp*__wInHcp97+XiIGVxBaxMp#9h`3x8&dhr7zzxnJJavl+nd6R<3>53*oJfy7!Ot zh-TspM!5;Q^rpXOBEKwycWgS+8mqy*Z@--_Su7ME>XQ=NUiA-|l&z6RZm@RQrp_FF z6WdL?Yz%*hMq zkxC0xzn*4JV8s|AdUP_}VH^;^qx5xszw1khRYE)FnWJBn8+rALNtX+JV#m`ZC*vIJ8Knou(bR_QQlgL6xM}le zHlw(kguh@#1}ZdICt-_qLM0B2y~nVPSIr}KJG1VN)k+SXXhMIk$W5KSkUTRD{cwzX z8vu@aQa~qmJ=)#PrCVGz$rsIlfeG8xtw%A8OcStqal8M#;+owT8vPzVkrW*cO3ck-&pOG%;^J2=h6mr^{Qu77(;n9OQIsvNo|UQhR~!4 zqmkuA7&ez39eHmf`Q`AnOwj$~P zN*{UWt_*3bJj9(8+`#!*eM`eZ8DkV(6{_hpFOX*Ny<9Fhc1xflTN^GHb@JG>!`4(K zyTQ)^>*AQ+P3eTSFE(IY;spGX^HIc+67k_2oU^h?GbqhQ&1yEV!k4@2M4b7aGJm-` zbHhqgJ<{kgX6Y7h_8#&=eJ2BIhejKFxx5BZ31ohg5tuAh!^Ph@E8P0#&|W*ai)^WR z!cL9|)sF##jDIXB4ZEsd(si2_{lyH9rRNf0ITiy#Ak+fgSuis7di2pV>>jD6f-bHN={3+^ zJ`2;Eho&q=dIc{-`iDCP!Adb4ld;3BvdYJ?2zM>D1W3EzBDVWqULXfCGbz1W!G3@1 zTUpt1ZMx697fE+75TMj#yCbhSD&9ansc6ytw;NSN8-FII_&W(n=SuGCO{#%P09 zrhADelw)HiR;3w}TU?5{xEPCY8d3PEtLoYVCC#5n2@a7Fc7(Q8vnBrE3sFZx{PwRs zDyg#bGIKN8n~UR2UZGzzwv`8la2u_;42`0vI--06UKOw#yuo$uzM7$qeE!;W$uY{u zjq&aCCA3QT9v#8A8w5(nUD+8EKjblJZa*-kxRvR&>~cxRz|VBc`w-`{vyH`|Hd#lv++G*0Snrtvvx~zq8}3c-DL7k z5W7L?aMi}Sv!_gwYi~{5y4lFE^WnKVaDzC zX72HorjwXYnbGcy=*prvytb~Za}RCoQM(>8dZ@R&v8QT>&(Bp2U3Igs*=Yz8ejn*v zeG_#B2!{`7n33}x!Oz&Zu z7@bQ6!ltO8*&}6qpp8?z*4kIHOt_i3a2sq{k?SQKCzqd}^xKf#zg=3Zs#3Hdg|BBN zv>W?VVWRqe+aS{Pe-qY2w=j@99yG5HjfYlS*NUA!c97Kk%8g#! zuqO#6mL)FV76LWn7hD1FJ$cVkfxzhbPGR)ZWm0iA0ljEcWvxo;idQC9l@<*qtH}Uy zZiI7{WnVn0udS-R9%agJe~lJ`Oq4YrB{5jX&AO}cTr7p%hf`!O=FNE(Ozw+^V?52wd~YAdDzsn*bbCk_J1`C zgL4j#8Ts0>&8*J;_f-8ZorN+@4D$QU(t^SOGW()}I>TklEGtAR0%JmN8F{Kt2_Nw; zDm!Lk9SPfdyNcKW-3EB^R zFVZmcw}tKNS3@qIorBnmFG5sP*<(?|p`y#bw1>nH5PT$zwzOH=K<>4AHS#&Vo`tLe zlRqhJOkt(q3NOx8^in~g+8|$F#3oNQ#%^Qtn7+-exiR09BBXp%j4Ygw2)C9lQ*Q|F z6PcqferiLc(~=c}w|tx%`Rhp~tk+z`zdb)^9#W9Jw>}G9E4r<`U39bx+rw^wd{Dr# zn)$)L{dK!VHg5|vUXMzU*|CDTS~if!vMH5Mq0N)A>}^Rz%gZptYIENjQ%Fc{1n~vA zdkN}5L7g(X=OOD@-F=jT`1HPpj5_~hJC`)M+$_ps1sj{YY(--jazp|=@8JEKnWUKi z;h4!XimAeeGTKva0Xur~hr0bKuL^Qc4|+t#T9x%eSh_EbG_vIhAb;}1woKRzB+Oay z1iTNa2-O=qVLqJZRb0k}-HdVz)ZhuRnIVDA@>h9ev+uvdSZ@0(bc{`W9JYAJNM7Hg z{w4htYbBBZS9upyT4Xrc61r82j0{6?p&?TrjD5)qYBY@FABU3LL80ZS!Ib%qM%n>O z%jcZ03pC>*e15YP`ZQrf+BExM+GKR<8`{O_Pq=9Mc4$PW-VkdNhm^dx5rbz7J&}WF zuMiSB`GmI1>l1wlg&FaRBUs=v!)ic_2M#QMiTz2F;HpbNGw}MBX=1>@4+8x;OP72{G+$ zB+$xj=?Q+P@x#EXmj%x6y#arf(^(PGDgScyZRDqSHhkp}qGBU>s z5{J1<474CUX6!Wb210&W4Z>57%5twwXq?l}{BT5QRw?de(>7+Dsa=BLJzv*=t?HBI zK2mLRH`=Y=vqoIo4JY@$7sE>U13$F;=Yrig4G9igq#+E5$BlH%D z2lXKXIndF!VzqwrHtLiE!$Wm`A9d+dJan24FG5&}rq{#h6j2;y-wWKg-$fOuoEKRT z)RE)|Cjy5Q#*ecIN_3M;#L7R#B8~&;lxL8;GN%JuF%xtW1)0JiDvrBta-JvU)3y=Q z7tu-DrK|XAh(#UvP~)x5PR+)fASbs^=*Mx!;jQ%!j4f+P^E>n>UwOallxo-~6{6rB z;*2cl7iym{6;jeu>}5&2X}c^*ydYPs7e!j`XmgBtFwG*h41m$84l2#0Oo@2Pztc9v zoe{hm3IQFI*-d@tmQ8J~&l>bZmk<@}7iqT&s}bX}c1&!uYe`>7- zBR;Z-uV9PMm`yP-!8(2JCI_S0smYga8$h@dNGoQ=K0;7Vz8p?7xZJQY6;rKAHVE|*AA`Yh@k93tpz~YZebpzj2 zjkpR~>v?VJ8N+D)5)%cxdllgYMeZQ<06u(`S(!+9d}{_-E2g94YH3fqVflC)mE5^hXYO0)8&VP=?`F3V@(41_=AU9hir_rcL8uS+z>bl?Y%tLi z45>S?_rPEYon+Pf9?QZOP%R!UIwNkN2Va4e7Hb&J`d(6xqgh=^&{TK}ospfx<|wek z3trGA#ptECObL`SnD+-eleC%+gF+a?R4XcG;W=UK=eV3#BuFnJNT)oTUesF|WhU?( zS=o&L(w4>|Y`85)V($1`SDQq`ih)WtRpF7r~^f$-t2v>NkX*SGWLr#%He@J z?8TxstnW684c+07MJ{ngX^8albY+4<6bmBwW;<#I*^YeIA2IbO2#|>@*57oMaLEMb z?aR;vp0<`2k}LaOi9s<0lO7YIo4trJ^ulY+N?-}^`3M(~X9^#v^ns8{Mc@llzq^CtO-q|NFsTOUy4=gWgc|7CXZ+o#EGGypp(ALsL;M zYfWqlmd4#KGRVD&`&FIOuSEz;IxStdxBzj(MAR_6zhE`oo|cY0td$%TLfvVV@CtFq!!V z@$02}+CIs2gcTXoO*kzHiS|nT2%`D3!eA3wH7WBNRURkaockn?>$NPe>|bLMwB z@$4btbv=Z|k&cNs<~)sX)2LUG`6;z9?aPgY2iVMt&CKM$w_z zwA|q;YnWs9<2+4o zjW*FpM=j3qX0p`!VP^0&%#whLA)8T)6pIL`i>IS?*fM)iQ6Y2piyn^K3kUt&^=M3s z?O$si-S(pX{#IM^9EV?{FIIa_&2@UoKExN|c~E^W8u z_MalT{EEn$2VbMtr2Gn-_HJZFcfdYHT7{c>?|}5ZaCf$Sc0aFiN1^K&(Y-a@|56$^ zm0M_oM>qKM*mk!bMQJ)2HCbY~p_!0a8w|&5LE1HIBS*_S|5!8R?U}ORn1Ew!{*9*T zDhO;*6k6u_=9RWMgt0bLMEX|k;@tfvtq!B{;|eIPUZy10bE-J90X=qfkXtZkp;@yVhcMuZ!Q4IcIp`ZK<>UuLtA zb|oSsYAtFZS`zlXz@BJbOW*V$xlFSxaL4Hm2JSLHD9H1f*x4`{nc5kHnA~mbfxAv9 zC;?%2dm|GokTaPv$Q*1dNO98EML`BO6{OJQRA5oC7YA8@-+4NMR6P~dOgybjcugsU zg-`_C`2YnrAZH^ocN=S4Cq8#Uia&b!fX~m>%oJpQM4YVzDLyE?Clj}G1d(wtaWJtk zO1gtx*(iii$OIfs&G=L#r2e4-_$EkU;p}YB$IR^J=Emg4&Sd9k&dkco%gfBd#>~dX z2uLtGdDuD|xii{2Q9i5qONRu=$;1(C?+muHC41IsWNhc+EJ#5CJSY3-_-yPI6#k{& z*6AN80C+IF8`(3nGO;k**f9USg_EykgYT2 z-&L5J{Hwjai=*|Q>6n@@gRDU|fT$DDE9<{?DJ`S${$DMgAutEq*#Bt-fc)r7Ffrl%n+hdIFyNI&)_)(>vno?S z6{m@*F*`3SJD`OZ#K>XFZp6q7V&h;mGBM^fH8wJ3V>e;>qsr8TPs+~G#t1Mc*v7~l z#B6VC{^!9n;C!O*WdteMnEn#|Q}f>1$k_~NAV?t(wsmp;FHtqH4M^45=ow8`E*@@H zZdOh<9u96^E{?yZ{in4C$k7S##AltXEKKYif0{p=g%6kv0Iboop8^X0)B|(j6L$m| zIomm^+1XhOQar05dv5u!#|l6=nHo78Nf3cSQy!wg8=sUhr9l>9{j&>1y&wo9%F7(6GkpmP8PtMc)1ykIgHsD z*;#qbcv*~CS&Vqt{$0D1otd+nkt0ad9KaF43UJRqSdqQ?V<@_Rmv*xNJzD_)jFE+l z@xK7$`3GRk|CliIGiUsB#sbX$7fb~H2>gv?fOdb?0pta6A@je;@E@2x)6W0KpMTim ze`61T>i=}|-@^AlaQz3a{}uxOE#d!o*MH#pZz1sC68?{O{eK1*%71OAK(;^@T}P-IXt5~6DEOGm3738roy>rdCJv$F@ke*K~$4-onG(l}o9EleFAz7BK{D`qGp z*f4%UID^Oj{so01U{0&Hp}hjZ3nj0b}2L8%c|V>>>4Y{1Kx0RSubtPzFWET&bG_>j*5=1k(8XQg!qVr zcOCXC7=;B*0IJxN2kKonG$d4H_4}lzhW`P@Mg?6#i0|)SMDLUN40x3Yih(Q?c@GIs z`We)^yU-Y@zhXOo1Y;%D3ge+q05UXax!fq3$I&B{BstOa9)N?Pxcw$ueAVsFjo!78*1?ue; zs0*(sWY{LC{;eRSQOW$R42r~4{k`B@#8Lgdz$(O1{hx|yj!fL&3o3_9+}}$8jZEC% z%JTsB!LTGSB`>Nt7!toGOd-98eOawiqK0#D`8z^dx>hW{b*-mILZU|^Z_-^~c8{2@ zpi7a?hp*t*j%UYXeHdP5W+qy60??T#ABDNM*hzkgYcdvkSqdRi(i&zI4}n9rnKHLppzQJ65b-Tefso2t@UaFdm# z47VzYFILaUpL@IWWy8Mhu^qt zFiQWM0iKQVKlFlUkNBT?!9NGFPXl;@jY+HuK~z|Q&7;?67|wNDe2XbAG9>|0;(l+vV3vt{oi;;F&~169B82XO z5d>I1R6>HX;1@Pz*gdj_bHbJl0?LsQG<9@z^Z@W9m8H{@wvBm@Wto?kcZWg51Bs0Z zpt%feX=#BYdaG|~lv0xY|BG5`*gB@FEj%W{mhh=~T-DJUpdhr9CN z`8=8>=!HNS)mIk~e}+CLB}-SZ>UDkmfN4MR-6+SVNXZq7-#-*Oo+K7c8BrskG<2g` zC_S&+4p!;cef5v*@h&AgpCu9YiNu#K$)^;3k*$Viht#+6NO?G@hB`|6qiuKisn zCl9tnfK9=wrJp3vFUVXpU=cbt1aq@T=ln`_%WsQN>pSbAbYWJu!}Ib)A>i&he{zie zLMy0iD6a=y5{DcGC{=gd5s?EEx$ZWK4YRWP&r&L~oRD3#dhDwfZ~oK5un(v9<*Gx4 z95DAZ_T(%(SgaGje{U(5&Eg~{C!^KZ4zGtnX_VtAuJ96k2n%aTn1aUWZ_OT`OqdGH z9>*Sc{P1tY^-N-aC$8tue{uJA3{=h@1ZxgYdEF7N2mj zWME(jN|Y`5k!!(Rz=5O|aBA_6ZK8FW?9_>_AhaMME;Mh`!BYauQl;rUa9HHPir;@7 zwtQ@kTdm#MSaBqet{~nY9)QOUQ!3)5Hoz?B@*&)0<$DejJIp;l1my3A{`t^LK&2fR z;C?OkuMcN&tx0;HQR=XplO~pCI`-_)j|!9O&^NFtym1S_7sd@O!GYeXkd*15fgbMu z#?^5n1l5jw!vG2I)=QM#9tQZH*T2nQOBgmTpQIW|7g-)^{7pn{G8!7%stWugCCd(L z9Mz>i{cX(zuVk?TEJKvkW|@XXT)uuh&#!lHpqpU3SfLInh1G}(FWB=CswFzPtyF`l z_9WCyC|9~19BeccpqP;%yZ#0+kJK%^ra4q-*U%@hLg*v0Z4Xp3OQ?BUI+0H(JWW*T z1l@w&wEj<6fafKt5XjL961%IP2@~x;s!9-S9miF{*oB21P#OzS5RkaZOi5XA5CF*t z9VDdt1PTKhVfmF7VgM<$-RCeX`oy6XpQlEZ5>yxr7$&$*>CTRYH;NT36{obxN6nAflPYQ~PYr;W&eSH)Zu24z{ zl?bk|Wj2rB5UNR=vD`f*Ml{OQX+OV<`>frdO$?=oppU$w`%3M|(H!dr=QJXWXD^H< zSFLO|AS{JkSo2O6OaG{}VKymWfSICCT1u*Xj!NcWsl~~>j^2ySp0T|Z z>Sme}FAvX$@GxWu48){cf9S;O7`75Nix4ZmL3{KFOeJ$oM`9BNt3>WL=2kj%jKG9Q zLX1T@Jv~k6gb1WdGyaHi=~A{#rM0z%vHbpW0n&>c6f)ZJlvU8a?~c{pmJQj$=@Vd0 zH)z{zdT~UU5>qx^b>+Pq2c%&oQ*X}0cuL+dQ_DQ)BxYiGC$zhk*C?uA6^G$vrKT>f z1X>sx<^YYi3S*gJucunTTiOsXSJXQd15J?d{`4lU7_~)#T))nfjIdIaPFRsddPG%Kl4;5p6y_13L%Dj5Wcn z2ZYkumLL_e6Y73CEM+O-?n_*LMdc7ez_kB7Ow;={@-EWm&Vk^;uV25)Hr>N;qBj`& zhTgxgdK+~@#T}J_jg^OujlK3UA)ka>Kw#yqeHN^D(#mb8zPhW1=30Z57S;6!2;2D_ z2S|bPMYlpXgMYjYL2Q0j)zBE3aA2!VX6d1wKd!U7x<;V|K`M-I!yDRNIh9sL{h(7{ z2tHPgle_zZr9zx%6}=bJX$$)9-5hVX`IQ=T@8O~KE8$c8EwdOMi}ls1jI{e`$=YnA z=`gBGQ}sp_mBdllY-nFr3Rf2&^+rX{6RMN8tK2UX3~gv0cFGZ*Zzu|*t$M<#z^bpX z^If64x%oj(+I*0^Tx(H2K@b;p!&~zHN_euRboKG`9BkURjQbn}Me= zMsmmV=sP(&Py=NnWWC11UOskEsaYRII(-LG*kVeq4XT0iP8YB|NT9%^meJ8nRDfZo zz?fNB&!xX}P)-zdU(Rl`YS6yu7RbKGtSN7(Q`Dco5{2A{rTkpVanosoHZF;$vOAG1Ay;4S+|BM(C*leDIcj=+(828}v9O zb6;ENc|iX)R5n%9$KzVCMBL{R`gHPj@;jRCI(H11b>`AqRU$3-R}qD=h|pCCj{z$j zQ{c0;-fpeqI7S(1^+Z90W|)Q5T>51>64jfTFuZH!an?7ke5=p`_|2`~q3zH+?g>1S z4;L*PECGM0Fi3pxkx_5FI{U1?DD@^y%4`6Gp1&cQ%Wbd|g=f5yP=+rb&z=<}Y;W&=bOrZBBTudxn-CW_E8<8jql*D8+nH ze+t<0YU57Uwc$vYIwouSA~aQqCF7%!n2Fk6%cInoJ}^^0)Cu2CL80hq--`o`ywATs zw$1stupPVwb?OFiL%er09k}h85-(P6Od;Cmb9zUmwd&_i^#CaX4(sSR_2RLj-pu7L zdC;Vmk26YFuI5d36@rEAesbv{w50k~2fF&Am_zK-0T!!?`kbV8nv`!uyUW*#dOHG{ zUc!6vbC%K2X1T_Eea)2`%dtM6bw2gQ)h!=d(!Z!sf59y;H#ZmXfpJ(;a%QBrUQm2W zYYvwoSggRluU#y5#!1@~C9N%)= z{9aU-AJkoiPL+q+I(5owr6qe#M@ByFw&+it8~&>41|?vuSnIc}Na%Yx)B>n_7SrD; zhn89kpQ=NsCDnJ%3-mQOqByuq+XB4HYOIqF1-DW`!h~4-p`oE25wSCz+E=mOG^gQT z=o#AXoDn^W_UbBz8eV?L#@6@W>0Xsv_aIk zl1tx4a#MuGx~E9BJac+#Pfg*9qsdMBr5(FiSQv+Pn}>7nyEao#czPn~e>-KFIi4f0u! zV8`Um%!HG;4iSCl)($7x4 zBfu{mXmoqc^{5}+NltHFN^5l@yS>76OiRi~>>7K7Ub|!5YM9Ws@UB(SUvPctXD8 z9PhYEA&6=>JkSXfIp3U!7wNTue}|aI^nl>j?A`b|c}2dfWrKDB$a7M2fbsfhj1F;d z{K5buV<)8cGwlS8|8*sDJ?NZ7tVwx|W6Immi`8C30<}#rC_kz6@JDz0fLw?Mzb=tq zioi!ZZ)fKjr!X#o37i07cqXVy@{=oV*K|YSQ_d9;rF}2P;h;x&j}P`mrH6=Hx!;Rg zchm{-E#@1M>zgEcOeIEQ+y;+-{jgr$xDUswF#%3A7#K^Go6|MU0Tx;)D*l{wztab{t_%#I}KvP_7QiyQ_KMGw1qSL1)R3nSV0$IuyEd@le`s zsOn0mZ2D`96H9ChKb3NI*kh}k$MJxRP9~8|ixrg`9gcx`(jXk<2eu9`q zfVIhVH))2j#AyR_G≪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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: 18845 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 36D50C324D for ; Tue, 18 Jul 2023 10:52:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B7B22628D2; Tue, 18 Jul 2023 12:52:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677546; 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=gVVLGnlVquLkew0Ej9BIUEZoATndIE92dQr2PholTQUEfWT1wdNYV0dZuJxY8mRhr n7rwD2WIVaiEg8ukGCQsRQsEOzB1PTsDZ5WtbsEiJZSL1OVAJgriLrZoBhdf0E5YfD jJqZLvPkDe1aq4yBDeX4hf3B4ZdYo7FI7p5RDHBtFCE3aEr76Bgwmz7MhDmXiU3tpa ftpGWICDlDjMjavQsG6PqNjHZv89zvqRkAqrmMQkybKPhPuBwU2L3vxITOMpef50QF hen14FJv6xlvjjZRwkdvlcSsMgsREQK7DabX1I2pBkNqnPiT34on0qYFK2ffBBwOT6 TQvjHhB59saOQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0EE4661E2B for ; Tue, 18 Jul 2023 12:52:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="erRBdDf0"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2580B838; Tue, 18 Jul 2023 12:51:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677491; bh=xDK91yoWnPn+8AhlpzdW8lSc0qCCbfllNaIxij+HUQg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=erRBdDf0nAgsK3Tgml+DoE8opR1C0c5+cGhuhEKsNe97/tebNEK7hiiVm0PehA3/3 D43pGhboIKglVKl/2Yj+q+wiYLoS7mcvUFHhcj29TEC6E56CDHWLx9o8L0L9Bk1N8U QBWJI2v2fo1+dR9cw/U/EVpT1JWeOipyeKAKp3a8= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:04 +0200 Message-Id: <20230718105210.83558-5-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Tue Jul 18 10:52:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18846 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 8164FC32AA for ; Tue, 18 Jul 2023 10:52:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1F767628CE; Tue, 18 Jul 2023 12:52:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677549; 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=bbo3ihS5c4vGjlVtv5cc/9i6HlqD17VskmKh7Bniyjf2ayzI2fJj4BRGq/JWGawyu 3kTNQCN8YP+LE3DAYV6ZQTnzr78qxvQQBuoVTf02MvTu0L2z4Fp6irF1/qdvlA3s6f J1MrSsVMyHHa9jnXmCArmil5acJj01uwrao9b+w7ivgZftDQahmD+qiZ220wYrbGks DMQl0XaJHFX9U+a1J1OSIM72IvA+wWAihbToKIUQOyJ593QXAdeWNG8EqxMxgx6f6m POzYivpNHCwPomba0VX+T344rd54jFgkgvFSwlfb6ZLZcVeUApmWVwTd1Ea5CYfn+x e9E2FJh10DB+Q== 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 F02E9628C3 for ; Tue, 18 Jul 2023 12:52:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NEbPxVH/"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E8A608D0; Tue, 18 Jul 2023 12:51:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677492; bh=C071+c9sKX4BUUndlfeOERBWDFRvAPyBmpu+wDpWu2E=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NEbPxVH/oJlAG6YlhMKYXSK6pI4SCfmF9796T3SPHwN4fFJ4arB/IOn29vYSisoGs 3fwT7sZveSp+add6Ounsuy9qaRvqGLKKGi+Mmxm43dKxdAqfrn4rJ60YKiym9BopEK 257Jjl9iVwKQBAt/zsv7u0kklKH8mP+ESD401gQo= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:05 +0200 Message-Id: <20230718105210.83558-6-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Tue Jul 18 10:52:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18847 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 0CDF4BDC71 for ; Tue, 18 Jul 2023 10:52:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BC21C628CB; Tue, 18 Jul 2023 12:52:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677550; bh=2iTaMg6BxE3UkjSfFi2uq5xTSFJoLzXzHoxMx7DNZjg=; 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=Xnw5ToXH0KN883D9YFfhM3b7Xu52kfutwWRs2+f8mpZNEbyfxpVxHq/zb4gY4vcry RBWhQxiWCy6OHowCjKPIOOKh9gX+s1YgL62izLIoll/XAYniDQT8KRAmhEDOEolum1 Nk3gdi0Jqltqe/DnZ1FSENtbIZIQmJqcs/OkdocRUL2ISa3MzIeDqvNW3wNQWcRvDA 6ratQSngiC8pgrEGa4RZM2RFTmDut6cZOQUVqiHpSK5XQtBXOw2ieVNqeEhXNoXPGO mrNJ8WSqTeGqMDfFpP5taLyKtV2RULMHDoYtzgRk78ePUavPsCii28Vk4khtzGYhF7 s2VHku+86x+VA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BD5CB628D3 for ; Tue, 18 Jul 2023 12:52:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RiHbfIZA"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E3B7B838; Tue, 18 Jul 2023 12:51:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677493; bh=2iTaMg6BxE3UkjSfFi2uq5xTSFJoLzXzHoxMx7DNZjg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RiHbfIZAoFqZTQ55SUMNbmjPTDdZdb2f81+lNZUs2deVCfr0wvN76BhUO/z8wvsIY hGI9pnNSSWh1DhITfwdt/OBvFZo7NgtvSLGxdiWE/wZwv5e+KJ9S4Fz4h+IaESiZpw RB9jXPkK8rxONuOs85C8H5SwJTGvzg3r8lGy94CY= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:06 +0200 Message-Id: <20230718105210.83558-7-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Transform t3 = 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 Tue Jul 18 10:52:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18848 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 82AB4C32AB for ; Tue, 18 Jul 2023 10:52:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 44D61628D9; Tue, 18 Jul 2023 12:52:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677551; bh=yqDVuGpaamdkaEB2dVC3yi1+f1QsnW54T2hpQtgCfXo=; 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=JZ4APllopNXW5zCm5HQ1xTxYi8VqjaAIXX5hjBDJmmM9tudQanE6q365y7hxlFfUf yosE7jX+YiBQiCDzwpb4ErBvLTZ3y/QOlRIIR1gB4xTdC6mp6JckGi7K0rvqVFIFAb W2AGd/BQUoc041a2gpKmgTDI1NcWioyh/TrnHjvqS0OibcGNP/ganKp2mZDM2kJrS9 hSqWO5AiguC9mrvr+Ct448Rc4BKy3QaTNTRzRb3HLJqUTSfXB+uZdX7pJb8ADM+/WX /UlkGNaq9QSU/VAXiYIBx4JvD/AAOanyhmP4lrKTIGSe9U5DOGdUhhPmIgVbk6D1jt kjos4YxLwnCkw== 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 89BCD61E2B for ; Tue, 18 Jul 2023 12:52:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WZgXf4rI"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A4DB98D0; Tue, 18 Jul 2023 12:51:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677494; bh=yqDVuGpaamdkaEB2dVC3yi1+f1QsnW54T2hpQtgCfXo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WZgXf4rIaZwFcfr4xcmLH3nw9LlAKTcuECm144G1LgLPY16zSsHTn8AqhB6YnKT5K NLNGr7q2gPtOkU5fv0aqJqsa+MkqMFj4AJPvlijGK93Mr4Go+jEcW67+mryGsWkelr WbZ5/HmFeLQ73giIGHPlRHHJ7ToJg88oV7a2svcY= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:07 +0200 Message-Id: <20230718105210.83558-8-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 Tue Jul 18 10:52:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18849 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 3D5FDC32AC for ; Tue, 18 Jul 2023 10:52:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B6A5E628E0; Tue, 18 Jul 2023 12:52:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677551; bh=rHECqQXSuBFFChP6fsCWWExJnx2HNzOorTkOkQ2f/kw=; 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=3nyufN5TXO6kY1e+cuJNKmkTafp6k5ooHfV3mMUO4/ZmWP7ET4hnhUFg4nSfmP/2+ JC3ZkWokw7ajAgbyjsMcwNrHRHb5Xj8coSy/evk5AKhTrP45GvHLoS+xJiCmujGE0M 4+cx8ybK+Un6XK80W6aF5N6U9UQc5tq0slPkO0FYLuPxvhpHDVPgNUBY3Y2DZZ3r+l MVPVXmoOHebJNE9LYd5GTZtszh2JB81diuGdCdZK5DQacNljLOIWPZCXZPaN3I4gdF okZYKeRJsOvFIlqvX8KUu4MG02L7c8xeVZUzqO+4VMhJvrYwGf7PfFmxZc3B3myuQz wa9ShEO81+Wzg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5D429628C0 for ; Tue, 18 Jul 2023 12:52:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HYMQWrQe"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 672B1838; Tue, 18 Jul 2023 12:51:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677495; bh=rHECqQXSuBFFChP6fsCWWExJnx2HNzOorTkOkQ2f/kw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HYMQWrQecUIIR58oSwWBYl+ecpr+O2d4fFVyL3TyC9SxVXRu7/37/RU810lNXCVEY sRFTGCjv1HGpBaLUsN3f983vOkK04ZAtaJrQk9X8Lf++deGiqo7Fs7q3gOhZA9UF6+ P6LxPO3lsdhzg++PTgkY3pfoY16mlOMz1YsSjF8Q= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:08 +0200 Message-Id: <20230718105210.83558-9-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 --- include/libcamera/transform.h | 7 + test/meson.build | 1 + test/transform.cpp | 335 ++++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 test/transform.cpp diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 847a1f94713c..833b50d46fd0 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -16,13 +16,20 @@ enum class Orientation; enum class Transform : int { Identity = 0, Rot0 = Identity, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, Rot180 = HVFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose }; diff --git a/test/meson.build b/test/meson.build index b227be818419..189e1428485a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -46,6 +46,7 @@ public_tests = [ {'name': 'public-api', 'sources': ['public-api.cpp']}, {'name': 'signal', 'sources': ['signal.cpp']}, {'name': 'span', 'sources': ['span.cpp']}, + {'name': 'transform', 'sources': ['transform.cpp']}, ] internal_tests = [ diff --git a/test/transform.cpp b/test/transform.cpp new file mode 100644 index 000000000000..f79a1a892d16 --- /dev/null +++ b/test/transform.cpp @@ -0,0 +1,335 @@ +/* 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, + }, + { + Orientation::rotate180, Orientation::rotate180, + Transform::Identity + }, + { + /* + * o2 t o1 + * -------------------------- + * AB * (H|V) = CD DC + * CD AB BA + */ + Orientation::rotate180, Orientation::rotate0, + Transform::Rot180 + }, + /* Test that transpositions are handled correctly. */ + { + /* + * o2 t o1 + * -------------------------- + * AB * (T|V) = CD CA + * CD AB DB + */ + Orientation::rotate90, Orientation::rotate0, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CA * (T|H) = AC AB + * DB BD CD + */ + Orientation::rotate0, Orientation::rotate90, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * AB * (T|H) = BA BD + * CD DC AC + */ + Orientation::rotate270, Orientation::rotate0, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * BD * (T|V) = AC AB + * AC BD CD + */ + Orientation::rotate0, Orientation::rotate270, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CD * (T|H) = DC DA + * BA AB CB + */ + Orientation::rotate90, Orientation::rotate180, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * DA * (T|V) = CB CD + * CB DA BA + */ + Orientation::rotate180, Orientation::rotate90, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * CD * (T|V) = BA BC + * BA CD AD + */ + Orientation::rotate270, Orientation::rotate180, + Transform::Rot90, + }, + { + /* + * o2 t o1 + * -------------------------- + * BC * (T|H) = CB CD + * AD DA BA + */ + Orientation::rotate180, Orientation::rotate270, + Transform::Rot270, + }, + { + /* + * o2 t o1 + * -------------------------- + * DA * (V|H) = AD BC + * CB BC AD + */ + Orientation::rotate270, Orientation::rotate90, + Transform::Rot180, + }, + /* Test that mirroring is handled correctly. */ + { + Orientation::rotate0, Orientation::rotate0Flip, + Transform::HFlip + }, + { + Orientation::rotate0Flip, Orientation::rotate0, + Transform::HFlip + }, + { + Orientation::rotate180, Orientation::rotate180Flip, + Transform::HFlip + }, + { + Orientation::rotate180Flip, Orientation::rotate180, + Transform::HFlip + }, + { + Orientation::rotate90, Orientation::rotate90Flip, + Transform::HFlip + }, + { + Orientation::rotate90Flip, Orientation::rotate90, + Transform::HFlip + }, + { + Orientation::rotate270, Orientation::rotate270Flip, + Transform::HFlip + }, + { + Orientation::rotate270Flip, Orientation::rotate270, + Transform::HFlip + }, + { + Orientation::rotate0, Orientation::rotate0Flip, + Transform::HFlip + }, + /* + * More exotic transforms which include Transpositions and + * mirroring. + */ + { + /* + * o2 t o1 + * ------------------ + * BC * (V) = AD + * AD BC + */ + Orientation::rotate90Flip, Orientation::rotate270, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * ------------------ + * CB * (T) = CD + * DA BA + */ + Orientation::rotate180, Orientation::rotate270Flip, + Transform::Transpose, + }, + { + /* + * o2 t o1 + * ------------------ + * AD * (T) = AB + * BC DC + */ + Orientation::rotate0, Orientation::rotate90Flip, + Transform::Transpose, + }, + { + /* + * o2 t o1 + * ------------------ + * AD * (V) = BC + * BC AD + */ + Orientation::rotate270, Orientation::rotate90Flip, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * ------------------ + * DA * (V) = CB + * CB DA + */ + Orientation::rotate270Flip, Orientation::rotate90, + Transform::VFlip, + }, + { + /* + * o2 t o1 + * -------------------------- + * CB * (V|H) = BC AD + * DA AD BC + */ + Orientation::rotate90Flip, Orientation::rotate270Flip, + Transform::Rot180, + }, + }; + + for (const auto &entry : testEntries) { + Transform transform = entry.o1 / entry.o2; + cout << "Testing: " << entry.o1 << " / " << entry.o2 + << " = " << transformToString(transform) << endl; + if (transform != entry.t) { + cerr << "Failed to validate: " << entry.o1 + << " / " << entry.o2 + << " = " << transformToString(entry.t) << endl; + cerr << "Got back: " + << transformToString(transform) << endl; + return TestFail; + } + + Orientation adjusted = entry.o2 * entry.t; + if (adjusted != entry.o1) { + cerr << "Failed to validate: " << entry.o2 + << " * " << transformToString(entry.t) + << " = " << entry.o1 << endl; + cerr << "Got back: " << adjusted << endl; + return TestFail; + } + } + + return TestPass; +} + +TEST_REGISTER(TransformTest) From patchwork Tue Jul 18 10:52:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18850 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 4BD3EC324D for ; Tue, 18 Jul 2023 10:52:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D1E75628D0; Tue, 18 Jul 2023 12:52:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677554; bh=4QsVM5ptn0096ZBEcP/4RRbf7v36jkFCllyd9OIHam8=; 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=VYOZfyQ7WZ8gBISjPGmX4B8yJ5G0ODtMVBhsPtY1P2yfzByMcOG+zPGNM8xS6LuwX k9hvfd9JdjaxfoVZ8jHLsavUUkstqoVHXaGnIUvieB6DU/yZodBKb0K+eMy1onYQlt X0+V7Po+w6UPP4/Wih5/iOA+pZfc3PLhKpaya1YafGo+AEWxAuLkQo7Qe6WzyQdCPW hy8OQqEADlAbdDs/PhL3cNSWofZTIatOCWH0DwprvJFdr/E4mqkEIx2fmVSs3o33uj qz+qqtufQ1mQt7sbBmt//PPMpU3LKmJPHYgoq9B+uy2rls+4LNZTSMVUjUUc38I5Ad tb/0IMNzOqKBQ== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E295B61E2B for ; Tue, 18 Jul 2023 12:52:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="L9ZGgCN4"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 49CEF16B4; Tue, 18 Jul 2023 12:51:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677496; bh=4QsVM5ptn0096ZBEcP/4RRbf7v36jkFCllyd9OIHam8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L9ZGgCN4LZJTQbnPfkdmXq/ZEhNXrpGLmJlFLffvucfnGNyYzHA0Kfw6hJOeFQOa3 /jYqSHErZxXAFmyTXYSKf8js5QmaUa+5hmo9uc8tr1XFUoZZPYD0L3JkBUPJkuS5I0 8sWfDK4Y+wk4ZDPusIv79f0Smxke7Uc87Qt0ArxQ= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:09 +0200 Message-Id: <20230718105210.83558-10-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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 +- include/libcamera/transform.h | 7 -- 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 +- 11 files changed, 66 insertions(+), 92 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/include/libcamera/transform.h b/include/libcamera/transform.h index 833b50d46fd0..847a1f94713c 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -16,20 +16,13 @@ enum class Orientation; enum class Transform : int { Identity = 0, Rot0 = Identity, - HFlip = 1, - VFlip = 2, - HVFlip = HFlip | VFlip, Rot180 = HVFlip, - Transpose = 4, - Rot270 = HFlip | Transpose, - Rot90 = VFlip | Transpose, - Rot180Transpose = HFlip | VFlip | Transpose }; diff --git a/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 Tue Jul 18 10:52:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18851 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 E6DACC32AD for ; Tue, 18 Jul 2023 10:52:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90702628CC; Tue, 18 Jul 2023 12:52:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677554; bh=xZiKf0IW1VQsytjg7nKnQa6dxUGuyIJmhWSQge3KGps=; 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=2URllzDrE2gXKIct5VNxGbruzepq9egyFcC6pWF3kLnRtzRgD23Dn0kvb/esMP76M 85OVmrdejZpldTaKmXmEUTy/mmwNCHo5K98sXx9F2kwjoswz4zz2jqznGGnKbz6zco pzpzsim6RZYB+b1EPiN7ZtK5qRnCne11ttSxNS/vB4X7UTFCNiY0enmHVsSkd/ohH1 sf3Klcmu66gTbKeqUIV9GLdzJ6GNMgSJryvy1fw7jKg3+L9E6dQT9CGaM6pi3WwlEi 5G/aRbIxijfZmofPMJ4lKeoXgyNXxVnMUrq51bJc1qpTygwg54r6H4EqFCm+uESrdT /XZH++qIZiQ9g== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B7EA3628C5 for ; Tue, 18 Jul 2023 12:52:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Ds9OppLd"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D1CE68D0; Tue, 18 Jul 2023 12:51:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677497; bh=xZiKf0IW1VQsytjg7nKnQa6dxUGuyIJmhWSQge3KGps=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ds9OppLdP5TRQCUTaGGSxNLeiq1SdujtijmOwbhNT6qQVII+r+z2gLpvBdKACM5KG hl6rHXILKywx0tAY2uu7iJ99GBDtSoanxVk76Ieikl8tMHxrDiHNnQN+Lfpk1IdEfN qyDieZa68pGJ+lOa88vXNTqbnk1MywD9HaprqeKM= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:10 +0200 Message-Id: <20230718105210.83558-11-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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,