From 0862db49b88a92fa9dc1b9c543ca259f57bf1e1e Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Fri, 4 Sep 2026 09:20:54 +0200 Subject: [PATCH] EXR: RGB / YC subtype support --- README.md | 15 +++++++++++++++ src/imageformats/exr.cpp | 31 +++++++++++++++++++++++++++++++ src/imageformats/exr_p.h | 8 ++++++++ 3 files changed, 54 insertions(+) diff --git a/README.md b/README.md index dc5ed6f..66191be 100644 --- a/README.md +++ b/README.md @@ -392,6 +392,21 @@ The plugin can set the following additional metadata: - `EXRLayerName`: A string containing the name of the EXR layer used to decode the image. +When writing, it is possible to specify compression ratio and quality. The +values ​​you can set depend on the version of the OpenEXR library you're using. +Please check the [exr_p.h](./src/imageformats/exr_p.h) for more +info. + +When writing, it is also possible to specify a subtype: +- `RGB` (default): preserves full-resolution color data (R, G, B) without + subsampling or loss of chromatic detail. +- `YC`: converts RGB data into luminance (Y) and 2x2 subsampled chroma + (RY, BY), cutting raw color data by ~50% for significantly smaller file + sizes. + +> [!note] +> When writing grayscale images, the subtype makes no difference. + ### The EPS plugin The plugin uses `Ghostscript` to convert the raster image. When reading it diff --git a/src/imageformats/exr.cpp b/src/imageformats/exr.cpp index 3a667a0..7f723fc 100644 --- a/src/imageformats/exr.cpp +++ b/src/imageformats/exr.cpp @@ -200,12 +200,17 @@ void K_OStream::seekg(Imf::Int64 pos) m_dev->seek(pos); } +#define EXR_SUBFORMAT_RGB QByteArray("RGB") + +#define EXR_SUBFORMAT_YC QByteArray("YC") + EXRHandler::EXRHandler() : m_compressionRatio(-1) , m_quality(-1) , m_imageNumber(0) , m_imageCount(0) , m_startPos(-1) + , m_subType(EXR_SUBFORMAT_RGB) { // Set the number of threads to use (0 is allowed) Imf::setGlobalThreadCount(QThread::idealThreadCount() / 2); @@ -688,6 +693,9 @@ bool EXRHandler::write(const QImage &image) // write the EXR K_OStream ostr(device()); auto channelsType = image.hasAlphaChannel() ? Imf::RgbaChannels::WRITE_RGBA : Imf::RgbaChannels::WRITE_RGB; + if (m_subType == EXR_SUBFORMAT_YC) { + channelsType = channelsType == Imf::RgbaChannels::WRITE_RGBA ? Imf::RgbaChannels::WRITE_YCA : Imf::RgbaChannels::WRITE_YC; + } if (image.format() == QImage::Format_Mono || image.format() == QImage::Format_MonoLSB || image.format() == QImage::Format_Grayscale16 || @@ -743,6 +751,15 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value) m_quality = q; } } + if (option == QImageIOHandler::SubType) { + auto subType = value.toByteArray(); + auto list = EXRHandler::option(QImageIOHandler::SupportedSubTypes).value>(); + if (list.contains(subType)) { + m_subType = subType; + } else { + m_subType = EXR_SUBFORMAT_RGB; + } + } } bool EXRHandler::supportsOption(ImageOption option) const @@ -761,6 +778,12 @@ bool EXRHandler::supportsOption(ImageOption option) const if (option == QImageIOHandler::Quality) { return true; } + if (option == QImageIOHandler::SubType) { + return true; + } + if (option == QImageIOHandler::SupportedSubTypes) { + return true; + } return false; } @@ -815,6 +838,14 @@ QVariant EXRHandler::option(ImageOption option) const v = QVariant(m_quality); } + if (option == QImageIOHandler::SupportedSubTypes) { + return QVariant::fromValue(QList() << EXR_SUBFORMAT_RGB << EXR_SUBFORMAT_YC); + } + + if (option == QImageIOHandler::SubType) { + return QVariant::fromValue(m_subType); + } + return v; } diff --git a/src/imageformats/exr_p.h b/src/imageformats/exr_p.h index fb202c8..20e8dd6 100644 --- a/src/imageformats/exr_p.h +++ b/src/imageformats/exr_p.h @@ -118,6 +118,14 @@ private: * The initial device position to allow multi image load (cache value). */ qint64 m_startPos; + + /*! + * \brief m_subType + * The EXR subtype to use when writing: + * - RGB: preserves full-resolution color data (R, G, B) without subsampling or loss of chromatic detail (default). + * - YC: converts RGB data into luminance (Y) and 2x2 subsampled chroma (RY, BY), cutting raw color data by ~50% for significantly smaller file sizes. + */ + QByteArray m_subType; }; class EXRPlugin : public QImageIOPlugin