Camera Raw panel's remaining groups and the Filter menu entry (12.8b2)

This commit is contained in:
雷电芽衣 committed 2026-09-28 05:40:09 -04:00
1 parent bb8129e8d5
commit 7ae263de80
26 files changed
+3227 -82

No files matched your search

+250
View File
@@ -0,0 +1,250 @@
#include "CameraRawRowTable.h"
#include "Document/BrushStroke.h"
#include "UI/CameraRawColorControls.h"
#include "UI/FilterSheet.h"
#include <QAbstractButton>
#include <QComboBox>
#include <QToolButton>
#include <QWindow>
// The groups' drawing, pages and gestures, and the shipped panel.
namespace {
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, 900);
controls->show();
if (!QTest::qWaitForWindowExposed(controls.get()))
throw std::runtime_error("the controls never showed");
}
template <typename Widget> Widget &child(const QString &name) const { return *controls->template findChild<Widget *>(name); }
const CameraRawSettings &raw() const { return session.filterEdit().value().settings.cameraRaw; }
void set(const std::function<void(CameraRawSettings &)> &change)
{
FilterSettings settings = session.filterEdit().value().settings;
change(settings.cameraRaw);
session.updateFilter(settings, true);
}
void arm(const std::function<void(CameraRawPanel &)> &change)
{
CameraRawPanel panel = session.filterEdit().value().rawPanel;
change(panel);
session.setCameraRawPanel(panel);
}
};
bool near(QColor seen, QColor wanted, int tolerance = 4)
{
return std::abs(seen.red() - wanted.red()) <= tolerance && std::abs(seen.green() - wanted.green()) <= tolerance
&& std::abs(seen.blue() - wanted.blue()) <= tolerance;
}
QColor over(QColor top, double alpha, QColor below)
{
return QColor(int(std::lround(top.red() * alpha + below.red() * (1 - alpha))), int(std::lround(top.green() * alpha + below.green() * (1 - alpha))),
int(std::lround(top.blue() * alpha + below.blue() * (1 - alpha))));
}
// A real double click's four events, through the window.
void doubleClick(QWidget &widget, QPoint at)
{
QTest::mouseDClick(widget.window()->windowHandle(), Qt::LeftButton, {}, widget.mapTo(widget.window(), at));
}
}
class CameraRawGroupDrawTests : public QObject {
Q_OBJECT
private slots:
void theCurvePagesShowTheirOwnControls();
void theCurveGraphDrawsItsGround();
void endPointsStayAndARealDoubleClickRemoves();
void theCurveGroupWorksInsideTheShippedSheet();
void swatchesAndTracksFollowTheirFamilies();
void theWheelDrawsAndTakesItsOwnCircle();
void aTypedFieldKeepsItsTypingThroughChanges();
};
void CameraRawGroupDrawTests::theCurvePagesShowTheirOwnControls()
{
Group<CameraRawCurveControls> group;
auto &channel = group.child<QToolButton>(QStringLiteral("pointChannel0"));
auto &amounts = group.child<CameraRawSlider>(QStringLiteral("curveLightsSlider"));
auto &preset = group.child<QComboBox>(QStringLiteral("curvePreset"));
QVERIFY(!channel.isVisible() && amounts.isVisible() && !preset.isVisible());
QCOMPARE(group.child<QToolButton>(QStringLiteral("curvePage0")).toolTip(), QString("Parametric lifts tonal regions. Point places anchors on the curve."));
group.arm([](CameraRawPanel &panel) { panel.curvePage = CameraRawCurvePage::point; });
QVERIFY(channel.isVisible() && !amounts.isVisible() && preset.isVisible());
QStringList presets;
for (int index = 0; index < preset.count(); ++index)
presets << preset.itemText(index);
QCOMPARE(presets, (QStringList{"Custom", "Linear", "Medium Contrast", "Strong Contrast"}));
QCOMPARE(preset.toolTip(), QString("Replaces this curve with a straight line or a contrast curve."));
QCOMPARE(channel.toolTip(), QString("RGB changes brightness. Red, green, and blue also shift the color."));
QCOMPARE(group.child<QWidget>(QStringLiteral("cameraRawCurveGraph")).toolTip(),
QString("Drag a point. Click the curve to add one. Double-click a point to remove it."));
QCOMPARE(group.child<QWidget>(QStringLiteral("curveSelectedPoint")).toolTip(), QString("Input and output of the selected curve point."));
QCOMPARE(group.child<QAbstractButton>(QStringLiteral("curveTargeted")).toolTip(),
QString("Drag on the picture to move the curve for the tone under the pointer."));
}
void CameraRawGroupDrawTests::theCurveGraphDrawsItsGround()
{
Group<CameraRawCurveControls> group;
auto &graph = group.child<QWidget>(QStringLiteral("cameraRawCurveGraph"));
QCOMPARE(graph.height(), 150);
const QColor window = graph.palette().color(QPalette::Window);
const QColor ground = over(Qt::black, 0.35, window);
QImage drawn = graph.grab().toImage();
const int w = drawn.width(), h = drawn.height();
// Away from every line: black at 35% over the window.
QVERIFY2(near(drawn.pixelColor(w * 3 / 4, h * 3 / 4), ground), qPrintable(drawn.pixelColor(w * 3 / 4, h * 3 / 4).name()));
// Each divider rises 8 points, 3 wide, from the bottom.
for (const double split : {25.0, 50.0, 75.0}) {
QVERIFY(near(drawn.pixelColor(int(split / 100 * w), h - 3), Qt::white, 30));
QVERIFY(near(drawn.pixelColor(int(split / 100 * w), h - 12), ground));
}
group.arm([](CameraRawPanel &panel) { panel.curvePage = CameraRawCurvePage::point; });
group.set([](CameraRawSettings &settings) { settings.curve.rgb = {{0, 0}, {0.5, 0.25}, {1, 1}}; });
drawn = graph.grab().toImage();
// A dot 8 across; the diagonal at 25% white.
QVERIFY(near(drawn.pixelColor(w / 2, int(h * 0.75)), Qt::white, 10));
QVERIFY(near(drawn.pixelColor(w / 2 + 3, int(h * 0.75)), Qt::white, 60));
QVERIFY(near(drawn.pixelColor(w / 4, h - int(h * 0.25)), over(Qt::white, 0.25, ground), 40));
}
void CameraRawGroupDrawTests::endPointsStayAndARealDoubleClickRemoves()
{
Group<CameraRawCurveControls> group;
group.arm([](CameraRawPanel &panel) { panel.curvePage = CameraRawCurvePage::point; });
auto &graph = group.child<QWidget>(QStringLiteral("cameraRawCurveGraph"));
const int w = graph.width(), h = graph.height();
// With no inner point a drag moves nothing.
QTest::mousePress(&graph, Qt::LeftButton, {}, QPoint(2, h - 2));
QMouseEvent move(QEvent::MouseMove, QPointF(40, 40), graph.mapToGlobal(QPointF(40, 40)), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(&graph, &move);
QTest::mouseRelease(&graph, Qt::LeftButton, {}, QPoint(40, 40));
QVERIFY(group.raw().curve.rgb == CameraRawCurveSettings::linear());
// An inner point is held within 0.02 and 0.98.
group.set([](CameraRawSettings &settings) { settings.curve.rgb = {{0, 0}, {0.5, 0.5}, {1, 1}}; });
QTest::mousePress(&graph, Qt::LeftButton, {}, QPoint(w / 2, h / 2));
QMouseEvent far(QEvent::MouseMove, QPointF(-50, h / 2), graph.mapToGlobal(QPointF(-50, h / 2)), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(&graph, &far);
QTest::mouseRelease(&graph, Qt::LeftButton, {}, QPoint(-50, h / 2));
QCOMPARE(group.raw().curve.rgb.size(), size_t(3));
QVERIFY(std::abs(group.raw().curve.rgb[1].x - 0.02) < 1e-9 && group.raw().curve.rgb.front().x == 0 && group.raw().curve.rgb.back().x == 1);
// A real double click: press, release, double click, release.
group.set([](CameraRawSettings &settings) { settings.curve.rgb = {{0, 0}, {0.5, 0.5}, {1, 1}}; });
doubleClick(graph, QPoint(w / 2, h / 2));
QCOMPARE(group.raw().curve.rgb.size(), size_t(2));
}
void CameraRawGroupDrawTests::theCurveGroupWorksInsideTheShippedSheet()
{
EditorSession session;
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);
FilterSheet sheet(session);
sheet.resize(440, 1400);
sheet.show();
QVERIFY(QTest::qWaitForWindowExposed(&sheet));
sheet.findChild<QToolButton *>(QStringLiteral("CurveSection"))->click();
CameraRawPanel panel = session.filterEdit().value().rawPanel;
panel.curvePage = CameraRawCurvePage::point;
session.setCameraRawPanel(panel);
auto *graph = sheet.findChild<QWidget *>(QStringLiteral("cameraRawCurveGraph"));
QVERIFY(graph && graph->isVisible());
QVERIFY(!graph->grab().isNull());
const QPoint at(graph->width() / 2, graph->height() / 4);
QTest::mouseClick(graph->window()->windowHandle(), Qt::LeftButton, {}, graph->mapTo(graph->window(), at));
QCOMPARE(session.filterEdit().value().settings.cameraRaw.curve.rgb.size(), size_t(3));
}
void CameraRawGroupDrawTests::swatchesAndTracksFollowTheirFamilies()
{
Group<CameraRawMixerControls> group;
group.arm([](CameraRawPanel &panel) {
panel.mixerPage = CameraRawMixerPage::color;
panel.mixerSwatch = 5;
});
// Each swatch: its centre at 0.8 and 0.9, chosen ringed.
for (int index = 0; index < 8; ++index) {
auto &swatch = group.child<QAbstractButton>(QStringLiteral("mixerSwatch%1").arg(index));
const QImage drawn = swatch.grab().toImage();
const QColor wanted = QColor::fromHsvF(float(CameraRawMixerSettings::centers.at(size_t(index)) / 360), 0.8f, 0.9f);
QVERIFY2(near(drawn.pixelColor(drawn.width() / 2, drawn.height() / 2), wanted), qPrintable(QString::number(index)));
QCOMPARE(near(drawn.pixelColor(drawn.width() / 2, 2), Qt::white, 40), index == 5);
QCOMPARE(swatch.toolTip(), QStringLiteral("Edit %1.").arg(CameraRawMixerSettings::names.at(size_t(index))));
}
// The Color page's hue row spans Blues, 50 each side.
const auto end = [&group](const char *name) {
const QImage drawn = group.child<CameraRawSlider>(QString::fromLatin1(name)).grab().toImage();
return drawn.pixelColor(drawn.width() - 6, drawn.height() / 2);
};
QVERIFY2(std::abs(end("colorHueSlider").hsvHue() - 290) < 8, qPrintable(end("colorHueSlider").name()));
// A point picked anew with another hue rebuilds its tracks.
group.arm([](CameraRawPanel &panel) {
panel.mixerPage = CameraRawMixerPage::point;
panel.pointIndex = 0;
});
group.set([](CameraRawSettings &settings) { settings.mixer.points = {CameraRawPointColor{.hue = 30, .saturation = 1, .luminance = 0.5}}; });
QVERIFY2(std::abs(end("hueShiftSlider").hsvHue() - 80) < 8, qPrintable(end("hueShiftSlider").name()));
group.set([](CameraRawSettings &settings) { settings.mixer.points = {CameraRawPointColor{.hue = 200, .saturation = 1, .luminance = 0.5}}; });
QVERIFY2(std::abs(end("hueShiftSlider").hsvHue() - 250) < 8, qPrintable(end("hueShiftSlider").name()));
const QImage picked = group.child<QAbstractButton>(QStringLiteral("pickedColor0")).grab().toImage();
QVERIFY(near(picked.pixelColor(picked.width() / 2, picked.height() / 2), QColor::fromHsvF(200.0f / 360, 1, 0.5f)));
QVERIFY(near(picked.pixelColor(picked.width() / 2, 2), Qt::white, 40));
}
void CameraRawGroupDrawTests::theWheelDrawsAndTakesItsOwnCircle()
{
Group<CameraRawGradingControls> group;
auto *shadows = group.controls->findChild<QWidget *>(QStringLiteral("gradeWheelShadows"));
auto *midtones = group.controls->findChild<QWidget *>(QStringLiteral("gradeWheelMidtones"));
// Swift's HStack: 30 points between the three columns.
QCOMPARE(midtones->x() - (shadows->x() + shadows->width()), 30);
QWidget *wheel = nullptr;
for (QWidget *child : shadows->findChildren<QWidget *>())
if (child->size() == QSize(86, 86))
wheel = child;
group.set([](CameraRawSettings &settings) { settings.grading.shadows = {90, 50, 0}; });
const QImage drawn = wheel->grab().toImage();
const QColor window = wheel->palette().color(QPalette::Window);
// Red at 85% on the right; the dot sits above.
QVERIFY2(near(drawn.pixelColor(80, 43), over(Qt::red, 0.85, window), 12), qPrintable(drawn.pixelColor(80, 43).name()));
QVERIFY(near(drawn.pixelColor(43, 43 - 18), Qt::white, 10));
QVERIFY(!near(drawn.pixelColor(43, 43 + 18), Qt::white, 60));
// A double click in a corner resets nothing.
QTest::mouseDClick(wheel, Qt::LeftButton, {}, QPoint(2, 2));
QCOMPARE(group.raw().grading.shadows.saturation, 50.0);
QTest::mouseDClick(wheel, Qt::LeftButton, {}, QPoint(43, 43));
QCOMPARE(group.raw().grading.shadows.saturation, 0.0);
QCOMPARE(wheel->toolTip(), QString("Drag to set hue and saturation. Double-click to reset this wheel."));
}
void CameraRawGroupDrawTests::aTypedFieldKeepsItsTypingThroughChanges()
{
Group<CameraRawCurveControls> group;
auto *field = group.controls->findChild<PickerField *>(QStringLiteral("curveLightsField"));
field->setFocus();
QTRY_VERIFY(field->hasFocus());
field->selectAll();
QTest::keyClicks(field, QStringLiteral("42"));
group.set([](CameraRawSettings &settings) { settings.curve.darks = 10; });
QCOMPARE(field->text(), QString("42"));
QTest::keyClick(field, Qt::Key_Return);
QCOMPARE(group.raw().curve.lights, 42.0);
}
QTEST_MAIN(CameraRawGroupDrawTests)
#include "CameraRawGroupDrawTests.moc"