diff --git a/README.md b/README.md index 8a9654a..74b0701 100644 --- a/README.md +++ b/README.md @@ -218,30 +218,35 @@ RGB. Where possible, plugins support large images. By convention, many of the large image plugins are limited to a maximum of 300,000 x 300,000 pixels. -Anyway, all plugins are also limited by the -`QImageIOReader::allocationLimit()`. Below are the maximum sizes for each -plugin ('n/a' means no limit, i.e. the limit depends on the format encoding). +Anyway, all plugins are also limited by the +`QImageIOReader::allocationLimit()`. +> [!note] +> You can change the maximum limit of 300000 pixels by setting the constant +> `KIF_LARGE_IMAGE_PIXEL_LIMIT` to the desired value in the cmake file. + +Below are the maximum sizes for each plugin ('n/a' means no limit, i.e. the +limit depends on the format encoding). - ANI: n/a - AVIF: 32,768 x 32,768 pixels, in any case no larger than 256 megapixels - DDS: n/a - EXR: 300,000 x 300,000 pixels -- EPS: n/a -- HDR: n/a (large image) +- EPS: same size as Qt's JPG plugin +- HDR: 300,000 x 300,000 pixels - HEIF: n/a - IFF: 65,535 x 65,535 pixels - JP2: 300,000 x 300,000 pixels, in any case no larger than 2 gigapixels - JXL: 262,144 x 262,144 pixels, in any case no larger than 256 megapixels -- JXR: n/a, in any case no larger than 4 GB +- JXR: 300,000 x 300,000 pixels, in any case no larger than 4 GB - KRA: same size as Qt's PNG plugin - ORA: same size as Qt's PNG plugin - PCX: 65,535 x 65,535 pixels -- PFM: n/a (large image) +- PFM: 300,000 x 300,000 pixels - PIC: 65,535 x 65,535 pixels - PSD: 300,000 x 300,000 pixels - PXR: 65,535 x 65,535 pixels - QOI: 300,000 x 300,000 pixels -- RAS: n/a (large image) +- RAS: 300,000 x 300,000 pixels - RAW: n/a (depends on the RAW format loaded) - RGB: 65,535 x 65,535 pixels - SCT: 300,000 x 300,000 pixels diff --git a/src/imageformats/exr.cpp b/src/imageformats/exr.cpp index 45a755a..ca0d793 100644 --- a/src/imageformats/exr.cpp +++ b/src/imageformats/exr.cpp @@ -7,6 +7,10 @@ SPDX-License-Identifier: LGPL-2.0-or-later */ +#include "exr_p.h" +#include "scanlineconverter_p.h" +#include "util_p.h" + /* *** EXR_CONVERT_TO_SRGB *** * If defined, the linear data is converted to sRGB on read to accommodate * programs that do not support color profiles. @@ -28,7 +32,7 @@ * The maximum size in pixel allowed by the plugin. */ #ifndef EXR_MAX_IMAGE_WIDTH -#define EXR_MAX_IMAGE_WIDTH 300000 +#define EXR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT #endif #ifndef EXR_MAX_IMAGE_HEIGHT #define EXR_MAX_IMAGE_HEIGHT EXR_MAX_IMAGE_WIDTH @@ -50,10 +54,6 @@ #define EXR_LINES_PER_BLOCK 128 #endif -#include "exr_p.h" -#include "scanlineconverter_p.h" -#include "util_p.h" - #include #include #include diff --git a/src/imageformats/hdr.cpp b/src/imageformats/hdr.cpp index bee4fdc..264d1ca 100644 --- a/src/imageformats/hdr.cpp +++ b/src/imageformats/hdr.cpp @@ -23,6 +23,16 @@ */ //#define HDR_HALF_QUALITY // default commented -> you should define it in your cmake file +/* *** HDR_MAX_IMAGE_WIDTH and HDR_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef HDR_MAX_IMAGE_WIDTH +#define HDR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef HDR_MAX_IMAGE_HEIGHT +#define HDR_MAX_IMAGE_HEIGHT HDR_MAX_IMAGE_WIDTH +#endif + typedef unsigned char uchar; Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg) @@ -42,7 +52,10 @@ public: Header(const Header&) = default; Header& operator=(const Header&) = default; - bool isValid() const { return width() > 0 && height() > 0; } + bool isValid() const + { + return width() > 0 && height() > 0 && width() <= HDR_MAX_IMAGE_WIDTH && height() <= HDR_MAX_IMAGE_HEIGHT; + } qint32 width() const { return(m_size.width()); } qint32 height() const { return(m_size.height()); } QString software() const { return(m_software); } diff --git a/src/imageformats/jp2.cpp b/src/imageformats/jp2.cpp index 7ea1350..180e0ed 100644 --- a/src/imageformats/jp2.cpp +++ b/src/imageformats/jp2.cpp @@ -21,7 +21,7 @@ * The maximum size in pixel allowed by the plugin. */ #ifndef JP2_MAX_IMAGE_WIDTH -#define JP2_MAX_IMAGE_WIDTH 300000 +#define JP2_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT #endif #ifndef JP2_MAX_IMAGE_HEIGHT #define JP2_MAX_IMAGE_HEIGHT JP2_MAX_IMAGE_WIDTH diff --git a/src/imageformats/jxr.cpp b/src/imageformats/jxr.cpp index 725196e..90f7524 100644 --- a/src/imageformats/jxr.cpp +++ b/src/imageformats/jxr.cpp @@ -74,6 +74,16 @@ Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg) // #define JXR_ENABLE_ADVANCED_METADATA +/* *** JXR_MAX_IMAGE_WIDTH and JXR_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef JXR_MAX_IMAGE_WIDTH +#define JXR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef JXR_MAX_IMAGE_HEIGHT +#define JXR_MAX_IMAGE_HEIGHT JXR_MAX_IMAGE_WIDTH +#endif + #ifndef JXR_MAX_METADATA_SIZE /*! * XMP and EXIF maximum size. @@ -386,6 +396,11 @@ public: if (pDecoder) { qint32 w, h; pDecoder->GetSize(pDecoder, &w, &h); + if (w > JXR_MAX_IMAGE_WIDTH || h > JXR_MAX_IMAGE_HEIGHT || w < 1 || h < 1) { + qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::imageSize() Maximum image size is limited to" << JXR_MAX_IMAGE_WIDTH << "x" + << JXR_MAX_IMAGE_HEIGHT << "pixels"; + return {}; + } return QSize(w, h); } return {}; @@ -1053,6 +1068,10 @@ bool JXRHandler::write(const QImage &image) qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() image too large: the image cannot exceed 4GB."; return false; } + if (image.width() > JXR_MAX_IMAGE_WIDTH || image.height() > JXR_MAX_IMAGE_HEIGHT) { + qCCritical(LOG_JXRPLUGIN) << "JXRHandler::write() Maximum image size is limited to" << JXR_MAX_IMAGE_WIDTH << "x" << JXR_MAX_IMAGE_HEIGHT << "pixels"; + return {}; + } if (!d->initForWriting()) { return false; diff --git a/src/imageformats/pfm.cpp b/src/imageformats/pfm.cpp index e431b2c..68ace1e 100644 --- a/src/imageformats/pfm.cpp +++ b/src/imageformats/pfm.cpp @@ -22,6 +22,16 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PFMPLUGIN) Q_LOGGING_CATEGORY(LOG_PFMPLUGIN, "kf.imageformats.plugins.pfm", QtWarningMsg) +/* *** PFM_MAX_IMAGE_WIDTH and PFM_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef PFM_MAX_IMAGE_WIDTH +#define PFM_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef PFM_MAX_IMAGE_HEIGHT +#define PFM_MAX_IMAGE_HEIGHT PFM_MAX_IMAGE_WIDTH +#endif + class PFMHeader { private: @@ -80,7 +90,7 @@ public: bool isValid() const { - return (m_width > 0 && m_height > 0); + return (m_width > 0 && m_height > 0 && m_width <= PFM_MAX_IMAGE_WIDTH && m_height <= PFM_MAX_IMAGE_HEIGHT); } bool isBlackAndWhite() const diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index 8960bbb..444b9d8 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -72,6 +72,16 @@ typedef quint8 uchar; */ // #define PSD_FORCE_RGBA +/* *** PSD_MAX_IMAGE_WIDTH and PSD_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef PSD_MAX_IMAGE_WIDTH +#define PSD_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef PSD_MAX_IMAGE_HEIGHT +#define PSD_MAX_IMAGE_HEIGHT PSD_MAX_IMAGE_WIDTH +#endif + namespace // Private. { @@ -678,7 +688,7 @@ static bool IsValid(const PSDHeader &header) qDebug() << "PSD header: invalid number of channels" << header.channel_count; return false; } - if (header.width > 300000 || header.height > 300000) { + if (header.width > std::min(300000, PSD_MAX_IMAGE_WIDTH) || header.height > std::min(300000, PSD_MAX_IMAGE_HEIGHT)) { qDebug() << "PSD header: invalid image size" << header.width << "x" << header.height; return false; } diff --git a/src/imageformats/qoi.cpp b/src/imageformats/qoi.cpp index 09fe138..89b8970 100644 --- a/src/imageformats/qoi.cpp +++ b/src/imageformats/qoi.cpp @@ -15,6 +15,16 @@ #include #include +/* *** QOI_MAX_IMAGE_WIDTH and QOI_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef QOI_MAX_IMAGE_WIDTH +#define QOI_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef QOI_MAX_IMAGE_HEIGHT +#define QOI_MAX_IMAGE_HEIGHT QOI_MAX_IMAGE_WIDTH +#endif + namespace // Private { @@ -92,7 +102,7 @@ static bool IsSupported(const QoiHeader &head) return false; } // Set a reasonable upper limit - if (head.Width > 300000 || head.Height > 300000) { + if (head.Width > QOI_MAX_IMAGE_WIDTH || head.Height > QOI_MAX_IMAGE_HEIGHT) { return false; } return true; diff --git a/src/imageformats/ras.cpp b/src/imageformats/ras.cpp index 33acf4a..ea2afea 100644 --- a/src/imageformats/ras.cpp +++ b/src/imageformats/ras.cpp @@ -15,6 +15,16 @@ #include #include +/* *** RAS_MAX_IMAGE_WIDTH and RAS_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef RAS_MAX_IMAGE_WIDTH +#define RAS_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef RAS_MAX_IMAGE_HEIGHT +#define RAS_MAX_IMAGE_HEIGHT RAS_MAX_IMAGE_WIDTH +#endif + namespace // Private. { // format info from http://www.fileformat.info/format/sunraster/egff.htm @@ -84,9 +94,10 @@ static bool IsSupported(const RasHeader &head) if (head.Depth != 1 && head.Depth != 8 && head.Depth != 24 && head.Depth != 32) { return false; } - if (head.Width == 0 || head.Height == 0) { + if (head.Width == 0 || head.Height == 0 || head.Width > RAS_MAX_IMAGE_WIDTH || head.Height > RAS_MAX_IMAGE_HEIGHT) { return false; } + // the Type field adds support for RLE(BGR), RGB and other encodings // we support Type 1: Normal(BGR), Type 2: RLE(BGR) and Type 3: Normal(RGB) ONLY! // TODO: add support for Type 4,5: TIFF/IFF @@ -388,7 +399,7 @@ bool RASHandler::read(QImage *outImage) s >> ras; if (ras.ColorMapLength > kMaxQVectorSize) { - qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength; + qWarning() << "read() unsupported image color map length in file header" << ras.ColorMapLength; return false; } diff --git a/src/imageformats/sct.cpp b/src/imageformats/sct.cpp index 8f70be5..7da6357 100644 --- a/src/imageformats/sct.cpp +++ b/src/imageformats/sct.cpp @@ -19,6 +19,16 @@ Q_LOGGING_CATEGORY(LOG_SCTPLUGIN, "kf.imageformats.plugins.scitex", QtWarningMsg) +/* *** SCT_MAX_IMAGE_WIDTH and SCT_MAX_IMAGE_HEIGHT *** + * The maximum size in pixel allowed by the plugin. + */ +#ifndef SCT_MAX_IMAGE_WIDTH +#define SCT_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef SCT_MAX_IMAGE_HEIGHT +#define SCT_MAX_IMAGE_HEIGHT SCT_MAX_IMAGE_WIDTH +#endif + #define CTRLBLOCK_SIZE 256 #define PRMSBLOCK_SIZE_CT 256 @@ -139,7 +149,7 @@ public: return false; } // Set a reasonable upper limit - if (width() > 300000 || height() > 300000) { + if (width() > SCT_MAX_IMAGE_WIDTH || height() > SCT_MAX_IMAGE_HEIGHT) { return false; } return m_cb.fileType() == QStringLiteral(FILETYPE_CT) && format() != QImage::Format_Invalid; diff --git a/src/imageformats/util_p.h b/src/imageformats/util_p.h index 2129364..1fbc775 100644 --- a/src/imageformats/util_p.h +++ b/src/imageformats/util_p.h @@ -13,6 +13,11 @@ #include #include +// Default maximum width and height for the large image plugins. +#ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT +#define KIF_LARGE_IMAGE_PIXEL_LIMIT 300000 +#endif + // Image metadata keys to use in plugins (so they are consistent) #define META_KEY_ALTITUDE "Altitude" #define META_KEY_AUTHOR "Author" diff --git a/src/imageformats/xcf.cpp b/src/imageformats/xcf.cpp index 26d74a7..bfc9b76 100644 --- a/src/imageformats/xcf.cpp +++ b/src/imageformats/xcf.cpp @@ -26,12 +26,16 @@ #define USE_FLOAT_IMAGES // default uncommented // Let's set a "reasonable" maximum size -#define MAX_IMAGE_WIDTH 300000 -#define MAX_IMAGE_HEIGHT 300000 +#ifndef XCF_MAX_IMAGE_WIDTH +#define XCF_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT +#endif +#ifndef XCF_MAX_IMAGE_HEIGHT +#define XCF_MAX_IMAGE_HEIGHT XCF_MAX_IMAGE_WIDTH +#endif #else // While it is possible to have images larger than 32767 pixels, QPainter seems unable to go beyond this threshold using Qt 5. -#define MAX_IMAGE_WIDTH 32767 -#define MAX_IMAGE_HEIGHT 32767 +#define XCF_MAX_IMAGE_WIDTH 32767 +#define XCF_MAX_IMAGE_HEIGHT 32767 #endif #ifdef USE_FLOAT_IMAGES @@ -844,8 +848,8 @@ bool XCFImageFormat::readXCFHeader(QDataStream &xcf_io, XCFImage::Header *header return false; } - if (header->width > MAX_IMAGE_WIDTH || header->height > MAX_IMAGE_HEIGHT) { - qCWarning(XCFPLUGIN) << "The maximum image size is limited to" << MAX_IMAGE_WIDTH << "x" << MAX_IMAGE_HEIGHT << "px"; + if (header->width > XCF_MAX_IMAGE_WIDTH || header->height > XCF_MAX_IMAGE_HEIGHT) { + qCWarning(XCFPLUGIN) << "The maximum image size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px"; return false; } @@ -1364,8 +1368,8 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image) qCWarning(XCFPLUGIN) << "On 32-bits programs the maximum layer size is limited to" << 16384 << "x" << 16384 << "px"; return false; } - if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) { - qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << MAX_IMAGE_WIDTH << "x" << MAX_IMAGE_HEIGHT << "px"; + if (layer.width > XCF_MAX_IMAGE_WIDTH || layer.height > XCF_MAX_IMAGE_HEIGHT) { + qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px"; return false; } @@ -2793,8 +2797,7 @@ void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image) QPainter painter(&image); painter.setOpacity(layer.opacity / 255.0); painter.setCompositionMode(QPainter::CompositionMode_Source); - if (x + layer.x_offset < MAX_IMAGE_WIDTH && - y + layer.y_offset < MAX_IMAGE_HEIGHT) { + if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) { painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]); } continue; @@ -3196,8 +3199,7 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image) qint32 x = qint32(i * TILE_WIDTH); QImage &tile = layer.image_tiles[j][i]; - if (x + layer.x_offset < MAX_IMAGE_WIDTH && - y + layer.y_offset < MAX_IMAGE_HEIGHT) { + if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) { painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile); } } @@ -3248,8 +3250,7 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image) QPainter painter(&image); painter.setOpacity(layer.opacity / 255.0); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); - if (x + layer.x_offset < MAX_IMAGE_WIDTH && - y + layer.y_offset < MAX_IMAGE_HEIGHT) { + if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) { painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]); } continue;