From 90d4256f3d41b0928f4d9e9e4a2227b6f3835820 Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Wed, 19 Feb 2025 11:56:19 +0000 Subject: [PATCH] AVIF: added support to XMP and EXIF metadata Allow to load/save info about: - GPS info (latitude, longitude, altitude) - Various text info (title, description, author, copyright, etc...) - Image resolution The compatibility of the modifications has been tested with GIMP. --- autotests/read/avif/metadata.avif | Bin 0 -> 18634 bytes autotests/read/avif/metadata.avif.json | 59 +++++++++++++++++++++++++ autotests/read/avif/metadata.png | Bin 0 -> 24524 bytes autotests/write/basic/avif.json | 57 ++++++++++++++++++++++++ src/imageformats/CMakeLists.txt | 2 +- src/imageformats/avif.cpp | 51 +++++++++++++++++++-- src/imageformats/microexif.cpp | 7 +++ src/imageformats/microexif_p.h | 8 ++++ 8 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 autotests/read/avif/metadata.avif create mode 100644 autotests/read/avif/metadata.avif.json create mode 100644 autotests/read/avif/metadata.png create mode 100644 autotests/write/basic/avif.json diff --git a/autotests/read/avif/metadata.avif b/autotests/read/avif/metadata.avif new file mode 100644 index 0000000000000000000000000000000000000000..158093a446efc711319ee0f9f18e327746a47ba8 GIT binary patch literal 18634 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 literal 0 HcmV?d00001 diff --git a/autotests/read/avif/metadata.avif.json b/autotests/read/avif/metadata.avif.json new file mode 100644 index 0000000..6aeffa1 --- /dev/null +++ b/autotests/read/avif/metadata.avif.json @@ -0,0 +1,59 @@ +[ + { + "fileName" : "metadata.png", + "metadata" : [ + { + "key" : "CreationDate", + "value" : "2025-02-19T08:27:22+01:00" + }, + { + "key" : "Software" , + "value" : "GIMP 3.0.0-RC3" + }, + { + "key" : "Altitude", + "value" : "34" + }, + { + "key" : "Author", + "value" : "KDE Project" + }, + { + "key" : "Copyright", + "value" : "@2025 KDE Project" + }, + { + "key" : "Description", + "value" : "TV broadcast test image." + }, + { + "key" : "Latitude", + "value" : "44.6478" + }, + { + "key" : "LensManufacturer", + "value" : "KDE Glasses" + }, + { + "key" : "LensModel", + "value" : "A1234" + }, + { + "key" : "Longitude", + "value" : "10.9254" + }, + { + "key" : "Manufacturer", + "value" : "KFramework" + }, + { + "key" : "Model", + "value" : "KImageFormats" + } + ], + "resolution" : { + "dotsPerMeterX" : 11811, + "dotsPerMeterY" : 5905 + } + } +] diff --git a/autotests/read/avif/metadata.png b/autotests/read/avif/metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..046a449c6dfe28fae955ceba5843429f94d11fbb GIT binary patch literal 24524 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 literal 0 HcmV?d00001 diff --git a/autotests/write/basic/avif.json b/autotests/write/basic/avif.json new file mode 100644 index 0000000..c640941 --- /dev/null +++ b/autotests/write/basic/avif.json @@ -0,0 +1,57 @@ +{ + "format" : "avif", + "metadata" : [ + { + "key" : "CreationDate", + "value" : "2025-01-14T13:53:32+01:00" + }, + { + "key" : "Software" , + "value" : "Adobe Photoshop 26.2 (Windows)" + }, + { + "key" : "Altitude", + "value" : "34" + }, + { + "key" : "Author", + "value" : "KDE Project" + }, + { + "key" : "Copyright", + "value" : "@2025 KDE Project" + }, + { + "key" : "Description", + "value" : "TV broadcast test image." + }, + { + "key" : "Latitude", + "value" : "44.6478" + }, + { + "key" : "LensManufacturer", + "value" : "KDE Glasses" + }, + { + "key" : "LensModel", + "value" : "A1234" + }, + { + "key" : "Longitude", + "value" : "10.9254" + }, + { + "key" : "Manufacturer", + "value" : "KFramework" + }, + { + "key" : "Model", + "value" : "KImageFormats" + } + ], + "resolution" : { + "dotsPerMeterX" : 11811, + "dotsPerMeterY" : 11812 + } +} diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index 8a0d082..c1b473e 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -25,7 +25,7 @@ kimageformats_add_plugin(kimg_ani SOURCES ani.cpp) ################################## if (TARGET avif) - kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp") + kimageformats_add_plugin(kimg_avif SOURCES avif.cpp microexif.cpp) target_link_libraries(kimg_avif PRIVATE "avif") endif() diff --git a/src/imageformats/avif.cpp b/src/imageformats/avif.cpp index 7abbcdf..616580d 100644 --- a/src/imageformats/avif.cpp +++ b/src/imageformats/avif.cpp @@ -12,6 +12,7 @@ #include #include "avif_p.h" +#include "microexif_p.h" #include "util_p.h" #include @@ -151,9 +152,6 @@ bool QAVIFHandler::ensureDecoder() m_decoder = avifDecoderCreate(); - m_decoder->ignoreExif = AVIF_TRUE; - m_decoder->ignoreXMP = AVIF_TRUE; - #if AVIF_VERSION >= 80400 m_decoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64); #endif @@ -534,12 +532,54 @@ bool QAVIFHandler::decode_one_frame() m_current_image.setColorSpace(colorspace); + if (m_decoder->image->exif.size) { + auto exif = MicroExif::fromRawData(reinterpret_cast(m_decoder->image->exif.data), m_decoder->image->exif.size); + // set image resolution + if (exif.horizontalResolution() > 0) + m_current_image.setDotsPerMeterX(qRound(exif.horizontalResolution() / 25.4 * 1000)); + if (exif.verticalResolution() > 0) + m_current_image.setDotsPerMeterY(qRound(exif.verticalResolution() / 25.4 * 1000)); + // set image metadata + exif.toImageMetadata(m_current_image); + } + + if (m_decoder->image->xmp.size) { + auto ba = QByteArray::fromRawData(reinterpret_cast(m_decoder->image->xmp.data), m_decoder->image->xmp.size); + m_current_image.setText(QStringLiteral(META_KEY_XMP_ADOBE), QString::fromUtf8(ba)); + } + m_estimated_dimensions = m_current_image.size(); m_must_jump_to_next_image = false; return true; } +static void setMetadata(avifImage *avif, const QImage& image) +{ + auto xmp = image.text(QStringLiteral(META_KEY_XMP_ADOBE)).toUtf8(); + if (!xmp.isEmpty()) { +#if AVIF_VERSION >= 1000000 + auto res = avifImageSetMetadataXMP(avif, reinterpret_cast(xmp.constData()), xmp.size()); + if (res != AVIF_RESULT_OK) { + qWarning("ERROR in avifImageSetMetadataXMP: %s", avifResultToString(res)); + } +#else + avifImageSetMetadataXMP(avif, reinterpret_cast(xmp.constData()), xmp.size()); +#endif + } + auto exif = MicroExif::fromImage(image).toByteArray(); + if (!exif.isEmpty()) { +#if AVIF_VERSION >= 1000000 + auto res = avifImageSetMetadataExif(avif, reinterpret_cast(exif.constData()), exif.size()); + if (res != AVIF_RESULT_OK) { + qWarning("ERROR in avifImageSetMetadataExif: %s", avifResultToString(res)); + } +#else + avifImageSetMetadataExif(avif, reinterpret_cast(exif.constData()), exif.size()); +#endif + } +} + bool QAVIFHandler::read(QImage *image) { if (!ensureOpened()) { @@ -689,6 +729,8 @@ bool QAVIFHandler::write(const QImage &image) #else avifImageAllocatePlanes(avif, AVIF_PLANES_YUV); #endif + // set EXIF and XMP metadata + setMetadata(avif, tmpgrayimage); if (tmpgrayimage.colorSpace().isValid()) { avif->colorPrimaries = (avifColorPrimaries)1; @@ -915,6 +957,9 @@ bool QAVIFHandler::write(const QImage &image) avif->colorPrimaries = primaries_to_save; avif->transferCharacteristics = transfer_to_save; + // set EXIF and XMP metadata + setMetadata(avif, tmpcolorimage); + if (iccprofile.size() > 0) { #if AVIF_VERSION >= 1000000 res = avifImageSetProfileICC(avif, reinterpret_cast(iccprofile.constData()), iccprofile.size()); diff --git a/src/imageformats/microexif.cpp b/src/imageformats/microexif.cpp index 61418ae..78e129e 100644 --- a/src/imageformats/microexif.cpp +++ b/src/imageformats/microexif.cpp @@ -1024,6 +1024,13 @@ MicroExif MicroExif::fromByteArray(const QByteArray &ba) return fromDevice(&buf); } +MicroExif MicroExif::fromRawData(const char *data, size_t size) +{ + if (data == nullptr || size == 0) + return {}; + return fromByteArray(QByteArray::fromRawData(data, size)); +} + MicroExif MicroExif::fromDevice(QIODevice *device) { if (device == nullptr || device->isSequential()) diff --git a/src/imageformats/microexif_p.h b/src/imageformats/microexif_p.h index 724a0ac..f5a7863 100644 --- a/src/imageformats/microexif_p.h +++ b/src/imageformats/microexif_p.h @@ -269,6 +269,14 @@ public: */ static MicroExif fromByteArray(const QByteArray &ba); + /*! + * \brief fromRawData + * Creates the class from RAW EXIF data. + * \return The created class (empty on error). + * \sa isEmpty + */ + static MicroExif fromRawData(const char *data, size_t size); + /*! * \brief fromDevice * Creates the class from a device.