Fix image allocation with Qt 6

To make the plugins fail to allocate if the image size is greater than QImageReader::allocationLimit() it is necessary to allocate the image with QImageIOHandler::allocateImage().

Note that not all plugins have been changed and some others are not tested in the CI (maybe due to missing libraries).

PS: the following message is printed by QImageIOHandler::allocateImage() if the size is exceeded: "qt.gui.imageio: QImageIOHandler: Rejecting image as it exceeds the current allocation limit of XXX megabytes"
This commit is contained in:
Mirco Miranda
2022-09-07 14:03:33 +00:00
committed by Albert Astals Cid
parent dfbc6e0f8c
commit feb6d9b20f
13 changed files with 85 additions and 30 deletions

View File

@ -6,6 +6,7 @@
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "util_p.h"
#include "xcf_p.h"
#include <QDebug>
@ -17,6 +18,9 @@
#include <QVector>
#include <QtEndian>
#include <QColorSpace>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QImageReader>
#endif
#include <stdlib.h>
#include <string.h>
@ -1099,6 +1103,18 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
return false;
}
// 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
// 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
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > QImageReader::allocationLimit()) {
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << QImageReader::allocationLimit() << "megabytes";
return false;
}
#endif
layer.image_tiles.resize(layer.nrows);
if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE) {
@ -1917,7 +1933,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
switch (layer.type) {
case RGB_GIMAGE:
if (layer.opacity == OPAQUE_OPACITY) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_RGB32);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_RGB32);
if (image.isNull()) {
return false;
}
@ -1926,7 +1942,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
} // else, fall through to 32-bit representation
Q_FALLTHROUGH();
case RGBA_GIMAGE:
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) {
return false;
}
@ -1935,7 +1951,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
case GRAY_GIMAGE:
if (layer.opacity == OPAQUE_OPACITY) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(256);
if (image.isNull()) {
return false;
@ -1946,7 +1962,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
} // else, fall through to 32-bit representation
Q_FALLTHROUGH();
case GRAYA_GIMAGE:
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) {
return false;
}
@ -1967,7 +1983,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
// or two-color palette. Have to ask about this...
if (xcf_image.num_colors <= 2) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image.setColorCount(xcf_image.num_colors);
if (image.isNull()) {
return false;
@ -1975,7 +1991,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
image.fill(0);
setPalette(xcf_image, image);
} else if (xcf_image.num_colors <= 256) {
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(xcf_image.num_colors);
if (image.isNull()) {
return false;
@ -1993,7 +2009,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
xcf_image.palette[1] = xcf_image.palette[0];
xcf_image.palette[0] = qRgba(255, 255, 255, 0);
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
image.setColorCount(xcf_image.num_colors);
if (image.isNull()) {
return false;
@ -2009,7 +2025,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
}
xcf_image.palette[0] = qRgba(255, 255, 255, 0);
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
image.setColorCount(xcf_image.num_colors);
if (image.isNull()) {
return false;
@ -2020,7 +2036,7 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
// No room for a transparent color, so this has to be promoted to
// true color. (There is no equivalent PNG representation output
// from The GIMP as of v1.2.)
image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
image = imageAlloc(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
if (image.isNull()) {
return false;
}
@ -2031,11 +2047,11 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
if (dpmx > std::numeric_limits<int>::max()) {
if (dpmx > float(std::numeric_limits<int>::max())) {
return false;
}
const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
if (dpmy > std::numeric_limits<int>::max()) {
if (dpmy > float(std::numeric_limits<int>::max())) {
return false;
}
image.setDotsPerMeterX((int)dpmx);