From 712eb47886b86c816ab3b75d152b3c93b57a0c0e Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sat, 9 Mar 2024 14:34:50 +0100 Subject: [PATCH] PNG: preserve ICC profiles that QColorSpace does not handle QColorSpace::isValid() returns true if QColorSpace can handle the profile. When called on a QColorSpace obtained via QColorSpace::fromIccProfile, this doesn't necessarily mean that the ICC profile itself was invalid; it could be that the ICC data was valid, but QColorSpace didn't know how to use it. This is especially true on Qt <= 6.7, where only XYZ/RGB matrix profiles were supported. We don't fully parse ICC v4, and we're lacking an API to differentiate between "ICC data was valid but QColorSpace doesn't handle it" vs "ICC data was invalid". Still, an invalid QColorSpace will still the original ICC data, so it can be saved again without loss of information. So: 1) when loading a PNG with embedded ICC data, keep the loaded profile even if it's "invalid"; 2) when writing a PNG, check if we have ICC data to save, and unconditionally write it if it's the case. This avoids data loss in the two directions. This work has been kindly sponsored by the QGIS project (https://qgis.org/). Pick-to: 6.6 6.5 Change-Id: I1f27f603acbca1590c820e80f52f3b994f5ea5c7 Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 003cef9d40f2e840f2690bd465994deaab5c2b95) Reviewed-by: Qt Cherry-pick Bot --- src/gui/image/qpnghandler.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index c9b12c15f8c..615a36fa36a 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -554,10 +554,10 @@ bool QPngHandlerPrivate::readPngHeader() #endif png_uint_32 profLen; png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen); - colorSpace = QColorSpace::fromIccProfile(QByteArray((const char *)profileData, profLen)); - if (!colorSpace.isValid()) { - qCDebug(lcImageIo) << "QPngHandler: Failed to parse ICC profile"; - } else { + Q_UNUSED(name); + Q_UNUSED(compressionType); + if (profLen > 0) { + colorSpace = QColorSpace::fromIccProfile(QByteArray((const char *)profileData, profLen)); QColorSpacePrivate *csD = QColorSpacePrivate::get(colorSpace); if (csD->description.isEmpty()) csD->description = QString::fromLatin1((const char *)name); @@ -926,15 +926,15 @@ bool QPNGImageWriter::writeImage(const QImage& image, int compression_in, const color_type, 0, 0, 0); // sets #channels #ifdef PNG_iCCP_SUPPORTED - if (image.colorSpace().isValid()) { - QColorSpace cs = image.colorSpace(); - // Support the old gamma making it override transferfunction. - if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma)) - cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); + QColorSpace cs = image.colorSpace(); + // Support the old gamma making it override transferfunction (if possible) + if (cs.isValid() && gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma)) + cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); + QByteArray iccProfile = cs.iccProfile(); + if (!iccProfile.isEmpty()) { QByteArray iccProfileName = cs.description().toLatin1(); if (iccProfileName.isEmpty()) iccProfileName = QByteArrayLiteral("Custom"); - QByteArray iccProfile = cs.iccProfile(); png_set_iCCP(png_ptr, info_ptr, #if PNG_LIBPNG_VER < 10500 iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),