color: input colorspace auto-detection, HDR export tags, LGG/white balance nodes, more LUT formats, waveform parade

- media color primaries/transfer tags now flow from the FFmpeg probe
  through VideoParams into Footage::get_colorspace_to_use(); precedence
  is user override > media tags > project default
- export nclc tags derive from the output colorspace (PQ/HLG/BT.2020,
  P3, sRGB, Rec.601, Rec.709) instead of hardcoded BT.709
- new OCIO Color Grading (Log) node (lift/gamma/gain) and White Balance
  node (kelvin temperature + tint, HDR-safe)
- LUT whitelist extended to 9 OCIO-supported formats
- waveform scope gains an RGB parade mode (GPU and software paths)
- tests updated for the new colorspace precedence
This commit is contained in:
2026-07-19 21:45:51 +08:00
parent a7ddc0f114
commit 76f5c2a65b
37 changed files with 1449 additions and 40 deletions
+12 -2
View File
@@ -29,6 +29,17 @@
namespace olive
{
const QStringList &LUTLibrary::supported_extensions()
{
// LUT formats OCIO FileTransform can load
static const QStringList extensions = {
QStringLiteral("cube"), QStringLiteral("3dl"), QStringLiteral("spi1d"),
QStringLiteral("spi3d"), QStringLiteral("spimtx"), QStringLiteral("csp"),
QStringLiteral("clf"), QStringLiteral("ctf"), QStringLiteral("cub"),
};
return extensions;
}
bool LUTLibrary::is_supported_extension(const QString &suffix)
{
QString s = suffix;
@@ -36,8 +47,7 @@ bool LUTLibrary::is_supported_extension(const QString &suffix)
s.remove(0, 1);
}
const QString lower = s.toLower();
return lower == QStringLiteral("cube") || lower == QStringLiteral("3dl");
return supported_extensions().contains(s.toLower());
}
QStringList LUTLibrary::get_directories()
+8 -1
View File
@@ -37,9 +37,16 @@ namespace olive
*/
class LUTLibrary {
public:
/**
* @brief All LUT file extensions supported by the library
*
* Extensions OCIO FileTransform can load, lowercase, without the dot.
*/
static const QStringList &supported_extensions();
/**
* @brief Returns true if the given file suffix is a supported LUT
* extension (.cube or .3dl, case-insensitive, leading dot tolerated)
* extension (case-insensitive, leading dot tolerated)
*/
static bool is_supported_extension(const QString &suffix);
+8
View File
@@ -419,6 +419,10 @@ void VideoParams::load(QXmlStreamReader *reader)
} else if (reader->name() == QStringLiteral("colorrange")) {
set_color_range(
static_cast<ColorRange>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("colorprimaries")) {
set_color_primaries(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("colortransfer")) {
set_color_transfer(reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
@@ -463,6 +467,10 @@ void VideoParams::save(QXmlStreamWriter *writer) const
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeTextElement(QStringLiteral("colorrange"),
QString::number(color_range_));
writer->writeTextElement(QStringLiteral("colorprimaries"),
QString::number(color_primaries_));
writer->writeTextElement(QStringLiteral("colortransfer"),
QString::number(color_transfer_));
}
}
+29
View File
@@ -378,6 +378,33 @@ public:
color_range_ = color_range;
}
/**
* @brief Color primaries/transfer as reported by the media
*
* Raw FFmpeg AVColorPrimaries/AVColorTransferCharacteristic values
* (0 = unset, 2 = unspecified). Used to auto-detect the input
* colorspace when no explicit colorspace has been set.
*/
int color_primaries() const
{
return color_primaries_;
}
void set_color_primaries(int p)
{
color_primaries_ = p;
}
int color_transfer() const
{
return color_transfer_;
}
void set_color_transfer(int t)
{
color_transfer_ = t;
}
int64_t get_time_in_timebase_units(const Rational &time) const;
void load(QXmlStreamReader *reader);
@@ -425,6 +452,8 @@ private:
float x_;
float y_;
ColorRange color_range_;
int color_primaries_ = 0;
int color_transfer_ = 0;
};
}