@@ -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 = [
new file mode 100644
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023, Ideas On Board Oy
+ *
+ * transform.cpp - Transform and Orientation tests
+ */
+
+#include <iostream>
+
+#include <libcamera/orientation.h>
+#include <libcamera/transform.h>
+
+#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.
+ */
+ static const struct RotationTestEntry {
+ Orientation o1;
+ Orientation o2;
+ Transform t;
+ } testEntries[] = {
+ /* Test identities transforms first. */
+ {
+ Orientation::Rotate0, Orientation::Rotate0,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate0Mirror, Orientation::Rotate0Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate180, Orientation::Rotate180,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate180Mirror, Orientation::Rotate180Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate90, Orientation::Rotate90,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate90Mirror, Orientation::Rotate90Mirror,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate270, Orientation::Rotate270,
+ Transform::Identity,
+ },
+ {
+ Orientation::Rotate270Mirror, Orientation::Rotate270Mirror,
+ Transform::Identity,
+ },
+ /*
+ * Combine 0 and 180 degrees rotation as they're the most common
+ * ones.
+ */
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (H|V) = BA AB
+ * BA CD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate180,
+ Transform::Rot180,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (H|V) = CD DC
+ * CD AB BA
+ */
+ Orientation::Rotate180, Orientation::Rotate0,
+ Transform::Rot180
+ },
+ /* Test that transpositions are handled correctly. */
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (T|V) = CD CA
+ * CD AB DB
+ */
+ Orientation::Rotate90, Orientation::Rotate0,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CA * (T|H) = AC AB
+ * DB BD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate90,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * AB * (T|H) = BA BD
+ * CD DC AC
+ */
+ Orientation::Rotate270, Orientation::Rotate0,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * BD * (T|V) = AC AB
+ * AC BD CD
+ */
+ Orientation::Rotate0, Orientation::Rotate270,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (T|H) = DC DA
+ * BA AB CB
+ */
+ Orientation::Rotate90, Orientation::Rotate180,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * DA * (T|V) = CB CD
+ * CB DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate90,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CD * (T|V) = BA BC
+ * BA CD AD
+ */
+ Orientation::Rotate270, Orientation::Rotate180,
+ Transform::Rot90,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * BC * (T|H) = CB CD
+ * AD DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate270,
+ Transform::Rot270,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * DA * (V|H) = AD BC
+ * CB BC AD
+ */
+ Orientation::Rotate270, Orientation::Rotate90,
+ Transform::Rot180,
+ },
+ /* Test that mirroring is handled correctly. */
+ {
+ Orientation::Rotate0, Orientation::Rotate0Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate0Mirror, Orientation::Rotate0,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate180, Orientation::Rotate180Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate180Mirror, Orientation::Rotate180,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate90, Orientation::Rotate90Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate90Mirror, Orientation::Rotate90,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate270, Orientation::Rotate270Mirror,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate270Mirror, Orientation::Rotate270,
+ Transform::HFlip
+ },
+ {
+ Orientation::Rotate0, Orientation::Rotate0Mirror,
+ Transform::HFlip
+ },
+ /*
+ * More exotic transforms which include Transpositions and
+ * mirroring.
+ */
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * BC * (V) = AD
+ * AD BC
+ */
+ Orientation::Rotate90Mirror, Orientation::Rotate270,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * CB * (T) = CD
+ * DA BA
+ */
+ Orientation::Rotate180, Orientation::Rotate270Mirror,
+ Transform::Transpose,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * AD * (T) = AB
+ * BC DC
+ */
+ Orientation::Rotate0, Orientation::Rotate90Mirror,
+ Transform::Transpose,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * AD * (V) = BC
+ * BC AD
+ */
+ Orientation::Rotate270, Orientation::Rotate90Mirror,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * ------------------
+ * DA * (V) = CB
+ * CB DA
+ */
+ Orientation::Rotate270Mirror, Orientation::Rotate90,
+ Transform::VFlip,
+ },
+ {
+ /*
+ * o2 t o1
+ * --------------------------
+ * CB * (V|H) = BC AD
+ * DA AD BC
+ */
+ Orientation::Rotate90Mirror, Orientation::Rotate270Mirror,
+ Transform::Rot180,
+ },
+ };
+
+ for (const auto &entry : testEntries) {
+ Transform transform = entry.o1 / entry.o2;
+ 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)