Compare commits

..

1 Commits

Author SHA1 Message Date
a33446f86a Use of heif_context_add_XMP_metadata instead
heif_context_add_XMP_metadata2

(cherry picked from commit 245c835d92)
2025-03-11 11:32:12 +01:00
55 changed files with 66 additions and 291 deletions

View File

@ -7,5 +7,5 @@ Dependencies:
Options:
test-before-installing: True
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON"
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON"
per-test-timeout: 90

View File

@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.16)
set(KF_VERSION "6.14.0") # handled by release scripts
set(KF_DEP_VERSION "6.14.0") # handled by release scripts
set(KF_VERSION "6.12.0") # handled by release scripts
set(KF_DEP_VERSION "6.12.0") # handled by release scripts
project(KImageFormats VERSION ${KF_VERSION})
include(FeatureSummary)
find_package(ECM 6.14.0 NO_MODULE)
find_package(ECM 6.12.0 NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
include(CheckIncludeFiles)
include(FindPkgConfig)
set(REQUIRED_QT_VERSION 6.7.0)
set(REQUIRED_QT_VERSION 6.6.0)
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
find_package(KF6Archive ${KF_DEP_VERSION})
@ -99,8 +99,8 @@ endif()
add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images")
ecm_set_disabled_deprecation_versions(
QT 6.9.0
KF 6.12.0
QT 6.8.0
KF 6.11.0
)
add_subdirectory(src)

View File

@ -122,12 +122,9 @@ if (LibHeif_FOUND)
kimageformats_read_tests(FUZZ 1
hej2
)
kimageformats_write_tests(FUZZ 1
hej2-nodatacheck-lossless
)
endif()
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.0")
kimageformats_read_tests(FUZZ 4
avci
)

View File

@ -258,15 +258,6 @@ int main(int argc, char **argv)
});
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
if (!formats.contains(format)) {
if (format == "avci" || format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : " << suffix << " is not supported with current libheif configuration!\n"
<< "********* "
<< "Finished basic read tests for " << suffix << " images *********\n";
return 0;
}
}
const QFileInfoList lstImgDir = imgdir.entryInfoList();
// Launch 2 runs for each test: first run on a random access device, second run on a sequential access device
for (int seq = 0; seq < 2; ++seq) {
@ -332,12 +323,7 @@ int main(int argc, char **argv)
OptionTest optionTest;
if (!optionTest.store(&inputReader)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
if (format == "heif") {
// libheif + ffmpeg decoder is unable to load all HEIF files.
++skipped;
} else {
++failed;
}
++failed;
continue;
}

View File

@ -31,7 +31,7 @@
},
{
"key" : "Description",
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
"value" : "TV broadcast test image."
},
{
"key" : "Latitude",

View File

@ -31,7 +31,7 @@
},
{
"key" : "Description",
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
"value" : "TV broadcast test image."
},
{
"key" : "Latitude",

View File

@ -1,65 +0,0 @@
{
"format" : "hej2",
"metadata" : [
{
"key" : "CreationDate",
"value" : "2025-01-14T13:53:32+01:00"
},
{
"key" : "Direction",
"value" : "123.7"
},
{
"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
}
}

View File

@ -31,7 +31,7 @@
},
{
"key" : "Description",
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
"value" : "TV broadcast test image."
},
{
"key" : "Latitude",

View File

@ -370,12 +370,7 @@ int formatTest(const QString &suffix, bool createTemplates)
QBuffer buffer(&ba);
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
if (writtenImage.isNull()) {
if (suffix.toLatin1() == "heif") {
// libheif + ffmpeg decoder is unable to load all HEIF files.
++skipped;
} else {
++failed;
}
++failed;
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
continue;
}
@ -627,28 +622,8 @@ int main(int argc, char **argv)
}
}
auto suffix = args.at(0);
// skip test if libheif configuration is obviously incomplete
QByteArray format = suffix.toLatin1();
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
if (!read_formats.contains(format)) {
if (format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary decoder(s)!\n";
return 0;
}
}
if (!write_formats.contains(format)) {
if (format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary encoder(s)!\n";
return 0;
}
}
// run test
auto suffix = args.at(0);
auto ret = basicTest(suffix, parser.isSet(lossless), parser.isSet(ignoreDataCheck), parser.isSet(skipOptTest), fuzzarg);
if (ret == 0) {
ret = formatTest(suffix, parser.isSet(createFormatTempates));

View File

@ -15,16 +15,6 @@ 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(ANDROID)
# Plugins should be named with lib prefix on Android
# Working name: libplugins_imageformats_kimg_avif_armeabi-v7a.so
# Doesn't work: plugins_imageformats_kimg_avif_armeabi-v7a.so
if(NOT ${CMAKE_SHARED_LIBRARY_PREFIX} STREQUAL "")
set_target_properties(${plugin} PROPERTIES PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
endif()
endif()
install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/imageformats)
endfunction()

View File

@ -2557,5 +2557,3 @@ QImageIOHandler *QDDSPlugin::create(QIODevice *device, const QByteArray &format)
handler->setFormat(format);
return handler;
}
#include "moc_dds_p.cpp"

View File

@ -16,8 +16,8 @@
#include <QDebug>
#include <QPointF>
#include <QSysInfo>
#include <cstring>
#include <limits>
#include <string.h>
#ifndef HEIF_MAX_METADATA_SIZE
/*!
@ -26,12 +26,12 @@
#define HEIF_MAX_METADATA_SIZE (4 * 1024 * 1024)
#endif
size_t HEIFHandler::m_initialized_count = 0;
bool HEIFHandler::m_plugins_queried = false;
bool HEIFHandler::m_heif_decoder_available = false;
bool HEIFHandler::m_heif_encoder_available = false;
bool HEIFHandler::m_hej2_decoder_available = false;
bool HEIFHandler::m_hej2_encoder_available = false;
bool HEIFHandler::m_avci_decoder_available = false;
extern "C" {
@ -155,14 +155,6 @@ bool HEIFHandler::write_helper(const QImage &image)
break;
}
heif_compression_format encoder_codec = heif_compression_HEVC;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (format() == "hej2") {
encoder_codec = heif_compression_JPEG2000;
save_depth = 8; // for compatibility reasons
}
#endif
heif_chroma chroma;
if (save_depth > 8) {
if (save_alpha) {
@ -295,7 +287,7 @@ bool HEIFHandler::write_helper(const QImage &image)
}
struct heif_encoder *encoder = nullptr;
err = heif_context_get_encoder_for_format(context, encoder_codec, &encoder);
err = heif_context_get_encoder_for_format(context, heif_compression_HEVC, &encoder);
if (err.code) {
qWarning() << "Unable to get an encoder instance:" << err.message;
heif_image_release(h_image);
@ -322,7 +314,7 @@ bool HEIFHandler::write_helper(const QImage &image)
}
}
struct heif_image_handle *handle;
struct heif_image_handle* handle;
err = heif_context_encode_image(context, h_image, encoder, encoder_options, &handle);
// exif metadata
@ -562,7 +554,7 @@ bool HEIFHandler::ensureDecoder()
QImage::Format target_image_format;
if (bit_depth == 10 || bit_depth == 12 || bit_depth == 16) {
if (bit_depth == 10 || bit_depth == 12) {
if (hasAlphaChannel) {
chroma = (QSysInfo::ByteOrder == QSysInfo::LittleEndian) ? heif_chroma_interleaved_RRGGBBAA_LE : heif_chroma_interleaved_RRGGBBAA_BE;
target_image_format = QImage::Format_RGBA64;
@ -654,35 +646,6 @@ bool HEIFHandler::ensureDecoder()
}
switch (bit_depth) {
case 16:
if (hasAlphaChannel) {
for (int y = 0; y < imageHeight; y++) {
memcpy(m_current_image.scanLine(y), src + (y * stride), 8 * size_t(imageWidth));
}
} else { // no alpha channel
for (int y = 0; y < imageHeight; y++) {
const uint16_t *src_word = reinterpret_cast<const uint16_t *>(src + (y * stride));
uint16_t *dest_data = reinterpret_cast<uint16_t *>(m_current_image.scanLine(y));
for (int x = 0; x < imageWidth; x++) {
// R
*dest_data = *src_word;
src_word++;
dest_data++;
// G
*dest_data = *src_word;
src_word++;
dest_data++;
// B
*dest_data = *src_word;
src_word++;
dest_data++;
// X = 0xffff
*dest_data = 0xffff;
dest_data++;
}
}
}
break;
case 12:
if (hasAlphaChannel) {
for (int y = 0; y < imageHeight; y++) {
@ -1028,13 +991,6 @@ bool HEIFHandler::isHej2DecoderAvailable()
return m_hej2_decoder_available;
}
bool HEIFHandler::isHej2EncoderAvailable()
{
HEIFHandler::queryHeifLib();
return m_hej2_encoder_available;
}
bool HEIFHandler::isAVCIDecoderAvailable()
{
HEIFHandler::queryHeifLib();
@ -1057,9 +1013,8 @@ void HEIFHandler::queryHeifLib()
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
m_hej2_encoder_available = heif_have_encoder_for_format(heif_compression_JPEG2000);
#endif
#if LIBHEIF_HAVE_VERSION(1, 19, 6)
#if LIBHEIF_HAVE_VERSION(1, 19, 0)
m_avci_decoder_available = heif_have_decoder_for_format(heif_compression_AVC);
#endif
m_plugins_queried = true;
@ -1126,9 +1081,6 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
if (HEIFHandler::isHej2DecoderAvailable()) {
format_cap |= CanRead;
}
if (HEIFHandler::isHej2EncoderAvailable()) {
format_cap |= CanWrite;
}
return format_cap;
}
@ -1158,7 +1110,7 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
}
}
if (device->isWritable() && (HEIFHandler::isHeifEncoderAvailable() || HEIFHandler::isHej2EncoderAvailable())) {
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
cap |= CanWrite;
}
return cap;

View File

@ -31,7 +31,6 @@ public:
static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable();
static bool isHej2DecoderAvailable();
static bool isHej2EncoderAvailable();
static bool isAVCIDecoderAvailable();
static bool isSupportedBMFFType(const QByteArray &header);
@ -63,7 +62,6 @@ private:
static bool m_heif_decoder_available;
static bool m_heif_encoder_available;
static bool m_hej2_decoder_available;
static bool m_hej2_encoder_available;
static bool m_avci_decoder_available;
static QMutex &getHEIFHandlerMutex();

View File

@ -20,7 +20,7 @@
#include <string.h>
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 3)
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 7) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 3))
#ifndef JXL_QT_AUTOTRANSFORM
#define JXL_QT_AUTOTRANSFORM
#endif

View File

@ -720,11 +720,11 @@ public:
auto exif = MicroExif::fromImage(image);
if (!exif.isEmpty()) {
auto exifIfd = exif.exifIfdByteArray(QDataStream::LittleEndian, MicroExif::V2);
auto exifIfd = exif.exifIfdByteArray(QDataStream::LittleEndian);
if (auto err = PKImageEncode_SetEXIFMetadata_WMP(pEncoder, reinterpret_cast<const quint8 *>(exifIfd.constData()), exifIfd.size())) {
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting EXIF data:" << err;
}
auto gpsIfd = exif.gpsIfdByteArray(QDataStream::LittleEndian, MicroExif::V2);
auto gpsIfd = exif.gpsIfdByteArray(QDataStream::LittleEndian);
if (auto err = PKImageEncode_SetGPSInfoMetadata_WMP(pEncoder, reinterpret_cast<const quint8 *>(gpsIfd.constData()), gpsIfd.size())) {
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting GPS data:" << err;
}
@ -991,7 +991,7 @@ bool JXRHandler::read(QImage *outImage)
return false;
}
for (qint32 y = 0, h = img.height(); y < h; ++y) {
std::memcpy(img.scanLine(y), ba.data() + convStrideSize * y, (std::min)(convStrideSize, qint64(img.bytesPerLine())));
std::memcpy(img.scanLine(y), ba.data() + convStrideSize * y, (std::min)(convStrideSize, img.bytesPerLine()));
}
}
PKFormatConverter_Release(&pConverter);

View File

@ -12,7 +12,6 @@
#include <QCoreApplication>
#include <QDataStream>
#include <QHash>
#include <QStringDecoder>
#include <QTimeZone>
// TIFF 6 specs
@ -112,17 +111,17 @@ static const KnownTags staticTagTypes = {
TagInfo(TIFF_IMAGEWIDTH, ExifTagType::Long),
TagInfo(TIFF_IMAGEHEIGHT, ExifTagType::Long),
TagInfo(TIFF_BITSPERSAMPLE, ExifTagType::Short),
TagInfo(TIFF_IMAGEDESCRIPTION, ExifTagType::Utf8),
TagInfo(TIFF_MAKE, ExifTagType::Utf8),
TagInfo(TIFF_MODEL, ExifTagType::Utf8),
TagInfo(TIFF_IMAGEDESCRIPTION, ExifTagType::Ascii),
TagInfo(TIFF_MAKE, ExifTagType::Ascii),
TagInfo(TIFF_MODEL, ExifTagType::Ascii),
TagInfo(TIFF_ORIENT, ExifTagType::Short),
TagInfo(TIFF_XRES, ExifTagType::Rational),
TagInfo(TIFF_YRES, ExifTagType::Rational),
TagInfo(TIFF_URES, ExifTagType::Short),
TagInfo(TIFF_SOFTWARE, ExifTagType::Utf8),
TagInfo(TIFF_ARTIST, ExifTagType::Utf8),
TagInfo(TIFF_SOFTWARE, ExifTagType::Ascii),
TagInfo(TIFF_ARTIST, ExifTagType::Ascii),
TagInfo(TIFF_DATETIME, ExifTagType::Ascii),
TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8),
TagInfo(TIFF_COPYRIGHT, ExifTagType::Ascii),
TagInfo(EXIF_EXIFIFD, ExifTagType::Long),
TagInfo(EXIF_GPSIFD, ExifTagType::Long),
TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii),
@ -135,10 +134,10 @@ static const KnownTags staticTagTypes = {
TagInfo(EXIF_PIXELYDIM, ExifTagType::Long),
TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii),
TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii),
TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8),
TagInfo(EXIF_LENSMODEL, ExifTagType::Utf8),
TagInfo(EXIF_LENSMAKE, ExifTagType::Ascii),
TagInfo(EXIF_LENSMODEL, ExifTagType::Ascii),
TagInfo(EXIF_LENSSERIALNUMBER, ExifTagType::Ascii),
TagInfo(EXIF_IMAGETITLE, ExifTagType::Utf8),
TagInfo(EXIF_IMAGETITLE, ExifTagType::Ascii),
TagInfo(EXIF_EXIFVERSION, ExifTagType::Undefined)
};
// clang-format on
@ -368,28 +367,6 @@ static void writeData(QDataStream &ds, const QVariant &value, const ExifTagType&
}
}
static ExifTagType updateDataType(const ExifTagType &dataType, const QVariant &value, const MicroExif::Version &ver)
{
if (dataType != ExifTagType::Utf8)
return dataType;
if (ver == MicroExif::V2)
return ExifTagType::Ascii;
// Note that in EXIF specs, UTF-8 is backward compatible with ASCII: all UTF-8 tags can also be ASCII.
// To maximize compatibility, I check if the string can be encoded in ASCII.
auto txt = value.toString();
// Exif ASCII data type allow only values up to 127 (7-bit ASCII).
auto u8 = txt.toUtf8();
for (auto &&c : u8) {
if (uchar(c) > 127)
return dataType;
}
return ExifTagType::Ascii;
}
/*!
* \brief writeIfd
* \param ds The stream.
@ -398,12 +375,7 @@ static ExifTagType updateDataType(const ExifTagType &dataType, const QVariant &v
* \param knownTags List of known and supported tags.
* \return True on success, otherwise false.
*/
static bool writeIfd(QDataStream &ds,
const MicroExif::Version &ver,
const MicroExif::Tags &tags,
TagPos &positions,
quint32 pos = 0,
const KnownTags &knownTags = staticTagTypes)
static bool writeIfd(QDataStream &ds, const MicroExif::Tags &tags, TagPos &positions, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes)
{
if (tags.isEmpty())
return true;
@ -418,7 +390,7 @@ static bool writeIfd(QDataStream &ds,
continue;
}
auto value = tags.value(key);
auto dataType = updateDataType(knownTags.value(key), value, ver);
auto dataType = knownTags.value(key);
auto count = countBytes(dataType, value);
ds << quint16(key);
@ -440,8 +412,9 @@ static bool writeIfd(QDataStream &ds,
if (!knownTags.contains(key)) {
continue;
}
auto value = tags.value(key);
auto dataType = updateDataType(knownTags.value(key), value, ver);
auto dataType = knownTags.value(key);
auto count = countBytes(dataType, value);
auto valueSize = count * EXIF_TAG_SIZEOF(dataType);
if (valueSize <= 4)
@ -561,16 +534,8 @@ static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, con
if (dataType == EXIF_TAG_DATATYPE(ExifTagType::Ascii) || dataType == EXIF_TAG_DATATYPE(ExifTagType::Utf8)) {
auto l = readBytes(ds, count, true);
if (!l.isEmpty()) {
// It seems that converting to Latin 1 never detects errors so, using UTF-8.
// Note that if the dataType is ASCII, by EXIF specification, it must use only the
// first 128 values so the UTF-8 conversion is correct.
auto dec = QStringDecoder(QStringDecoder::Utf8);
// QStringDecoder raise an error only after converting to QString
auto ut8 = QString(dec(l));
// If there are errors in the conversion to UTF-8, then I try with latin1 (extended ASCII)
tags.insert(tagId, dec.hasError() ? QString::fromLatin1(l) : ut8);
}
if (!l.isEmpty())
tags.insert(tagId, dataType == EXIF_TAG_DATATYPE(ExifTagType::Utf8) ? QString::fromUtf8(l) : QString::fromLatin1(l));
} else if (dataType == EXIF_TAG_DATATYPE(ExifTagType::Undefined)) {
auto l = readBytes(ds, count, false);
if (!l.isEmpty())
@ -1067,7 +1032,7 @@ void MicroExif::setImageDirection(double degree, bool isMagnetic)
m_gpsTags.insert(GPS_IMGDIRECTION, degree);
}
QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder) const
{
QByteArray ba;
{
@ -1078,16 +1043,16 @@ QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder, const
return ba;
}
QByteArray MicroExif::exifIfdByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
QByteArray MicroExif::exifIfdByteArray(const QDataStream::ByteOrder &byteOrder) const
{
QByteArray ba;
{
QDataStream ds(&ba, QIODevice::WriteOnly);
ds.setByteOrder(byteOrder);
auto exifTags = m_exifTags;
exifTags.insert(EXIF_EXIFVERSION, version == Version::V3 ? QByteArray("0300") : QByteArray("0232"));
exifTags.insert(EXIF_EXIFVERSION, QByteArray("0300"));
TagPos positions;
if (!writeIfd(ds, version, exifTags, positions))
if (!writeIfd(ds, exifTags, positions))
return {};
}
return ba;
@ -1100,7 +1065,7 @@ bool MicroExif::setExifIfdByteArray(const QByteArray &ba, const QDataStream::Byt
return readIfd(ds, m_exifTags, 0, staticTagTypes);
}
QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder) const
{
QByteArray ba;
{
@ -1109,7 +1074,7 @@ QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder, c
auto gpsTags = m_gpsTags;
gpsTags.insert(GPS_GPSVERSION, QByteArray("2400"));
TagPos positions;
if (!writeIfd(ds, version, gpsTags, positions, 0, staticGpsTagTypes))
if (!writeIfd(ds, gpsTags, positions, 0, staticGpsTagTypes))
return {};
return ba;
}
@ -1122,7 +1087,7 @@ bool MicroExif::setGpsIfdByteArray(const QByteArray &ba, const QDataStream::Byte
return readIfd(ds, m_gpsTags, 0, staticGpsTagTypes);
}
bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder, const Version &version) const
bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder) const
{
if (device == nullptr || device->isSequential() || isEmpty())
return false;
@ -1131,7 +1096,7 @@ bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder
ds.setByteOrder(byteOrder);
if (!writeHeader(ds))
return false;
if (!writeIfds(ds, version))
if (!writeIfds(ds))
return false;
}
device->close();
@ -1362,30 +1327,30 @@ bool MicroExif::writeHeader(QDataStream &ds) const
return ds.status() == QDataStream::Ok;
}
bool MicroExif::writeIfds(QDataStream &ds, const Version &version) const
bool MicroExif::writeIfds(QDataStream &ds) const
{
auto tiffTags = m_tiffTags;
auto exifTags = m_exifTags;
auto gpsTags = m_gpsTags;
updateTags(tiffTags, exifTags, gpsTags, version);
updateTags(tiffTags, exifTags, gpsTags);
TagPos positions;
if (!writeIfd(ds, version, tiffTags, positions))
if (!writeIfd(ds, tiffTags, positions))
return false;
if (!writeIfd(ds, version, exifTags, positions, positions.value(EXIF_EXIFIFD)))
if (!writeIfd(ds, exifTags, positions, positions.value(EXIF_EXIFIFD)))
return false;
if (!writeIfd(ds, version, gpsTags, positions, positions.value(EXIF_GPSIFD), staticGpsTagTypes))
if (!writeIfd(ds, gpsTags, positions, positions.value(EXIF_GPSIFD), staticGpsTagTypes))
return false;
return true;
}
void MicroExif::updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const Version &version) const
void MicroExif::updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags) const
{
if (exifTags.isEmpty()) {
tiffTags.remove(EXIF_EXIFIFD);
} else {
tiffTags.insert(EXIF_EXIFIFD, quint32());
exifTags.insert(EXIF_EXIFVERSION, version == Version::V3 ? QByteArray("0300") : QByteArray("0232"));
exifTags.insert(EXIF_EXIFVERSION, QByteArray("0300"));
}
if (gpsTags.isEmpty()) {
tiffTags.remove(EXIF_GPSIFD);

View File

@ -37,15 +37,6 @@ class MicroExif
public:
using Tags = QMap<quint16, QVariant>;
/*!
* \brief The Version enum
* Exif specs version used when writing.
*/
enum Version {
V2, // V2.xx
V3 // V3.xx, use of UTF-8 data type (default)
};
/*!
* \brief MicroExif
* Constructs an empty class.
@ -274,20 +265,18 @@ public:
* - EXIF IFD
* - GPS IFD
* \param byteOrder Sets the serialization byte order for EXIF data.
* \param version The EXIF specs version to use.
* \return A byte array containing the serialized data.
* \sa write
*/
QByteArray toByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
QByteArray toByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
/*!
* \brief exifIfdByteArray
* Convert the EXIF IFD only to RAW data. Useful when you want to add EXIF data to an existing TIFF container.
* \param byteOrder Sets the serialization byte order for the data.
* \param version The EXIF specs version to use.
* \return A byte array containing the serialized data.
*/
QByteArray exifIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
QByteArray exifIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
/*!
* \brief setExifIfdByteArray
* \param ba The RAW data of EXIF IFD.
@ -300,10 +289,9 @@ public:
* \brief gpsIfdByteArray
* Convert the GPS IFD only to RAW data. Useful when you want to add GPS data to an existing TIFF container.
* \param byteOrder Sets the serialization byte order for the data.
* \param version The EXIF specs version to use.
* \return A byte array containing the serialized data.
*/
QByteArray gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
QByteArray gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
/*!
* \brief setGpsIfdByteArray
* \param ba The RAW data of GPS IFD.
@ -321,11 +309,10 @@ public:
* - GPS IFD
* \param device A random access device.
* \param byteOrder Sets the serialization byte order for EXIF data.
* \param version The EXIF specs version to use.
* \return True on success, otherwise false.
* \sa toByteArray
*/
bool write(QIODevice *device, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
bool write(QIODevice *device, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
/*!
* \brief updateImageMetadata
@ -386,8 +373,8 @@ private:
void setGpsString(quint16 tagId, const QString& s);
QString gpsString(quint16 tagId) const;
bool writeHeader(QDataStream &ds) const;
bool writeIfds(QDataStream &ds, const Version &version) const;
void updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const Version &version) const;
bool writeIfds(QDataStream &ds) const;
void updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags) const;
static void setString(Tags &tags, quint16 tagId, const QString &s);
static QString string(const Tags &tags, quint16 tagId);

View File

@ -1504,18 +1504,7 @@ bool PSDHandler::read(QImage *image)
img.setColorSpace(QColorSpace(QColorSpace::SRgb));
#endif
} else if (!setColorSpace(img, irs)) {
// Float images are used by Photoshop as linear: if no color space
// is present, a linear one should be chosen.
if (header.color_mode == CM_RGB && header.depth == 32) {
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (header.color_mode == CM_GRAYSCALE && header.depth == 32) {
auto qs = QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::Linear);
qs.setDescription(QStringLiteral("Linear grayscale"));
img.setColorSpace(qs);
}
#endif
// qDebug() << "No colorspace info set!";
}
// XMP data

View File

@ -457,4 +457,3 @@ QImageIOHandler *ScitexPlugin::create(QIODevice *device, const QByteArray &forma
return handler;
}
#include "moc_sct_p.cpp"

View File

@ -962,7 +962,11 @@ bool XCFImageFormat::loadImageProperties(QDataStream &xcf_io, XCFImage &xcf_imag
case PROP_PARASITES:
while (!property.atEnd()) {
char *tag;
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
quint32 size;
#else
qint64 size;
#endif
property.readBytes(tag, size);