Camera Raw panel's remaining groups and the Filter menu entry (12.8b2)
This commit is contained in:
1 parent
bb8129e8d5
commit
7ae263de80
26 files changed
+3227
-82
No files matched your search
@@ -0,0 +1,260 @@
|
||||
#include "Document/BrushStroke.h"
|
||||
#include "Document/EditorSession.h"
|
||||
#include "UI/CameraRawColorControls.h"
|
||||
#include "UI/LayerIcons.h"
|
||||
#include <QAbstractButton>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QStyleOptionSlider>
|
||||
#include <QtTest>
|
||||
|
||||
// Swift's Curve and Color Mixer groups.
|
||||
namespace {
|
||||
// The knob's middle, where a double click resets.
|
||||
QPoint knob(const CameraRawSlider &slider)
|
||||
{
|
||||
QStyleOptionSlider option;
|
||||
option.initFrom(&slider);
|
||||
option.orientation = Qt::Horizontal;
|
||||
option.minimum = slider.minimum();
|
||||
option.maximum = slider.maximum();
|
||||
option.sliderPosition = slider.sliderPosition();
|
||||
option.sliderValue = slider.value();
|
||||
return slider.style()->subControlRect(QStyle::CC_Slider, &option, QStyle::SC_SliderHandle, &slider).center();
|
||||
}
|
||||
|
||||
template <typename Controls> struct Group {
|
||||
EditorSession session;
|
||||
std::unique_ptr<Controls> controls;
|
||||
Group()
|
||||
{
|
||||
session.createDocument(8, 8);
|
||||
QImage image = BrushRaster::context(8, 8, false);
|
||||
image.fill(QColor(200, 120, 60));
|
||||
session.insert(ImportedImage(image, image, QStringLiteral("Warm")));
|
||||
session.beginFilter(FilterKind::cameraRaw);
|
||||
controls = std::make_unique<Controls>(session);
|
||||
controls->resize(374, 600);
|
||||
controls->show();
|
||||
}
|
||||
template <typename Widget> Widget &child(const QString &name) const
|
||||
{
|
||||
auto *found = controls->template findChild<Widget *>(name);
|
||||
if (!found)
|
||||
throw std::runtime_error(name.toStdString());
|
||||
return *found;
|
||||
}
|
||||
const CameraRawSettings &raw() const { return session.filterEdit().value().settings.cameraRaw; }
|
||||
const CameraRawPanel &panel() const { return session.filterEdit().value().rawPanel; }
|
||||
void set(const std::function<void(CameraRawSettings &)> &change)
|
||||
{
|
||||
FilterSettings settings = session.filterEdit().value().settings;
|
||||
change(settings.cameraRaw);
|
||||
session.updateFilter(settings, true);
|
||||
}
|
||||
};
|
||||
|
||||
void press(QWidget &widget, QPoint at)
|
||||
{
|
||||
QTest::mousePress(&widget, Qt::LeftButton, {}, at);
|
||||
}
|
||||
|
||||
void drag(QWidget &widget, QPoint to)
|
||||
{
|
||||
QMouseEvent move(QEvent::MouseMove, to, widget.mapToGlobal(to), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
|
||||
QApplication::sendEvent(&widget, &move);
|
||||
}
|
||||
}
|
||||
|
||||
class CameraRawCurveMixerTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void theParametricPageMovesRegionsAndDividers();
|
||||
void thePointPageAddsMovesAndRemovesPoints();
|
||||
void presetsAndRefineSaturation();
|
||||
void targetedAdjustmentArmsOneAtATime();
|
||||
void hslRowsWriteTheirTabsFamilies();
|
||||
void theColorPageEditsTheChosenFamily();
|
||||
void pointColorsArePickedChosenAndShifted();
|
||||
};
|
||||
|
||||
void CameraRawCurveMixerTests::theParametricPageMovesRegionsAndDividers()
|
||||
{
|
||||
Group<CameraRawCurveControls> group;
|
||||
QVERIFY(group.child<QToolButton>(QStringLiteral("curvePage0")).isChecked() && !group.child<QWidget>(QStringLiteral("curveSelectedPoint")).isVisible());
|
||||
for (const auto &[name, member] : {std::pair("curveHighlights", &CameraRawCurveSettings::highlights), std::pair("curveLights", &CameraRawCurveSettings::lights),
|
||||
std::pair("curveDarks", &CameraRawCurveSettings::darks), std::pair("curveShadows", &CameraRawCurveSettings::shadows)}) {
|
||||
auto &slider = group.child<CameraRawSlider>(QString::fromLatin1(name) + QStringLiteral("Slider"));
|
||||
slider.setValue(800);
|
||||
QCOMPARE(group.raw().curve.*member, 60.0);
|
||||
// A double click on the knob resets it.
|
||||
QTest::mouseDClick(&slider, Qt::LeftButton, {}, knob(slider));
|
||||
QCOMPARE(group.raw().curve.*member, 0.0);
|
||||
}
|
||||
// A drag moves the nearest divider, within 2 and 98.
|
||||
auto &graph = group.child<QWidget>(QStringLiteral("cameraRawCurveGraph"));
|
||||
press(graph, QPoint(int(graph.width() * 0.3), 75));
|
||||
drag(graph, QPoint(int(graph.width() * 0.35), 75));
|
||||
QVERIFY(std::abs(group.raw().curve.shadowSplit - 35) < 1 && group.raw().curve.darkSplit == 50);
|
||||
drag(graph, QPoint(graph.width() + 40, 75));
|
||||
QVERIFY(group.raw().curve.lightSplit == 98 || group.raw().curve.shadowSplit == 98 || group.raw().curve.darkSplit == 98);
|
||||
QTest::mouseRelease(&graph, Qt::LeftButton, {}, QPoint(graph.width() + 40, 75));
|
||||
// A double click here leaves the point curve's points.
|
||||
group.set([](CameraRawSettings &settings) { settings.curve.rgb = {{0, 0}, {0.5, 0.5}, {1, 1}}; });
|
||||
QTest::mouseDClick(&graph, Qt::LeftButton, {}, QPoint(graph.width() / 2, graph.height() / 2));
|
||||
QCOMPARE(group.raw().curve.rgb.size(), size_t(3));
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::thePointPageAddsMovesAndRemovesPoints()
|
||||
{
|
||||
Group<CameraRawCurveControls> group;
|
||||
group.child<QToolButton>(QStringLiteral("curvePage1")).click();
|
||||
QVERIFY(group.panel().curvePage == CameraRawCurvePage::point);
|
||||
group.child<QToolButton>(QStringLiteral("pointChannel1")).click();
|
||||
QVERIFY(group.panel().pointChannel == CameraRawPointChannel::red);
|
||||
auto &graph = group.child<QWidget>(QStringLiteral("cameraRawCurveGraph"));
|
||||
const int w = graph.width(), h = graph.height();
|
||||
// A still click adds a point where it lands.
|
||||
QTest::mouseClick(&graph, Qt::LeftButton, {}, QPoint(w / 2, h / 4));
|
||||
QCOMPARE(group.raw().curve.red.size(), size_t(3));
|
||||
QVERIFY(std::abs(group.raw().curve.red[1].x - 0.5) < 0.01 && std::abs(group.raw().curve.red[1].y - 0.75) < 0.01);
|
||||
QVERIFY(group.raw().curve.rgb == CameraRawCurveSettings::linear());
|
||||
QCOMPARE(group.child<QLabel>(QStringLiteral("curveSelectedPoint")).text(), QString("In %1 Out %2").arg(std::lround(group.raw().curve.red[1].x * 255))
|
||||
.arg(std::lround(group.raw().curve.red[1].y * 255)));
|
||||
// A drag moves the nearest inner point.
|
||||
press(graph, QPoint(w / 2, h / 4));
|
||||
drag(graph, QPoint(int(w * 0.6), h / 2));
|
||||
QTest::mouseRelease(&graph, Qt::LeftButton, {}, QPoint(int(w * 0.6), h / 2));
|
||||
QCOMPARE(group.raw().curve.red.size(), size_t(3));
|
||||
QVERIFY(std::abs(group.raw().curve.red[1].x - 0.6) < 0.01 && std::abs(group.raw().curve.red[1].y - 0.5) < 0.01);
|
||||
// A double click far off removes nothing; near, it does.
|
||||
QTest::mouseDClick(&graph, Qt::LeftButton, {}, QPoint(int(w * 0.1), h / 2));
|
||||
QCOMPARE(group.raw().curve.red.size(), size_t(3));
|
||||
const size_t before = group.raw().curve.red.size();
|
||||
QTest::mouseDClick(&graph, Qt::LeftButton, {}, QPoint(int(w * 0.6), h / 2));
|
||||
QCOMPARE(group.raw().curve.red.size(), before - 1);
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::presetsAndRefineSaturation()
|
||||
{
|
||||
Group<CameraRawCurveControls> group;
|
||||
group.child<QToolButton>(QStringLiteral("curvePage1")).click();
|
||||
auto &preset = group.child<QComboBox>(QStringLiteral("curvePreset"));
|
||||
QCOMPARE(preset.currentText(), QString("Linear"));
|
||||
emit preset.activated(3);
|
||||
QVERIFY(group.raw().curve.rgb == CameraRawCurveSettings::strongContrast());
|
||||
QCOMPARE(preset.currentText(), QString("Strong Contrast"));
|
||||
emit preset.activated(2);
|
||||
QVERIFY(group.raw().curve.rgb == CameraRawCurveSettings::mediumContrast());
|
||||
emit preset.activated(1);
|
||||
QVERIFY(group.raw().curve.rgb == CameraRawCurveSettings::linear());
|
||||
group.set([](CameraRawSettings &settings) { settings.curve.rgb = {{0, 0}, {0.4, 0.3}, {1, 1}}; });
|
||||
QCOMPARE(preset.currentText(), QString("Custom"));
|
||||
// Custom changes nothing; Refine Saturation shows for RGB alone.
|
||||
emit preset.activated(0);
|
||||
QCOMPARE(group.raw().curve.rgb.size(), size_t(3));
|
||||
auto &refine = group.child<CameraRawSlider>(QStringLiteral("refineSaturationSlider"));
|
||||
QVERIFY(refine.isVisible());
|
||||
refine.setValue(750);
|
||||
QCOMPARE(group.raw().curve.refineSaturation, 50.0);
|
||||
group.child<QToolButton>(QStringLiteral("pointChannel2")).click();
|
||||
QVERIFY(!refine.isVisible());
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::targetedAdjustmentArmsOneAtATime()
|
||||
{
|
||||
Group<CameraRawCurveControls> curve;
|
||||
CameraRawMixerControls mixer(curve.session);
|
||||
auto &curveTarget = curve.child<QPushButton>(QStringLiteral("curveTargeted"));
|
||||
auto *mixerTarget = mixer.findChild<QPushButton *>(QStringLiteral("mixerTargeted"));
|
||||
curveTarget.click();
|
||||
QVERIFY(curve.panel().targetsCurve && curveTarget.isChecked());
|
||||
mixerTarget->click();
|
||||
QVERIFY(curve.panel().targetsMixer && !curve.panel().targetsCurve && !curveTarget.isChecked() && mixerTarget->isChecked());
|
||||
mixerTarget->click();
|
||||
QVERIFY(!curve.panel().targetsMixer);
|
||||
mixerTarget->click();
|
||||
curveTarget.click();
|
||||
QVERIFY(curve.panel().targetsCurve && !curve.panel().targetsMixer);
|
||||
// Both carry Swift's scope glyph.
|
||||
const qreal ratio = curveTarget.devicePixelRatioF();
|
||||
const QImage scope = LayerIcons::pixmap(LayerIcon::scope, 14, curveTarget.palette().color(QPalette::ButtonText), ratio).toImage();
|
||||
QVERIFY(curveTarget.icon().pixmap(QSize(14, 14), ratio).toImage() == scope && mixerTarget->icon().pixmap(QSize(14, 14), ratio).toImage() == scope);
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::hslRowsWriteTheirTabsFamilies()
|
||||
{
|
||||
Group<CameraRawMixerControls> group;
|
||||
QVERIFY(group.child<QToolButton>(QStringLiteral("mixerPage0")).isChecked());
|
||||
for (int tab = 0; tab < 3; ++tab) {
|
||||
group.child<QToolButton>(QStringLiteral("mixerTab%1").arg(tab)).click();
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
const QString name = CameraRawMixerSettings::names.at(size_t(index));
|
||||
auto &slider = group.child<CameraRawSlider>(QStringLiteral("family") + name + QStringLiteral("Slider"));
|
||||
QCOMPARE(slider.toolTip(), QStringLiteral("%1 of %2.").arg(tab == 0 ? "Hue" : tab == 1 ? "Saturation" : "Luminance", name));
|
||||
slider.setValue(600 + index * 10);
|
||||
const std::array<double, 8> &family = tab == 0 ? group.raw().mixer.hue : tab == 1 ? group.raw().mixer.saturation : group.raw().mixer.luminance;
|
||||
QVERIFY(std::abs(family.at(size_t(index)) - (20 + index * 2)) < 1e-9);
|
||||
}
|
||||
}
|
||||
// Each tab's rows show its own track: saturation starts gray.
|
||||
const QImage drawn = group.child<CameraRawSlider>(QStringLiteral("familyGreensSlider")).grab().toImage();
|
||||
group.child<QToolButton>(QStringLiteral("mixerTab1")).click();
|
||||
const QImage gray = group.child<CameraRawSlider>(QStringLiteral("familyGreensSlider")).grab().toImage();
|
||||
QVERIFY(drawn != gray);
|
||||
const QColor start = gray.pixelColor(10, gray.height() / 2);
|
||||
QVERIFY(std::abs(start.red() - start.green()) < 12);
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::theColorPageEditsTheChosenFamily()
|
||||
{
|
||||
Group<CameraRawMixerControls> group;
|
||||
group.child<QToolButton>(QStringLiteral("mixerPage1")).click();
|
||||
QVERIFY(group.panel().mixerPage == CameraRawMixerPage::color);
|
||||
group.child<QAbstractButton>(QStringLiteral("mixerSwatch5")).click();
|
||||
QCOMPARE(group.panel().mixerSwatch, 5);
|
||||
group.child<CameraRawSlider>(QStringLiteral("colorLuminanceSlider")).setValue(250);
|
||||
QCOMPARE(group.raw().mixer.luminance[5], -50.0);
|
||||
QCOMPARE(group.raw().mixer.luminance[4], 0.0);
|
||||
group.child<CameraRawSlider>(QStringLiteral("colorHueSlider")).setValue(750);
|
||||
QCOMPARE(group.raw().mixer.hue[5], 50.0);
|
||||
QVERIFY(!group.controls->findChild<QWidget *>(QStringLiteral("colorHueField")));
|
||||
}
|
||||
|
||||
void CameraRawCurveMixerTests::pointColorsArePickedChosenAndShifted()
|
||||
{
|
||||
Group<CameraRawMixerControls> group;
|
||||
group.child<QToolButton>(QStringLiteral("mixerPage2")).click();
|
||||
group.child<QToolButton>(QStringLiteral("pointColorSampler")).click();
|
||||
QVERIFY(group.panel().samplesPointColor);
|
||||
QVERIFY(!group.child<QWidget>(QStringLiteral("visualizeRange")).isVisible());
|
||||
group.set([](CameraRawSettings &settings) {
|
||||
settings.mixer.points = {CameraRawPointColor{.hue = 30, .saturation = 1, .luminance = 0.5},
|
||||
CameraRawPointColor{.hue = 200, .saturation = 0.5, .luminance = 0.5}};
|
||||
});
|
||||
QVERIFY(group.child<QAbstractButton>(QStringLiteral("pickedColor1")).isVisible());
|
||||
group.child<QAbstractButton>(QStringLiteral("pickedColor1")).click();
|
||||
QCOMPARE(group.panel().pointIndex, 1);
|
||||
group.child<CameraRawSlider>(QStringLiteral("hueShiftSlider")).setValue(750);
|
||||
QCOMPARE(group.raw().mixer.points[1].hueShift, 50.0);
|
||||
QCOMPARE(group.raw().mixer.points[0].hueShift, 0.0);
|
||||
auto &hueRange = group.child<CameraRawSlider>(QStringLiteral("hueRangeSlider"));
|
||||
hueRange.setValue(1000);
|
||||
QCOMPARE(group.raw().mixer.points[1].hueRange, 180.0);
|
||||
QTest::mouseDClick(&hueRange, Qt::LeftButton, {}, knob(hueRange));
|
||||
QCOMPARE(group.raw().mixer.points[1].hueRange, 30.0);
|
||||
auto &range = group.child<CameraRawSlider>(QStringLiteral("saturationRangeSlider"));
|
||||
QTest::mouseDClick(&range, Qt::LeftButton, {}, knob(range));
|
||||
group.child<CameraRawSlider>(QStringLiteral("saturationRangeSlider")).setValue(0);
|
||||
QCOMPARE(group.raw().mixer.points[1].saturationRange, 0.05);
|
||||
group.child<QCheckBox>(QStringLiteral("visualizeRange")).click();
|
||||
QVERIFY(group.raw().mixer.points[1].visualize && !group.raw().mixer.points[0].visualize);
|
||||
group.child<QCheckBox>(QStringLiteral("visualizeRange")).click();
|
||||
QVERIFY(!group.raw().mixer.points[1].visualize);
|
||||
}
|
||||
|
||||
QTEST_MAIN(CameraRawCurveMixerTests)
|
||||
#include "CameraRawCurveMixerTests.moc"
|
||||
Reference in new issue
Block a user