mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-06-13 10:49:10 -04:00
Improve buffer memory management
This commit is contained in:
committed by
Mirco Miranda
parent
86b0fe60c5
commit
ec640db10e
@@ -157,7 +157,7 @@ bool QAVIFHandler::ensureDecoder()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rawData = device()->readAll();
|
m_rawData = deviceRead(device(), kMaxQVectorSize);
|
||||||
|
|
||||||
m_rawAvifData.data = reinterpret_cast<const uint8_t *>(m_rawData.constData());
|
m_rawAvifData.data = reinterpret_cast<const uint8_t *>(m_rawData.constData());
|
||||||
m_rawAvifData.size = m_rawData.size();
|
m_rawAvifData.size = m_rawData.size();
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QImageReader>
|
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
@@ -187,7 +186,7 @@ public:
|
|||||||
|
|
||||||
bool isImageValid(const opj_image_t *i) const
|
bool isImageValid(const opj_image_t *i) const
|
||||||
{
|
{
|
||||||
return i && i->comps && i->numcomps > 0;
|
return i && i->comps && i->numcomps > 0 && i->numcomps < 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableThreads(opj_codec_t *codec) const
|
void enableThreads(opj_codec_t *codec) const
|
||||||
@@ -359,15 +358,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
|
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
|
||||||
const int allocationLimit = QImageReader::allocationLimit();
|
if (!checkImageSize(width, height, nchannels * 4)) {
|
||||||
if (allocationLimit > 0) {
|
qCCritical(LOG_JP2PLUGIN) << "Rejecting image as it exceeds the current allocation limit.";
|
||||||
auto maxBytes = qint64(allocationLimit) * 1024 * 1024;
|
return false;
|
||||||
auto neededBytes = qint64(width) * height * nchannels * 4;
|
|
||||||
if (maxBytes > 0 && neededBytes > maxBytes) {
|
|
||||||
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024)
|
|
||||||
<< "MiB are needed!";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -384,6 +377,11 @@ public:
|
|||||||
if (isImageValid(m_jp2_image)) {
|
if (isImageValid(m_jp2_image)) {
|
||||||
auto &&c0 = m_jp2_image->comps[0];
|
auto &&c0 = m_jp2_image->comps[0];
|
||||||
auto tmp = QSize(c0.w, c0.h);
|
auto tmp = QSize(c0.w, c0.h);
|
||||||
|
for (quint32 c = 1; c < m_jp2_image->numcomps; ++c) {
|
||||||
|
auto &&cc = m_jp2_image->comps[c];
|
||||||
|
if (QSize(cc.w, cc.h) != tmp)
|
||||||
|
tmp = QSize();
|
||||||
|
}
|
||||||
if (checkSizeLimits(tmp, m_jp2_image->numcomps))
|
if (checkSizeLimits(tmp, m_jp2_image->numcomps))
|
||||||
sz = tmp;
|
sz = tmp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <jxl/cms.h>
|
#include <jxl/cms.h>
|
||||||
#include <jxl/encode.h>
|
#include <jxl/encode.h>
|
||||||
|
#include <jxl/memory_manager.h>
|
||||||
#include <jxl/thread_parallel_runner.h>
|
#include <jxl/thread_parallel_runner.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -56,6 +57,21 @@ Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg)
|
|||||||
#define MAX_IMAGE_PIXELS FEATURE_LEVEL_5_PIXELS
|
#define MAX_IMAGE_PIXELS FEATURE_LEVEL_5_PIXELS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void *QtJXLMemoryManagerAlloc(void *opaque, size_t size)
|
||||||
|
{
|
||||||
|
if (opaque) {
|
||||||
|
size_t maxBytes = *(size_t*)opaque;
|
||||||
|
if (maxBytes && size > maxBytes)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtJXLMemoryManagerFree(void *, void *address)
|
||||||
|
{
|
||||||
|
free(address);
|
||||||
|
}
|
||||||
|
|
||||||
QJpegXLHandler::QJpegXLHandler()
|
QJpegXLHandler::QJpegXLHandler()
|
||||||
: m_parseState(ParseJpegXLNotParsed)
|
: m_parseState(ParseJpegXLNotParsed)
|
||||||
, m_quality(90)
|
, m_quality(90)
|
||||||
@@ -70,6 +86,7 @@ QJpegXLHandler::QJpegXLHandler()
|
|||||||
, m_alpha_channel_id(0)
|
, m_alpha_channel_id(0)
|
||||||
, m_input_image_format(QImage::Format_Invalid)
|
, m_input_image_format(QImage::Format_Invalid)
|
||||||
, m_target_image_format(QImage::Format_Invalid)
|
, m_target_image_format(QImage::Format_Invalid)
|
||||||
|
, m_maxBytes(size_t(QImageReader::allocationLimit()) * 1024 * 1024)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +170,7 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rawData = device()->readAll();
|
m_rawData = deviceRead(device(), kMaxQVectorSize);
|
||||||
|
|
||||||
if (m_rawData.isEmpty()) {
|
if (m_rawData.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -165,7 +182,14 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_decoder = JxlDecoderCreate(nullptr);
|
// Creating a simple memory manager
|
||||||
|
JxlMemoryManager memory_manager = {
|
||||||
|
.opaque = &m_maxBytes,
|
||||||
|
.alloc = QtJXLMemoryManagerAlloc,
|
||||||
|
.free = QtJXLMemoryManagerFree
|
||||||
|
};
|
||||||
|
// Creating the decoder (it makes a deep copy of memory manager)
|
||||||
|
m_decoder = JxlDecoderCreate(&memory_manager);
|
||||||
if (!m_decoder) {
|
if (!m_decoder) {
|
||||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderCreate failed");
|
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderCreate failed");
|
||||||
m_parseState = ParseJpegXLError;
|
m_parseState = ParseJpegXLError;
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ private:
|
|||||||
QImage::Format m_target_image_format;
|
QImage::Format m_target_image_format;
|
||||||
|
|
||||||
JxlPixelFormat m_input_pixel_format;
|
JxlPixelFormat m_input_pixel_format;
|
||||||
|
|
||||||
|
size_t m_maxBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QJpegXLPlugin : public QImageIOPlugin
|
class QJpegXLPlugin : public QImageIOPlugin
|
||||||
|
|||||||
@@ -89,12 +89,14 @@ const auto supported_formats = QSet<QByteArray>{
|
|||||||
* \brief rawImageSize
|
* \brief rawImageSize
|
||||||
* \return The size in pixels of the RAW image.
|
* \return The size in pixels of the RAW image.
|
||||||
*/
|
*/
|
||||||
static QSize rawImageSize(LibRaw *rawProcessor)
|
static QSize rawImageSize(LibRaw *rawProcessor, qint32 *bytesPerPixel = nullptr)
|
||||||
{
|
{
|
||||||
auto w = libraw_get_iwidth(&rawProcessor->imgdata);
|
int w = 0, h = 0, c = 0, b = 0;
|
||||||
auto h = libraw_get_iheight(&rawProcessor->imgdata);
|
rawProcessor->get_mem_image_format(&w, &h, &c, &b);
|
||||||
// flip & 4: taken from LibRaw code
|
if (bytesPerPixel) {
|
||||||
return (rawProcessor->imgdata.sizes.flip & 4) ? QSize(h, w) : QSize(w, h);
|
*bytesPerPixel = std::max(1, b * c / 8);
|
||||||
|
}
|
||||||
|
return QSize(w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int raw_scanf_one(const QByteArray &ba, const char *fmt, void *val)
|
inline int raw_scanf_one(const QByteArray &ba, const char *fmt, void *val)
|
||||||
@@ -381,9 +383,9 @@ QString createTag(libraw_gps_info_t gps, const char *tag)
|
|||||||
if (gps.latref != '\0') {
|
if (gps.latref != '\0') {
|
||||||
auto lc = QLocale::c();
|
auto lc = QLocale::c();
|
||||||
auto value = QStringLiteral("%1,%2%3")
|
auto value = QStringLiteral("%1,%2%3")
|
||||||
.arg(lc.toString(gps.latitude[0], 'f', 0))
|
.arg(lc.toString(gps.latitude[0], 'f', 0),
|
||||||
.arg(lc.toString(gps.latitude[1] + gps.latitude[2] / 60, 'f', 4))
|
lc.toString(gps.latitude[1] + gps.latitude[2] / 60, 'f', 4),
|
||||||
.arg(QChar::fromLatin1(gps.latref));
|
QChar::fromLatin1(gps.latref));
|
||||||
return createTag(value, tag);
|
return createTag(value, tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,9 +393,9 @@ QString createTag(libraw_gps_info_t gps, const char *tag)
|
|||||||
if (gps.longref != '\0') {
|
if (gps.longref != '\0') {
|
||||||
auto lc = QLocale::c();
|
auto lc = QLocale::c();
|
||||||
auto value = QStringLiteral("%1,%2%3")
|
auto value = QStringLiteral("%1,%2%3")
|
||||||
.arg(lc.toString(gps.longitude[0], 'f', 0))
|
.arg(lc.toString(gps.longitude[0], 'f', 0),
|
||||||
.arg(lc.toString(gps.longitude[1] + gps.longitude[2] / 60, 'f', 4))
|
lc.toString(gps.longitude[1] + gps.longitude[2] / 60, 'f', 4),
|
||||||
.arg(QChar::fromLatin1(gps.longref));
|
QChar::fromLatin1(gps.longref));
|
||||||
return createTag(value, tag);
|
return createTag(value, tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -688,7 +690,7 @@ bool LoadTHUMB(QImageIOHandler *handler, QImage &img)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
auto all = device->readAll();
|
auto all = deviceRead(device(), kMaxQVectorSize);
|
||||||
if (rawProcessor->open_buffer(all.data(), all.size()) != LIBRAW_SUCCESS) {
|
if (rawProcessor->open_buffer(all.data(), all.size()) != LIBRAW_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -751,18 +753,23 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
auto ba = device->readAll();
|
auto ba = deviceRead(device(), kMaxQVectorSize);
|
||||||
if (rawProcessor->open_buffer(ba.data(), ba.size()) != LIBRAW_SUCCESS) {
|
if (rawProcessor->open_buffer(ba.data(), ba.size()) != LIBRAW_SUCCESS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// *** Limiting the maximum image size on a reasonable size
|
// *** Limiting the maximum image size on a reasonable size
|
||||||
auto size = rawImageSize(rawProcessor.get());
|
qint32 bytesPerPixel = 0;
|
||||||
|
auto size = rawImageSize(rawProcessor.get(), &bytesPerPixel);
|
||||||
if (size.width() >= RAW_MAX_IMAGE_WIDTH || size.height() >= RAW_MAX_IMAGE_HEIGHT) {
|
if (size.width() >= RAW_MAX_IMAGE_WIDTH || size.height() >= RAW_MAX_IMAGE_HEIGHT) {
|
||||||
qCWarning(LOG_RAWPLUGIN) << "The maximum image size is limited to" << (RAW_MAX_IMAGE_WIDTH - 1) << "x" << (RAW_MAX_IMAGE_HEIGHT - 1) << "px";
|
qCWarning(LOG_RAWPLUGIN) << "The maximum image size is limited to" << (RAW_MAX_IMAGE_WIDTH - 1) << "x" << (RAW_MAX_IMAGE_HEIGHT - 1) << "px";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!checkImageSize(size, bytesPerPixel)) {
|
||||||
|
qCWarning(LOG_RAWPLUGIN) << "Rejecting image as it exceeds the current allocation limit.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// *** Unpacking selected image
|
// *** Unpacking selected image
|
||||||
if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
|
if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
|
||||||
@@ -1056,7 +1063,7 @@ bool RAWHandler::canRead(QIODevice *device)
|
|||||||
LibRaw_QIODevice stream(device);
|
LibRaw_QIODevice stream(device);
|
||||||
auto ok = rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS;
|
auto ok = rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS;
|
||||||
#else
|
#else
|
||||||
auto ba = device->readAll();
|
auto ba = deviceRead(device(), kMaxQVectorSize);
|
||||||
auto ok = rawProcessor->open_buffer(ba.data(), ba.size()) == LIBRAW_SUCCESS;
|
auto ok = rawProcessor->open_buffer(ba.data(), ba.size()) == LIBRAW_SUCCESS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ bool SGIImagePrivate::readImage(QImage &img)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_data = _dev->readAll();
|
_data = deviceRead(_dev, kMaxQVectorSize);
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (_rle) {
|
if (_rle) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QImageIOHandler>
|
#include <QImageIOHandler>
|
||||||
|
#include <QImageReader>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QPixelFormat>
|
#include <QPixelFormat>
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ enum class ImageInitToZero
|
|||||||
* \brief imageAlloc
|
* \brief imageAlloc
|
||||||
* Helper function to initialize framework images.
|
* Helper function to initialize framework images.
|
||||||
* \param size The image size.
|
* \param size The image size.
|
||||||
* \param format The image format,
|
* \param format The image format.
|
||||||
* \param init Whether and which images should be initialized to zero.
|
* \param init Whether and which images should be initialized to zero.
|
||||||
* \return The allocated image or a null image on error.
|
* \return The allocated image or a null image on error.
|
||||||
*/
|
*/
|
||||||
@@ -103,21 +104,60 @@ inline QImage imageAlloc(const QSize &size, const QImage::Format &format, const
|
|||||||
auto isFloat = pixelFormat.typeInterpretation() == QPixelFormat::FloatingPoint;
|
auto isFloat = pixelFormat.typeInterpretation() == QPixelFormat::FloatingPoint;
|
||||||
auto isPremul = pixelFormat.premultiplied();
|
auto isPremul = pixelFormat.premultiplied();
|
||||||
if (init == ImageInitToZero::All) {
|
if (init == ImageInitToZero::All) {
|
||||||
img.fill(0);
|
img.fill(Qt::black);
|
||||||
} else if (isFloat && (init == ImageInitToZero::FPOnly || init == ImageInitToZero::FPAndPremul)) {
|
} else if (isFloat && (init == ImageInitToZero::FPOnly || init == ImageInitToZero::FPAndPremul)) {
|
||||||
img.fill(0);
|
img.fill(Qt::black);
|
||||||
} else if (isPremul && (init == ImageInitToZero::PremulOnly || init == ImageInitToZero::FPAndPremul)) {
|
} else if (isPremul && (init == ImageInitToZero::PremulOnly || init == ImageInitToZero::FPAndPremul)) {
|
||||||
img.fill(0);
|
img.fill(Qt::black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief imageAlloc
|
||||||
|
* Helper function to initialize framework images.
|
||||||
|
* \param width The image width.
|
||||||
|
* \param height The image height.
|
||||||
|
* \param format The image format.
|
||||||
|
* \param init Whether and which images should be initialized to zero.
|
||||||
|
* \return The allocated image or a null image on error.
|
||||||
|
*/
|
||||||
inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &format, const ImageInitToZero& init = ImageInitToZero::None)
|
inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &format, const ImageInitToZero& init = ImageInitToZero::None)
|
||||||
{
|
{
|
||||||
return imageAlloc(QSize(width, height), format, init);
|
return imageAlloc(QSize(width, height), format, init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief checkImageSize
|
||||||
|
* Helper function to make sure the image size does not exceed the limit set in Qt.
|
||||||
|
* \param width The image width.
|
||||||
|
* \param height The image height.
|
||||||
|
* \param bytesPerPixel The number of bytes for each pixel of the image.
|
||||||
|
* \return True if the limit is respected, false otherwise.
|
||||||
|
*/
|
||||||
|
inline bool checkImageSize(qint32 width, qint32 height, qint32 bytesPerPixel)
|
||||||
|
{
|
||||||
|
size_t maxBytes = size_t(QImageReader::allocationLimit()) * 1024 * 1024;
|
||||||
|
if (maxBytes == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
size_t bytes = size_t(width) * height * bytesPerPixel;
|
||||||
|
return bytes <= maxBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief checkImageSize
|
||||||
|
* Helper function to make sure the image size does not exceed the limit set in Qt.
|
||||||
|
* \param size The image size.
|
||||||
|
* \param bytesPerPixel The number of bytes for each pixel of the image.
|
||||||
|
* \return True if the limit is respected, false otherwise.
|
||||||
|
*/
|
||||||
|
inline bool checkImageSize(const QSize& size, qint32 bytesPerPixel)
|
||||||
|
{
|
||||||
|
return checkImageSize(size.width(), size.height(), bytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
template<class TI, class SF> // SF = source FP, TI = target INT
|
template<class TI, class SF> // SF = source FP, TI = target INT
|
||||||
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
||||||
{
|
{
|
||||||
@@ -199,7 +239,7 @@ static QByteArray deviceRead(QIODevice *d, qint64 maxSize)
|
|||||||
return{};
|
return{};
|
||||||
}
|
}
|
||||||
|
|
||||||
const qint64 blockSize = 32 * 1024 * 1024;
|
const qint64 blockSize = 1024 * 1024;
|
||||||
auto devSize = d->isSequential() ? qint64() : d->size();
|
auto devSize = d->isSequential() ? qint64() : d->size();
|
||||||
|
|
||||||
if (devSize > 0) {
|
if (devSize > 0) {
|
||||||
|
|||||||
@@ -12,16 +12,15 @@
|
|||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QImageReader>
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStack>
|
#include <QStack>
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
|
|
||||||
#ifndef XCF_QT5_SUPPORT
|
// Float images can be disabled to reduce memory usage.
|
||||||
// Float images are not supported by Qt 5 and can be disabled in QT 6 to reduce memory usage.
|
// Unfortunately enabling/disabling this define results in slightly different images,
|
||||||
// Unfortunately enabling/disabling this define results in slightly different images, so leave the default if possible.
|
// so leave the default if possible.
|
||||||
#define USE_FLOAT_IMAGES // default uncommented
|
#define USE_FLOAT_IMAGES // default uncommented
|
||||||
|
|
||||||
// Let's set a "reasonable" maximum size
|
// Let's set a "reasonable" maximum size
|
||||||
@@ -31,11 +30,6 @@
|
|||||||
#ifndef XCF_MAX_IMAGE_HEIGHT
|
#ifndef XCF_MAX_IMAGE_HEIGHT
|
||||||
#define XCF_MAX_IMAGE_HEIGHT XCF_MAX_IMAGE_WIDTH
|
#define XCF_MAX_IMAGE_HEIGHT XCF_MAX_IMAGE_WIDTH
|
||||||
#endif
|
#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 XCF_MAX_IMAGE_WIDTH 32767
|
|
||||||
#define XCF_MAX_IMAGE_HEIGHT 32767
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_FLOAT_IMAGES
|
#ifdef USE_FLOAT_IMAGES
|
||||||
#include <qrgbafloat.h>
|
#include <qrgbafloat.h>
|
||||||
@@ -1384,20 +1378,14 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef XCF_QT5_SUPPORT
|
// The required memory to build the image is at least doubled because tiles are loaded
|
||||||
// Qt 6 image allocation limit calculation: we have to check the limit here because the image is split 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.
|
// 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
|
||||||
const qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
|
const qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
|
||||||
const int allocationLimit = QImageReader::allocationLimit();
|
if (!checkImageSize(layer.width, layer.height, channels * 2)) {
|
||||||
if (allocationLimit > 0) {
|
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit.";
|
||||||
if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > allocationLimit) {
|
return false;
|
||||||
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << allocationLimit << "megabytes";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
layer.image_tiles.resize(layer.nrows);
|
layer.image_tiles.resize(layer.nrows);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user