File indexing completed on 2024-05-12 05:04:17
0001 // SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de> 0002 // SPDX-License-Identifier: LGPL-2.0-or-later 0003 0004 #include "blurhashimageprovider.h" 0005 0006 #include "blurhash.hpp" 0007 0008 /* 0009 * Qt unfortunately re-encodes the base83 string in QML. 0010 * The only special ASCII characters used in the blurhash base83 string are: 0011 * #$%*+,-.:;=?@[]^_{|}~ 0012 * QUrl::fromPercentEncoding is too greedy, and spits out invalid characters 0013 * for parts of valid base83 like %14. 0014 */ 0015 // clang-format off 0016 static const QMap<QLatin1String, QLatin1String> knownEncodings = { 0017 {QLatin1String("%23A"), QLatin1String(":")}, 0018 {QLatin1String("%3F"), QLatin1String("?")}, 0019 {QLatin1String("%23"), QLatin1String("#")}, 0020 {QLatin1String("%5B"), QLatin1String("[")}, 0021 {QLatin1String("%5D"), QLatin1String("]")}, 0022 {QLatin1String("%40"), QLatin1String("@")}, 0023 {QLatin1String("%24"), QLatin1String("$")}, 0024 {QLatin1String("%2A"), QLatin1String("*")}, 0025 {QLatin1String("%2B"), QLatin1String("+")}, 0026 {QLatin1String("%2C"), QLatin1String(",")}, 0027 {QLatin1String("%2D"), QLatin1String("-")}, 0028 {QLatin1String("%2E"), QLatin1String(".")}, 0029 {QLatin1String("%3D"), QLatin1String("=")}, 0030 {QLatin1String("%25"), QLatin1String("%")}, 0031 {QLatin1String("%5E"), QLatin1String("^")}, 0032 {QLatin1String("%7C"), QLatin1String("|")}, 0033 {QLatin1String("%7B"), QLatin1String("{")}, 0034 {QLatin1String("%7D"), QLatin1String("}")}, 0035 {QLatin1String("%7E"), QLatin1String("~")}, 0036 }; 0037 // clang-format on 0038 0039 class AsyncImageResponseRunnable : public QObject, public QRunnable 0040 { 0041 Q_OBJECT 0042 0043 Q_SIGNALS: 0044 void done(QImage image); 0045 0046 public: 0047 AsyncImageResponseRunnable(const QString &id, const QSize &requestedSize) 0048 : m_id(id) 0049 , m_requestedSize(requestedSize) 0050 { 0051 if (m_requestedSize.width() == -1) { 0052 m_requestedSize.setWidth(64); 0053 } 0054 if (m_requestedSize.height() == -1) { 0055 m_requestedSize.setHeight(64); 0056 } 0057 } 0058 0059 void run() override 0060 { 0061 if (m_id.isEmpty()) { 0062 return; 0063 } 0064 0065 QString decodedId = m_id; 0066 0067 QMap<QLatin1String, QLatin1String>::const_iterator i; 0068 for (i = knownEncodings.constBegin(); i != knownEncodings.constEnd(); ++i) 0069 decodedId.replace(i.key(), i.value()); 0070 0071 auto data = blurhash::decode(decodedId.toLatin1().constData(), m_requestedSize.width(), m_requestedSize.height()); 0072 QImage image(data.image.data(), static_cast<int>(data.width), static_cast<int>(data.height), static_cast<int>(data.width * 3), QImage::Format_RGB888); 0073 0074 Q_EMIT done(image.convertToFormat(QImage::Format_RGB32)); 0075 } 0076 0077 private: 0078 QString m_id; 0079 QSize m_requestedSize; 0080 }; 0081 0082 AsyncImageResponse::AsyncImageResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool) 0083 { 0084 auto runnable = new AsyncImageResponseRunnable(id, requestedSize); 0085 connect(runnable, &AsyncImageResponseRunnable::done, this, &AsyncImageResponse::handleDone); 0086 pool->start(runnable); 0087 } 0088 0089 void AsyncImageResponse::handleDone(QImage image) 0090 { 0091 m_image = std::move(image); 0092 Q_EMIT finished(); 0093 } 0094 0095 QQuickTextureFactory *AsyncImageResponse::textureFactory() const 0096 { 0097 return QQuickTextureFactory::textureFactoryForImage(m_image); 0098 } 0099 0100 QQuickImageResponse *BlurhashImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) 0101 { 0102 return new AsyncImageResponse(id, requestedSize, &pool); 0103 } 0104 0105 #include "blurhashimageprovider.moc"