From ae6031c02e8cb9c519d4d01698f7fe3ef6e1a619 Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Fri, 16 Oct 2026 12:27:36 +0200 Subject: [PATCH] expose the underlying library version in the image metadata --- .kde-ci.yml | 2 +- CMakeLists.txt | 2 ++ autotests/readtest.cpp | 8 +++++++- src/imageformats/CMakeLists.txt | 3 +++ src/imageformats/avif.cpp | 4 ++++ src/imageformats/exr.cpp | 10 ++++++++++ src/imageformats/heif.cpp | 4 ++++ src/imageformats/jp2.cpp | 5 +++++ src/imageformats/jxl.cpp | 7 +++++++ src/imageformats/jxr.cpp | 19 +++++++++++++++++++ src/imageformats/raw.cpp | 4 ++++ src/imageformats/util_p.h | 3 +++ 12 files changed, 69 insertions(+), 2 deletions(-) diff --git a/.kde-ci.yml b/.kde-ci.yml index 898f3a0..9f61322 100644 --- a/.kde-ci.yml +++ b/.kde-ci.yml @@ -7,6 +7,6 @@ Dependencies: Options: test-before-installing: True require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows'] - cmake-options: "-DKIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR=ON -DKIMAGEFORMATS_HEIF=ON -DKIMAGEFORMATS_HEIC_TEST:STRING=OFF -DKIMAGEFORMATS_HEIF_TEST:STRING=OFF -DKIMAGEFORMATS_HEJ2_TEST:STRING=OFF -DKIMAGEFORMATS_AVCI_TEST:STRING=OFF" + cmake-options: "-DKIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR=ON -DKIMAGEFORMATS_HEIF=ON -DKIMAGEFORMATS_HEIC_TEST:STRING=OFF -DKIMAGEFORMATS_HEIF_TEST:STRING=OFF -DKIMAGEFORMATS_HEJ2_TEST:STRING=OFF -DKIMAGEFORMATS_AVCI_TEST:STRING=OFF -DKIMAGEFORMATS_EXPOSE_LIBVER=ON" per-test-timeout: 90 enable-lsan: False diff --git a/CMakeLists.txt b/CMakeLists.txt index ed660f8..ded60de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,8 @@ if(KIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR) endif() add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images") +option(KIMAGEFORMATS_EXPOSE_LIBVER "Store the version of the underlying library in the image metadata" OFF) + ecm_set_disabled_deprecation_versions( QT 6.11.0 KF 6.27.0 diff --git a/autotests/readtest.cpp b/autotests/readtest.cpp index 6d1a5f0..3e7453f 100644 --- a/autotests/readtest.cpp +++ b/autotests/readtest.cpp @@ -198,7 +198,7 @@ int main(int argc, char **argv) QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR)); QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR)); QCoreApplication::setApplicationName(QStringLiteral("readtest")); - QCoreApplication::setApplicationVersion(QStringLiteral("1.3.0")); + QCoreApplication::setApplicationVersion(QStringLiteral("1.3.1")); QCommandLineParser parser; parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking.")); @@ -352,6 +352,12 @@ int main(int argc, char **argv) continue; } + // version of the underlying library used, if any + QString libVer = inputImage.text(QStringLiteral("Library")); + if (!libVer.isEmpty()) { + QTextStream(stdout) << "INFO : using " << libVer << "\n"; + } + if (!optionTest.compare(&inputReader)) { QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while comparing options\n"; ++failed; diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index a79214e..49fa4c9 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -15,6 +15,9 @@ function(kimageformats_add_plugin plugin) target_sources(${plugin} PRIVATE ${KIF_ADD_PLUGIN_SOURCES}) set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats) target_link_libraries(${plugin} PRIVATE Qt6::Gui) + if(KIMAGEFORMATS_EXPOSE_LIBVER) + target_compile_definitions(${plugin} PRIVATE KIF_EXPOSE_LIBVER) + endif() if(ANDROID) # Plugins should be named with lib prefix on Android diff --git a/src/imageformats/avif.cpp b/src/imageformats/avif.cpp index 5017bf9..c48f5cb 100644 --- a/src/imageformats/avif.cpp +++ b/src/imageformats/avif.cpp @@ -609,6 +609,10 @@ bool QAVIFHandler::read(QImage *image) jumpToNextImage(); } +#ifdef KIF_EXPOSE_LIBVER + m_current_image.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("LibAVIF %1").arg(avifVersion())); +#endif + *image = m_current_image; if (imageCount() >= 2) { m_must_jump_to_next_image = true; diff --git a/src/imageformats/exr.cpp b/src/imageformats/exr.cpp index 9336b5f..bb391c8 100644 --- a/src/imageformats/exr.cpp +++ b/src/imageformats/exr.cpp @@ -83,6 +83,10 @@ #include #include +#ifdef KIF_EXPOSE_LIBVER +#include +#endif + #include #include @@ -553,6 +557,12 @@ bool EXRHandler::read(QImage *outImage) // final color operations readColorSpace(header, image); +#ifdef KIF_EXPOSE_LIBVER + int maj = 0, min = 0, pat = 0; + exr_get_library_version(&maj, &min, &pat, nullptr); + image.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("OpenEXR %1.%2.%3").arg(maj).arg(min).arg(pat)); +#endif + *outImage = image; return true; diff --git a/src/imageformats/heif.cpp b/src/imageformats/heif.cpp index 191fa95..dee5968 100644 --- a/src/imageformats/heif.cpp +++ b/src/imageformats/heif.cpp @@ -191,6 +191,10 @@ bool HEIFHandler::read(QImage *outImage) return false; } +#ifdef KIF_EXPOSE_LIBVER + m_current_image.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("LibHEIF %1").arg(heif_get_version())); +#endif + *outImage = m_current_image; return true; } diff --git a/src/imageformats/jp2.cpp b/src/imageformats/jp2.cpp index 76c6868..da781b1 100644 --- a/src/imageformats/jp2.cpp +++ b/src/imageformats/jp2.cpp @@ -342,6 +342,11 @@ public: img.setColorSpace(colorSpace()); +#ifdef KIF_EXPOSE_LIBVER + quint32 ver = opjVersion(); + img.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("OpenJPEG %1.%2.%3").arg((ver >> 16) & 0xff).arg((ver >> 8) & 0xff).arg(ver & 0xff)); +#endif + return img; } diff --git a/src/imageformats/jxl.cpp b/src/imageformats/jxl.cpp index dbac0f6..45e7604 100644 --- a/src/imageformats/jxl.cpp +++ b/src/imageformats/jxl.cpp @@ -842,6 +842,13 @@ bool QJpegXLHandler::read(QImage *image) } if (decode_one_frame()) { +#ifdef KIF_EXPOSE_LIBVER + quint32 ver = JxlDecoderVersion(); + int maj = ver / 1000000; + int min = (ver % 1000000) / 1000; + int pat = ver % 1000; + m_current_image.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("LibJXL %1.%2.%3").arg(maj).arg(min).arg(pat)); +#endif *image = m_current_image; return true; } else { diff --git a/src/imageformats/jxr.cpp b/src/imageformats/jxr.cpp index 331ce7c..f922bba 100644 --- a/src/imageformats/jxr.cpp +++ b/src/imageformats/jxr.cpp @@ -159,6 +159,19 @@ public: } } + quint32 jxrVersion() const + { + quint32 ver = 0; +#ifdef JXR_VERSION +# if JXR_VERSION < JXR_MAKEVERSION(1, 4, 0) + ver = JXR_VERSION; +# else + PKLibJxr_GetVersion(&ver); +# endif +#endif + return ver; + } + QString fileName() const { return m_jxrFile->fileName(); @@ -1164,6 +1177,12 @@ bool JXRHandler::read(QImage *outImage) } #endif +#ifdef KIF_EXPOSE_LIBVER + if (auto ver = d->jxrVersion()) { + img.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("JXRlib %1.%2.%3").arg((ver >> 16) & 0xff).arg((ver >> 8) & 0xff).arg(ver & 0xff)); + } +#endif + *outImage = img; return true; } diff --git a/src/imageformats/raw.cpp b/src/imageformats/raw.cpp index 133a4ef..d005d47 100644 --- a/src/imageformats/raw.cpp +++ b/src/imageformats/raw.cpp @@ -941,6 +941,10 @@ bool RAWHandler::read(QImage *image) return false; } +#ifdef KIF_EXPOSE_LIBVER + img.setText(QStringLiteral(META_KEY_LIBRARY), QStringLiteral("LibRAW %1").arg(libraw_version())); +#endif + *image = img; return true; } diff --git a/src/imageformats/util_p.h b/src/imageformats/util_p.h index 091e123..4f556dd 100644 --- a/src/imageformats/util_p.h +++ b/src/imageformats/util_p.h @@ -77,6 +77,9 @@ #define META_KEY_LENS_MODEL "LensModel" #define META_KEY_LENS_SERIALNUMBER "LensSerialNumber" +// The name and version of the library used (e.g. OpenEXR 3.4.2) +#define META_KEY_LIBRARY "Library" + // QList uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira static constexpr int kMaxQVectorSize = std::numeric_limits::max() - 32;