Compare commits

..
Author SHA1 Message Date
Albert Astals Cid 8658355701 Fix crash on malformed files
oss-fuzz/449485443
2025-10-05 21:25:48 +00:00
51 changed files with 231 additions and 404 deletions
+5 -5
View File
@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(KF_VERSION "6.21.0") # handled by release scripts set(KF_VERSION "6.20.0") # handled by release scripts
set(KF_DEP_VERSION "6.21.0") # handled by release scripts set(KF_DEP_VERSION "6.19.0") # handled by release scripts
project(KImageFormats VERSION ${KF_VERSION}) project(KImageFormats VERSION ${KF_VERSION})
include(FeatureSummary) include(FeatureSummary)
find_package(ECM 6.21.0 NO_MODULE) find_package(ECM 6.19.0 NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") 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) feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
@@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
include(CheckIncludeFiles) include(CheckIncludeFiles)
include(FindPkgConfig) include(FindPkgConfig)
set(REQUIRED_QT_VERSION 6.8.0) set(REQUIRED_QT_VERSION 6.7.0)
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
find_package(KF6Archive ${KF_DEP_VERSION}) find_package(KF6Archive ${KF_DEP_VERSION})
@@ -100,7 +100,7 @@ add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR
ecm_set_disabled_deprecation_versions( ecm_set_disabled_deprecation_versions(
QT 6.10.0 QT 6.10.0
KF 6.19.0 KF 6.18.0
) )
add_subdirectory(src) add_subdirectory(src)
+2 -4
View File
@@ -290,7 +290,7 @@ in your cmake options.**
The following defines can be defined in cmake to modify the behavior of the The following defines can be defined in cmake to modify the behavior of the
plugin: plugin:
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride alignment based on DDS - `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride aligment based on DDS
pitch: it is known that some writers do not set it correctly. pitch: it is known that some writers do not set it correctly.
### The HEIF plugin ### The HEIF plugin
@@ -382,7 +382,7 @@ The following defines can be defined in cmake to modify the behavior of the
plugin: plugin:
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently - `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently
any HDR data will be lost. any HDR data will be lost.
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the needs of additional memory by - `JXR_DISABLE_DEPTH_CONVERSION`: remove the neeeds of additional memory by
disabling the conversion between different color depths (e.g. RGBA64bpp to disabling the conversion between different color depths (e.g. RGBA64bpp to
RGBA32bpp) at the cost of reduced compatibility. RGBA32bpp) at the cost of reduced compatibility.
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out - `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out
@@ -414,8 +414,6 @@ PSD support has the following limitations:
- Multichannel images are treated as CMYK if they have 2 or more channels. - Multichannel images are treated as CMYK if they have 2 or more channels.
- Multichannel images are treated as Grayscale if they have 1 channel. - Multichannel images are treated as Grayscale if they have 1 channel.
- Duotone images are treated as grayscale images. - Duotone images are treated as grayscale images.
- Grayscale images with alpha channel or at 32 bit depth are converted to
RGBA due to the lack of the appropriate Qt grayscale container.
- Extra channels other than alpha are discarded. - Extra channels other than alpha are discarded.
The following defines can be defined in cmake to modify the behavior of the The following defines can be defined in cmake to modify the behavior of the
+1 -1
View File
@@ -38,7 +38,7 @@ git clone --depth 1 -b v1.2.1 https://github.com/AOMediaCodec/libavif.git
git clone --depth 1 https://github.com/strukturag/libde265.git git clone --depth 1 https://github.com/strukturag/libde265.git
git clone --depth 1 -b v2.5.3 https://github.com/uclouvain/openjpeg.git git clone --depth 1 -b v2.5.3 https://github.com/uclouvain/openjpeg.git
git clone --depth 1 https://github.com/strukturag/libheif.git git clone --depth 1 https://github.com/strukturag/libheif.git
git clone --depth=1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git git clone --depth=1 --branch v0.11.x --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
git clone --depth 1 https://github.com/LibRaw/LibRaw git clone --depth 1 https://github.com/LibRaw/LibRaw
git clone --depth 1 https://github.com/mircomir/jxrlib.git git clone --depth 1 https://github.com/mircomir/jxrlib.git
git clone --depth 1 -b v2.6.0 https://github.com/cisco/openh264.git git clone --depth 1 -b v2.6.0 https://github.com/cisco/openh264.git
+2 -2
View File
@@ -2,8 +2,8 @@
{ {
"fileName" : "32bit_grayscale.png", "fileName" : "32bit_grayscale.png",
"colorSpace" : { "colorSpace" : {
"description" : "RGB emulation of \"Linear Grayscale Profile\"", "description" : "Linear Grayscale Profile",
"colorModel" : "Rgb", "colorModel" : "Gray",
"primaries" : "Custom", "primaries" : "Custom",
"transferFunction" : "Linear", "transferFunction" : "Linear",
"gamma" : 1 "gamma" : 1
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
@@ -1,26 +0,0 @@
[
{
"fileName" : "testcard_graya16.png",
"colorSpace" : {
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
"colorModel" : "Rgb",
"primaries" : "SRgb",
"transferFunction" : "Gamma",
"gamma" : 2.19922
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:27:47"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.
@@ -1,26 +0,0 @@
[
{
"fileName" : "testcard_graya32.png",
"colorSpace" : {
"description" : "RGB emulation of \"Profilo scala di grigio lineare\"",
"colorModel" : "Rgb",
"primaries" : "Custom",
"transferFunction" : "Linear",
"gamma" : 1
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:29:19"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.
@@ -1,28 +0,0 @@
[
{
"fileName" : "testcard_graya8.png",
"fuzziness" : 1,
"perceptiveFuzziness" : true,
"colorSpace" : {
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
"colorModel" : "Rgb",
"primaries" : "SRgb",
"transferFunction" : "Gamma",
"gamma" : 2.19922
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:28:50"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]
-6
View File
@@ -1,6 +0,0 @@
[
{
"fileName" : "fruktpilot.png",
"fuzziness" : 1
}
]
@@ -1,6 +0,0 @@
[
{
"fileName" : "fruktpilot16_icc.png",
"fuzziness" : 1
}
]
@@ -1,6 +0,0 @@
[
{
"fileName" : "fruktpilot32_icc.png",
"fuzziness" : 1
}
]
@@ -1,6 +0,0 @@
[
{
"fileName" : "fruktpilot32fplin_icc.png",
"fuzziness" : 1
}
]
@@ -1,6 +0,0 @@
[
{
"fileName" : "fruktpilot_icc.png",
"fuzziness" : 1
}
]
+2
View File
@@ -138,11 +138,13 @@ bool TemplateImage::checkOptionaInfo(const QImage& image, QString& error) const
return false; return false;
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto clmName = QString(QMetaEnum::fromType<QColorSpace::ColorModel>().valueToKey(quint64(cs.colorModel()))); auto clmName = QString(QMetaEnum::fromType<QColorSpace::ColorModel>().valueToKey(quint64(cs.colorModel())));
if (clmName != clm) { if (clmName != clm) {
error = QStringLiteral("ColorSpace ColorModel mismatch (current: %1, expected: %2)!").arg(clmName, clm); error = QStringLiteral("ColorSpace ColorModel mismatch (current: %1, expected: %2)!").arg(clmName, clm);
return false; return false;
} }
#endif
} }
// Test resolution // Test resolution
+1 -1
View File
@@ -56,7 +56,7 @@ public:
/*! /*!
* \brief skipSequentialDeviceTest * \brief skipSequentialDeviceTest
* \return True if the sequential test should be skipped. * \return tre it the sequential test should be skipped.
*/ */
bool skipSequentialDeviceTest() const; bool skipSequentialDeviceTest() const;
+2
View File
@@ -319,9 +319,11 @@ QImage formatSourceImage(const QImage::Format &format)
image = QImage(QStringLiteral("%1/rgba16.png").arg(folder)); image = QImage(QStringLiteral("%1/rgba16.png").arg(folder));
break; break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888: case QImage::Format_CMYK8888:
image = QImage(QStringLiteral("%1/cmyk8.tif").arg(folder)); image = QImage(QStringLiteral("%1/cmyk8.tif").arg(folder));
break; break;
#endif
default: default:
break; break;
+20 -1
View File
@@ -293,7 +293,9 @@ bool QAVIFHandler::decode_one_frame()
colorspace = QColorSpace::fromIccProfile(icc_data); colorspace = QColorSpace::fromIccProfile(icc_data);
if (!colorspace.isValid()) { if (!colorspace.isValid()) {
qCWarning(LOG_AVIFPLUGIN, "AVIF image has Qt-unsupported or invalid ICC profile!"); qCWarning(LOG_AVIFPLUGIN, "AVIF image has Qt-unsupported or invalid ICC profile!");
} else { }
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
else {
if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) { if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
qCWarning(LOG_AVIFPLUGIN, "CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!"); qCWarning(LOG_AVIFPLUGIN, "CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
colorspace = QColorSpace(); colorspace = QColorSpace();
@@ -323,6 +325,7 @@ bool QAVIFHandler::decode_one_frame()
} }
} }
} }
#endif
} else { } else {
float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f}; float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY // outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY
@@ -357,12 +360,14 @@ bool QAVIFHandler::decode_one_frame()
case 13: case 13:
q_trc = QColorSpace::TransferFunction::SRgb; q_trc = QColorSpace::TransferFunction::SRgb;
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16: /* AVIF_TRANSFER_CHARACTERISTICS_PQ */ case 16: /* AVIF_TRANSFER_CHARACTERISTICS_PQ */
q_trc = QColorSpace::TransferFunction::St2084; q_trc = QColorSpace::TransferFunction::St2084;
break; break;
case 18: /* AVIF_TRANSFER_CHARACTERISTICS_HLG */ case 18: /* AVIF_TRANSFER_CHARACTERISTICS_HLG */
q_trc = QColorSpace::TransferFunction::Hlg; q_trc = QColorSpace::TransferFunction::Hlg;
break; break;
#endif
default: default:
qCWarning(LOG_AVIFPLUGIN, "CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.", qCWarning(LOG_AVIFPLUGIN, "CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
m_decoder->image->colorPrimaries, m_decoder->image->colorPrimaries,
@@ -372,9 +377,11 @@ bool QAVIFHandler::decode_one_frame()
} }
if (q_trc != QColorSpace::TransferFunction::Custom) { // we create new colorspace using Qt if (q_trc != QColorSpace::TransferFunction::Custom) { // we create new colorspace using Qt
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
if (loadgray) { if (loadgray) {
colorspace = QColorSpace(whitePoint, q_trc, q_trc_gamma); colorspace = QColorSpace(whitePoint, q_trc, q_trc_gamma);
} else { } else {
#endif
switch (m_decoder->image->colorPrimaries) { switch (m_decoder->image->colorPrimaries) {
/* AVIF_COLOR_PRIMARIES_BT709 */ /* AVIF_COLOR_PRIMARIES_BT709 */
case 0: case 0:
@@ -390,7 +397,9 @@ bool QAVIFHandler::decode_one_frame()
colorspace = QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma); colorspace = QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
break; break;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
} }
#endif
} }
if (!colorspace.isValid()) { if (!colorspace.isValid()) {
@@ -738,12 +747,14 @@ bool QAVIFHandler::write(const QImage &image)
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */ /* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
avif->transferCharacteristics = (avifTransferCharacteristics)13; avif->transferCharacteristics = (avifTransferCharacteristics)13;
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case QColorSpace::TransferFunction::St2084: case QColorSpace::TransferFunction::St2084:
avif->transferCharacteristics = (avifTransferCharacteristics)16; avif->transferCharacteristics = (avifTransferCharacteristics)16;
break; break;
case QColorSpace::TransferFunction::Hlg: case QColorSpace::TransferFunction::Hlg:
avif->transferCharacteristics = (avifTransferCharacteristics)18; avif->transferCharacteristics = (avifTransferCharacteristics)18;
break; break;
#endif
default: default:
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */ /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
break; break;
@@ -788,6 +799,7 @@ bool QAVIFHandler::write(const QImage &image)
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpcolorimage; QImage tmpcolorimage;
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
@@ -802,6 +814,9 @@ bool QAVIFHandler::write(const QImage &image)
} else { } else {
tmpcolorimage = image.convertToFormat(tmpformat); tmpcolorimage = image.convertToFormat(tmpformat);
} }
#else
QImage tmpcolorimage = image.convertToFormat(tmpformat);
#endif
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420; avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
if (m_quality >= KIMG_AVIF_QUALITY_HIGH) { if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
@@ -861,12 +876,14 @@ bool QAVIFHandler::write(const QImage &image)
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */ /* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
transfer_to_save = (avifTransferCharacteristics)13; transfer_to_save = (avifTransferCharacteristics)13;
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case QColorSpace::TransferFunction::St2084: case QColorSpace::TransferFunction::St2084:
transfer_to_save = (avifTransferCharacteristics)16; transfer_to_save = (avifTransferCharacteristics)16;
break; break;
case QColorSpace::TransferFunction::Hlg: case QColorSpace::TransferFunction::Hlg:
transfer_to_save = (avifTransferCharacteristics)18; transfer_to_save = (avifTransferCharacteristics)18;
break; break;
#endif
default: default:
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */ /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
transfer_to_save = (avifTransferCharacteristics)2; transfer_to_save = (avifTransferCharacteristics)2;
@@ -902,12 +919,14 @@ bool QAVIFHandler::write(const QImage &image)
case 5: // AVIF_TRANSFER_CHARACTERISTICS_BT470BG case 5: // AVIF_TRANSFER_CHARACTERISTICS_BT470BG
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.8f)); tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.8f));
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16: case 16:
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::St2084)); tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::St2084));
break; break;
case 18: case 18:
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::Hlg)); tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::Hlg));
break; break;
#endif
default: // AVIF_TRANSFER_CHARACTERISTICS_SRGB + any other default: // AVIF_TRANSFER_CHARACTERISTICS_SRGB + any other
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb)); tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb));
transfer_to_save = (avifTransferCharacteristics)13; transfer_to_save = (avifTransferCharacteristics)13;
+48 -50
View File
@@ -235,11 +235,6 @@ void IFFChunk::setRecursionCounter(qint32 cnt)
_recursionCnt = cnt; _recursionCnt = cnt;
} }
quint32 IFFChunk::dataBytes() const
{
return std::min(bytes(), quint32(data().size()));
}
IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent) IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent)
{ {
auto tmp = false; auto tmp = false;
@@ -385,7 +380,7 @@ BMHDChunk::BMHDChunk() : IFFChunk()
bool BMHDChunk::isValid() const bool BMHDChunk::isValid() const
{ {
if (dataBytes() < 20) { if (bytes() < 20) {
return false; return false;
} }
return chunkId() == BMHDChunk::defaultChunkId(); return chunkId() == BMHDChunk::defaultChunkId();
@@ -526,7 +521,7 @@ qint32 CMAPChunk::count() const
if (!isValid()) { if (!isValid()) {
return 0; return 0;
} }
return dataBytes() / 3; return bytes() / 3;
} }
QList<QRgb> CMAPChunk::palette(bool halfbride) const QList<QRgb> CMAPChunk::palette(bool halfbride) const
@@ -550,8 +545,12 @@ bool CMAPChunk::innerReadStructure(QIODevice *d)
QList<QRgb> CMAPChunk::innerPalette() const QList<QRgb> CMAPChunk::innerPalette() const
{ {
QList<QRgb> l; QList<QRgb> l;
auto &&d = data(); const QByteArray &d = data();
for (qint32 i = 0, n = count(); i < n; ++i) { const qint32 n = count();
if (n * 3 > d.size()) {
return {};
}
for (qint32 i = 0; i < n; ++i) {
auto i3 = i * 3; auto i3 = i * 3;
l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2)); l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2));
} }
@@ -583,7 +582,7 @@ qint32 CMYKChunk::count() const
if (!isValid()) { if (!isValid()) {
return 0; return 0;
} }
return dataBytes() / 4; return bytes() / 4;
} }
QList<QRgb> CMYKChunk::innerPalette() const QList<QRgb> CMYKChunk::innerPalette() const
@@ -617,7 +616,7 @@ CAMGChunk::CAMGChunk() : IFFChunk()
bool CAMGChunk::isValid() const bool CAMGChunk::isValid() const
{ {
if (dataBytes() != 4) { if (bytes() != 4) {
return false; return false;
} }
return chunkId() == CAMGChunk::defaultChunkId(); return chunkId() == CAMGChunk::defaultChunkId();
@@ -659,7 +658,7 @@ bool DPIChunk::isValid() const
quint16 DPIChunk::dpiX() const quint16 DPIChunk::dpiX() const
{ {
if (dataBytes() < 4) { if (bytes() < 4) {
return 0; return 0;
} }
return ui16(data().at(1), data().at(0)); return ui16(data().at(1), data().at(0));
@@ -667,7 +666,7 @@ quint16 DPIChunk::dpiX() const
quint16 DPIChunk::dpiY() const quint16 DPIChunk::dpiY() const
{ {
if (dataBytes() < 4) { if (bytes() < 4) {
return 0; return 0;
} }
return ui16(data().at(3), data().at(2)); return ui16(data().at(3), data().at(2));
@@ -711,7 +710,7 @@ bool XBMIChunk::isValid() const
quint16 XBMIChunk::dpiX() const quint16 XBMIChunk::dpiX() const
{ {
if (dataBytes() < 6) { if (bytes() < 6) {
return 0; return 0;
} }
return ui16(data().at(3), data().at(2)); return ui16(data().at(3), data().at(2));
@@ -719,7 +718,7 @@ quint16 XBMIChunk::dpiX() const
quint16 XBMIChunk::dpiY() const quint16 XBMIChunk::dpiY() const
{ {
if (dataBytes() < 6) { if (bytes() < 6) {
return 0; return 0;
} }
return ui16(data().at(5), data().at(4)); return ui16(data().at(5), data().at(4));
@@ -727,7 +726,7 @@ quint16 XBMIChunk::dpiY() const
XBMIChunk::PictureType XBMIChunk::pictureType() const XBMIChunk::PictureType XBMIChunk::pictureType() const
{ {
if (dataBytes() < 6) { if (bytes() < 6) {
return PictureType(-1); return PictureType(-1);
} }
return PictureType(i16(data().at(1), data().at(0))); return PictureType(i16(data().at(1), data().at(0)));
@@ -1019,7 +1018,7 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMH
auto pal = cmap->palette(); auto pal = cmap->palette();
if (ipal) { if (ipal) {
auto tmp = ipal->palette(y); auto tmp = ipal->palette(y);
if (!tmp.isEmpty()) if (tmp.size() == pal.size())
pal = tmp; pal = tmp;
} }
// HAM 6: 2 control bits+4 bits of data, 16-color palette // HAM 6: 2 control bits+4 bits of data, 16-color palette
@@ -1603,7 +1602,7 @@ TBHDChunk::TBHDChunk()
bool TBHDChunk::isValid() const bool TBHDChunk::isValid() const
{ {
if (dataBytes() != 24 && dataBytes() != 32) { if (bytes() != 24 && bytes() != 32) {
return false; return false;
} }
return chunkId() == TBHDChunk::defaultChunkId(); return chunkId() == TBHDChunk::defaultChunkId();
@@ -1637,7 +1636,7 @@ QSize TBHDChunk::size() const
qint32 TBHDChunk::left() const qint32 TBHDChunk::left() const
{ {
if (dataBytes() != 32) { if (bytes() != 32) {
return 0; return 0;
} }
return i32(data().at(27), data().at(26), data().at(25), data().at(24)); return i32(data().at(27), data().at(26), data().at(25), data().at(24));
@@ -1645,7 +1644,7 @@ qint32 TBHDChunk::left() const
qint32 TBHDChunk::top() const qint32 TBHDChunk::top() const
{ {
if (dataBytes() != 32) { if (bytes() != 32) {
return 0; return 0;
} }
return i32(data().at(31), data().at(30), data().at(29), data().at(28)); return i32(data().at(31), data().at(30), data().at(29), data().at(28));
@@ -2419,12 +2418,10 @@ QList<QRgb> BEAMChunk::palette(qint32 y) const
for (auto c = 0; c < col; ++c) { for (auto c = 0; c < col; ++c) {
// 2 bytes per color (0x0R 0xGB) // 2 bytes per color (0x0R 0xGB)
auto idx = bpp * y + c * 2; auto idx = bpp * y + c * 2;
if (idx + 1 < dt.size()) { auto r = quint8(dt[idx] & 0x0F);
auto r = quint8(dt[idx] & 0x0F); auto g = quint8(dt[idx + 1] & 0xF0);
auto g = quint8(dt[idx + 1] & 0xF0); auto b = quint8(dt[idx + 1] & 0x0F);
auto b = quint8(dt[idx + 1] & 0x0F); pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
}
} }
return pal; return pal;
} }
@@ -2473,7 +2470,7 @@ SHAMChunk::SHAMChunk()
bool SHAMChunk::isValid() const bool SHAMChunk::isValid() const
{ {
if (dataBytes() < 2) { if (bytes() < 2) {
return false; return false;
} }
auto &&dt = data(); auto &&dt = data();
@@ -2512,12 +2509,10 @@ QList<QRgb> SHAMChunk::palette(qint32 y) const
for (auto c = 0, col = bpp / 2, idx0 = y / div * bpp + 2; c < col; ++c) { for (auto c = 0, col = bpp / 2, idx0 = y / div * bpp + 2; c < col; ++c) {
// 2 bytes per color (0x0R 0xGB) // 2 bytes per color (0x0R 0xGB)
auto idx = idx0 + c * 2; auto idx = idx0 + c * 2;
if (idx + 1 < dt.size()) { auto r = quint8(dt[idx] & 0x0F);
auto r = quint8(dt[idx] & 0x0F); auto g = quint8(dt[idx + 1] & 0xF0);
auto g = quint8(dt[idx + 1] & 0xF0); auto b = quint8(dt[idx + 1] & 0x0F);
auto b = quint8(dt[idx + 1] & 0x0F); pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
}
} }
return pal; return pal;
} }
@@ -2574,18 +2569,16 @@ QList<QRgb> RASTChunk::palette(qint32 y) const
QList<QRgb> pal; QList<QRgb> pal;
for (auto c = 0; c < col; ++c) { for (auto c = 0; c < col; ++c) {
auto idx = bpp * y + 2 + c * 2; auto idx = bpp * y + 2 + c * 2;
if (idx + 1 < dt.size()) { // The Atari ST uses 3 bits per color (512 colors) while the Atari STE
// The Atari ST uses 3 bits per color (512 colors) while the Atari STE // uses 4 bits per color (4096 colors). This strange encoding with the
// uses 4 bits per color (4096 colors). This strange encoding with the // least significant bit set as MSB is, I believe, to ensure hardware
// least significant bit set as MSB is, I believe, to ensure hardware // compatibility between the two machines.
// compatibility between the two machines. #define H1L(a) ((quint8(a) & 0x7) << 1) | ((quint8(a) >> 3) & 1)
#define H1L(a) ((quint8(a) & 0x7) << 1) | ((quint8(a) >> 3) & 1) auto r = H1L(dt[idx]);
auto r = H1L(dt[idx]); auto g = H1L(dt[idx + 1] >> 4);
auto g = H1L(dt[idx + 1] >> 4); auto b = H1L(dt[idx + 1]);
auto b = H1L(dt[idx + 1]); #undef H1L
#undef H1L pal << qRgb(r | (r << 4), (g << 4) | g, b | (b << 4));
pal << qRgb(r | (r << 4), (g << 4) | g, b | (b << 4));
}
} }
return pal; return pal;
} }
@@ -2693,7 +2686,7 @@ bool PCHGChunk::hasAlpha() const
bool PCHGChunk::isValid() const bool PCHGChunk::isValid() const
{ {
if (dataBytes() < 20) { if (bytes() < 20) {
return false; return false;
} }
return chunkId() == PCHGChunk::defaultChunkId(); return chunkId() == PCHGChunk::defaultChunkId();
@@ -2795,6 +2788,10 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
return {}; return {};
} }
QByteArray out;
out.resize(originalSize);
char* outPtr = out.data();
// Emulate a3 pointer to words: // Emulate a3 pointer to words:
// a2 points to the *last word* => word index (0..treeWords-1) // a2 points to the *last word* => word index (0..treeWords-1)
auto resetA3 = [&]() { auto resetA3 = [&]() {
@@ -2819,9 +2816,10 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
return true; return true;
}; };
int produced = 0;
// Main decode loop: produce exactly originalSize bytes // Main decode loop: produce exactly originalSize bytes
QByteArray out; while (produced < originalSize) {
while (out.size() < qsizetype(originalSize)) {
if (bits == 0) { if (bits == 0) {
if (!refill()) { if (!refill()) {
// Not enough bits to complete output // Not enough bits to complete output
@@ -2857,7 +2855,7 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
a3_word = next; a3_word = next;
} else { } else {
// Leaf: emit low 8 bits, reset a3 // Leaf: emit low 8 bits, reset a3
out.append(static_cast<char>(w & 0xFF)); outPtr[produced++] = static_cast<char>(w & 0xFF);
a3_word = resetA3(); a3_word = resetA3();
} }
} else { } else {
@@ -2877,7 +2875,7 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
// Non-negative: check bit #8; if set -> leaf // Non-negative: check bit #8; if set -> leaf
if ((w & 0x0100) != 0) { if ((w & 0x0100) != 0) {
out.append(static_cast<char>(w & 0xFF)); outPtr[produced++] = static_cast<char>(w & 0xFF);
a3_word = resetA3(); a3_word = resetA3();
} else { } else {
// Not a leaf: continue scanning // Not a leaf: continue scanning
+3 -9
View File
@@ -36,7 +36,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define LIST_CHUNK QByteArray("LIST") #define LIST_CHUNK QByteArray("LIST")
#define PROP_CHUNK QByteArray("PROP") #define PROP_CHUNK QByteArray("PROP")
// Main chunks (Maya) // Main chuncks (Maya)
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment #define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
#define FOR4_CHUNK QByteArray("FOR4") #define FOR4_CHUNK QByteArray("FOR4")
#define LIS4_CHUNK QByteArray("LIS4") #define LIS4_CHUNK QByteArray("LIS4")
@@ -145,7 +145,7 @@ public:
/*! /*!
* \brief bytes * \brief bytes
* \return The size (in bytes) of the chunk data. * \return The size (in bytes) of the chunck data.
*/ */
quint32 bytes() const; quint32 bytes() const;
@@ -350,12 +350,6 @@ protected:
static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr); static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr);
/*!
* \brief dataBytes
* \return Maximum usable cache data size.
*/
quint32 dataBytes() const;
private: private:
char _chunkId[4]; char _chunkId[4];
@@ -870,7 +864,7 @@ public:
/*! /*!
* \brief transformation * \brief transformation
* \return The image transformation. * \return The image transformation.
* \note The Default implementation returns the transformation of EXIF chunk (if any). * \note The Default implentation returns the trasformation of EXIF chunk (if any).
*/ */
virtual QImageIOHandler::Transformation transformation() const; virtual QImageIOHandler::Transformation transformation() const;
+3 -1
View File
@@ -20,7 +20,7 @@
#include <cmath> #include <cmath>
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT #ifndef DDS_DISABLE_STRIDE_ALIGNMENT
// Disable the stride alignment based on DDS pitch: it is known that some writers do not set it correctly // Disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly
// #define DDS_DISABLE_STRIDE_ALIGNMENT // #define DDS_DISABLE_STRIDE_ALIGNMENT
#endif #endif
@@ -2120,8 +2120,10 @@ bool writeL8(const QImage &outImage, QDataStream &s)
} }
ScanLineConverter slc(QImage::Format_Grayscale8); ScanLineConverter slc(QImage::Format_Grayscale8);
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if(outImage.colorSpace().isValid()) if(outImage.colorSpace().isValid())
slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb)); slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
#endif
for (int y = 0, h = outImage.height(); y < h; ++y) { for (int y = 0, h = outImage.height(); y < h; ++y) {
const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y)); const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
+1 -1
View File
@@ -179,7 +179,7 @@ bool EPSHandler::read(QImage *image)
const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs")); const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs"));
if (gsExec.isEmpty()) { if (gsExec.isEmpty()) {
qCWarning(EPSPLUGIN) << "Couldn't find gs executable (from GhostScript) in PATH."; qCWarning(EPSPLUGIN) << "Couldn't find gs exectuable (from GhostScript) in PATH.";
return false; return false;
} }
+2 -3
View File
@@ -404,7 +404,6 @@ bool EXRHandler::read(QImage *outImage)
Imf::Array2D<Imf::Rgba> pixels; Imf::Array2D<Imf::Rgba> pixels;
pixels.resizeErase(EXR_LINES_PER_BLOCK, width); pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
std::memset(pixels[0], 0, sizeof(Imf::Rgba) * EXR_LINES_PER_BLOCK * width);
bool isRgba = image.hasAlphaChannel(); bool isRgba = image.hasAlphaChannel();
for (int y = 0, n = 0; y < height; y += n) { for (int y = 0, n = 0; y < height; y += n) {
@@ -609,13 +608,13 @@ bool EXRHandler::write(const QImage &image)
pixels.resizeErase(EXR_LINES_PER_BLOCK, width); pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
// convert the image and write into the stream // convert the image and write into the stream
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA32FPx4 : QImage::Format_RGBX32FPx4; auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4;
ScanLineConverter slc(convFormat); ScanLineConverter slc(convFormat);
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb)); slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear)); slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
for (int y = 0, n = 0; y < height; y += n) { for (int y = 0, n = 0; y < height; y += n) {
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) { for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
auto scanLine = reinterpret_cast<const float *>(slc.convertedScanLine(image, y + n)); auto scanLine = reinterpret_cast<const qfloat16 *>(slc.convertedScanLine(image, y + n));
if (scanLine == nullptr) { if (scanLine == nullptr) {
return false; return false;
} }
+8 -13
View File
@@ -125,16 +125,14 @@ public:
Header h; Header h;
int cnt = 0; int cnt = 0;
int len = 0; int len;
QByteArray line(MAXLINE, char()); QByteArray line(MAXLINE + 1, Qt::Uninitialized);
QByteArray format; QByteArray format;
// Parse header // Parse header
do { do {
len = device->readLine(line.data(), line.size()); len = device->readLine(line.data(), MAXLINE);
if (len < 0) {
break;
}
if (line.startsWith("FORMAT=")) { if (line.startsWith("FORMAT=")) {
format = line.mid(7, len - 7).trimmed(); format = line.mid(7, len - 7).trimmed();
} }
@@ -175,11 +173,7 @@ public:
return h; return h;
} }
len = device->readLine(line.data(), line.size()); len = device->readLine(line.data(), MAXLINE);
if (len < 0) {
qCDebug(HDRPLUGIN) << "Invalid HDR file, error while reading the first line after the header";
return h;
}
line.resize(len); line.resize(len);
/* /*
@@ -328,7 +322,8 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
return false; return false;
} }
QByteArray lineArray(4 * width, char()); QByteArray lineArray;
lineArray.resize(4 * width);
uchar *image = reinterpret_cast<uchar *>(lineArray.data()); uchar *image = reinterpret_cast<uchar *>(lineArray.data());
for (int cline = 0; cline < height; cline++) { for (int cline = 0; cline < height; cline++) {
@@ -514,7 +509,7 @@ bool HDRHandler::canRead(QIODevice *device)
return true; return true;
} }
// allow to load official test cases: https://radsite.lbl.gov/radiance/framed.html // allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
device->startTransaction(); device->startTransaction();
auto h = HDRHandlerPrivate::readHeader(device); auto h = HDRHandlerPrivate::readHeader(device);
device->rollbackTransaction(); device->rollbackTransaction();
+10 -1
View File
@@ -189,6 +189,7 @@ bool HEIFHandler::write_helper(const QImage &image)
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpimage; QImage tmpimage;
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
@@ -204,6 +205,9 @@ bool HEIFHandler::write_helper(const QImage &image)
} else { } else {
tmpimage = image.convertToFormat(tmpformat); tmpimage = image.convertToFormat(tmpformat);
} }
#else
QImage tmpimage = image.convertToFormat(tmpformat);
#endif
struct heif_context *context = heif_context_alloc(); struct heif_context *context = heif_context_alloc();
struct heif_error err; struct heif_error err;
@@ -864,7 +868,9 @@ bool HEIFHandler::ensureDecoder()
QColorSpace colorspace = QColorSpace::fromIccProfile(ba); QColorSpace colorspace = QColorSpace::fromIccProfile(ba);
if (!colorspace.isValid()) { if (!colorspace.isValid()) {
qCWarning(LOG_HEIFPLUGIN) << "HEIC image has Qt-unsupported or invalid ICC profile!"; qCWarning(LOG_HEIFPLUGIN) << "HEIC image has Qt-unsupported or invalid ICC profile!";
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) { }
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
qCWarning(LOG_HEIFPLUGIN) << "CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!"; qCWarning(LOG_HEIFPLUGIN) << "CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!";
colorspace = QColorSpace(); colorspace = QColorSpace();
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray) { } else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray) {
@@ -891,6 +897,7 @@ bool HEIFHandler::ensureDecoder()
m_current_image.convertTo(bit_depth > 8 ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8); m_current_image.convertTo(bit_depth > 8 ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
} }
} }
#endif
m_current_image.setColorSpace(colorspace); m_current_image.setColorSpace(colorspace);
} }
} else { } else {
@@ -927,12 +934,14 @@ bool HEIFHandler::ensureDecoder()
case 13: case 13:
q_trc = QColorSpace::TransferFunction::SRgb; q_trc = QColorSpace::TransferFunction::SRgb;
break; break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16: case 16:
q_trc = QColorSpace::TransferFunction::St2084; q_trc = QColorSpace::TransferFunction::St2084;
break; break;
case 18: case 18:
q_trc = QColorSpace::TransferFunction::Hlg; q_trc = QColorSpace::TransferFunction::Hlg;
break; break;
#endif
default: default:
qCWarning(LOG_HEIFPLUGIN) << "CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet." qCWarning(LOG_HEIFPLUGIN) << "CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet."
<< nclx->color_primaries << nclx->color_primaries
+7 -1
View File
@@ -73,7 +73,7 @@ static void info_callback(const char *msg, void *client_data)
static OPJ_SIZE_T jp2_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) static OPJ_SIZE_T jp2_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{ {
auto dev = (QIODevice*)p_user_data; auto dev = (QIODevice*)p_user_data;
if (dev == nullptr || dev->atEnd()) { if (dev == nullptr) {
return OPJ_SIZE_T(-1); return OPJ_SIZE_T(-1);
} }
return OPJ_SIZE_T(dev->read((char*)p_buffer, (qint64)p_nb_bytes)); return OPJ_SIZE_T(dev->read((char*)p_buffer, (qint64)p_nb_bytes));
@@ -391,6 +391,7 @@ public:
prec = 0; prec = 0;
} }
auto jp2cs = m_jp2_image->color_space; auto jp2cs = m_jp2_image->color_space;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) { if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
auto cs = colorSpace(); auto cs = colorSpace();
if (cs.colorModel() == QColorSpace::ColorModel::Cmyk) if (cs.colorModel() == QColorSpace::ColorModel::Cmyk)
@@ -400,6 +401,7 @@ public:
else if (cs.colorModel() == QColorSpace::ColorModel::Gray) else if (cs.colorModel() == QColorSpace::ColorModel::Gray)
jp2cs = OPJ_CLRSPC_GRAY; jp2cs = OPJ_CLRSPC_GRAY;
} }
#endif
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) { if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
if (m_jp2_image->numcomps == 1) if (m_jp2_image->numcomps == 1)
jp2cs = OPJ_CLRSPC_GRAY; jp2cs = OPJ_CLRSPC_GRAY;
@@ -429,8 +431,10 @@ public:
} }
} else if (jp2cs == OPJ_CLRSPC_CMYK) { } else if (jp2cs == OPJ_CLRSPC_CMYK) {
if (m_jp2_image->numcomps == 4) { if (m_jp2_image->numcomps == 4) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (prec == 8 || prec == 16) if (prec == 8 || prec == 16)
fmt = QImage::Format_CMYK8888; fmt = QImage::Format_CMYK8888;
#endif
} }
} }
} }
@@ -575,6 +579,7 @@ public:
prec = 16; prec = 16;
convFormat = QImage::Format_RGBA64; convFormat = QImage::Format_RGBA64;
break; break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+ case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+
if (opjVersion() >= QT_VERSION_CHECK(2, 5, 3)) { if (opjVersion() >= QT_VERSION_CHECK(2, 5, 3)) {
ncomp = 4; ncomp = 4;
@@ -583,6 +588,7 @@ public:
} else { } else {
Q_FALLTHROUGH(); Q_FALLTHROUGH();
} }
#endif
default: default:
if (image.depth() > 32) { if (image.depth() > 32) {
qCWarning(LOG_JP2PLUGIN) << "The image is saved losing precision!"; qCWarning(LOG_JP2PLUGIN) << "The image is saved losing precision!";
+38 -2
View File
@@ -26,9 +26,13 @@ Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtDebugMsg)
Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg) Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg)
#endif #endif
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 3)
#ifndef JXL_QT_AUTOTRANSFORM #ifndef JXL_QT_AUTOTRANSFORM
#define JXL_QT_AUTOTRANSFORM #define JXL_QT_AUTOTRANSFORM
#endif #endif
#endif
#ifndef JXL_HDR_PRESERVATION_DISABLED #ifndef JXL_HDR_PRESERVATION_DISABLED
// Define JXL_HDR_PRESERVATION_DISABLED to disable HDR preservation // Define JXL_HDR_PRESERVATION_DISABLED to disable HDR preservation
@@ -180,7 +184,7 @@ bool QJpegXLHandler::ensureDecoder()
int num_worker_threads = QThread::idealThreadCount(); int num_worker_threads = QThread::idealThreadCount();
if (!m_runner && num_worker_threads >= 4) { if (!m_runner && num_worker_threads >= 4) {
/* use half of the threads because plug-in is usually used in environment /* use half of the threads because plug-in is usually used in environment
* where application performs other tasks in the background (pre-load other images) */ * where application performs another tasks in backround (pre-load other images) */
num_worker_threads = num_worker_threads / 2; num_worker_threads = num_worker_threads / 2;
num_worker_threads = qBound(2, num_worker_threads, 64); num_worker_threads = qBound(2, num_worker_threads, 64);
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads); m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
@@ -410,6 +414,7 @@ bool QJpegXLHandler::countALLFrames()
m_framedelays[0] = 0; m_framedelays[0] = 0;
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
// CMYK detection // CMYK detection
if ((m_basicinfo.uses_original_profile == JXL_TRUE) && (m_basicinfo.num_color_channels == 3) && (m_colorspace.isValid())) { if ((m_basicinfo.uses_original_profile == JXL_TRUE) && (m_basicinfo.num_color_channels == 3) && (m_colorspace.isValid())) {
bool alpha_found = false; bool alpha_found = false;
@@ -466,6 +471,7 @@ bool QJpegXLHandler::countALLFrames()
qCWarning(LOG_JXLPLUGIN, "JXL has CMYK colorspace but BLACK channel was not found!"); qCWarning(LOG_JXLPLUGIN, "JXL has CMYK colorspace but BLACK channel was not found!");
} }
} }
#endif
#ifndef JXL_DECODE_BOXES_DISABLED #ifndef JXL_DECODE_BOXES_DISABLED
if (!decodeContainer()) { if (!decodeContainer()) {
@@ -492,6 +498,7 @@ bool QJpegXLHandler::decode_one_frame()
} }
if (m_isCMYK) { // CMYK decoding if (m_isCMYK) { // CMYK decoding
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
uchar *pixels_cmy = nullptr; uchar *pixels_cmy = nullptr;
uchar *pixels_black = nullptr; uchar *pixels_black = nullptr;
@@ -732,6 +739,11 @@ bool QJpegXLHandler::decode_one_frame()
free(pixels_cmy); free(pixels_cmy);
pixels_cmy = nullptr; pixels_cmy = nullptr;
} }
#else
// CMYK not supported in older Qt
m_parseState = ParseJpegXLError;
return false;
#endif
} else { // RGB or GRAY } else { // RGB or GRAY
m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format); m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
if (m_current_image.isNull()) { if (m_current_image.isNull()) {
@@ -909,9 +921,11 @@ bool QJpegXLHandler::write(const QImage &image)
} }
bool save_cmyk = false; bool save_cmyk = false;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (image.format() == QImage::Format_CMYK8888 && image.colorSpace().isValid() && image.colorSpace().colorModel() == QColorSpace::ColorModel::Cmyk) { if (image.format() == QImage::Format_CMYK8888 && image.colorSpace().isValid() && image.colorSpace().colorModel() == QColorSpace::ColorModel::Cmyk) {
save_cmyk = true; save_cmyk = true;
} }
#endif
JxlEncoderStatus status; JxlEncoderStatus status;
JxlPixelFormat pixel_format; JxlPixelFormat pixel_format;
@@ -922,6 +936,7 @@ bool QJpegXLHandler::write(const QImage &image)
auto xmp_data = image.text(QStringLiteral(META_KEY_XMP_ADOBE)).toUtf8(); auto xmp_data = image.text(QStringLiteral(META_KEY_XMP_ADOBE)).toUtf8();
if (save_cmyk) { // CMYK is always lossless if (save_cmyk) { // CMYK is always lossless
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
output_info.uses_original_profile = JXL_TRUE; output_info.uses_original_profile = JXL_TRUE;
output_info.xsize = image.width(); output_info.xsize = image.width();
output_info.ysize = image.height(); output_info.ysize = image.height();
@@ -1094,6 +1109,13 @@ bool QJpegXLHandler::write(const QImage &image)
JxlEncoderDestroy(encoder); JxlEncoderDestroy(encoder);
return false; return false;
} }
#else
if (runner) {
JxlThreadParallelRunnerDestroy(runner);
}
JxlEncoderDestroy(encoder);
return false;
#endif
} else { // RGB or GRAY saving } else { // RGB or GRAY saving
int save_depth = 8; // 8 / 16 / 32 int save_depth = 8; // 8 / 16 / 32
bool save_fp = false; bool save_fp = false;
@@ -1132,7 +1154,9 @@ bool QJpegXLHandler::write(const QImage &image)
case QImage::Format_RGBX8888: case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888: case QImage::Format_RGBA8888:
case QImage::Format_RGBA8888_Premultiplied: case QImage::Format_RGBA8888_Premultiplied:
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888: case QImage::Format_CMYK8888:
#endif
save_depth = 8; save_depth = 8;
break; break;
case QImage::Format_Grayscale16: case QImage::Format_Grayscale16:
@@ -1227,6 +1251,7 @@ bool QJpegXLHandler::write(const QImage &image)
} }
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpimage; QImage tmpimage;
if (image.colorSpace().isValid()) { if (image.colorSpace().isValid()) {
if (is_gray && image.colorSpace().colorModel() != QColorSpace::ColorModel::Gray) { if (is_gray && image.colorSpace().colorModel() != QColorSpace::ColorModel::Gray) {
@@ -1279,6 +1304,9 @@ bool QJpegXLHandler::write(const QImage &image)
} else { // no ColorSpace or invalid } else { // no ColorSpace or invalid
tmpimage = image.convertToFormat(tmpformat); tmpimage = image.convertToFormat(tmpformat);
} }
#else
QImage tmpimage = image.convertToFormat(tmpformat);
#endif
output_info.xsize = tmpimage.width(); output_info.xsize = tmpimage.width();
output_info.ysize = tmpimage.height(); output_info.ysize = tmpimage.height();
@@ -1304,7 +1332,10 @@ bool QJpegXLHandler::write(const QImage &image)
output_info.uses_original_profile = JXL_FALSE; output_info.uses_original_profile = JXL_FALSE;
if (tmpimage.colorSpace().isValid()) { if (tmpimage.colorSpace().isValid()) {
const QPointF whiteP = image.colorSpace().whitePoint(); QPointF whiteP(0.3127f, 0.329f);
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
whiteP = image.colorSpace().whitePoint();
#endif
switch (tmpimage.colorSpace().primaries()) { switch (tmpimage.colorSpace().primaries()) {
case QColorSpace::Primaries::SRgb: case QColorSpace::Primaries::SRgb:
@@ -1333,6 +1364,9 @@ bool QJpegXLHandler::write(const QImage &image)
break; break;
case QColorSpace::Primaries::ProPhotoRgb: case QColorSpace::Primaries::ProPhotoRgb:
color_profile.white_point = JXL_WHITE_POINT_CUSTOM; color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
whiteP = QPointF(0.3457f, 0.3585f);
#endif
color_profile.white_point_xy[0] = whiteP.x(); color_profile.white_point_xy[0] = whiteP.x();
color_profile.white_point_xy[1] = whiteP.y(); color_profile.white_point_xy[1] = whiteP.y();
color_profile.primaries = JXL_PRIMARIES_CUSTOM; color_profile.primaries = JXL_PRIMARIES_CUSTOM;
@@ -1343,6 +1377,7 @@ bool QJpegXLHandler::write(const QImage &image)
color_profile.primaries_blue_xy[0] = 0.0366; color_profile.primaries_blue_xy[0] = 0.0366;
color_profile.primaries_blue_xy[1] = 0.0001; color_profile.primaries_blue_xy[1] = 0.0001;
break; break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QColorSpace::Primaries::Bt2020: case QColorSpace::Primaries::Bt2020:
color_profile.white_point = JXL_WHITE_POINT_D65; color_profile.white_point = JXL_WHITE_POINT_D65;
color_profile.primaries = JXL_PRIMARIES_2100; color_profile.primaries = JXL_PRIMARIES_2100;
@@ -1353,6 +1388,7 @@ bool QJpegXLHandler::write(const QImage &image)
color_profile.primaries_blue_xy[0] = 0.131; color_profile.primaries_blue_xy[0] = 0.131;
color_profile.primaries_blue_xy[1] = 0.046; color_profile.primaries_blue_xy[1] = 0.046;
break; break;
#endif
default: default:
if (is_gray && !whiteP.isNull()) { if (is_gray && !whiteP.isNull()) {
color_profile.white_point = JXL_WHITE_POINT_CUSTOM; color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
+7 -1
View File
@@ -45,7 +45,7 @@ Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
// #define JXR_DENY_FLOAT_IMAGE // #define JXR_DENY_FLOAT_IMAGE
/*! /*!
* Remove the needs of additional memory by disabling the conversion between * Remove the neeeds of additional memory by disabling the conversion between
* different color depths (e.g. RGBA64bpp to RGBA32bpp). * different color depths (e.g. RGBA64bpp to RGBA32bpp).
* *
* NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility. * NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility.
@@ -579,7 +579,9 @@ public:
// IMPORTANT: these values must be in exactMatchingFormat() // IMPORTANT: these values must be in exactMatchingFormat()
// clang-format off // clang-format off
auto valid = QSet<QImage::Format>() auto valid = QSet<QImage::Format>()
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
<< QImage::Format_CMYK8888 << QImage::Format_CMYK8888
#endif
#ifndef JXR_DENY_FLOAT_IMAGE #ifndef JXR_DENY_FLOAT_IMAGE
<< QImage::Format_RGBA16FPx4 << QImage::Format_RGBA16FPx4
<< QImage::Format_RGBX16FPx4 << QImage::Format_RGBX16FPx4
@@ -669,8 +671,10 @@ public:
wmiSCP->bVerbose = FALSE; wmiSCP->bVerbose = FALSE;
if (fmt == QImage::Format_Grayscale8 || fmt == QImage::Format_Grayscale16 || fmt == QImage::Format_Mono) if (fmt == QImage::Format_Grayscale8 || fmt == QImage::Format_Grayscale16 || fmt == QImage::Format_Mono)
wmiSCP->cfColorFormat = Y_ONLY; wmiSCP->cfColorFormat = Y_ONLY;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
else if (fmt == QImage::Format_CMYK8888) else if (fmt == QImage::Format_CMYK8888)
wmiSCP->cfColorFormat = CMYK; wmiSCP->cfColorFormat = CMYK;
#endif
else else
wmiSCP->cfColorFormat = YUV_444; wmiSCP->cfColorFormat = YUV_444;
wmiSCP->bdBitDepth = BD_LONG; wmiSCP->bdBitDepth = BD_LONG;
@@ -808,8 +812,10 @@ private:
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBA32FPx4_Premultiplied, GUID_PKPixelFormat128bppPRGBAFloat) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBA32FPx4_Premultiplied, GUID_PKPixelFormat128bppPRGBAFloat)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBX32FPx4, GUID_PKPixelFormat128bppRGBFloat) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBX32FPx4, GUID_PKPixelFormat128bppRGBFloat)
#endif // JXR_DENY_FLOAT_IMAGE #endif // JXR_DENY_FLOAT_IMAGE
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYK) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYK)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYKDIRECT) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYKDIRECT)
#endif
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Mono, GUID_PKPixelFormatBlackWhite) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Mono, GUID_PKPixelFormatBlackWhite)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale8, GUID_PKPixelFormat8bppGray) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale8, GUID_PKPixelFormat8bppGray)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale16, GUID_PKPixelFormat16bppGray) << std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale16, GUID_PKPixelFormat16bppGray)
+2 -2
View File
@@ -256,7 +256,7 @@ static bool checkHeader(QDataStream &ds)
/*! /*!
* \brief updatePos * \brief updatePos
* Write the current stream position in \a pos position as uint32. * Write the current stram position in \a pos position as uint32.
* \return True on success, otherwise false; * \return True on success, otherwise false;
*/ */
static bool updatePos(QDataStream &ds, quint32 pos) static bool updatePos(QDataStream &ds, quint32 pos)
@@ -510,7 +510,7 @@ static QByteArray readBytes(QDataStream &ds, quint32 count, bool asciiz)
* \param pos The position of the IFD. * \param pos The position of the IFD.
* \param knownTags List of known and supported tags. * \param knownTags List of known and supported tags.
* \param nextIfd The position of next IFD (0 if none). * \param nextIfd The position of next IFD (0 if none).
* \return True on success, otherwise false. * \return True on succes, otherwise false.
*/ */
static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr) static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr)
{ {
+1 -1
View File
@@ -125,7 +125,7 @@ public:
/*! /*!
* \brief transformation * \brief transformation
* \return The orientation converted in the equivalent Qt transformation. * \return The orientation converted in the equvalent Qt transformation.
* \sa orientation * \sa orientation
*/ */
QImageIOHandler::Transformation transformation() const; QImageIOHandler::Transformation transformation() const;
+2
View File
@@ -781,11 +781,13 @@ static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
auto tcs = QColorSpace(); auto tcs = QColorSpace();
auto tfmt = image.format(); auto tfmt = image.format();
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb); tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32; tfmt = QImage::Format_RGB32;
} }
#endif
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) { if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
tfmt = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; tfmt = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
} }
+2
View File
@@ -275,11 +275,13 @@ bool SoftimagePICHandler::write(const QImage &image)
bool alpha = image.hasAlphaChannel(); bool alpha = image.hasAlphaChannel();
auto tcs = QColorSpace(); auto tcs = QColorSpace();
auto tfmt = image.format(); auto tfmt = image.format();
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb); tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32; tfmt = QImage::Format_RGB32;
} }
#endif
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) { if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
tfmt = alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; tfmt = alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
} }
+28 -97
View File
@@ -91,7 +91,7 @@ typedef quint8 uchar;
namespace // Private. namespace // Private.
{ {
#if defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED) #if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) || defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
# define CMYK_FORMAT QImage::Format_Invalid # define CMYK_FORMAT QImage::Format_Invalid
#else #else
# define CMYK_FORMAT QImage::Format_CMYK8888 # define CMYK_FORMAT QImage::Format_CMYK8888
@@ -327,18 +327,11 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
*ok = true; *ok = true;
// Section size // Section size
quint32 tmpSize; qint32 sectioSize;
s >> tmpSize; s >> sectioSize;
qint64 sectioSize = tmpSize;
// Reading Image resource block // Reading Image resource block
for (auto size = sectioSize; size > 0;) { for (auto size = sectioSize; size > 0;) {
#define DEC_SIZE(value) \
if ((size -= qint64(value)) < 0) { \
*ok = false; \
break; }
// Length Description // Length Description
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// 4 Signature: '8BIM' // 4 Signature: '8BIM'
@@ -353,7 +346,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
quint32 signature; quint32 signature;
s >> signature; s >> signature;
DEC_SIZE(sizeof(signature)) size -= sizeof(signature);
// NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD. // NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD.
if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Signature!"; qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Signature!";
@@ -364,7 +357,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
// id // id
quint16 id; quint16 id;
s >> id; s >> id;
DEC_SIZE(sizeof(id)) size -= sizeof(id);
// getting data // getting data
PSDImageResourceBlock irb; PSDImageResourceBlock irb;
@@ -372,25 +365,20 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
// name // name
qint32 bytes = 0; qint32 bytes = 0;
irb.name = readPascalString(s, 2, &bytes); irb.name = readPascalString(s, 2, &bytes);
DEC_SIZE(bytes) size -= bytes;
// data read // data read
quint32 dataSize; quint32 dataSize;
s >> dataSize; s >> dataSize;
DEC_SIZE(sizeof(dataSize)) size -= sizeof(dataSize);
if (auto dev = s.device()) { // NOTE: Qt device::read() and QDataStream::readRawData() could read less data than specified.
if (dataSize > size) { // The read code should be improved.
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Data Size!"; if (auto dev = s.device())
*ok = false; irb.data = dev->read(dataSize);
break;
}
irb.data = deviceRead(dev, dataSize);
}
auto read = irb.data.size(); auto read = irb.data.size();
if (read > 0) { if (read > 0)
DEC_SIZE(read) size -= read;
} if (quint32(read) != dataSize) {
if (read != qint64(dataSize)) {
qCDebug(LOG_PSDPLUGIN) << "Image Resource Block Read Error!"; qCDebug(LOG_PSDPLUGIN) << "Image Resource Block Read Error!";
*ok = false; *ok = false;
break; break;
@@ -398,15 +386,12 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
if (auto pad = dataSize % 2) { if (auto pad = dataSize % 2) {
auto skipped = s.skipRawData(pad); auto skipped = s.skipRawData(pad);
if (skipped > 0) { if (skipped > 0)
DEC_SIZE(skipped); size -= skipped;
}
} }
// insert IRB // insert IRB
irs.insert(id, irb); irs.insert(id, irb);
#undef DEC_SIZE
} }
return irs; return irs;
@@ -504,13 +489,7 @@ PSDColorModeDataSection readColorModeDataSection(QDataStream &s, bool *ok = null
qint32 size; qint32 size;
s >> size; s >> size;
if (size < 0) { if (size != 768) { // read the duotone data (524 bytes)
*ok = false;
} else if (size > 8 * 1024 * 1024) {
// The known color sections are all in the order of a few hundred bytes.
// I skip the ones that are too big, I don't know what to do with them.
*ok = s.skipRawData(size) == size;
} else if (size != 768) { // read the duotone data (524 bytes)
// NOTE: A RGB/Gray float image has a 112 bytes ColorModeData that could be // NOTE: A RGB/Gray float image has a 112 bytes ColorModeData that could be
// the "32-bit Toning Options" of Photoshop (starts with 'hdrt'). // the "32-bit Toning Options" of Photoshop (starts with 'hdrt').
// Official Adobe specification tells "Only indexed color and duotone // Official Adobe specification tells "Only indexed color and duotone
@@ -546,21 +525,8 @@ static bool setColorSpace(QImage &img, const PSDImageResourceSection &irs)
auto cs = QColorSpace::fromIccProfile(irb.data); auto cs = QColorSpace::fromIccProfile(irb.data);
if (!cs.isValid()) if (!cs.isValid())
return false; return false;
if (cs.colorModel() == QColorSpace::ColorModel::Gray && img.pixelFormat().colorModel() != QPixelFormat::Grayscale) {
// I created an RGB from a grayscale without using color profile conversion (fast).
// I'll try to create an RGB profile that looks the same.
if (cs.transferFunction() != QColorSpace::TransferFunction::Custom) {
auto tmp = QColorSpace(QColorSpace::Primaries::SRgb, cs.transferFunction(), cs.gamma());
tmp.setWhitePoint(cs.whitePoint());
tmp.setDescription(QStringLiteral("RGB emulation of \"%1\"").arg(cs.description()));
if (tmp.isValid())
cs = tmp;
}
}
img.setColorSpace(cs); img.setColorSpace(cs);
return img.colorSpace().isValid(); return true;
} }
/*! /*!
@@ -640,7 +606,7 @@ static bool setResolution(QImage &img, const PSDImageResourceSection &irs)
s.skipRawData(4); // Display data (not used here) s.skipRawData(4); // Display data (not used here)
s >> i32; // Vertical resolution in pixels per inch. s >> i32; // Vertial resolution in pixels per inch.
if (i32 <= 0) if (i32 <= 0)
return false; return false;
auto vres = dpi2ppm(fixedPointToDouble(i32)); auto vres = dpi2ppm(fixedPointToDouble(i32));
@@ -732,7 +698,7 @@ static bool IsValid(const PSDHeader &header)
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid number of channels" << header.channel_count; qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid number of channels" << header.channel_count;
return false; return false;
} }
if (header.width > uint(std::min(300000, PSD_MAX_IMAGE_WIDTH)) || header.height > uint(std::min(300000, PSD_MAX_IMAGE_HEIGHT))) { if (header.width > std::min(300000, PSD_MAX_IMAGE_WIDTH) || header.height > std::min(300000, PSD_MAX_IMAGE_HEIGHT)) {
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid image size" << header.width << "x" << header.height; qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid image size" << header.width << "x" << header.height;
return false; return false;
} }
@@ -813,14 +779,6 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
} }
break; break;
case CM_GRAYSCALE: case CM_GRAYSCALE:
if (header.depth == 32) {
format = !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied;
} else if (header.depth == 16) {
format = !alpha ? QImage::Format_Grayscale16 : QImage::Format_RGBA64_Premultiplied;
} else {
format = !alpha ? QImage::Format_Grayscale8 : QImage::Format_RGBA8888_Premultiplied;
}
break;
case CM_DUOTONE: case CM_DUOTONE:
format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16; format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16;
break; break;
@@ -1123,7 +1081,7 @@ bool readChannel(QByteArray &target, QDataStream &stream, quint32 compressedSize
if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) { if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
return false; return false;
} }
if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) != target.size()) { if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) {
return false; return false;
} }
} else if (stream.readRawData(target.data(), target.size()) != target.size()) { } else if (stream.readRawData(target.data(), target.size()) != target.size()) {
@@ -1330,6 +1288,7 @@ bool PSDHandler::read(QImage *image)
} }
auto imgChannels = imageChannels(img.format()); auto imgChannels = imageChannels(img.format());
auto channel_num = std::min(qint32(header.channel_count), imgChannels);
auto raw_count = qsizetype(header.width * header.depth + 7) / 8; auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
auto native_cmyk = img.format() == CMYK_FORMAT; auto native_cmyk = img.format() == CMYK_FORMAT;
@@ -1377,13 +1336,11 @@ bool PSDHandler::read(QImage *image)
// CMYK with spots (e.g. CMYKA) ICC conversion to RGBA/RGBX // CMYK with spots (e.g. CMYKA) ICC conversion to RGBA/RGBX
QImage tmpCmyk; QImage tmpCmyk;
ScanLineConverter iccConv(img.format()); ScanLineConverter iccConv(img.format());
#if !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED) #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) && !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
if (header.color_mode == CM_CMYK && img.format() != QImage::Format_CMYK8888) { if (header.color_mode == CM_CMYK && img.format() != QImage::Format_CMYK8888) {
auto tmpi = QImage(header.width, 1, QImage::Format_CMYK8888); auto tmpi = QImage(header.width, 1, QImage::Format_CMYK8888);
if (setColorSpace(tmpi, irs)) { if (setColorSpace(tmpi, irs))
tmpi.fill(0);
tmpCmyk = tmpi; tmpCmyk = tmpi;
}
iccConv.setTargetColorSpace(QColorSpace(QColorSpace::SRgb)); iccConv.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
} }
#endif #endif
@@ -1437,14 +1394,6 @@ bool PSDHandler::read(QImage *image)
else if (header.depth == 32) else if (header.depth == 32)
premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P); premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
} }
if (header.color_mode == CM_GRAYSCALE) {
if (header.depth == 8)
premulConversion<quint8>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
else if (header.depth == 16)
premulConversion<quint16>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
else if (header.depth == 32)
premulConversion<float>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
}
} }
// Conversion to RGB // Conversion to RGB
@@ -1482,21 +1431,9 @@ bool PSDHandler::read(QImage *image)
else if (header.depth == 32) else if (header.depth == 32)
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width); rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
} }
if (header.color_mode == CM_GRAYSCALE) {
for (auto c = 0; c < imgChannels; ++c) { // GRAYA to RGBA
auto sc = qBound(0, c - 2, int(header.channel_count));
if (header.depth == 8)
rawChannelCopy<quint8>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
else if (header.depth == 16)
rawChannelCopy<quint16>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
else if (header.depth == 32)
rawChannelCopy<float>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
}
}
} }
} else { } else {
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage // Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
auto channel_num = std::min(qint32(header.channel_count), header.color_mode == CM_GRAYSCALE ? 1 : imgChannels);
for (qint32 c = 0; c < channel_num; ++c) { for (qint32 c = 0; c < channel_num; ++c) {
for (qint32 y = 0, h = header.height; y < h; ++y) { for (qint32 y = 0, h = header.height; y < h; ++y) {
auto&& strideSize = strides.at(c * qsizetype(h) + y); auto&& strideSize = strides.at(c * qsizetype(h) + y);
@@ -1525,13 +1462,8 @@ bool PSDHandler::read(QImage *image)
// 32-bits float images: RGB/RGBA // 32-bits float images: RGB/RGBA
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels); planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
} else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) { } else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
if (imgChannels >= 3) { // GRAY to RGB // 32-bits float images: Grayscale (coverted to equivalent integer 16-bits)
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 0, imgChannels); planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 1, imgChannels);
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 2, imgChannels);
} else { // 32-bits float images: Grayscale (converted to equivalent integer 16-bits)
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
}
} }
} }
} }
@@ -1556,11 +1488,13 @@ bool PSDHandler::read(QImage *image)
if (header.color_mode == CM_RGB && header.depth == 32) { if (header.color_mode == CM_RGB && header.depth == 32) {
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear)); img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (header.color_mode == CM_GRAYSCALE && header.depth == 32) { if (header.color_mode == CM_GRAYSCALE && header.depth == 32) {
auto qs = QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::Linear); auto qs = QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::Linear);
qs.setDescription(QStringLiteral("Linear grayscale")); qs.setDescription(QStringLiteral("Linear grayscale"));
img.setColorSpace(qs); img.setColorSpace(qs);
} }
#endif
} }
// XMP data // XMP data
@@ -1667,9 +1601,6 @@ bool PSDHandler::canRead(QIODevice *device)
if (header.color_mode == CM_RGB && header.channel_count > 3) { if (header.color_mode == CM_RGB && header.channel_count > 3) {
return false; // supposing extra channel as alpha return false; // supposing extra channel as alpha
} }
if (header.color_mode == CM_GRAYSCALE && (header.channel_count > 1 || header.depth == 32)) {
return false; // supposing extra channel as alpha
}
} }
return IsSupported(header); return IsSupported(header);
+1 -1
View File
@@ -160,7 +160,7 @@ static bool LoadQOI(QIODevice *device, const QoiHeader &qoi, QImage &img)
// Handle the byte stream // Handle the byte stream
QByteArray ba; QByteArray ba;
for (quint32 y = 0, run = 0; y < qoi.Height; ++y) { for (quint32 y = 0, run = 0; y < qoi.Height; ++y) {
if (quint64(ba.size()) < px_len && !device->atEnd()) { if (quint64(ba.size()) < px_len) {
ba.append(device->read(px_len)); ba.append(device->read(px_len));
} }
+3 -1
View File
@@ -610,7 +610,7 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
* \brief use_auto_wb * \brief use_auto_wb
* Average the whole image for white balance (0 - off, 1 - on) * Average the whole image for white balance (0 - off, 1 - on)
* *
* This is useful if no camera white balance is available. * This is usefull if no camera white balance is available.
*/ */
params.use_auto_wb = T_AW(quality); params.use_auto_wb = T_AW(quality);
@@ -841,9 +841,11 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
if (params.output_color == 7) { if (params.output_color == 7) {
img.setColorSpace(QColorSpace(QColorSpace::DisplayP3)); img.setColorSpace(QColorSpace(QColorSpace::DisplayP3));
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (params.output_color == 8) { if (params.output_color == 8) {
img.setColorSpace(QColorSpace(QColorSpace::Bt2020)); img.setColorSpace(QColorSpace(QColorSpace::Bt2020));
} }
#endif
} }
// *** Set the metadata // *** Set the metadata
+1 -1
View File
@@ -49,7 +49,7 @@ private:
* _: reserved (should be zero) * _: reserved (should be zero)
* I: interpolation quality (0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB, 11 - DHT, 12 - AAHD) * I: interpolation quality (0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB, 11 - DHT, 12 - AAHD)
* C: output colorspace (0 - raw, 1 - sRGB, 2 - Adobe, 3 - Wide, 4 - ProPhoto, 5 - XYZ, 6 - ACES, 7 - DCI-P3, 8 - Rec2020) * C: output colorspace (0 - raw, 1 - sRGB, 2 - Adobe, 3 - Wide, 4 - ProPhoto, 5 - XYZ, 6 - ACES, 7 - DCI-P3, 8 - Rec2020)
* W: use camera white balance (0 - off, 1 - on) * W: use camera white balace (0 - off, 1 - on)
* A: use auto white balance (0 - off, 1 - on) * A: use auto white balance (0 - off, 1 - on)
* B: output bit per sample (0 - 8-bits, 1 - 16-bits) * B: output bit per sample (0 - 8-bits, 1 - 16-bits)
* H: half size image (0 - off, 1 - on) * H: half size image (0 - off, 1 - on)
+4 -3
View File
@@ -338,9 +338,6 @@ bool SGIImagePrivate::readImage(QImage &img)
return false; return false;
} }
} }
for (; l < _numrows; l++) {
_lengthtab[l] = 0;
}
} }
if (_stream.status() != QDataStream::Ok) { if (_stream.status() != QDataStream::Ok) {
@@ -769,11 +766,15 @@ bool SGIImagePrivate::writeImage(const QImage &image)
auto tcs = QColorSpace(); auto tcs = QColorSpace();
auto tfmt = image.format(); auto tfmt = image.format();
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb); tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32; tfmt = QImage::Format_RGB32;
} else if (hasAlpha && tfmt != QImage::Format_ARGB32) { } else if (hasAlpha && tfmt != QImage::Format_ARGB32) {
#else
if (hasAlpha && tfmt != QImage::Format_ARGB32) {
#endif
tfmt = QImage::Format_ARGB32; tfmt = QImage::Format_ARGB32;
} else if (!hasAlpha && tfmt != QImage::Format_RGB32) { } else if (!hasAlpha && tfmt != QImage::Format_RGB32) {
tfmt = QImage::Format_RGB32; tfmt = QImage::Format_RGB32;
+6
View File
@@ -72,12 +72,18 @@ const uchar *ScanLineConverter::convertedScanLine(const QImage &image, qint32 y)
if (!cs.isValid()) { if (!cs.isValid()) {
cs = _defaultColorSpace; cs = _defaultColorSpace;
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (tmp.depth() < 8 && cs.colorModel() == QColorSpace::ColorModel::Gray) { if (tmp.depth() < 8 && cs.colorModel() == QColorSpace::ColorModel::Gray) {
tmp.convertTo(QImage::Format_Grayscale8); tmp.convertTo(QImage::Format_Grayscale8);
} }
else if (tmp.depth() < 24 && cs.colorModel() == QColorSpace::ColorModel::Rgb) { else if (tmp.depth() < 24 && cs.colorModel() == QColorSpace::ColorModel::Rgb) {
tmp.convertTo(tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); tmp.convertTo(tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
} }
#else
if (tmp.depth() < 24) {
tmp.convertTo(tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
}
#endif
tmp.setColorSpace(cs); tmp.setColorSpace(cs);
tmp.convertToColorSpace(_colorSpace); tmp.convertToColorSpace(_colorSpace);
} }
+2
View File
@@ -177,10 +177,12 @@ public:
QImage::Format format() const QImage::Format format() const
{ {
auto format = QImage::Format_Invalid; auto format = QImage::Format_Invalid;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (m_pb.colorCount() == 4) { if (m_pb.colorCount() == 4) {
if (m_pb.bitMask() == 15) if (m_pb.bitMask() == 15)
format = QImage::Format_CMYK8888; format = QImage::Format_CMYK8888;
} }
#endif
if (m_pb.colorCount() == 3) { if (m_pb.colorCount() == 3) {
if (m_pb.bitMask() == 7) if (m_pb.bitMask() == 7)
format = QImage::Format_RGB888; format = QImage::Format_RGB888;
+9 -12
View File
@@ -82,13 +82,6 @@ enum TGAType {
#define TGA_XMPP_TAGID 0x7002 // Xmp packet preceded by "xMPP" string #define TGA_XMPP_TAGID 0x7002 // Xmp packet preceded by "xMPP" string
#define TGA_ICCP_TAGID 0x7003 // Icc profile preceded by "iCCP" string #define TGA_ICCP_TAGID 0x7003 // Icc profile preceded by "iCCP" string
/*
* Maximum size of a tag in the developer area.
*
* TGA is a 32-bit format so, a metadata should not be greater than some MB.
*/
#define DEV_TAG_MAX_SIZE (32 * 1024 * 1024)
/** Tga Header. */ /** Tga Header. */
struct TgaHeader { struct TgaHeader {
uchar id_length = 0; uchar id_length = 0;
@@ -617,7 +610,11 @@ static bool LoadTGA(QIODevice *dev, const TgaHeader &tga, QImage &img)
// Read palette. // Read palette.
if (info.pal) { if (info.pal) {
QList<QRgb> colorTable; QList<QRgb> colorTable;
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
colorTable.resize(tga.colormap_length);
#else
colorTable.resizeForOverwrite(tga.colormap_length); colorTable.resizeForOverwrite(tga.colormap_length);
#endif
if (tga.colormap_size == 32) { if (tga.colormap_size == 32) {
char data[4]; // BGRA char data[4]; // BGRA
@@ -992,11 +989,15 @@ bool TGAHandler::writeRGBA(const QImage &image)
auto format = image.format(); auto format = image.format();
const bool hasAlpha = image.hasAlphaChannel(); const bool hasAlpha = image.hasAlphaChannel();
auto tcs = QColorSpace(); auto tcs = QColorSpace();
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace(); auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) { if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
format = QImage::Format_RGB32; format = QImage::Format_RGB32;
tcs = QColorSpace(QColorSpace::SRgb); tcs = QColorSpace(QColorSpace::SRgb);
} else if (hasAlpha && image.format() != QImage::Format_ARGB32) { } else if (hasAlpha && image.format() != QImage::Format_ARGB32) {
#else
if (hasAlpha && image.format() != QImage::Format_ARGB32) {
#endif
format = QImage::Format_ARGB32; format = QImage::Format_ARGB32;
} else if (!hasAlpha && image.format() != QImage::Format_RGB32) { } else if (!hasAlpha && image.format() != QImage::Format_RGB32) {
format = QImage::Format_RGB32; format = QImage::Format_RGB32;
@@ -1239,10 +1240,6 @@ bool TGAHandler::readMetadata(QImage &image)
if (!dev->seek(f.offset)) { if (!dev->seek(f.offset)) {
return false; return false;
} }
if (f.size > DEV_TAG_MAX_SIZE) {
qCWarning(LOG_TGAPLUGIN) << "readMetadata: the size of TAG" << f.tagId << "is larger than" << (DEV_TAG_MAX_SIZE/1024/1024) << "MiB, so it will be ignored";
continue;
}
if (f.tagId == TGA_EXIF_TAGID) { if (f.tagId == TGA_EXIF_TAGID) {
auto ba = dev->read(f.size); auto ba = dev->read(f.size);
if (ba.startsWith(QByteArray("eXif"))) { if (ba.startsWith(QByteArray("eXif"))) {
@@ -1347,7 +1344,7 @@ bool TGAHandler::canRead(QIODevice *device)
TgaHeader tga; TgaHeader tga;
if (!peekHeader(device, tga)) { if (!peekHeader(device, tga)) {
qCDebug(LOG_TGAPLUGIN) << "TGAHandler::canRead() error while reading the header"; qCWarning(LOG_TGAPLUGIN) << "TGAHandler::canRead() error while reading the header";
return false; return false;
} }
-45
View File
@@ -12,7 +12,6 @@
#include <QImage> #include <QImage>
#include <QImageIOHandler> #include <QImageIOHandler>
#include <QIODevice>
// Default maximum width and height for the large image plugins. // Default maximum width and height for the large image plugins.
#ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT #ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT
@@ -133,48 +132,4 @@ inline float fppm2dpi(qint32 ppm, bool *ok = nullptr)
return ppm2dpi_T<float>(ppm, ok); return ppm2dpi_T<float>(ppm, ok);
} }
/*!
* \brief deviceRead
* A function for reading from devices.
*
* Similar to QIODevice::read(qint64) but limits the initial memory allocation. Useful for reading corrupted streams.
* \param d The device.
* \param maxSize The maximum size to read.
* \return The byte array read.
*/
static QByteArray deviceRead(QIODevice *d, qint64 maxSize)
{
if (d == nullptr) {
return{};
}
const qint64 blockSize = 32 * 1024 * 1024;
auto devSize = d->isSequential() ? qint64() : d->size();
if (devSize > 0) {
// random access device
maxSize = std::min(maxSize, devSize - d->pos());
return d->read(maxSize);
} else if (maxSize < blockSize) {
// small read
return d->read(maxSize);
}
// sequential device
QByteArray ba;
while (ba.size() < maxSize) {
auto toRead = std::min(blockSize, maxSize - ba.size());
if (toRead + ba.size() > QByteArray::maxSize()) {
break;
}
auto tmp = d->read(toRead);
if (tmp.isEmpty()) {
break;
}
ba.append(tmp);
}
return ba;
}
#endif // UTIL_P_H #endif // UTIL_P_H
+8 -35
View File
@@ -1379,13 +1379,13 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
// SANITY CHECK: Avoid to load XCF with a layer grater than 10 times the final image // SANITY CHECK: Avoid to load XCF with a layer grater than 10 times the final image
if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) { if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
if (qint64(layer.width) * layer.height > 16384 * 16384) { // large layers only if (qint64(layer.width) * layer.height > 16384 * 16384) { // large layers only
qCWarning(XCFPLUGIN) << "Heuristic sanity check: the image may be corrupted!"; qCWarning(XCFPLUGIN) << "Euristic sanity check: the image may be corrupted!";
return false; return false;
} }
} }
#ifndef XCF_QT5_SUPPORT #ifndef XCF_QT5_SUPPORT
// Qt 6 image allocation limit calculation: we have to check the limit here because the image is split in // Qt 6 image allocation limit calculation: we have to check the limit here because the image is splitted in
// tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded // tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded
// and then the final image is created by copying the tiles inside it. // and then the final image is created by copying the tiles inside it.
// NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM // NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM
@@ -1437,7 +1437,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
layer.image_tiles[j][i].setColorCount(0); layer.image_tiles[j][i].setColorCount(0);
layer.image_tiles[j][i].fill(0);
break; break;
case GRAY_GIMAGE: case GRAY_GIMAGE:
@@ -1446,17 +1445,15 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
layer.image_tiles[j][i].setColorCount(256); layer.image_tiles[j][i].setColorCount(256);
layer.image_tiles[j][i].fill(0);
setGrayPalette(layer.image_tiles[j][i]); setGrayPalette(layer.image_tiles[j][i]);
break; break;
case GRAYA_GIMAGE: case GRAYA_GIMAGE:
layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
layer.image_tiles[j][i].setColorCount(256);
if (layer.image_tiles[j][i].isNull()) { if (layer.image_tiles[j][i].isNull()) {
return false; return false;
} }
layer.image_tiles[j][i].setColorCount(256);
layer.image_tiles[j][i].fill(0);
setGrayPalette(layer.image_tiles[j][i]); setGrayPalette(layer.image_tiles[j][i]);
layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
@@ -1464,17 +1461,15 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
layer.alpha_tiles[j][i].setColorCount(256); layer.alpha_tiles[j][i].setColorCount(256);
layer.alpha_tiles[j][i].fill(0);
setGrayPalette(layer.alpha_tiles[j][i]); setGrayPalette(layer.alpha_tiles[j][i]);
break; break;
case INDEXED_GIMAGE: case INDEXED_GIMAGE:
layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
if (layer.image_tiles[j][i].isNull()) { if (layer.image_tiles[j][i].isNull()) {
return false; return false;
} }
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
layer.image_tiles[j][i].fill(0);
setPalette(xcf_image, layer.image_tiles[j][i]); setPalette(xcf_image, layer.image_tiles[j][i]);
break; break;
@@ -1484,7 +1479,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors); layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
layer.image_tiles[j][i].fill(0);
setPalette(xcf_image, layer.image_tiles[j][i]); setPalette(xcf_image, layer.image_tiles[j][i]);
layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
@@ -1492,7 +1486,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false; return false;
} }
layer.alpha_tiles[j][i].setColorCount(256); layer.alpha_tiles[j][i].setColorCount(256);
layer.alpha_tiles[j][i].fill(0);
setGrayPalette(layer.alpha_tiles[j][i]); setGrayPalette(layer.alpha_tiles[j][i]);
} }
if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) { if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
@@ -1530,11 +1523,10 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
#endif #endif
if (layer.mask_offset != 0) { if (layer.mask_offset != 0) {
layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8); layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
layer.mask_tiles[j][i].setColorCount(256);
if (layer.mask_tiles[j][i].isNull()) { if (layer.mask_tiles[j][i].isNull()) {
return false; return false;
} }
layer.mask_tiles[j][i].setColorCount(256);
layer.mask_tiles[j][i].fill(0);
setGrayPalette(layer.mask_tiles[j][i]); setGrayPalette(layer.mask_tiles[j][i]);
} }
} }
@@ -1809,25 +1801,14 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
dataPtr[x + 1] = qFromBigEndian(src[x + 1]); dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
dataPtr[x + 2] = qFromBigEndian(src[x + 2]); dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
dataPtr[x + 3] = qfloat16(1); dataPtr[x + 3] = qfloat16(1);
if (!dataPtr[x + 0].isFinite() || !dataPtr[x + 1].isFinite() || !dataPtr[x + 2].isFinite()) { if (dataPtr[x + 0].isNaN() || dataPtr[x + 1].isNaN() || dataPtr[x + 2].isNaN()) {
return false; return false;
} }
} }
} }
break; break;
case QImage::Format_RGBA16FPx4: case QImage::Format_RGBA16FPx4:
for (int y = 0; y < height; y++) { static_assert(sizeof(QRgbaFloat16) == sizeof(QRgba64), "Different sizes for float and int 16 bit pixels");
const size_t bpl = width * sizeof(QRgbaFloat16);
qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.scanLine(y));
const qfloat16 *dataPtr = reinterpret_cast<qfloat16 *>(image.scanLine(y));
for (int x = 0; x < width * 4; ++x) {
if (!dataPtr[x].isFinite()) {
return false;
}
}
}
break;
#endif #endif
case QImage::Format_RGBA64: case QImage::Format_RGBA64:
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
@@ -1840,13 +1821,6 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
const size_t bpl = width * sizeof(QRgbaFloat32); const size_t bpl = width * sizeof(QRgbaFloat32);
qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.scanLine(y)); qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.scanLine(y));
const float *dataPtr = reinterpret_cast<float *>(image.scanLine(y));
for (int x = 0; x < width * 4; ++x) {
if (!std::isfinite(dataPtr[x])) {
return false;
}
}
} }
break; break;
case QImage::Format_RGBX32FPx4: case QImage::Format_RGBX32FPx4:
@@ -1858,7 +1832,7 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
dataPtr[x + 1] = qFromBigEndian(src[x + 1]); dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
dataPtr[x + 2] = qFromBigEndian(src[x + 2]); dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
dataPtr[x + 3] = 1.f; dataPtr[x + 3] = 1.f;
if (!std::isfinite(dataPtr[x + 0]) || !std::isfinite(dataPtr[x + 1]) || !std::isfinite(dataPtr[x + 2])) { if (std::isnan(dataPtr[x + 0]) || std::isnan(dataPtr[x + 1]) || std::isnan(dataPtr[x + 2])) {
return false; return false;
} }
} }
@@ -2071,7 +2045,6 @@ bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, co
QList<uchar> buffer; QList<uchar> buffer;
if (needConvert) { if (needConvert) {
buffer.resize(blockSize * (bpp == 2 ? 2 : 1)); buffer.resize(blockSize * (bpp == 2 ? 2 : 1));
buffer.fill(uchar());
} }
for (uint j = 0; j < layer.nrows; j++) { for (uint j = 0; j < layer.nrows; j++) {
for (uint i = 0; i < layer.ncols; i++) { for (uint i = 0; i < layer.ncols; i++) {