145 bool autoDetectImageFormat,
146 bool ignoresFormatAndExtension)
148 if (!autoDetectImageFormat && format.isEmpty())
152 QImageIOHandler *handler =
nullptr;
155 qCDebug(lcImageReader) <<
"Finding read handler for" << device <<
"and format" << format;
157#if QT_CONFIG(imageformatplugin)
158 Q_CONSTINIT
static QBasicMutex mutex;
159 const auto locker = qt_scoped_lock(mutex);
161 typedef QMultiMap<
int, QString> PluginKeyMap;
164 auto l = QImageReaderWriterHelpers::pluginLoader();
165 const PluginKeyMap keyMap = l->keyMap();
167 qCDebug(lcImageReader) << keyMap.uniqueKeys().size() <<
"plugins available:" << keyMap.values();
169 int testFormatPluginIndex = -1;
172 if (device && format.isEmpty() && autoDetectImageFormat && !ignoresFormatAndExtension) {
174 if (QFile *file = qobject_cast<QFile *>(device)) {
175 suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
176 qCDebug(lcImageReader) <<
"Resolved format" << suffix <<
"from file name suffix";
180 QByteArray testFormat = !form.isEmpty() ? form : suffix;
181 if (ignoresFormatAndExtension)
184#if QT_CONFIG(imageformatplugin)
185 if (!testFormat.isEmpty()) {
188 qCDebug(lcImageReader) <<
"Checking if any plugins have explicitly declared support"
189 <<
"for the format" << testFormat;
190 const qint64 pos = device ? device->pos() : 0;
191 for (
int testIndex : keyMap.keys(QLatin1StringView(testFormat))) {
192 QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(testIndex));
193 if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
194 qCDebug(lcImageReader) << plugin <<
"can read the format" << testFormat;
195 handler = plugin->create(device, testFormat);
196 testFormatPluginIndex = testIndex;
200 if (device && !device->isSequential())
207 if (!handler && !testFormat.isEmpty()) {
208 qCDebug(lcImageReader) <<
"Checking if any built in handlers recognize the format"
211#ifndef QT_NO_IMAGEFORMAT_PNG
212 }
else if (testFormat ==
"png") {
213 handler =
new QPngHandler;
215#ifndef QT_NO_IMAGEFORMAT_BMP
216 }
else if (testFormat ==
"bmp") {
217 handler =
new QBmpHandler;
218 }
else if (testFormat ==
"dib") {
219 handler =
new QBmpHandler(QBmpHandler::DibFormat);
221#ifndef QT_NO_IMAGEFORMAT_XPM
222 }
else if (testFormat ==
"xpm") {
223 handler =
new QXpmHandler;
225#ifndef QT_NO_IMAGEFORMAT_XBM
226 }
else if (testFormat ==
"xbm") {
227 handler =
new QXbmHandler;
228 handler->setOption(QImageIOHandler::SubType, testFormat);
230#ifndef QT_NO_IMAGEFORMAT_PPM
231 }
else if (testFormat ==
"pbm" || testFormat ==
"pbmraw" || testFormat ==
"pgm"
232 || testFormat ==
"pgmraw" || testFormat ==
"ppm" || testFormat ==
"ppmraw") {
233 handler =
new QPpmHandler;
234 handler->setOption(QImageIOHandler::SubType, testFormat);
239 qCDebug(lcImageReader) <<
"Using the built-in handler for format" << testFormat;
242#if QT_CONFIG(imageformatplugin)
243 if (!handler && !testFormat.isEmpty() && autoDetectImageFormat) {
246 qCDebug(lcImageReader) <<
"Checking if any plugins recognize the format" << testFormat;
248 const qint64 pos = device ? device->pos() : 0;
250 const int keyCount = keyMap.size();
251 for (
int i = 0; i < keyCount; ++i) {
252 if (i != testFormatPluginIndex) {
253 QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(i));
254 if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
255 qCDebug(lcImageReader) << plugin <<
"can read the format" << testFormat;
256 handler = plugin->create(device, testFormat);
261 if (device && !device->isSequential())
266 if (handler && device && !suffix.isEmpty()) {
267 Q_ASSERT(qobject_cast<QFile *>(device));
270 const qint64 pos = device->pos();
271 handler->setDevice(device);
273 handler->setFormat(form);
274 bool canRead = handler->canRead();
280 qCDebug(lcImageReader) << handler <<
"claimed support for" << suffix
281 <<
"but could not read the file";
288#if QT_CONFIG(imageformatplugin)
289 if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
291 qCDebug(lcImageReader) <<
"Checking if any plugins recognize the format"
292 <<
"based on the contents in" << device;
293 const qint64 pos = device ? device->pos() : 0;
294 const int keyCount = keyMap.size();
295 for (
int i = 0; i < keyCount; ++i) {
296 if (i != testFormatPluginIndex) {
297 QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(i));
298 if (plugin && plugin->capabilities(device, QByteArray()) & QImageIOPlugin::CanRead) {
299 handler = plugin->create(device, testFormat);
300 qCDebug(lcImageReader) << plugin <<
"can read the data in" << device;
305 if (device && !device->isSequential())
310 if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
313 qCDebug(lcImageReader) <<
"Checking if any built in handlers recognize the format"
314 <<
"based on the contents in" << device;
315 int currentFormat = 0;
316 if (!suffix.isEmpty()) {
319 for (
int i = 0; i < _qt_NumFormats; ++i) {
320 if (_qt_BuiltInFormats[i].extension == suffix) {
328 int numFormats = _qt_NumFormats;
329 while (device && numFormats >= 0) {
330 const qint64 pos = device->pos();
331 switch (currentFormat) {
332#ifndef QT_NO_IMAGEFORMAT_PNG
334 if (QPngHandler::canRead(device))
335 handler =
new QPngHandler;
338#ifndef QT_NO_IMAGEFORMAT_BMP
340 if (QBmpHandler::canRead(device))
341 handler =
new QBmpHandler;
344#ifndef QT_NO_IMAGEFORMAT_XPM
346 if (QXpmHandler::canRead(device))
347 handler =
new QXpmHandler;
350#ifndef QT_NO_IMAGEFORMAT_PPM
354 if (QPpmHandler::canRead(device, &subType)) {
355 handler =
new QPpmHandler;
356 handler->setOption(QImageIOHandler::SubType, subType);
360#ifndef QT_NO_IMAGEFORMAT_XBM
362 if (QXbmHandler::canRead(device))
363 handler =
new QXbmHandler;
369 if (!device->isSequential())
373 qCDebug(lcImageReader,
"The %s built-in handler can read this data",
374 _qt_BuiltInFormats[currentFormat].extension);
380 if (currentFormat >= _qt_NumFormats)
386 qCDebug(lcImageReader,
"No handlers found. Giving up.");
391 handler->setDevice(device);
393 handler->setFormat(form);
471 if (!device || (!deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly))) {
472 imageReaderError = QImageReader::DeviceError;
473 errorString = QImageReader::tr(
"Invalid device");
478 if (deleteDevice && !device->isOpen() && !device->open(QIODevice::ReadOnly) && autoDetectImageFormat) {
479 Q_ASSERT(qobject_cast<QFile*>(device) !=
nullptr);
480 QFile *file =
static_cast<QFile *>(device);
482 if (file->error() == QFileDevice::ResourceError) {
484 imageReaderError = QImageReader::DeviceError;
485 errorString = file->errorString();
490 if (!format.isEmpty()) {
492 int currentFormatIndex = extensions.indexOf(format.toLower());
493 if (currentFormatIndex > 0)
494 extensions.swapItemsAt(0, currentFormatIndex);
497 int currentExtension = 0;
499 QString fileName = file->fileName();
503 file->setFileName(fileName + u'.'
504 + QLatin1StringView(extensions.at(currentExtension++).constData()));
505 fileIsOpen = file->open(QIODevice::ReadOnly);
506 }
while (!fileIsOpen && currentExtension < extensions.size());
509 imageReaderError = QImageReader::FileNotFoundError;
510 errorString = QImageReader::tr(
"File not found");
511 file->setFileName(fileName);
517 if ((handler = createReadHandlerHelper(device, format, autoDetectImageFormat, ignoresFormatAndExtension)) ==
nullptr) {
518 imageReaderError = QImageReader::UnsupportedFormatError;
519 errorString = QImageReader::tr(
"Unsupported image format");
1128bool QImageReader::read(QImage *image)
1131 qWarning(
"QImageReader::read: cannot read into null pointer");
1135 if (!d->initHandler())
1138 QSize scaledSize = d->scaledSize;
1139 if ((scaledSize.width() <= 0 && scaledSize.height() > 0) ||
1140 (scaledSize.height() <= 0 && scaledSize.width() > 0)) {
1143 if (
const QSize originalSize = size(); !originalSize.isEmpty()) {
1144 if (scaledSize.width() <= 0) {
1145 const auto ratio = qreal(scaledSize.height()) / originalSize.height();
1146 scaledSize.setWidth(qRound(originalSize.width() * ratio));
1148 const auto ratio = qreal(scaledSize.width()) / originalSize.width();
1149 scaledSize.setHeight(qRound(originalSize.height() * ratio));
1154 const bool supportScaledSize = supportsOption(QImageIOHandler::ScaledSize) && scaledSize.isValid();
1155 const bool supportClipRect = supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull();
1156 const bool supportScaledClipRect = supportsOption(QImageIOHandler::ScaledClipRect) && !d->scaledClipRect.isNull();
1159 if (supportScaledSize) {
1160 if (supportClipRect || d->clipRect.isNull()) {
1163 d->handler->setOption(QImageIOHandler::ScaledSize, scaledSize);
1166 if (supportClipRect)
1167 d->handler->setOption(QImageIOHandler::ClipRect, d->clipRect);
1168 if (supportScaledClipRect)
1169 d->handler->setOption(QImageIOHandler::ScaledClipRect, d->scaledClipRect);
1170 if (supportsOption(QImageIOHandler::Quality))
1171 d->handler->setOption(QImageIOHandler::Quality, d->quality);
1174 QString filename = fileName();
1175 if (Q_TRACE_ENABLED(QImageReader_read_before_reading)) {
1176 Q_TRACE(QImageReader_read_before_reading,
this, filename.isEmpty() ? u"unknown"_s : filename);
1179 const bool result = d->handler->read(image);
1181 Q_TRACE(QImageReader_read_after_reading,
this, result);
1184 d->imageReaderError = InvalidDataError;
1185 d->errorString = QImageReader::tr(
"Unable to read image data");
1191 if (supportClipRect) {
1192 if (supportScaledSize) {
1193 if (supportScaledClipRect) {
1197 if (!d->scaledClipRect.isNull())
1198 *image = image->copy(d->scaledClipRect);
1201 if (supportScaledClipRect) {
1205 if (scaledSize.isValid()) {
1206 *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1208 if (d->scaledClipRect.isValid()) {
1209 *image = image->copy(d->scaledClipRect);
1214 if (supportScaledSize && d->clipRect.isNull()) {
1215 if (supportScaledClipRect) {
1219 if (d->scaledClipRect.isValid()) {
1220 *image = image->copy(d->scaledClipRect);
1224 if (supportScaledClipRect) {
1230 if (d->clipRect.isValid())
1231 *image = image->copy(d->clipRect);
1232 if (scaledSize.isValid())
1233 *image = image->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1234 if (d->scaledClipRect.isValid())
1235 *image = image->copy(d->scaledClipRect);
1241 static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty(
"QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
1242 if (!disableNxImageLoading) {
1243 const QByteArray suffix = QFileInfo(filename).baseName().right(3).toLatin1();
1244 if (suffix.size() == 3 && suffix[0] ==
'@' && suffix[1] >=
'2' && suffix[1] <=
'9' && suffix[2] ==
'x')
1245 image->setDevicePixelRatio(suffix[1] -
'0');
1247 if (autoTransform())
1248 qt_imageTransform(*image, transformation());