Files
external_libcamera/src/libcamera/transform.cpp
Laurent Pinchart fd98779d78 libcamera: transform: Make the transformFromOrientation() function static
Now that the transformFromOrientation() function isn't used outside of
transform.cpp, make it static to remove it from the public API.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
2023-10-23 16:06:22 +03:00

410 lines
12 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020, Raspberry Pi Ltd
*
* transform.cpp - 2D plane transforms.
*/
#include <libcamera/transform.h>
#include <libcamera/orientation.h>
/**
* \file transform.h
* \brief Enum to represent and manipulate 2D plane transforms
*/
namespace libcamera {
/**
* \enum Transform
* \brief Enum to represent a 2D plane transform
*
* The Transform can take 8 distinct values, representing the usual 2D plane
* transforms listed below. Each of these transforms can be constructed
* out of 3 basic operations, namely a horizontal flip (mirror), a vertical
* flip, and a transposition (about the main diagonal). The transforms are
* encoded such that a single bit indicates the presence of each of the 3
* basic operations:
*
* - bit 0 - presence of a horizontal flip
* - bit 1 - presence of a vertical flip
* - bit 2 - presence of a transposition.
*
* We regard these 3 basic operations as being applied in a specific order:
* first the two flip operations (actually they commute, so the order between
* them is unimportant) and finally any transpose operation.
*
* Functions are provided to manipulate directly the bits within the transform
* encoding, but there are also higher-level functions to invert and compose
* transforms. Transforms are composed according to the usual mathematical
* convention such that the right transform is applied first, and the left
* transform is applied second.
*
* Finally, we have a total of 8 distinct transformations, as follows (a
* couple of them have additional synonyms for convenience). We illustrate each
* with its nominal effect on a rectangle with vertices labelled A, B, C and D.
*
* \sa https://en.wikipedia.org/wiki/Examples_of_groups#dihedral_group_of_order_8
*
* The set of 2D plane transforms is also known as the symmetry group of a
* square, described in the link. Note that the group can be generated by
* only 2 elements (the horizontal flip and a 90 degree rotation, for
* example), however, the encoding used here makes the presence of the vertical
* flip explicit.
*
* \var Transform::Identity
*
* Identity transform.
~~~
A-B A-B
Input image | | goes to output image | |
C-D C-D
~~~
* Numeric value: 0 (no bits set).
*
* \var Transform::Rot0
*
* Synonym for Transform::Identity (zero degree rotation).
*
* \var Transform::HFlip
*
* Horizontal flip.
~~~
A-B B-A
Input image | | goes to output image | |
C-D D-C
~~~
* Numeric value: 1 (horizontal flip bit set only).
*
* \var Transform::VFlip
*
* Vertical flip.
~~~
A-B C-D
Input image | | goes to output image | |
C-D A-B
~~~
* Numeric value: 2 (vertical flip bit set only).
*
* \var Transform::HVFlip
*
* Horizontal and vertical flip (identical to a 180 degree rotation).
~~~
A-B D-C
Input image | | goes to output image | |
C-D B-A
~~~
* Numeric value: 3 (horizontal and vertical flip bits set).
*
* \var Transform::Rot180
*
* Synonym for `HVFlip` (180 degree rotation).
*
* \var Transform::Transpose
*
* Transpose (about the main diagonal).
~~~
A-B A-C
Input image | | goes to output image | |
C-D B-D
~~~
* Numeric value: 4 (transpose bit set only).
*
* \var Transform::Rot270
*
* Rotation by 270 degrees clockwise (90 degrees anticlockwise).
~~~
A-B B-D
Input image | | goes to output image | |
C-D A-C
~~~
* Numeric value: 5 (transpose and horizontal flip bits set).
*
* \var Transform::Rot90
*
* Rotation by 90 degrees clockwise (270 degrees anticlockwise).
~~~
A-B C-A
Input image | | goes to output image | |
C-D D-B
~~~
* Numeric value: 6 (transpose and vertical flip bits set).
*
* \var Transform::Rot180Transpose
*
* Rotation by 180 degrees followed by transpose (alternatively, transposition
* about the "opposite diagonal").
~~~
A-B D-B
Input image | | goes to output image | |
C-D C-A
~~~
* Numeric value: 7 (all bits set).
*/
/**
* \fn operator &(Transform t0, Transform t1)
* \brief Apply bitwise AND operator between the bits in the two transforms
* \param[in] t0 The first transform
* \param[in] t1 The second transform
*/
/**
* \fn operator |(Transform t0, Transform t1)
* \brief Apply bitwise OR operator between the bits in the two transforms
* \param[in] t0 The first transform
* \param[in] t1 The second transform
*/
/**
* \fn operator ^(Transform t0, Transform t1)
* \brief Apply bitwise XOR operator between the bits in the two transforms
* \param[in] t0 The first transform
* \param[in] t1 The second transform
*/
/**
* \fn operator &=(Transform &t0, Transform t1)
* \brief Apply bitwise AND-assignment operator between the bits in the two
* transforms
* \param[in] t0 The first transform
* \param[in] t1 The second transform
*/
/**
* \fn operator |=(Transform &t0, Transform t1)
* \brief Apply bitwise OR-assignment operator between the bits in the two
* transforms
* \param[in] t0 The first transform
* \param[in] t1 The second transform
*/
/**
* \fn operator ^=(Transform &t0, Transform t1)
* \brief Apply bitwise XOR-assignment operator between the bits in the two
* transforms
* \param[in] t0 The first transform
* \param[in] t1 The second 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 into a transform that is equivalent to first applying
* \a t0 and then applying \a t1. For example, `HFlip * Transpose` performs
* `HFlip` first and then the `Transpose` yielding `Rot270`, as shown below.
~~~
A-B B-A B-D
Input image | | -> HFLip -> | | -> Transpose -> | | = Rot270
C-D D-C A-C
~~~
* Note that composition is generally non-commutative for Transforms, and not
* the same as XOR-ing the underlying bit representations.
*
* \return A Transform equivalent to applying \a t0 and then \a t1
*/
Transform operator*(Transform t0, Transform t1)
{
/*
* Reorder the operations so that we imagine doing t0's transpose
* (if any) after t1's flips. The effect is to swap t1's hflips for
* vflips and vice versa, after which we can just xor all the bits.
*/
Transform reordered = t1;
if (!!(t0 & Transform::Transpose)) {
reordered = t1 & Transform::Transpose;
if (!!(t1 & Transform::HFlip))
reordered |= Transform::VFlip;
if (!!(t1 & Transform::VFlip))
reordered |= Transform::HFlip;
}
return reordered ^ t0;
}
/**
* \brief Invert a transform
* \param[in] t The transform to be inverted
*
* That is, we return the transform such that `t * (-t)` and `(-t) * t` both
* yield the identity transform.
*/
Transform operator-(Transform t)
{
/* All are self-inverses, except for Rot270 and Rot90. */
static const Transform inverses[] = {
Transform::Identity,
Transform::HFlip,
Transform::VFlip,
Transform::HVFlip,
Transform::Transpose,
Transform::Rot90,
Transform::Rot270,
Transform::Rot180Transpose
};
return inverses[static_cast<int>(t)];
}
/**
* \fn operator!(Transform t)
* \brief Return `true` if the transform is the `Identity`, otherwise `false`
* \param[in] t The transform to be tested
*/
/**
* \fn operator~(Transform t)
* \brief Return the transform with all the bits inverted individually
* \param[in] t The transform of which the bits will be inverted
*
* This inverts the bits that encode the transform in a bitwise manner. Note
* that this is not the proper inverse of transform \a t (for which use \a
* operator-).
*/
/**
* \brief Return the transform representing a rotation of the given angle
* clockwise
* \param[in] angle The angle of rotation in a clockwise sense. Negative values
* can be used to represent anticlockwise rotations
* \param[out] success Set to `true` if the angle is a multiple of 90 degrees,
* otherwise `false`
* \return The transform corresponding to the rotation if \a success was set to
* `true`, otherwise the `Identity` transform
*/
Transform transformFromRotation(int angle, bool *success)
{
angle = angle % 360;
if (angle < 0)
angle += 360;
if (success != nullptr)
*success = true;
switch (angle) {
case 0:
return Transform::Identity;
case 90:
return Transform::Rot90;
case 180:
return Transform::Rot180;
case 270:
return Transform::Rot270;
}
if (success != nullptr)
*success = false;
return Transform::Identity;
}
namespace {
/**
* \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::Rotate0Mirror:
return Transform::HFlip;
case Orientation::Rotate180:
return Transform::Rot180;
case Orientation::Rotate180Mirror:
return Transform::VFlip;
case Orientation::Rotate90Mirror:
return Transform::Transpose;
case Orientation::Rotate90:
return Transform::Rot90;
case Orientation::Rotate270Mirror:
return Transform::Rot180Transpose;
case Orientation::Rotate270:
return Transform::Rot270;
}
return Transform::Identity;
}
} /* namespace */
/**
* \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.
*
* \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 orientation \a o
* \param o The 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 orientation first and
* then apply \a t to it.
*/
switch (transformFromOrientation(o) * t) {
case Transform::Identity:
return Orientation::Rotate0;
case Transform::HFlip:
return Orientation::Rotate0Mirror;
case Transform::VFlip:
return Orientation::Rotate180Mirror;
case Transform::Rot180:
return Orientation::Rotate180;
case Transform::Transpose:
return Orientation::Rotate90Mirror;
case Transform::Rot270:
return Orientation::Rotate270;
case Transform::Rot90:
return Orientation::Rotate90;
case Transform::Rot180Transpose:
return Orientation::Rotate270Mirror;
}
return Orientation::Rotate0;
}
/**
* \brief Return a character string describing the transform
* \param[in] t The transform to be described.
*/
const char *transformToString(Transform t)
{
static const char *strings[] = {
"identity",
"hflip",
"vflip",
"hvflip",
"transpose",
"rot270",
"rot90",
"rot180transpose"
};
return strings[static_cast<int>(t)];
}
} /* namespace libcamera */