summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEirik Aavitsland <[email protected]>2024-10-08 17:29:57 +0200
committerEirik Aavitsland <[email protected]>2024-10-23 14:40:31 +0200
commit74487c313a96b45ad1afae06a365487be07fc908 (patch)
treee84ed0d6bbaa606ee6cf536699ae26df750841ef
parent6eefe0ca59dc25f7953b93e8b80c62c78a6ccfde (diff)
Avoid loading image format plugins until needed
Any image loading, even images with a normally built-in handler like png, would cause all image format plugins to be loaded. This would even happen during application initialization because of built-in style and cursor images etc. The reason was that QImageReader would first query all plugins for their support of the given file suffix or format name, in order to allow custom plugins to override the built-in format handlers. However, image format plugins are required to register the format names they support in the plugin metadata (the plugin json file). So in order to support the override scenario, it is enough to check the suffix/format against the plugins' metadata, and if a match is found, to load and query the matching plugin(s). This commit implements that change, moving the querying of all other plugins to be done only if the suffix/format matches none of the built-in formats. Fixes: QTBUG-129330 Change-Id: I799df9c340d4d86316de77bff32fdbd75caffd5f Reviewed-by: Allan Sandfeld Jensen <[email protected]>
-rw-r--r--src/gui/image/qimagereader.cpp119
-rw-r--r--tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp2
2 files changed, 48 insertions, 73 deletions
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index 1f85fef3bdc..c66ca724bbb 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -152,7 +152,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
QImageIOHandler *handler = nullptr;
QByteArray suffix;
-#ifndef QT_NO_IMAGEFORMATPLUGIN
+#if QT_CONFIG(imageformatplugin)
Q_CONSTINIT static QBasicMutex mutex;
const auto locker = qt_scoped_lock(mutex);
@@ -167,93 +167,44 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
<< keyMap.uniqueKeys().size() << "plugins available: " << keyMap;
#endif
- int suffixPluginIndex = -1;
-#endif // QT_NO_IMAGEFORMATPLUGIN
+ int testFormatPluginIndex = -1;
+#endif // QT_CONFIG(imageformatplugin)
if (device && format.isEmpty() && autoDetectImageFormat && !ignoresFormatAndExtension) {
- // if there's no format, see if \a device is a file, and if so, find
- // the file suffix and find support for that format among our plugins.
- // this allows plugins to override our built-in handlers.
+ // if there's no format, see if \a device is a file, and if so, find the file suffix
if (QFile *file = qobject_cast<QFile *>(device)) {
+ suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1();
#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: device is a file:" << file->fileName();
+ qDebug() << "QImageReader::createReadHandler: device file suffix:" << suffix;
#endif
- if (!(suffix = QFileInfo(file->fileName()).suffix().toLower().toLatin1()).isEmpty()) {
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- const int index = keyMap.key(QString::fromLatin1(suffix), -1);
- if (index != -1) {
-#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: suffix recognized; the"
- << suffix << "plugin might be able to read this";
-#endif
- suffixPluginIndex = index;
- }
-#endif // QT_NO_IMAGEFORMATPLUGIN
- }
}
}
QByteArray testFormat = !form.isEmpty() ? form : suffix;
-
if (ignoresFormatAndExtension)
testFormat = QByteArray();
-#ifndef QT_NO_IMAGEFORMATPLUGIN
- if (suffixPluginIndex != -1) {
- // check if the plugin that claims support for this format can load
- // from this device with this format.
+#if QT_CONFIG(imageformatplugin)
+ if (!testFormat.isEmpty()) {
+ // Check first support for the given format name or suffix among our plugins' registered
+ // formats. This allows plugins to override our built-in handlers.
const qint64 pos = device ? device->pos() : 0;
- const int index = keyMap.key(QString::fromLatin1(suffix), -1);
- if (index != -1) {
- QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(index));
+ for (int testIndex : keyMap.keys(QLatin1StringView(testFormat))) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(testIndex));
if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
- handler = plugin->create(device, testFormat);
-#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: using the" << suffix
- << "plugin";
-#endif
- }
- }
- if (device && !device->isSequential())
- device->seek(pos);
- }
-
- if (!handler && !testFormat.isEmpty() && !ignoresFormatAndExtension) {
- // check if any plugin supports the format (they are not allowed to
- // read from the device yet).
- const qint64 pos = device ? device->pos() : 0;
-
- if (autoDetectImageFormat) {
- const int keyCount = keyMap.size();
- for (int i = 0; i < keyCount; ++i) {
- if (i != suffixPluginIndex) {
- QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(i));
- if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
-#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: the" << keyMap.keys().at(i) << "plugin can read this format";
-#endif
- handler = plugin->create(device, testFormat);
- break;
- }
- }
- }
- } else {
- 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) {
#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: the" << testFormat << "plugin can read this format";
+ qDebug() << "QImageReader::createReadHandler: format" << testFormat
+ << "recognized, the" << keyMap.values(testIndex) << "plugin can read this format.";
#endif
- handler = plugin->create(device, testFormat);
- }
+ handler = plugin->create(device, testFormat);
+ testFormatPluginIndex = testIndex;
+ break;
}
}
if (device && !device->isSequential())
- device->seek(pos);
+ device->seek(pos); // Should not have moved, but guard against buggy plugins
}
-
-#endif // QT_NO_IMAGEFORMATPLUGIN
+#endif // QT_CONFIG(imageformatplugin)
// if we don't have a handler yet, check if we have built-in support for
// the format
@@ -292,6 +243,30 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
#endif
}
+#if QT_CONFIG(imageformatplugin)
+ if (!handler && !testFormat.isEmpty() && autoDetectImageFormat) {
+ // check if any other plugin supports the format name (they are not allowed to
+ // read from the device yet).
+ const qint64 pos = device ? device->pos() : 0;
+
+ const int keyCount = keyMap.size();
+ for (int i = 0; i < keyCount; ++i) {
+ if (i != testFormatPluginIndex) {
+ QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(i));
+ if (plugin && plugin->capabilities(device, testFormat) & QImageIOPlugin::CanRead) {
+#ifdef QIMAGEREADER_DEBUG
+ qDebug() << "QImageReader::createReadHandler: the" << keyMap.values(i) << "plugin can read this format";
+#endif
+ handler = plugin->create(device, testFormat);
+ break;
+ }
+ }
+ }
+ if (device && !device->isSequential())
+ device->seek(pos); // Should not have moved, but guard against buggy plugins
+ }
+#endif // QT_CONFIG(imageformatplugin)
+
if (handler && device && !suffix.isEmpty()) {
Q_ASSERT(qobject_cast<QFile *>(device));
// We have a file claiming to be of a recognized format. Now confirm that
@@ -315,18 +290,18 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
handler = nullptr;
}
-#ifndef QT_NO_IMAGEFORMATPLUGIN
+#if QT_CONFIG(imageformatplugin)
if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
// check if any of our plugins recognize the file from its contents.
const qint64 pos = device ? device->pos() : 0;
const int keyCount = keyMap.size();
for (int i = 0; i < keyCount; ++i) {
- if (i != suffixPluginIndex) {
+ if (i != testFormatPluginIndex) {
QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(l->instance(i));
if (plugin && plugin->capabilities(device, QByteArray()) & QImageIOPlugin::CanRead) {
handler = plugin->create(device, testFormat);
#ifdef QIMAGEREADER_DEBUG
- qDebug() << "QImageReader::createReadHandler: the" << keyMap.value(i) << "plugin can read this data";
+ qDebug() << "QImageReader::createReadHandler: the" << keyMap.values(i) << "plugin can read this data";
#endif
break;
}
@@ -335,7 +310,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device,
if (device && !device->isSequential())
device->seek(pos);
}
-#endif // QT_NO_IMAGEFORMATPLUGIN
+#endif // QT_CONFIG(imageformatplugin)
if (!handler && (autoDetectImageFormat || ignoresFormatAndExtension)) {
// check if any of our built-in handlers recognize the file from its
diff --git a/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp b/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp
index aefc427cac5..bc2181f54c8 100644
--- a/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp
+++ b/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp
@@ -89,7 +89,7 @@ void tst_QImageIOHandler::pluginRead_data()
QTest::newRow("testplugin") << "green" << "foo" << QStringList({ "formatname-matched" });
QTest::newRow("overridden-builtin") << "red" << "png" << QStringList({ "formatname-matched" });
- QTest::newRow("builtin") << "black" << "bmp" << QStringList({ "formatname-unmatched" }); //### Should be null
+ QTest::newRow("builtin") << "black" << "bmp" << QStringList();
QTest::newRow("no-suffix") << "blue" << "" << QStringList({ "contents-matched" });
QTest::newRow("wrong-suffix") << "yellow" << "jpg" << QStringList({ "contents-matched" });
QTest::newRow("unknown-suffix") << "black" << "bar" << QStringList({ "formatname-unmatched", "contents-unmatched" });