14.19 round 1: Scanlines normalized with the filter, Two Colors rounded, sheet tests apart
This commit is contained in:
4 files changed
+214
-166
No files matched your search
@@ -0,0 +1,196 @@
|
||||
#include "Document/BrushStroke.h"
|
||||
#include "Document/EditorSession.h"
|
||||
#include "Document/Scanlines.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include "UI/FilterSheet.h"
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QSlider>
|
||||
#include <QtTest>
|
||||
|
||||
// Swift 1.4.9's Scanlines in the Filter menu and its sheet.
|
||||
namespace {
|
||||
QImage filled(int width, int height, QColor colour)
|
||||
{
|
||||
QImage image = BrushRaster::context(width, height, false);
|
||||
image.fill(colour);
|
||||
return image;
|
||||
}
|
||||
|
||||
std::vector<int> column(const QImage &image, int x)
|
||||
{
|
||||
std::vector<int> values;
|
||||
for (int y = 0; y < image.height(); ++y)
|
||||
values.push_back(image.constScanLine(y)[x * 4]);
|
||||
return values;
|
||||
}
|
||||
|
||||
struct Shown {
|
||||
EditorSession session;
|
||||
std::unique_ptr<FilterSheet> sheet;
|
||||
Shown()
|
||||
{
|
||||
session.createDocument(40, 30);
|
||||
const QImage image = filled(40, 30, QColor(200, 120, 40));
|
||||
session.insert(ImportedImage(image, image, QStringLiteral("Colour")));
|
||||
session.beginFilter(FilterKind::scanlines);
|
||||
sheet = std::make_unique<FilterSheet>(session);
|
||||
sheet->show();
|
||||
}
|
||||
const ScanlinesSettings &lines() const { return session.filterEdit().value().settings.scanlines; }
|
||||
QStringList rows() const
|
||||
{
|
||||
QStringList titles;
|
||||
QLayout *column = sheet->layout();
|
||||
for (int index = 0; index < column->count(); ++index) {
|
||||
QWidget *each = column->itemAt(index)->widget();
|
||||
if (!each || each->isHidden())
|
||||
continue;
|
||||
for (QLabel *label : each->findChildren<QLabel *>(QString(), Qt::FindDirectChildrenOnly)) {
|
||||
if (label->buddy())
|
||||
titles << label->text() + u'|' + each->toolTip();
|
||||
}
|
||||
}
|
||||
return titles;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class ScanlinesSheetTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void theFilterIsListedAndPreviewsWhole();
|
||||
void theSheetShowsSwiftsRows();
|
||||
void theRowsWriteTheirSettings();
|
||||
void twoColorsPickTheScreenAndTheLines();
|
||||
};
|
||||
|
||||
void ScanlinesSheetTests::theFilterIsListedAndPreviewsWhole()
|
||||
{
|
||||
QCOMPARE(rawValue(FilterKind::scanlines), QString("Scanlines"));
|
||||
const auto at = std::find(allFilterKinds.begin(), allFilterKinds.end(), FilterKind::scanlines);
|
||||
QVERIFY(at != allFilterKinds.end() && *(at - 1) == FilterKind::dither && *(at + 1) == FilterKind::tonalContrast);
|
||||
QVERIFY(!isImageAdjustment(FilterKind::scanlines) && !isAutomatic(FilterKind::scanlines) && repeatsAsLastFilter(FilterKind::scanlines));
|
||||
EditorSession session;
|
||||
session.createDocument(3000, 20);
|
||||
const QImage wide = filled(3000, 20, Qt::white);
|
||||
session.insert(ImportedImage(wide, wide, QStringLiteral("Wide")));
|
||||
session.beginFilter(FilterKind::scanlines);
|
||||
QCOMPARE(session.filterEdit().value().previewScale, 1.0);
|
||||
// Its commit is one step that draws the lines.
|
||||
QVERIFY(session.filterEdit().value().settings.scanlines == ScanlinesSettings());
|
||||
bool done = false;
|
||||
session.commitFilter([&done] { done = true; });
|
||||
QTRY_VERIFY(done);
|
||||
QCOMPARE(session.history.undoName(), QString("Scanlines"));
|
||||
const QImage drawn = session.document().value().layers.front().asset.value().image();
|
||||
// Beam 1.4 round the middle 2: row 0 covers 0.4.
|
||||
QCOMPARE(column(drawn, 5)[0], int(std::lround(255 * 1.35f * 0.4f)));
|
||||
QCOMPARE(column(drawn, 5)[2], 255);
|
||||
}
|
||||
|
||||
void ScanlinesSheetTests::theSheetShowsSwiftsRows()
|
||||
{
|
||||
Shown shown;
|
||||
QCOMPARE(shown.rows(),
|
||||
(QStringList{"Line Spacing|How far apart the screen's lines are", "Thickness|How much of the gap a bright line fills; dimmer parts draw it thinner",
|
||||
"Glow|Light blooming around the lines, like a CRT's phosphors",
|
||||
"Dots|Break the lines into beads where the picture is darker than this, blending into solid lines where it's brighter",
|
||||
"Displace|Lift the lines where the picture is bright, or lower them when negative, so they swell into its shapes",
|
||||
"Smoothness|How far the brightness is smoothed before it displaces the lines, from sharp ridges to rounded hills",
|
||||
"Threshold|Draw no line where the picture is darker than this, leaving the screen dark",
|
||||
"Wobble|Make the lines waver sideways down the screen, like a CRT losing sync",
|
||||
"Color Split|Move the red and blue apart, for colored fringes on the lines", "Density|Darker or lighter before the lines are drawn",
|
||||
"Contrast|", "Black Level|How bright the lines are where the picture is black, so they show even over black", "Colors|"}));
|
||||
using Field = double ScanlinesSettings::*;
|
||||
const std::tuple<const char *, Field, double, double, QString> sliders[] = {
|
||||
{"lineSpacing", &ScanlinesSettings::lineSpacing, 2, 32, "px"}, {"thickness", &ScanlinesSettings::thickness, 5, 100, "%"},
|
||||
{"glow", &ScanlinesSettings::glow, 0, 100, "%"}, {"dots", &ScanlinesSettings::dots, 0, 100, "%"},
|
||||
{"displace", &ScanlinesSettings::displace, -100, 100, "px"}, {"smoothness", &ScanlinesSettings::smoothness, 0, 100, "%"},
|
||||
{"threshold", &ScanlinesSettings::threshold, 0, 100, "%"}, {"wobble", &ScanlinesSettings::wobble, 0, 64, "px"},
|
||||
{"colorSplit", &ScanlinesSettings::split, 0, 16, "px"}, {"density", &ScanlinesSettings::density, -100, 100, ""},
|
||||
{"contrast", &ScanlinesSettings::contrast, -100, 100, ""}, {"blackLevel", &ScanlinesSettings::blackLevel, 0, 100, "%"}};
|
||||
for (const auto &[name, field, low, high, unit] : sliders) {
|
||||
QSlider *slider = shown.sheet->findChild<QSlider *>(QString::fromLatin1(name) + "Slider");
|
||||
QLineEdit *edit = shown.sheet->findChild<QLineEdit *>(QString::fromLatin1(name) + "Field");
|
||||
QVERIFY2(slider && edit, name);
|
||||
slider->setValue(slider->maximum());
|
||||
QCOMPARE(shown.lines().*field, high);
|
||||
slider->setValue(slider->minimum());
|
||||
QCOMPARE(shown.lines().*field, low);
|
||||
QCOMPARE(edit->parentWidget()->findChildren<QLabel *>(QString(), Qt::FindDirectChildrenOnly).last()->text(), unit);
|
||||
}
|
||||
}
|
||||
|
||||
void ScanlinesSheetTests::theRowsWriteTheirSettings()
|
||||
{
|
||||
Shown shown;
|
||||
const std::tuple<const char *, double ScanlinesSettings::*, const char *, double> rows[] = {
|
||||
{"lineSpacingField", &ScanlinesSettings::lineSpacing, "12", 12}, {"displaceField", &ScanlinesSettings::displace, "-30", -30},
|
||||
{"colorSplitField", &ScanlinesSettings::split, "5", 5}, {"blackLevelField", &ScanlinesSettings::blackLevel, "40", 40},
|
||||
{"smoothnessField", &ScanlinesSettings::smoothness, "10", 10}, {"thicknessField", &ScanlinesSettings::thickness, "55", 55}};
|
||||
for (const auto &[name, field, typed, expected] : rows) {
|
||||
ScanlinesSettings wanted = shown.lines();
|
||||
QLineEdit *edit = shown.sheet->findChild<QLineEdit *>(QString::fromLatin1(name));
|
||||
QVERIFY2(edit, name);
|
||||
edit->setFocus();
|
||||
edit->selectAll();
|
||||
QTest::keyClicks(edit, QString::fromLatin1(typed));
|
||||
QTest::keyClick(edit, Qt::Key_Return);
|
||||
wanted.*field = expected;
|
||||
QVERIFY2(shown.lines() == wanted, name);
|
||||
}
|
||||
}
|
||||
|
||||
void ScanlinesSheetTests::twoColorsPickTheScreenAndTheLines()
|
||||
{
|
||||
Shown shown;
|
||||
auto *colors = shown.sheet->findChild<QComboBox *>(QStringLiteral("scanlinesColors"));
|
||||
auto *dark = shown.sheet->findChild<SwatchButton *>(QStringLiteral("scanlinesDark"));
|
||||
auto *light = shown.sheet->findChild<SwatchButton *>(QStringLiteral("scanlinesLight"));
|
||||
QVERIFY(colors && dark && light && !dark->isVisible());
|
||||
QVERIFY(dark->toolTip() == QString("Choose the screen's color") && light->toolTip() == QString("Choose the lines' color"));
|
||||
colors->activated(int(DitherColors::twoColors));
|
||||
QVERIFY(shown.lines().colors == DitherColors::twoColors && dark->isVisible() && light->isVisible());
|
||||
// The menu follows the settings, however they change.
|
||||
FilterSettings original = shown.session.filterEdit().value().settings;
|
||||
original.scanlines.colors = DitherColors::original;
|
||||
shown.session.updateFilter(original, true);
|
||||
QVERIFY(colors->currentIndex() == int(DitherColors::original) && !dark->isVisible());
|
||||
original.scanlines.colors = DitherColors::twoColors;
|
||||
shown.session.updateFilter(original, true);
|
||||
QCOMPARE(colors->currentIndex(), int(DitherColors::twoColors));
|
||||
// The picker starts from the lines' own colour.
|
||||
FilterSettings settings = shown.session.filterEdit().value().settings;
|
||||
settings.scanlines.light = AdjustmentColor(0, 1, 0), settings.scanlines.dark = AdjustmentColor(0.2, 0, 0);
|
||||
shown.session.updateFilter(settings, true);
|
||||
light->click();
|
||||
QCOMPARE(shown.session.colorPicker().value().target.title(), QString("Color Picker (Light Color)"));
|
||||
QVERIFY(shown.session.colorPicker().value().original == (PaletteColor{0, 1, 0}));
|
||||
struct Paints : QObject {
|
||||
int count = 0;
|
||||
bool eventFilter(QObject *, QEvent *event) override { return count += event->type() == QEvent::Paint, false; }
|
||||
} paints;
|
||||
QCoreApplication::processEvents();
|
||||
light->installEventFilter(&paints);
|
||||
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{1, 0, 0}));
|
||||
shown.session.previewDitherColor();
|
||||
// A new colour repaints the swatch.
|
||||
QTRY_VERIFY(paints.count > 0);
|
||||
QVERIFY(shown.lines().light == AdjustmentColor(1, 0, 0));
|
||||
QVERIFY(shown.session.filterEdit().value().settings.dither.light == AdjustmentColor(1, 1, 1));
|
||||
QCOMPARE(light->grab().toImage().pixelColor(12, 12), QColor(255, 0, 0));
|
||||
shown.session.closeColorPicker(false);
|
||||
QVERIFY(shown.lines().light == AdjustmentColor(0, 1, 0));
|
||||
dark->click();
|
||||
QCOMPARE(shown.session.colorPicker().value().target.title(), QString("Color Picker (Dark Color)"));
|
||||
QVERIFY(shown.session.colorPicker().value().original == (PaletteColor{0.2, 0, 0}));
|
||||
shown.session.setColorPickerHSB(PickerHSB(PaletteColor{0, 0, 1}));
|
||||
shown.session.closeColorPicker(true);
|
||||
QVERIFY(shown.lines().dark == AdjustmentColor(0, 0, 1));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ScanlinesSheetTests)
|
||||
#include "ScanlinesSheetTests.moc"
|
||||
Reference in new issue
Block a user