| 1 | // Copyright (C) 2018 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "private/qimagereaderwriterhelpers_p.h" |
| 5 | |
| 6 | #include <qcborarray.h> |
| 7 | #include <qmutex.h> |
| 8 | #include <private/qfactoryloader_p.h> |
| 9 | |
| 10 | QT_BEGIN_NAMESPACE |
| 11 | |
| 12 | using namespace Qt::StringLiterals; |
| 13 | |
| 14 | namespace QImageReaderWriterHelpers { |
| 15 | |
| 16 | #ifndef QT_NO_IMAGEFORMATPLUGIN |
| 17 | |
| 18 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, irhLoader, |
| 19 | (QImageIOHandlerFactoryInterface_iid, "/imageformats"_L1 )) |
| 20 | Q_GLOBAL_STATIC(QMutex, irhLoaderMutex) |
| 21 | |
| 22 | static void appendImagePluginFormats(QFactoryLoader *loader, |
| 23 | QImageIOPlugin::Capability cap, |
| 24 | QList<QByteArray> *result) |
| 25 | { |
| 26 | typedef QMultiMap<int, QString> PluginKeyMap; |
| 27 | typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator; |
| 28 | |
| 29 | const PluginKeyMap keyMap = loader->keyMap(); |
| 30 | const PluginKeyMapConstIterator cend = keyMap.constEnd(); |
| 31 | int i = -1; |
| 32 | QImageIOPlugin *plugin = nullptr; |
| 33 | result->reserve(asize: result->size() + keyMap.size()); |
| 34 | for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) { |
| 35 | if (it.key() != i) { |
| 36 | i = it.key(); |
| 37 | plugin = qobject_cast<QImageIOPlugin *>(object: loader->instance(index: i)); |
| 38 | } |
| 39 | const QByteArray key = it.value().toLatin1(); |
| 40 | if (plugin && (plugin->capabilities(device: nullptr, format: key) & cap) != 0) |
| 41 | result->append(t: key); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | static void appendImagePluginMimeTypes(QFactoryLoader *loader, |
| 46 | QImageIOPlugin::Capability cap, |
| 47 | QList<QByteArray> *result, |
| 48 | QList<QByteArray> *resultKeys = nullptr) |
| 49 | { |
| 50 | QList<QPluginParsedMetaData> metaDataList = loader->metaData(); |
| 51 | const int pluginCount = metaDataList.size(); |
| 52 | for (int i = 0; i < pluginCount; ++i) { |
| 53 | const QCborMap metaData = metaDataList.at(i).value(k: QtPluginMetaDataKeys::MetaData).toMap(); |
| 54 | const QCborArray keys = metaData.value(key: "Keys"_L1 ).toArray(); |
| 55 | const QCborArray mimeTypes = metaData.value(key: "MimeTypes"_L1 ).toArray(); |
| 56 | QImageIOPlugin *plugin = qobject_cast<QImageIOPlugin *>(object: loader->instance(index: i)); |
| 57 | const int keyCount = keys.size(); |
| 58 | for (int k = 0; k < keyCount; ++k) { |
| 59 | const QByteArray key = keys.at(i: k).toString().toLatin1(); |
| 60 | if (plugin && (plugin->capabilities(device: nullptr, format: key) & cap) != 0) { |
| 61 | result->append(t: mimeTypes.at(i: k).toString().toLatin1()); |
| 62 | if (resultKeys) |
| 63 | resultKeys->append(t: key); |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | QSharedPointer<QFactoryLoader> pluginLoader() |
| 70 | { |
| 71 | irhLoaderMutex()->lock(); |
| 72 | return QSharedPointer<QFactoryLoader>(irhLoader(), [](QFactoryLoader *) { |
| 73 | irhLoaderMutex()->unlock(); |
| 74 | }); |
| 75 | } |
| 76 | |
| 77 | static inline QImageIOPlugin::Capability pluginCapability(Capability cap) |
| 78 | { |
| 79 | return cap == CanRead ? QImageIOPlugin::CanRead : QImageIOPlugin::CanWrite; |
| 80 | } |
| 81 | |
| 82 | #endif // QT_NO_IMAGEFORMATPLUGIN |
| 83 | |
| 84 | QList<QByteArray> supportedImageFormats(Capability cap) |
| 85 | { |
| 86 | QList<QByteArray> formats; |
| 87 | formats.reserve(asize: _qt_NumFormats); |
| 88 | for (int i = 0; i < _qt_NumFormats; ++i) |
| 89 | formats << _qt_BuiltInFormats[i].extension; |
| 90 | |
| 91 | #ifndef QT_NO_IMAGEFORMATPLUGIN |
| 92 | appendImagePluginFormats(loader: irhLoader(), cap: pluginCapability(cap), result: &formats); |
| 93 | #endif // QT_NO_IMAGEFORMATPLUGIN |
| 94 | |
| 95 | std::sort(first: formats.begin(), last: formats.end()); |
| 96 | formats.erase(abegin: std::unique(first: formats.begin(), last: formats.end()), aend: formats.end()); |
| 97 | return formats; |
| 98 | } |
| 99 | |
| 100 | static constexpr QByteArrayView imagePrefix() noexcept { return "image/" ; } |
| 101 | |
| 102 | QList<QByteArray> supportedMimeTypes(Capability cap) |
| 103 | { |
| 104 | QList<QByteArray> mimeTypes; |
| 105 | mimeTypes.reserve(asize: _qt_NumFormats); |
| 106 | for (const auto &fmt : _qt_BuiltInFormats) |
| 107 | mimeTypes.emplace_back(args: imagePrefix() + fmt.mimeType); |
| 108 | |
| 109 | #ifndef QT_NO_IMAGEFORMATPLUGIN |
| 110 | appendImagePluginMimeTypes(loader: irhLoader(), cap: pluginCapability(cap), result: &mimeTypes); |
| 111 | #endif // QT_NO_IMAGEFORMATPLUGIN |
| 112 | |
| 113 | std::sort(first: mimeTypes.begin(), last: mimeTypes.end()); |
| 114 | mimeTypes.erase(abegin: std::unique(first: mimeTypes.begin(), last: mimeTypes.end()), aend: mimeTypes.end()); |
| 115 | return mimeTypes; |
| 116 | } |
| 117 | |
| 118 | QList<QByteArray> imageFormatsForMimeType(QByteArrayView mimeType, Capability cap) |
| 119 | { |
| 120 | QList<QByteArray> formats; |
| 121 | if (mimeType.startsWith(other: imagePrefix())) { |
| 122 | const QByteArrayView type = mimeType.mid(pos: imagePrefix().size()); |
| 123 | for (const auto &fmt : _qt_BuiltInFormats) { |
| 124 | if (fmt.mimeType == type && !formats.contains(t: fmt.extension)) |
| 125 | formats << fmt.extension; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | #ifndef QT_NO_IMAGEFORMATPLUGIN |
| 130 | QList<QByteArray> mimeTypes; |
| 131 | QList<QByteArray> keys; |
| 132 | appendImagePluginMimeTypes(loader: irhLoader(), cap: pluginCapability(cap), result: &mimeTypes, resultKeys: &keys); |
| 133 | for (int i = 0; i < mimeTypes.size(); ++i) { |
| 134 | if (mimeTypes.at(i) == mimeType) { |
| 135 | const auto &key = keys.at(i); |
| 136 | if (!formats.contains(t: key)) |
| 137 | formats << key; |
| 138 | } |
| 139 | } |
| 140 | #endif // QT_NO_IMAGEFORMATPLUGIN |
| 141 | |
| 142 | return formats; |
| 143 | } |
| 144 | |
| 145 | } // QImageReaderWriterHelpers |
| 146 | |
| 147 | QT_END_NAMESPACE |
| 148 | |