Files
OmaPhoto/tests/CompositorTests.cpp
T
雷电芽衣andClaude c864304695
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s
OmaPhoto 1.0.0: the Linux port of Compositor
A layered image editor for Linux and Omarchy, ported from Compositor
(github.com/robbietilton/Compositor), whose Swift source is its
specification. It reads and writes Compositor's .comp projects.

Packages for Ubuntu, Fedora and Arch come from the release workflow;
scripts/install/ holds one install script per distribution family,
and flake.nix serves NixOS.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-26 00:44:52 -04:00

200 lines
7.7 KiB
C++

#include "Document/EditorSession.h"
#include "Rendering/CanvasViewport.h"
#include <QtTest>
class CompositorTests : public QObject {
Q_OBJECT
const QSizeF document{1920, 1080};
private slots:
void dimensionValidation();
void actualPixelsAndRoundTrip_data();
void actualPixelsAndRoundTrip();
void zoomKeepsCursorPixelFixed();
void fitAndResizeModes();
void limitsAndNewDocumentReset();
void fitWaitsForAViewSize();
void backingScaleNeverDropsBelowOne();
void viewportsCompareExactly();
};
void CompositorTests::dimensionValidation()
{
QCOMPARE(CanvasDocument::validDimension("0"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("-1"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("1.5"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("30001"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("9999999999999999999999"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension(" 1920 "), std::optional<int>(1920));
QCOMPARE(CanvasDocument::validDimension("30000"), std::optional<int>(30000));
QCOMPARE(CanvasDocument::validDimension("\t+64\u00a0"), std::optional<int>(64));
QCOMPARE(CanvasDocument::validDimension("1920\n"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension(QString::fromUtf8("1920\0px", 7)), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("19 20"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension("+"), std::nullopt);
QCOMPARE(CanvasDocument::validDimension(""), std::nullopt);
QCOMPARE(CanvasDocument::validDimension(QString::fromUtf8("\u0661\u0662")), std::nullopt);
}
void CompositorTests::actualPixelsAndRoundTrip_data()
{
QTest::addColumn<double>("backing");
QTest::newRow("1x") << 1.0;
QTest::newRow("2x") << 2.0;
}
void CompositorTests::actualPixelsAndRoundTrip()
{
QFETCH(double, backing);
CanvasViewport viewport;
viewport.resize(QSizeF(800, 600), backing, std::nullopt);
for (double zoom : {0.25, 1.0, 3.75}) {
viewport.setZoom(zoom, viewport.center(), document);
viewport.translate(QSizeF(73.5, -44.25));
const QPointF pixel(183.25, 837.5);
const QPointF viewPoint = viewport.viewPoint(pixel, document);
const QPointF result = viewport.documentPoint(viewPoint, document);
QVERIFY(std::abs(result.x() - pixel.x()) < 0.000001);
QVERIFY(std::abs(result.y() - pixel.y()) < 0.000001);
QVERIFY(std::abs(viewport.documentRect(document).width() * backing - document.width() * zoom) < 0.000001);
}
}
void CompositorTests::zoomKeepsCursorPixelFixed()
{
CanvasViewport viewport;
viewport.resize(QSizeF(1000, 700), 2, document);
const QPointF anchor(157, 221);
const QPointF before = viewport.documentPoint(anchor, document);
QVERIFY(viewport.followsFit());
viewport.setZoom(4, anchor, document);
QCOMPARE(viewport.zoom(), 4.0);
QVERIFY(!viewport.followsFit());
const QPointF after = viewport.documentPoint(anchor, document);
QVERIFY(std::abs(before.x() - after.x()) < 0.000001);
QVERIFY(std::abs(before.y() - after.y()) < 0.000001);
}
void CompositorTests::fitAndResizeModes()
{
CanvasViewport viewport;
viewport.resize(QSizeF(800, 600), 2, document);
const QRectF rect = viewport.documentRect(document);
QVERIFY(rect.width() <= 704.000001);
QVERIFY(rect.height() <= 504.000001);
QCOMPARE(rect.center().x(), 400.0);
QCOMPARE(rect.center().y(), 300.0);
viewport.translate(QSizeF(60, -35));
QCOMPARE(viewport.pan, QSizeF(60, -35));
QVERIFY(!viewport.followsFit());
const QPointF before = viewport.documentPoint(viewport.center(), document);
const double zoom = viewport.zoom();
viewport.resize(QSizeF(1200, 800), 1, document);
const QPointF after = viewport.documentPoint(viewport.center(), document);
QCOMPARE(viewport.zoom(), zoom);
QVERIFY(std::abs(before.x() - after.x()) < 0.000001);
QVERIFY(std::abs(before.y() - after.y()) < 0.000001);
viewport.fit(document);
QCOMPARE(viewport.pan, QSizeF(0, 0));
QVERIFY(viewport.followsFit());
}
void CompositorTests::limitsAndNewDocumentReset()
{
EditorSession session;
session.viewport.resize(QSizeF(800, 600), 2, std::nullopt);
session.zoom(5);
QCOMPARE(session.viewport.zoom(), 1.0);
session.createDocument(1920, 1080);
session.zoom(1000);
QCOMPARE(session.viewport.zoom(), CanvasViewport::maximumZoom);
session.zoom(0);
QCOMPARE(session.viewport.zoom(), CanvasViewport::minimumZoom);
session.zoom(std::nan(""));
QCOMPARE(session.viewport.zoom(), CanvasViewport::minimumZoom);
session.viewport.translate(QSizeF(999, 888));
session.createDocument(400, 300);
QVERIFY(session.viewport.followsFit());
QCOMPARE(session.viewport.pan, QSizeF(0, 0));
QCOMPARE(session.document().value().width, 400);
session.createDocument(0, 200);
session.createDocument(200, 0);
session.createDocument(30'001, 200);
session.createDocument(200, 30'001);
QCOMPARE(session.document().value().width, 400);
session.createDocument(30'000, 1);
QCOMPARE(session.document().value().size(), QSizeF(30'000, 1));
session.createDocument(1, 30'000);
QCOMPARE(session.document().value().size(), QSizeF(1, 30'000));
// Zooming about a point keeps that document pixel under it.
session.createDocument(1920, 1080);
const QPointF anchor(157, 221);
const QPointF before = session.viewport.documentPoint(anchor, session.document().value().size());
session.zoom(4, anchor);
const QPointF after = session.viewport.documentPoint(anchor, session.document().value().size());
QVERIFY(std::abs(before.x() - after.x()) < 0.000001 && std::abs(before.y() - after.y()) < 0.000001);
session.viewport.translate(QSizeF(10, 10));
session.fit();
QVERIFY(session.viewport.followsFit());
}
void CompositorTests::fitWaitsForAViewSize()
{
CanvasViewport viewport;
viewport.setZoom(2, QPointF(0, 0), document);
viewport.fit(document);
QVERIFY(viewport.followsFit());
QCOMPARE(viewport.zoom(), 2.0);
viewport.resize(QSizeF(1056, 636), 1, document);
QCOMPARE(viewport.zoom(), 0.5);
QCOMPARE(viewport.pan, QSizeF(0, 0));
}
void CompositorTests::backingScaleNeverDropsBelowOne()
{
CanvasViewport viewport;
viewport.resize(QSizeF(800, 600), 0.5, std::nullopt);
QCOMPARE(viewport.backingScale, 1.0);
QCOMPARE(viewport.pointsPerPixel(), 1.0);
}
void CompositorTests::viewportsCompareExactly()
{
CanvasViewport viewport;
viewport.resize(QSizeF(800, 600), 2, std::nullopt);
viewport.setZoom(1.5, viewport.center(), document);
QVERIFY(viewport == CanvasViewport(viewport));
CanvasViewport other = viewport;
other.viewSize = QSizeF(800 + 1e-10, 600);
QVERIFY(!(viewport == other));
other = viewport;
other.viewSize = QSizeF(800, 600 + 1e-10);
QVERIFY(!(viewport == other));
other = viewport;
other.backingScale = 2 + 1e-13;
QVERIFY(!(viewport == other));
other = viewport;
other.pan = QSizeF(1e-13, 0);
QVERIFY(!(viewport == other));
other = viewport;
other.pan = QSizeF(0, 1e-13);
QVERIFY(!(viewport == other));
CanvasViewport zoomed = viewport;
zoomed.setZoom(1.5 + 1e-13, zoomed.center(), document);
QVERIFY(zoomed.pan.width() == viewport.pan.width() && zoomed.pan.height() == viewport.pan.height());
QCOMPARE(zoomed.followsFit(), viewport.followsFit());
QVERIFY(!(viewport == zoomed));
CanvasViewport unsized;
unsized.translate(QSizeF(0, 0));
CanvasViewport following = unsized;
following.fit(document);
QCOMPARE(following.zoom(), unsized.zoom());
QVERIFY(following.followsFit() && !unsized.followsFit());
QVERIFY(!(unsized == following));
}
QTEST_GUILESS_MAIN(CompositorTests)
#include "CompositorTests.moc"