Files
OmaPhoto/tests/MarchingAntsTests.cpp

202 lines
8.2 KiB
C++

#include "SelectionCanvasFixtures.h"
// Swift 1.2.10 (8eba077): ants from a screen-resolution outline.
namespace {
// Alternate single pixels: far past the 20,000 elements drawn whole.
QPainterPath checkerboard(QRect region)
{
QPainterPath path;
path.setFillRule(Qt::WindingFill);
for (int y = region.top(); y <= region.bottom(); ++y) {
for (int x = region.left(); x <= region.right(); ++x) {
if ((x + y) % 2 == 0)
path.addRect(QRectF(x, y, 1, 1));
}
}
return path;
}
// Pixels the ants changed within `area`, against the bare canvas.
int marked(const QImage &shot, const QImage &bare, QRect area)
{
int count = 0;
for (int y = area.top(); y <= area.bottom(); ++y) {
for (int x = area.left(); x <= area.right(); ++x)
count += shot.pixel(x, y) != bare.pixel(x, y);
}
return count;
}
// A border band around a document rectangle, in view pixels.
QRect band(Canvas &shown, QRect region)
{
const QSizeF size = shown.documentSize();
return QRectF(shown.session.viewport.viewPoint(region.topLeft(), size), shown.session.viewport.viewPoint(region.bottomRight() + QPoint(1, 1), size))
.toAlignedRect()
.adjusted(-2, -2, 2, 2);
}
QTimer &ants(Canvas &shown)
{
return *shown.canvas->findChild<QTimer *>(QStringLiteral("antsTimer"));
}
QRect viewRect(Canvas &shown, QRect region)
{
const QSizeF size = shown.documentSize();
return QRectF(shown.session.viewport.viewPoint(region.topLeft(), size), shown.session.viewport.viewPoint(region.bottomRight() + QPoint(1, 1), size))
.toAlignedRect();
}
}
class MarchingAntsTests : public QObject {
Q_OBJECT
private slots:
void aComplexOutlineZoomedOutIsTracedAtScreenResolution();
void simpleOutlinesAndActualPixelsDrawInFull();
void aTickWaitsForTheRepaintBeforeIt();
void aNewSourceDropsTheOldOutline();
void aNewStepKeepsTheOldOutlineUntilItLands();
void theStepFollowsTheZoomAndTheFillRuleHolds();
};
void MarchingAntsTests::aComplexOutlineZoomedOutIsTracedAtScreenResolution()
{
Canvas shown;
shown.session.zoom(0.25);
shown.canvas->synchronizeDisplay();
const QImage bare = shown.canvas->grab().toImage();
const QRect region(100, 100, 150, 150);
const QPainterPath board = checkerboard(region);
QVERIFY(board.elementCount() > 20'000);
shown.session.setSelection(DocumentSelection{board}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
ants(shown).stop();
// While the first outline is traced, no ants show.
QCOMPARE(shown.canvas->grab().toImage(), bare);
QTRY_VERIFY(shown.canvas->grab().toImage() != bare);
// The trace covers the board: its edge alone shows.
const QImage shot = shown.canvas->grab().toImage();
const QRect view = viewRect(shown, region);
QCOMPARE(marked(shot, bare, view.adjusted(3, 3, -3, -3)), 0);
QVERIFY(marked(shot, bare, view.adjusted(-2, -2, 2, 2)) > view.width() * 2);
}
void MarchingAntsTests::simpleOutlinesAndActualPixelsDrawInFull()
{
// At actual pixels the complex outline draws whole, at once.
Canvas shown;
const QImage bare = shown.canvas->grab().toImage();
const QRect region(100, 100, 150, 150);
shown.session.setSelection(DocumentSelection{checkerboard(region)}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
ants(shown).stop();
const QImage full = shown.canvas->grab().toImage();
QVERIFY(marked(full, bare, viewRect(shown, region).adjusted(10, 10, -10, -10)) > 1'000);
// Zoomed out, a marquee's outline draws at once.
Canvas other;
other.session.zoom(0.25);
other.canvas->synchronizeDisplay();
const QImage plain = other.canvas->grab().toImage();
other.session.applySelection(rectPath(QRectF(region)), SelectionMode::replace, QStringLiteral("Select"));
other.canvas->synchronizeDisplay();
ants(other).stop();
QVERIFY(other.canvas->grab().toImage() != plain);
}
void MarchingAntsTests::aTickWaitsForTheRepaintBeforeIt()
{
Canvas first, second;
for (Canvas *shown : {&first, &second}) {
shown->session.applySelection(rectPath(QRectF(50, 50, 100, 100)), SelectionMode::replace, QStringLiteral("Select"));
shown->canvas->synchronizeDisplay();
ants(*shown).stop();
}
QCOMPARE(first.canvas->grab().toImage(), second.canvas->grab().toImage());
// Two ticks before a repaint step once, as one.
QVERIFY(QMetaObject::invokeMethod(&ants(first), "timeout"));
QVERIFY(QMetaObject::invokeMethod(&ants(first), "timeout"));
QVERIFY(QMetaObject::invokeMethod(&ants(second), "timeout"));
const QImage stepped = first.canvas->grab().toImage();
QCOMPARE(stepped, second.canvas->grab().toImage());
// Once painted, the next tick steps again.
QVERIFY(QMetaObject::invokeMethod(&ants(first), "timeout"));
QVERIFY(first.canvas->grab().toImage() != stepped);
}
void MarchingAntsTests::aNewSourceDropsTheOldOutline()
{
Canvas shown;
shown.session.zoom(0.25);
shown.canvas->synchronizeDisplay();
const QImage bare = shown.canvas->grab().toImage();
const QRect first(10, 10, 120, 120), second(200, 100, 120, 120);
shown.session.setSelection(DocumentSelection{checkerboard(first)}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
ants(shown).stop();
QTRY_VERIFY(shown.canvas->grab().toImage() != bare);
// Another outline: the first goes at once; the second shows.
shown.session.setSelection(DocumentSelection{checkerboard(second)}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
QCOMPARE(shown.canvas->grab().toImage(), bare);
QTRY_VERIFY(shown.canvas->grab().toImage() != bare);
const QImage shot = shown.canvas->grab().toImage();
QCOMPARE(marked(shot, bare, band(shown, first)), 0);
QVERIFY(marked(shot, bare, band(shown, second)) > 0);
}
void MarchingAntsTests::aNewStepKeepsTheOldOutlineUntilItLands()
{
Canvas shown;
shown.session.zoom(0.5);
shown.canvas->synchronizeDisplay();
const QImage half = shown.canvas->grab().toImage();
shown.session.zoom(0.25);
shown.canvas->synchronizeDisplay();
const QImage quarter = shown.canvas->grab().toImage();
const QRect region(10, 10, 120, 120);
shown.session.setSelection(DocumentSelection{checkerboard(region)}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
ants(shown).stop();
QTRY_VERIFY(shown.canvas->grab().toImage() != quarter);
// A step in, the quarter's outline stands in meanwhile.
shown.session.zoom(0.5);
shown.canvas->synchronizeDisplay();
QVERIFY(marked(shown.canvas->grab().toImage(), half, band(shown, region)) > 0);
}
void MarchingAntsTests::theStepFollowsTheZoomAndTheFillRuleHolds()
{
// Boards two pixels apart: a half step keeps the gap.
Canvas shown;
shown.session.zoom(0.5);
shown.canvas->synchronizeDisplay();
const QImage bare = shown.canvas->grab().toImage();
QPainterPath boards = checkerboard(QRect(10, 10, 60, 180));
boards.addPath(checkerboard(QRect(72, 10, 60, 180)));
shown.session.setSelection(DocumentSelection{boards}, QStringLiteral("Select"));
shown.canvas->synchronizeDisplay();
ants(shown).stop();
QTRY_VERIFY(shown.canvas->grab().toImage() != bare);
const QSizeF size = shown.documentSize();
const QPointF gap = shown.session.viewport.viewPoint(QPointF(71, 100), size);
QVERIFY(marked(shown.canvas->grab().toImage(), bare, QRect(gap.toPoint() - QPoint(1, 20), QSize(3, 40))) > 0);
// An odd-even ring keeps its hole through the trace.
Canvas ring;
ring.session.zoom(0.25);
ring.canvas->synchronizeDisplay();
const QImage plain = ring.canvas->grab().toImage();
QPainterPath holed = checkerboard(QRect(40, 40, 200, 50));
holed.addRect(QRectF(40, 40, 200, 200));
holed.addRect(QRectF(100, 100, 80, 80));
holed.setFillRule(Qt::OddEvenFill);
ring.session.setSelection(DocumentSelection{holed}, QStringLiteral("Select"));
ring.canvas->synchronizeDisplay();
ants(ring).stop();
QTRY_VERIFY(ring.canvas->grab().toImage() != plain);
QVERIFY(marked(ring.canvas->grab().toImage(), plain, band(ring, QRect(100, 100, 80, 80))) > 0);
}
QTEST_MAIN(MarchingAntsTests)
#include "MarchingAntsTests.moc"