Files
OmaPhoto/tests/MaskEffectsCanvasTests.cpp

160 lines
6.9 KiB
C++

#include "EffectsCanvasFixtures.h"
// Swift 1.2.11 (b683acb): a painted mask keeps effects on.
namespace {
bool nearly(QColor colour, QColor expected)
{
return std::abs(colour.red() - expected.red()) <= 40 && std::abs(colour.green() - expected.green()) <= 40
&& std::abs(colour.blue() - expected.blue()) <= 40;
}
// Black down the mask, eight wide; a later pass cuts.
void paintHole(Scene &scene)
{
EditorSession &session = scene.session;
session.selectLayerTarget(session.activeLayerID().value(), true);
session.selectTool(NavigationTool::brush);
session.setMaskPaintWhite(false);
BrushSettings settings = session.brushSettings();
settings.diameter = 8;
settings.hardness = 1;
session.setBrushSettings(settings);
session.beginBrush(QPointF(30, 2));
session.continueBrush(QPointF(30, 6));
scene.shot();
session.continueBrush(QPointF(30, 35));
}
}
class MaskEffectsCanvasTests : public QObject {
Q_OBJECT
private slots:
void aPaintedMaskRedoesTheEffects();
void aPlacedMaskRedoesThemInTheLayersGrid();
void untouchedTilesKeepTheOldMask();
void aFinerPlacedMaskMapsIntoTheLayersGrid();
};
void MaskEffectsCanvasTests::aPaintedMaskRedoesTheEffects()
{
Scene scene;
scene.session.setEffects(outline(2));
scene.session.addLayerMask(true);
scene.landed();
QVERIFY(nearly(scene.at(QPoint(27, 20)), QColor(0, 0, 255)));
paintHole(scene);
// Mid-stroke the outline follows the hole: its middle is clear.
QVERIFY2(nearly(scene.at(QPoint(27, 20)), yellow), qPrintable(scene.at(QPoint(27, 20)).name()));
QVERIFY2(nearly(scene.at(QPoint(33, 20)), yellow), qPrintable(scene.at(QPoint(33, 20)).name()));
QCOMPARE(scene.at(QPoint(30, 20)), QColor(Qt::white));
QVERIFY(nearly(scene.at(QPoint(25, 20)), QColor(0, 0, 255)));
// Committed, the surface stands in until new effects land.
QVERIFY(scene.session.finishBrushImmediately());
scene.canvas.synchronizeDisplay();
QVERIFY(scene.session.effectsPreviews.rendered(scene.square).value().placement);
QVERIFY(nearly(scene.at(QPoint(27, 20)), yellow));
QTRY_VERIFY(!scene.session.effectsPreviews.rendered(scene.square).value().placement);
QVERIFY(nearly(scene.at(QPoint(27, 20)), yellow));
}
void MaskEffectsCanvasTests::aPlacedMaskRedoesThemInTheLayersGrid()
{
Scene scene;
scene.session.setEffects(outline(2));
scene.session.addLayerMask(true);
// A white mask in the layer's grid, placed on it.
rewrite(scene.session, [&](ProjectSnapshot &snapshot) {
ProjectLayerRecord &square = record(snapshot, scene.square);
QImage white = BrushRaster::context(10, 10, true);
white.fill(255);
snapshot.masks.insert_or_assign(scene.square, ImportedImage(white, white, QStringLiteral("Mask")));
square.maskPlacement = square.transform;
square.maskLinked = false;
});
scene.session.selectLayer(scene.square);
QVERIFY(layerWith(scene.session, scene.square).mask.value().placement);
scene.landed();
paintHole(scene);
QVERIFY2(nearly(scene.at(QPoint(27, 20)), yellow), qPrintable(scene.at(QPoint(27, 20)).name()));
QVERIFY2(nearly(scene.at(QPoint(33, 20)), yellow), qPrintable(scene.at(QPoint(33, 20)).name()));
QCOMPARE(scene.at(QPoint(30, 20)), QColor(Qt::white));
QVERIFY(scene.session.finishBrushImmediately());
scene.canvas.synchronizeDisplay();
QVERIFY(nearly(scene.at(QPoint(27, 20)), yellow));
}
void MaskEffectsCanvasTests::untouchedTilesKeepTheOldMask()
{
// A layer wider than a tile, painted at one end.
EditorSession session;
CanvasView canvas{session};
session.createDocument(700, 40);
canvas.resize(700, 40);
session.viewport.resize(QSizeF(700, 40), 1, QSizeF(700, 40));
session.zoom(1);
const QImage blue = solid(600, 10, qRgba(0, 0, 255, 255));
session.insert(ImportedImage(blue, blue, QStringLiteral("Wide")), QPointF(350, 20));
session.setEffects(outline(2));
session.addLayerMask(true);
canvas.grab();
session.selectLayerTarget(session.activeLayerID().value(), true);
session.selectTool(NavigationTool::brush);
session.setMaskPaintWhite(false);
session.beginBrush(QPointF(100, 20));
const QImage shot = canvas.grab().toImage();
const QPointF far = session.viewport.viewPoint(QPointF(600.5, 20.5), QSizeF(700, 40));
QCOMPARE(shot.pixelColor(far.toPoint()), QColor(0, 0, 255));
}
void MaskEffectsCanvasTests::aFinerPlacedMaskMapsIntoTheLayersGrid()
{
// The committed mask's landed effects are the oracle.
Scene scene;
scene.session.setEffects(outline(2));
scene.session.addLayerMask(true);
rewrite(scene.session, [&](ProjectSnapshot &snapshot) {
ProjectLayerRecord &square = record(snapshot, scene.square);
QImage white = BrushRaster::context(600, 600, true);
white.fill(255);
snapshot.masks.insert_or_assign(scene.square, ImportedImage(white, white, QStringLiteral("Mask")));
square.maskPlacement = square.transform;
square.maskLinked = false;
});
scene.session.selectLayer(scene.square);
scene.landed();
scene.session.selectLayerTarget(scene.square, true);
scene.session.selectTool(NavigationTool::brush);
scene.session.setMaskPaintWhite(false);
BrushSettings settings = scene.session.brushSettings();
settings.diameter = 1;
settings.hardness = 1;
scene.session.setBrushSettings(settings);
scene.session.beginBrush(QPointF(32, 16));
scene.shot();
scene.session.continueBrush(QPointF(32, 24));
const QImage painting = scene.shot();
// The oracle: a whole render through the stroke's mask.
const ImageLayer square = layerWith(scene.session, scene.square);
const QImage preview = scene.session.brushStroke()->placedMaskPreview(scene.session.brushStroke()->paintTransform).value();
const LayerEffectsRenderer::Rendered expected = LayerEffectsRenderer::render(square.asset.value().image(), preview, outline(2));
int largest = 0;
for (int y = 10; y < 30; ++y) {
for (int x = 20; x < 40; ++x) {
const QPoint at(x - 25 + int(expected.inset), y - 15 + int(expected.inset));
if (!expected.image.rect().contains(at))
continue;
const QColor over = expected.image.pixelColor(at);
const QColor shown = painting.pixelColor(scene.shown(QPointF(x + 0.5, y + 0.5)));
// Over white, premultiplied: the channel plus what shows through.
const auto on = [&](int channel) { return channel + 255 - over.alpha(); };
largest = std::max({largest, std::abs(shown.red() - on(qRound(over.redF() * over.alphaF() * 255))),
std::abs(shown.green() - on(qRound(over.greenF() * over.alphaF() * 255))),
std::abs(shown.blue() - on(qRound(over.blueF() * over.alphaF() * 255)))});
}
}
QVERIFY2(largest <= 2, qPrintable(QString::number(largest)));
}
QTEST_MAIN(MaskEffectsCanvasTests)
#include "MaskEffectsCanvasTests.moc"