mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-06-03 17:08:08 -04:00
Added support for resolution and EXIF/XMP metadata to HEIF
This commit is contained in:
parent
c0261f4926
commit
e77986c7e0
BIN
autotests/read/heif/metadata.heif
Normal file
BIN
autotests/read/heif/metadata.heif
Normal file
Binary file not shown.
59
autotests/read/heif/metadata.heif.json
Normal file
59
autotests/read/heif/metadata.heif.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "metadata.png",
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2025-02-26T16:52:06Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "LIFE Pro 2.18.10 (Linux)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/heif/metadata.png
Normal file
BIN
autotests/read/heif/metadata.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
61
autotests/write/basic/heif.json
Normal file
61
autotests/write/basic/heif.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"format" : "heif",
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "CreationDate",
|
||||||
|
"value" : "2025-01-14T13:53:32+01:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2025-02-14T15:58:44+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
|
||||||
|
}
|
||||||
|
}
|
@ -68,7 +68,7 @@ kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp)
|
|||||||
##################################
|
##################################
|
||||||
|
|
||||||
if (LibHeif_FOUND)
|
if (LibHeif_FOUND)
|
||||||
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
|
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp microexif.cpp)
|
||||||
target_link_libraries(kimg_heif PRIVATE PkgConfig::LibHeif)
|
target_link_libraries(kimg_heif PRIVATE PkgConfig::LibHeif)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "heif_p.h"
|
#include "heif_p.h"
|
||||||
|
#include "microexif_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
#include <libheif/heif.h>
|
#include <libheif/heif.h>
|
||||||
|
|
||||||
@ -18,6 +19,14 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef HEIF_MAX_METADATA_SIZE
|
||||||
|
/*!
|
||||||
|
* XMP and EXIF maximum size.
|
||||||
|
*/
|
||||||
|
#define HEIF_MAX_METADATA_SIZE (4 * 1024 * 1024)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
size_t HEIFHandler::m_initialized_count = 0;
|
size_t HEIFHandler::m_initialized_count = 0;
|
||||||
bool HEIFHandler::m_plugins_queried = false;
|
bool HEIFHandler::m_plugins_queried = false;
|
||||||
bool HEIFHandler::m_heif_decoder_available = false;
|
bool HEIFHandler::m_heif_decoder_available = false;
|
||||||
@ -305,7 +314,25 @@ bool HEIFHandler::write_helper(const QImage &image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = heif_context_encode_image(context, h_image, encoder, encoder_options, nullptr);
|
struct heif_image_handle* handle;
|
||||||
|
err = heif_context_encode_image(context, h_image, encoder, encoder_options, &handle);
|
||||||
|
|
||||||
|
// exif metadata
|
||||||
|
if (err.code == heif_error_Ok) {
|
||||||
|
auto exif = MicroExif::fromImage(tmpimage);
|
||||||
|
if (!exif.isEmpty()) {
|
||||||
|
auto ba = exif.toByteArray();
|
||||||
|
err = heif_context_add_exif_metadata(context, handle, ba.constData(), ba.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// xmp metadata
|
||||||
|
if (err.code == heif_error_Ok) {
|
||||||
|
auto xmp = image.text(META_KEY_XMP_ADOBE);
|
||||||
|
if (!xmp.isEmpty()) {
|
||||||
|
auto ba = xmp.toUtf8();
|
||||||
|
err = heif_context_add_XMP_metadata2(context, handle, ba.constData(), ba.size(), heif_metadata_compression_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (encoder_options) {
|
if (encoder_options) {
|
||||||
heif_encoding_options_free(encoder_options);
|
heif_encoding_options_free(encoder_options);
|
||||||
@ -904,6 +931,43 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
m_current_image.setColorSpace(QColorSpace(QColorSpace::SRgb));
|
m_current_image.setColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read metadata
|
||||||
|
if (auto numMetadata = heif_image_handle_get_number_of_metadata_blocks(handle, nullptr)) {
|
||||||
|
QVector<heif_item_id> ids(numMetadata);
|
||||||
|
heif_image_handle_get_list_of_metadata_block_IDs(handle, nullptr, ids.data(), numMetadata);
|
||||||
|
for (int n = 0; n < numMetadata; ++n) {
|
||||||
|
auto itemtype = heif_image_handle_get_metadata_type(handle, ids[n]);
|
||||||
|
auto contenttype = heif_image_handle_get_metadata_content_type(handle, ids[n]);
|
||||||
|
auto isExif = !std::strcmp(itemtype, "Exif");
|
||||||
|
auto isXmp = !std::strcmp(contenttype, "application/rdf+xml");
|
||||||
|
if (isExif || isXmp) {
|
||||||
|
auto sz = heif_image_handle_get_metadata_size(handle, ids[n]);
|
||||||
|
if (sz == 0 || sz >= HEIF_MAX_METADATA_SIZE)
|
||||||
|
continue;
|
||||||
|
QByteArray ba(sz, char());
|
||||||
|
auto err = heif_image_handle_get_metadata(handle, ids[n], ba.data());
|
||||||
|
if (err.code != heif_error_Ok) {
|
||||||
|
qWarning() << "Error while reading metadata" << err.message;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isXmp) {
|
||||||
|
m_current_image.setText(META_KEY_XMP_ADOBE, QString::fromUtf8(ba));
|
||||||
|
} else if (isExif) {
|
||||||
|
auto exif = MicroExif::fromByteArray(ba.mid(4));
|
||||||
|
if (!exif.isEmpty()) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
heif_image_release(img);
|
heif_image_release(img);
|
||||||
heif_image_handle_release(handle);
|
heif_image_handle_release(handle);
|
||||||
heif_context_free(ctx);
|
heif_context_free(ctx);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user