[{"id":12202,"web_url":"https://patchwork.libcamera.org/comment/12202/","msgid":"<39aa8a13-f7db-7eeb-3caf-0b1d15cbb8a6@ideasonboard.com>","date":"2020-08-28T15:27:09","subject":"Re: [libcamera-devel] [PATCH v4 3/7] libcamera: Add Transform enum\n\tto represet 2D plane transforms.","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi David,\n\nA minor 's/represet/represent/' in $SUBJECT.\n\nOn 28/08/2020 15:41, David Plowman wrote:\n> We implement 2D transforms as an enum class with 8 elements,\n> consisting of the usual 2D plane transformations (flips, rotations\n> etc.).\n> \n> The transform is made up of 3 bits, indicating whether the transform\n> includes: a transpose, a horizontal flip (mirror) and a vertical flip.\n> \n> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nThis all reads quite well to me.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> ---\n>  include/libcamera/meson.build |   1 +\n>  include/libcamera/transform.h |  73 ++++++++\n>  src/libcamera/meson.build     |   1 +\n>  src/libcamera/transform.cpp   | 312 ++++++++++++++++++++++++++++++++++\n>  4 files changed, 387 insertions(+)\n>  create mode 100644 include/libcamera/transform.h\n>  create mode 100644 src/libcamera/transform.cpp\n> \n> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build\n> index cdb8e03..7fae5e5 100644\n> --- a/include/libcamera/meson.build\n> +++ b/include/libcamera/meson.build\n> @@ -19,6 +19,7 @@ libcamera_public_headers = files([\n>      'span.h',\n>      'stream.h',\n>      'timer.h',\n> +    'transform.h',\n>  ])\n>  \n>  include_dir = join_paths(libcamera_include_dir, 'libcamera')\n> diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h\n> new file mode 100644\n> index 0000000..7d88937\n> --- /dev/null\n> +++ b/include/libcamera/transform.h\n> @@ -0,0 +1,73 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Raspberry Pi (Trading) Limited\n> + *\n> + * transform.h - 2D plane transforms\n> + */\n> +\n> +#ifndef __LIBCAMERA_TRANSFORM_H__\n> +#define __LIBCAMERA_TRANSFORM_H__\n> +\n> +#include <string>\n> +\n> +namespace libcamera {\n> +\n> +enum class Transform : int {\n> +\tIdentity = 0,\n> +\tRot0 = Identity,\n> +\tHFlip = 1,\n> +\tVFlip = 2,\n> +\tHVFlip = HFlip | VFlip,\n> +\tRot180 = HVFlip,\n> +\tTranspose = 4,\n> +\tRot270 = HFlip | Transpose,\n> +\tRot90 = VFlip | Transpose,\n> +\tRot180Transpose = HFlip | VFlip | Transpose\n> +};\n> +\n> +constexpr Transform operator&(Transform t0, Transform t1)\n> +{\n> +\treturn static_cast<Transform>(static_cast<int>(t0) & static_cast<int>(t1));\n> +}\n> +\n> +constexpr Transform operator|(Transform t0, Transform t1)\n> +{\n> +\treturn static_cast<Transform>(static_cast<int>(t0) | static_cast<int>(t1));\n> +}\n> +\n> +constexpr Transform operator^(Transform t0, Transform t1)\n> +{\n> +\treturn static_cast<Transform>(static_cast<int>(t0) ^ static_cast<int>(t1));\n> +}\n> +\n> +constexpr Transform &operator&=(Transform &t0, Transform t1)\n> +{\n> +\treturn t0 = t0 & t1;\n> +}\n> +\n> +constexpr Transform &operator|=(Transform &t0, Transform t1)\n> +{\n> +\treturn t0 = t0 | t1;\n> +}\n> +\n> +constexpr Transform &operator^=(Transform &t0, Transform t1)\n> +{\n> +\treturn t0 = t0 ^ t1;\n> +}\n> +\n> +Transform operator*(Transform t0, Transform t1);\n> +\n> +Transform operator-(Transform t);\n> +\n> +constexpr bool operator!(Transform t)\n> +{\n> +\treturn t == Transform::Identity;\n> +}\n> +\n> +Transform transformFromRotation(int angle, bool *success = nullptr);\n> +\n> +const char *transformToString(Transform t);\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_TRANSFORM_H__ */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index af2f3d9..edec55e 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -44,6 +44,7 @@ libcamera_sources = files([\n>      'sysfs.cpp',\n>      'thread.cpp',\n>      'timer.cpp',\n> +    'transform.cpp',\n>      'utils.cpp',\n>      'v4l2_controls.cpp',\n>      'v4l2_device.cpp',\n> diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp\n> new file mode 100644\n> index 0000000..88798d4\n> --- /dev/null\n> +++ b/src/libcamera/transform.cpp\n> @@ -0,0 +1,312 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Raspberry Pi (Trading) Limited\n> + *\n> + * transform.cpp - 2D plane transforms.\n> + */\n> +\n> +#include <libcamera/transform.h>\n> +\n> +/**\n> + * \\file transform.h\n> + * \\brief Enum to represent and manipulate 2D plane transforms.\n> + */\n> +\n> +namespace libcamera {\n> +\n> +/**\n> + * \\enum Transform\n> + * \\brief Enum to represent a 2D plane transform.\n> + *\n> + * The Transform can take 8 distinct values, representing the usual 2D plane\n> + * transforms listed below. Each of these transforms can be constructed\n> + * out of 3 basic operations, namely a horizontal flip (mirror), a vertical\n> + * flip, and a transposition (about the main diagonal). The transforms are\n> + * encoded such that a single bit indicates the presence of each of the 3\n> + * basic operations:\n> + *\n> + * - bit 0 - presence of a horizontal flip\n> + * - bit 1 - presence of a vertical flip\n> + * - bit 2 - presence of a transposition.\n> + *\n> + * We regard these 3 basic operations as being applied in a specific order:\n> + * first the two flip operations (actually they commute, so the order between\n> + * them is unimportant) and finally any transpose operation.\n> + *\n> + * Functions are provided to manipulate directly the bits within the transform\n> + * encoding, but there are also higher-level functions to invert and compose\n> + * transforms. Transforms are composed according to the usual mathematical\n> + * convention such that the right transform is applied first, and the left\n> + * transform is applied second.\n> + *\n> + * Finally, we have a total of 8 distinct transformations, as follows (a\n> + * couple of them have additional synonyms for convenience). We illustrate each\n> + * with its nominal effect on a rectangle with vertices labelled A, B, C and D.\n> + *\n> + * **Identity**\n> + *\n> + * Identity transform.\n> +~~~\n> +              A-B                          A-B\n> +Input image   | |   goes to output image   | |\n> +              C-D                          C-D\n> +~~~\n> + * Numeric value: 0 (no bits set).\n> + *\n> + * **Rot0**\n> + *\n> + * Synonym for `Identity` (zero degree rotation).\n> + *\n> + * **HFlip**\n> + *\n> + * Horizontal flip.\n> +~~~\n> +              A-B                          B-A\n> +Input image   | |   goes to output image   | |\n> +              C-D                          D-C\n> +~~~\n> + * Numeric value: 1 (horizontal flip bit set only).\n> + *\n> + * **VFlip**\n> + *\n> + * Vertical flip.\n> +~~~\n> +              A-B                          C-D\n> +Input image   | |   goes to output image   | |\n> +              C-D                          A-B\n> +~~~\n> + * Numeric value: 2 (vertical flip bit set only).\n> + *\n> + * **HVFlip**\n> + *\n> + * Horizontal and vertical flip (identical to a 180 degree rotation).\n> +~~~\n> +              A-B                          D-C\n> +Input image   | |   goes to output image   | |\n> +              C-D                          B-A\n> +~~~\n> + * Numeric value: 3 (horizontal and vertical flip bits set).\n> + *\n> + * **Rot180**\n> + *\n> + * Synonym for `HVFlip` (180 degree rotation).\n> + *\n> + * **Transpose**\n> + *\n> + * Transpose (about the main diagonal).\n> +~~~\n> +              A-B                          A-C\n> +Input image   | |   goes to output image   | |\n> +              C-D                          B-D\n> +~~~\n> + * Numeric value: 4 (transpose bit set only).\n> + *\n> + * **Rot270**\n> + *\n> + * Rotation by 270 degrees clockwise (90 degrees anticlockwise).\n> +~~~\n> +              A-B                          B-D\n> +Input image   | |   goes to output image   | |\n> +              C-D                          A-C\n> +~~~\n> + * Numeric value: 5 (transpose and horizontal flip bits set).\n> + *\n> + * **Rot90**\n> + *\n> + * Rotation by 90 degrees clockwise (270 degrees anticlockwise).\n> +~~~\n> +              A-B                          C-A\n> +Input image   | |   goes to output image   | |\n> +              C-D                          D-B\n> +~~~\n> + * Numeric value: 6 (transpose and vertical flip bits set).\n> + *\n> + * **Rot180Transpose**\n> + *\n> + * Rotation by 180 degrees followed by transpose (alternatively, transposition\n> + * about the \"opposite diagonal\").\n> +~~~\n> +              A-B                          D-B\n> +Input image   | |   goes to output image   | |\n> +              C-D                          C-A\n> +~~~\n> + * Numeric value: 7 (all bits set).\n> + *\n> + * \\sa https://en.wikipedia.org/wiki/Examples_of_groups#dihedral_group_of_order_8\n> + *\n> + * The set of 2D plane transforms is also known as the symmetry group of a\n> + * square, described in the link. Note that the group can be generated by\n> + * only 2 elements (the horizontal flip and a 90 degree rotation, for\n> + * example), however, the encoding used here makes the presence of the vertical\n> + * flip explicit.\n> + */\n> +\n> +/**\n> + * \\fn operator &(Transform t0, Transform t1)\n> + * \\brief Apply bitwise AND operator between the bits in the two transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\fn operator |(Transform t0, Transform t1)\n> + * \\brief Apply bitwise OR operator between the bits in the two transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\fn operator ^(Transform t0, Transform t1)\n> + * \\brief Apply bitwise XOR operator between the bits in the two transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\fn operator &=(Transform &t0, Transform t1)\n> + * \\brief Apply bitwise AND-assignment operator between the bits in the two\n> + * transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\fn operator |=(Transform &t0, Transform t1)\n> + * \\brief Apply bitwise OR-assignment operator between the bits in the two\n> + * transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\fn operator ^=(Transform &t0, Transform t1)\n> + * \\brief Apply bitwise XOR-assignment operator between the bits in the two\n> + * transforms.\n> + * \\param[in] t0 The first transform.\n> + * \\param[in] t1 The second transform.\n> + */\n> +\n> +/**\n> + * \\brief Compose two transforms together.\n> + * \\param[in] t1 The second transform.\n> + * \\param[in] t0 The first transform.\n> + *\n> + * Composing transforms follows the usual mathematical convention for\n> + * composing functions. That is, when performing `t1 * t0`, \\a t0 is applied\n> + * first, and then \\a t1.\n> + * For example, `Transpose * HFlip` performs `HFlip` first and then the\n> + * `Transpose` yielding `Rot270`, as shown below.\n> +~~~\n> +             A-B                 B-A                     B-D\n> +Input image  | |   -> HFLip ->   | |   -> Transpose ->   | |   = Rot270\n> +             C-D                 D-C                     A-C\n> +~~~\n> + * Note that composition is generally non-commutative for Transforms,\n> + * and not the same as XOR-ing the underlying bit representations.\n> + */\n> +Transform operator*(Transform t1, Transform t0)\n> +{\n> +\t/*\n> +\t * Reorder the operations so that we imagine doing t0's transpose\n> +\t * (if any) after t1's flips. The effect is to swap t1's hflips for\n> +\t * vflips and vice versa, after which we can just xor all the bits.\n> +\t */\n> +\tTransform reordered = t1;\n> +\tif (!!(t0 & Transform::Transpose)) {\n> +\t\treordered = t1 & Transform::Transpose;\n> +\t\tif (!!(t1 & Transform::HFlip))\n> +\t\t\treordered |= Transform::VFlip;\n> +\t\tif (!!(t1 & Transform::VFlip))\n> +\t\t\treordered |= Transform::HFlip;\n> +\t}\n> +\n> +\treturn reordered ^ t0;\n> +}\n> +\n> +/**\n> + * \\brief Invert a transform.\n> + * \\param[in] t The transform to be inverted.\n> + *\n> + * That is, we return the transform such that `t * (-t)` and `(-t) * t` both\n> + * yield the identity transform.\n> + */\n> +Transform operator-(Transform t)\n> +{\n> +\t/* All are self-inverses, except for Rot270 and Rot90. */\n> +\tstatic const Transform inverses[] = {\n> +\t\tTransform::Identity,\n> +\t\tTransform::HFlip,\n> +\t\tTransform::VFlip,\n> +\t\tTransform::HVFlip,\n> +\t\tTransform::Transpose,\n> +\t\tTransform::Rot90,\n> +\t\tTransform::Rot270,\n> +\t\tTransform::Rot180Transpose\n> +\t};\n> +\n> +\treturn inverses[static_cast<int>(t)];\n> +}\n> +\n> +/**\n> + * \\fn operator!(Transform t)\n> + * \\brief Return `true` if the transform is the `Identity`, otherwise `false`.\n> + * \\param[in] t The transform to be tested.\n> + */\n> +\n> +/**\n> + * \\brief Return the transform representing a rotation of the given angle\n> + * clockwise.\n> + * \\param[in] angle The angle of rotation in a clockwise sense. Negative values\n> + * can be used to represent anticlockwise rotations.\n> + * \\param[out] success Set to `true` if the angle is a multiple of 90 degrees,\n> + * otherwise `false`.\n> + * \\return The transform corresponding to the rotation if \\a success was set to\n> + * `true`, otherwise the `Identity` transform.\n> + */\n> +Transform transformFromRotation(int angle, bool *success)\n> +{\n> +\tangle = angle % 360;\n> +\tif (angle < 0)\n> +\t\tangle += 360;\n> +\n> +\tif (success != nullptr)\n> +\t\t*success = true;\n> +\n> +\tswitch (angle) {\n> +\tcase 0:\n> +\t\treturn Transform::Identity;\n> +\tcase 90:\n> +\t\treturn Transform::Rot90;\n> +\tcase 180:\n> +\t\treturn Transform::Rot180;\n> +\tcase 270:\n> +\t\treturn Transform::Rot270;\n> +\t}\n> +\n> +\tif (success != nullptr)\n> +\t\t*success = false;\n> +\n> +\treturn Transform::Identity;\n> +}\n> +\n> +/**\n> + * \\brief Return a character string describing the transform.\n> + * \\param[in] t The transform to be described.\n> + */\n> +const char *transformToString(Transform t)\n> +{\n> +\tstatic const char *strings[] = {\n> +\t\t\"identity\",\n> +\t\t\"hflip\",\n> +\t\t\"vflip\",\n> +\t\t\"hvflip\",\n> +\t\t\"transpose\",\n> +\t\t\"rot270\",\n> +\t\t\"rot90\",\n> +\t\t\"rot180transpose\"\n> +\t};\n> +\n> +\treturn strings[static_cast<int>(t)];\n> +}\n> +\n> +} /* namespace libcamera */\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 081AEBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 28 Aug 2020 15:27:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CC3AA62919;\n\tFri, 28 Aug 2020 17:27:18 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D795E61EA0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 28 Aug 2020 17:27:16 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BE868303;\n\tFri, 28 Aug 2020 17:27:11 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"RvHGDXo1\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1598628431;\n\tbh=o/xWv4o6I+riSGSOojFjsjKUvvsbWntfjTM/m1TuMz8=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=RvHGDXo117x47sqDMM+2Lp2ncnQowBnZIKghNhL4NmtkZ9GPZcAqfHzcOK3bsBHHo\n\tkzAana/aKnS65QuHyBU1gBzhRAwgDnuPR0k+YfaY+pmoS+uCS9nUbfR45qDQI6144O\n\tZ6f9NCNg7ufVy/Ya3D+Nrei4j4yHxHM3RobvbWI8=","To":"David Plowman <david.plowman@raspberrypi.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20200828144110.17303-1-david.plowman@raspberrypi.com>\n\t<20200828144110.17303-4-david.plowman@raspberrypi.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<39aa8a13-f7db-7eeb-3caf-0b1d15cbb8a6@ideasonboard.com>","Date":"Fri, 28 Aug 2020 16:27:09 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.10.0","MIME-Version":"1.0","In-Reply-To":"<20200828144110.17303-4-david.plowman@raspberrypi.com>","Content-Language":"en-GB","Subject":"Re: [libcamera-devel] [PATCH v4 3/7] libcamera: Add Transform enum\n\tto represet 2D plane transforms.","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Reply-To":"kieran.bingham@ideasonboard.com","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]