summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qimagereader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qimagereader.cpp')
-rw-r--r--src/gui/image/qimagereader.cpp188
1 files changed, 87 insertions, 101 deletions
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 8af36f911b..4a5bd6136b 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QIMAGEREADER_DEBUG
@@ -47,7 +11,6 @@
\inmodule QtGui
\reentrant
\ingroup painting
- \ingroup io
The most common way to read images is through QImage and QPixmap's
constructors, or by calling QImage::load() and
@@ -172,6 +135,10 @@
QT_BEGIN_NAMESPACE
using namespace QImageReaderWriterHelpers;
+using namespace Qt::StringLiterals;
+
+Q_TRACE_POINT(qtgui, QImageReader_read_before_reading, QImageReader *reader, const QString &filename);
+Q_TRACE_POINT(qtgui, QImageReader_read_after_reading, QImageReader *reader, bool result);
static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
const QByteArray &format,
@@ -186,7 +153,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
QByteArray suffix;
#ifndef QT_NO_IMAGEFORMATPLUGIN
- static QBasicMutex mutex;
+ Q_CONSTINIT static QBasicMutex mutex;
const auto locker = qt_scoped_lock(mutex);
typedef QMultiMap<int, QString> PluginKeyMap;
@@ -271,7 +238,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
}
}
} else {
- const int testIndex = keyMap.key(QLatin1String(testFormat), -1);
+ const int testIndex = keyMap.key(QLatin1StringView(testFormat), -1);
if (testIndex != -1) {
QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(testIndex));
if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
@@ -496,7 +463,7 @@ public:
static int maxAlloc;
};
-int QImageReaderPrivate::maxAlloc = 128; // 128 MB is enough for an 8K 32bpp image
+int QImageReaderPrivate::maxAlloc = 256; // 256 MB is enough for an 8K 64bpp image
/*!
\internal
@@ -519,9 +486,9 @@ QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq)
*/
QImageReaderPrivate::~QImageReaderPrivate()
{
+ delete handler;
if (deleteDevice)
delete device;
- delete handler;
}
/*!
@@ -529,6 +496,9 @@ QImageReaderPrivate::~QImageReaderPrivate()
*/
bool QImageReaderPrivate::initHandler()
{
+ if (handler)
+ return true;
+
// check some preconditions
if (!device || (!deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly))) {
imageReaderError = QImageReader::DeviceError;
@@ -561,8 +531,8 @@ bool QImageReaderPrivate::initHandler()
QString fileName = file->fileName();
do {
- file->setFileName(fileName + QLatin1Char('.')
- + QLatin1String(extensions.at(currentExtension++).constData()));
+ file->setFileName(fileName + u'.'
+ + QLatin1StringView(extensions.at(currentExtension++).constData()));
file->open(QIODevice::ReadOnly);
} while (!file->isOpen() && currentExtension < extensions.size());
@@ -575,7 +545,7 @@ bool QImageReaderPrivate::initHandler()
}
// assign a handler
- if (!handler && (handler = createReadHandlerHelper(device, format, autoDetectImageFormat, ignoresFormatAndExtension)) == nullptr) {
+ if ((handler = createReadHandlerHelper(device, format, autoDetectImageFormat, ignoresFormatAndExtension)) == nullptr) {
imageReaderError = QImageReader::UnsupportedFormatError;
errorString = QImageReader::tr("Unsupported image format");
return false;
@@ -588,7 +558,7 @@ bool QImageReaderPrivate::initHandler()
*/
void QImageReaderPrivate::getText()
{
- if (text.isEmpty() && (handler || initHandler()) && handler->supportsOption(QImageIOHandler::Description))
+ if (text.isEmpty() && q->supportsOption(QImageIOHandler::Description))
text = qt_getImageTextFromDescription(handler->option(QImageIOHandler::Description).toString());
}
@@ -778,12 +748,12 @@ bool QImageReader::decideFormatFromContent() const
*/
void QImageReader::setDevice(QIODevice *device)
{
+ delete d->handler;
+ d->handler = nullptr;
if (d->device && d->deleteDevice)
delete d->device;
d->device = device;
d->deleteDevice = false;
- delete d->handler;
- d->handler = nullptr;
d->text.clear();
}
@@ -878,10 +848,7 @@ int QImageReader::quality() const
*/
QSize QImageReader::size() const
{
- if (!d->initHandler())
- return QSize();
-
- if (d->handler->supportsOption(QImageIOHandler::Size))
+ if (supportsOption(QImageIOHandler::Size))
return d->handler->option(QImageIOHandler::Size).toSize();
return QSize();
@@ -901,10 +868,7 @@ QSize QImageReader::size() const
*/
QImage::Format QImageReader::imageFormat() const
{
- if (!d->initHandler())
- return QImage::Format_Invalid;
-
- if (d->handler->supportsOption(QImageIOHandler::ImageFormat))
+ if (supportsOption(QImageIOHandler::ImageFormat))
return (QImage::Format)d->handler->option(QImageIOHandler::ImageFormat).toInt();
return QImage::Format_Invalid;
@@ -976,6 +940,10 @@ QRect QImageReader::clipRect() const
support scaling), QImageReader will use QImage::scale() with
Qt::SmoothScaling.
+ If only one dimension is set in \a size, the other one will be
+ computed from the image's \l {size()} {natural size} so as to
+ maintain the aspect ratio.
+
\sa scaledSize(), setClipRect(), setScaledClipRect()
*/
void QImageReader::setScaledSize(const QSize &size)
@@ -1026,9 +994,7 @@ QRect QImageReader::scaledClipRect() const
*/
void QImageReader::setBackgroundColor(const QColor &color)
{
- if (!d->initHandler())
- return;
- if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ if (supportsOption(QImageIOHandler::BackgroundColor))
d->handler->setOption(QImageIOHandler::BackgroundColor, color);
}
@@ -1043,9 +1009,7 @@ void QImageReader::setBackgroundColor(const QColor &color)
*/
QColor QImageReader::backgroundColor() const
{
- if (!d->initHandler())
- return QColor();
- if (d->handler->supportsOption(QImageIOHandler::BackgroundColor))
+ if (supportsOption(QImageIOHandler::BackgroundColor))
return qvariant_cast<QColor>(d->handler->option(QImageIOHandler::BackgroundColor));
return QColor();
}
@@ -1060,9 +1024,7 @@ QColor QImageReader::backgroundColor() const
*/
bool QImageReader::supportsAnimation() const
{
- if (!d->initHandler())
- return false;
- if (d->handler->supportsOption(QImageIOHandler::Animation))
+ if (supportsOption(QImageIOHandler::Animation))
return d->handler->option(QImageIOHandler::Animation).toBool();
return false;
}
@@ -1074,10 +1036,7 @@ bool QImageReader::supportsAnimation() const
*/
QByteArray QImageReader::subType() const
{
- if (!d->initHandler())
- return QByteArray();
-
- if (d->handler->supportsOption(QImageIOHandler::SubType))
+ if (supportsOption(QImageIOHandler::SubType))
return d->handler->option(QImageIOHandler::SubType).toByteArray();
return QByteArray();
}
@@ -1089,10 +1048,7 @@ QByteArray QImageReader::subType() const
*/
QList<QByteArray> QImageReader::supportedSubTypes() const
{
- if (!d->initHandler())
- return QList<QByteArray>();
-
- if (d->handler->supportsOption(QImageIOHandler::SupportedSubTypes))
+ if (supportsOption(QImageIOHandler::SupportedSubTypes))
return qvariant_cast<QList<QByteArray> >(d->handler->option(QImageIOHandler::SupportedSubTypes));
return QList<QByteArray>();
}
@@ -1108,7 +1064,7 @@ QList<QByteArray> QImageReader::supportedSubTypes() const
QImageIOHandler::Transformations QImageReader::transformation() const
{
int option = QImageIOHandler::TransformationNone;
- if (d->initHandler() && d->handler->supportsOption(QImageIOHandler::ImageTransformation))
+ if (supportsOption(QImageIOHandler::ImageTransformation))
option = d->handler->option(QImageIOHandler::ImageTransformation).toInt();
return QImageIOHandler::Transformations(option);
}
@@ -1223,31 +1179,48 @@ bool QImageReader::read(QImage *image)
return false;
}
- if (!d->handler && !d->initHandler())
+ if (!d->initHandler())
return false;
+ QSize scaledSize = d->scaledSize;
+ if ((scaledSize.width() <= 0 && scaledSize.height() > 0) ||
+ (scaledSize.height() <= 0 && scaledSize.width() > 0)) {
+ // if only one dimension is given, let's try to calculate the second one
+ // based on the original image size and maintaining the aspect ratio
+ if (const QSize originalSize = size(); !originalSize.isEmpty()) {
+ if (scaledSize.width() <= 0) {
+ const auto ratio = qreal(scaledSize.height()) / originalSize.height();
+ scaledSize.setWidth(qRound(originalSize.width() * ratio));
+ } else {
+ const auto ratio = qreal(scaledSize.width()) / originalSize.width();
+ scaledSize.setHeight(qRound(originalSize.height() * ratio));
+ }
+ }
+ }
+
+ const bool supportScaledSize = supportsOption(QImageIOHandler::ScaledSize) && scaledSize.isValid();
+ const bool supportClipRect = supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull();
+ const bool supportScaledClipRect = supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull();
+
// set the handler specific options.
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
- if ((d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
- || d->clipRect.isNull()) {
+ if (supportScaledSize) {
+ if (supportClipRect || d->clipRect.isNull()) {
// Only enable the ScaledSize option if there is no clip rect, or
// if the handler also supports ClipRect.
- d->handler->setOption(QImageIOHandler::ScaledSize, d->scaledSize);
+ d->handler->setOption(QImageIOHandler::ScaledSize, scaledSize);
}
}
- if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull())
+ if (supportClipRect)
d->handler->setOption(QImageIOHandler::ClipRect, d->clipRect);
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull())
+ if (supportScaledClipRect)
d->handler->setOption(QImageIOHandler::ScaledClipRect, d->scaledClipRect);
- if (d->handler->supportsOption(QImageIOHandler::Quality))
+ if (supportsOption(QImageIOHandler::Quality))
d->handler->setOption(QImageIOHandler::Quality, d->quality);
// read the image
+ QString filename = fileName();
if (Q_TRACE_ENABLED(QImageReader_read_before_reading)) {
- QString fileName = QStringLiteral("unknown");
- if (QFile *file = qobject_cast<QFile *>(d->device))
- fileName = file->fileName();
- Q_TRACE(QImageReader_read_before_reading, this, fileName);
+ Q_TRACE(QImageReader_read_before_reading, this, filename.isEmpty() ? u"unknown"_s : filename);
}
const bool result = d->handler->read(image);
@@ -1262,9 +1235,9 @@ bool QImageReader::read(QImage *image)
// provide default implementations for any unsupported image
// options
- if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportClipRect) {
+ if (supportScaledSize) {
+ if (supportScaledClipRect) {
// all features are supported by the handler; nothing to do.
} else {
// the image is already scaled, so apply scaled clipping.
@@ -1272,12 +1245,12 @@ bool QImageReader::read(QImage *image)
*image = image->copy(d->scaledClipRect);
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledClipRect) {
// supports scaled clipping but not scaling, most
// likely a broken handler.
} else {
- if (d->scaledSize.isValid()) {
- *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (scaledSize.isValid()) {
+ *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
if (d->scaledClipRect.isValid()) {
*image = image->copy(d->scaledClipRect);
@@ -1285,8 +1258,8 @@ bool QImageReader::read(QImage *image)
}
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid() && d->clipRect.isNull()) {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledSize && d->clipRect.isNull()) {
+ if (supportScaledClipRect) {
// nothing to do (ClipRect is ignored!)
} else {
// provide all workarounds.
@@ -1295,7 +1268,7 @@ bool QImageReader::read(QImage *image)
}
}
} else {
- if (d->handler->supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull()) {
+ if (supportScaledClipRect) {
// this makes no sense; a handler that supports
// ScaledClipRect but not ScaledSize is broken, and we
// can't work around it.
@@ -1303,8 +1276,8 @@ bool QImageReader::read(QImage *image)
// provide all workarounds.
if (d->clipRect.isValid())
*image = image->copy(d->clipRect);
- if (d->scaledSize.isValid())
- *image = image->scaled(d->scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (scaledSize.isValid())
+ *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (d->scaledClipRect.isValid())
*image = image->copy(d->scaledClipRect);
}
@@ -1314,8 +1287,8 @@ bool QImageReader::read(QImage *image)
// successful read; check for "@Nx" file name suffix and set device pixel ratio.
static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
if (!disableNxImageLoading) {
- const QByteArray suffix = QFileInfo(fileName()).baseName().right(3).toLatin1();
- if (suffix.length() == 3 && suffix[0] == '@' && suffix[1] >= '2' && suffix[1] <= '9' && suffix[2] == 'x')
+ const QByteArray suffix = QFileInfo(filename).baseName().right(3).toLatin1();
+ if (suffix.size() == 3 && suffix[0] == '@' && suffix[1] >= '2' && suffix[1] <= '9' && suffix[2] == 'x')
image->setDevicePixelRatio(suffix[1] - '0');
}
if (autoTransform())
@@ -1584,7 +1557,13 @@ QList<QByteArray> QImageReader::imageFormatsForMimeType(const QByteArray &mimeTy
*/
int QImageReader::allocationLimit()
{
- return QImageReaderPrivate::maxAlloc;
+ static int envLimit = []() {
+ bool ok = false;
+ int res = qEnvironmentVariableIntValue("QT_IMAGEIO_MAXALLOC", &ok);
+ return ok ? res : -1;
+ }();
+
+ return envLimit >= 0 ? envLimit : QImageReaderPrivate::maxAlloc;
}
/*!
@@ -1592,11 +1571,18 @@ int QImageReader::allocationLimit()
Sets the allocation limit to \a mbLimit megabytes. Images that would
require a QImage memory allocation above this limit will be rejected.
+ If \a mbLimit is 0, the allocation size check will be disabled.
This limit helps applications avoid unexpectedly large memory usage from
loading corrupt image files. It is normally not needed to change it. The
default limit is large enough for all commonly used image sizes.
+ At runtime, this value may be overridden by the environment variable \c QT_IMAGEIO_MAXALLOC.
+
+ \note The memory requirements are calculated for a minimum of 32 bits per pixel, since Qt will
+ typically convert an image to that depth when it is used in GUI. This means that the effective
+ allocation limit is significantly smaller than \a mbLimit when reading 1 bpp and 8 bpp images.
+
\sa allocationLimit()
*/
void QImageReader::setAllocationLimit(int mbLimit)