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
+16
-165
@@ -2,14 +2,7 @@
|
||||
#include "Document/EditorSession.h"
|
||||
#include "Document/PixelAdjust.h"
|
||||
#include "Document/Scanlines.h"
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include "UI/FilterSheet.h"
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QPainter>
|
||||
#include <QSlider>
|
||||
#include <QtTest>
|
||||
#include <numeric>
|
||||
extern "C" {
|
||||
@@ -102,35 +95,6 @@ QImage kernel(int width, int height, const ScanlinesParams ¶ms)
|
||||
return image;
|
||||
}
|
||||
|
||||
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 ScanlinesTests : public QObject {
|
||||
@@ -149,10 +113,6 @@ private slots:
|
||||
void normalizingClampsEverySetting();
|
||||
void clearPixelsStayClear();
|
||||
void theGlowIsTheBlurredLightEasedNearWhite();
|
||||
void theFilterIsListedAndPreviewsWhole();
|
||||
void theSheetShowsSwiftsRows();
|
||||
void theRowsWriteTheirSettings();
|
||||
void twoColorsPickTheScreenAndTheLines();
|
||||
};
|
||||
|
||||
void ScanlinesTests::scanlinesAreLinesOfLightThatBloomWithBrightness()
|
||||
@@ -279,6 +239,17 @@ void ScanlinesTests::theSettingsReachTheKernel()
|
||||
settings.lineSpacing = 5, settings.thickness = 40, settings.contrast = 60, settings.colors = DitherColors::twoColors;
|
||||
settings.dark = AdjustmentColor(10 / 255.0, 20 / 255.0, 200 / 255.0), settings.light = AdjustmentColor(250 / 255.0, 200 / 255.0, 10 / 255.0);
|
||||
QCOMPARE(settings.apply(pattern(37, 53)), kernel(37, 53, made([](ScanlinesParams &p) { p.spacing = 5; p.thickness = 0.4f; p.contrast = 0.6f; p.smoothness = 0.5f; rgb(p.dark, 10, 20, 200); rgb(p.light, 250, 200, 10); })));
|
||||
// Half a channel rounds to 128, lit at 1.35 times.
|
||||
ScanlinesSettings halves;
|
||||
halves.colors = DitherColors::twoColors, halves.dark = AdjustmentColor(0.5, 0.5, 0.5), halves.light = AdjustmentColor(0.5, 0.5, 0.5);
|
||||
const QImage greyLines = halves.apply(filled(8, 8, Qt::white));
|
||||
const int lit = int(std::lround(128 / 255.0f * 1.35f * 255));
|
||||
const auto pixel = [&greyLines](int y) {
|
||||
const uchar *at = greyLines.constScanLine(y);
|
||||
return std::array<int, 4>{at[0], at[1], at[2], at[3]};
|
||||
};
|
||||
QCOMPARE(pixel(1), (std::array<int, 4>{lit, lit, lit, 255}));
|
||||
QCOMPARE(pixel(0)[0], int(std::lround(128 + (lit - 128) * 0.4f)));
|
||||
// Picked colours count only for Two Colors.
|
||||
settings.colors = DitherColors::blackWhite;
|
||||
QCOMPARE(settings.apply(pattern(37, 53)), kernel(37, 53, made([](ScanlinesParams &p) { p.spacing = 5; p.thickness = 0.4f; p.contrast = 0.6f; p.smoothness = 0.5f; })));
|
||||
@@ -317,6 +288,11 @@ void ScanlinesTests::normalizingClampsEverySetting()
|
||||
wild.lineSpacing = 5.6, wild.thickness = 500, wild.split = 2.4, wild.displace = 300;
|
||||
QCOMPARE(wild.apply(pattern(37, 53)), wild.normalized().apply(pattern(37, 53)));
|
||||
QCOMPARE(fnv(wild.apply(pattern(37, 53))), fnv(kernel(37, 53, made([](ScanlinesParams &p) { p.spacing = 6; p.thickness = 1; p.split = 2; p.displace = 100; p.smoothness = 0.5f; }))));
|
||||
// The filter's settings normalize Scanlines with the rest.
|
||||
FilterSettings filter;
|
||||
filter.scanlines = wild;
|
||||
QVERIFY(filter.normalized().scanlines == wild.normalized());
|
||||
QVERIFY(filter.normalized().scanlines.lineSpacing == 6 && filter.normalized().scanlines.thickness == 100 && filter.normalized().scanlines.split == 2);
|
||||
ScanlinesSettings colours;
|
||||
colours.dark = AdjustmentColor(-1, 2, 0.5), colours.light = AdjustmentColor(3, -2, 0.25);
|
||||
QVERIFY(colours.normalized().dark == AdjustmentColor(0, 1, 0.5) && colours.normalized().light == AdjustmentColor(1, 0, 0.25));
|
||||
@@ -358,130 +334,5 @@ void ScanlinesTests::theGlowIsTheBlurredLightEasedNearWhite()
|
||||
}
|
||||
}
|
||||
|
||||
void ScanlinesTests::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 ScanlinesTests::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 ScanlinesTests::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 ScanlinesTests::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(ScanlinesTests)
|
||||
#include "ScanlinesTests.moc"
|
||||
Reference in new issue
Block a user