color: global LUT library, LUT error reporting, clamp + display fixes

- Add a global LUT library: user-configurable directories (new
  Preferences > LUT tab) scanned recursively for .cube/.3dl files; LUT
  node file pickers offer the library dirs as sidebar shortcuts via a
  'lut_library' input property handled by the param view bridge
- OCIOLutNode no longer fails silently: missing files, unsupported
  extensions and OCIO load errors are recorded in last_error() and
  surfaced in the status bar (input still passes through for rendering
  safety)
- ColorDialog: re-enable the display -> reference conversion using
  ColorProcessor::kInverse with a validity guard, and re-enable the
  Display tab in ColorValuesWidget; covered by a round-trip regression
  test proving the old OCIO inverse crash no longer occurs
- OCIOGradingTransformLinearNode: enforce the OCIO clampWhite >
  clampBlack invariant per frame in Value() so keyframed/connected
  values cannot produce invalid grading transforms, and constrain the
  white clamp UI minimum whenever the black clamp is static
- Regression tests for LUT extension checks, direction switching, node
  error reporting, LUT library scanning, display inverse round-trip and
  clamp enforcement
This commit is contained in:
2026-07-16 22:53:33 +08:00
parent ddca6a5e01
commit 547c2480e0
18 changed files with 829 additions and 33 deletions
+24 -4
View File
@@ -24,6 +24,7 @@
#include <QFileDialog>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QUrl>
#include "ui/icons/icons.h"
@@ -56,11 +57,30 @@ void FileField::BrowseBtnClicked()
{
QString s;
if (directory_mode_) {
s = QFileDialog::getExistingDirectory(this, tr("Open Directory"));
if (sidebar_urls_.isEmpty()) {
if (directory_mode_) {
s = QFileDialog::getExistingDirectory(this, tr("Open Directory"));
} else {
s = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
name_filter_);
}
} else {
s = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
name_filter_);
// Sidebar URLs require the non-static dialog API
QFileDialog dialog(this, tr("Open File"));
dialog.setFileMode(directory_mode_ ? QFileDialog::Directory :
QFileDialog::ExistingFile);
dialog.setAcceptMode(QFileDialog::AcceptOpen);
if (!directory_mode_ && !name_filter_.isEmpty()) {
dialog.setNameFilter(name_filter_);
}
dialog.setSidebarUrls(sidebar_urls_);
if (directory_mode_) {
dialog.setOption(QFileDialog::ShowDirsOnly, true);
}
if (dialog.exec() == QDialog::Accepted && !dialog.selectedFiles().isEmpty()) {
s = dialog.selectedFiles().first();
}
}
if (!s.isEmpty()) {