diff --git a/helper/thumbnailer.cpp b/helper/thumbnailer.cpp index b3ae614..ba7c2b2 100644 --- a/helper/thumbnailer.cpp +++ b/helper/thumbnailer.cpp @@ -21,6 +21,8 @@ #include +#include "thumbnailerjob.h" + #include #include @@ -34,20 +36,35 @@ AsyncImageResponse::AsyncImageResponse(const QString &id, const QSize &requested : m_id(id) , m_requestedSize(requestedSize) { - QStringList plugins = KIO::PreviewJob::defaultPlugins(); - auto job = new KIO::PreviewJob(KFileItemList() << KFileItem(QUrl::fromUserInput(id)), requestedSize, &plugins); - - connect(job, &KIO::PreviewJob::gotPreview, [this](KFileItem, QPixmap pixmap) { + auto job_ = new ThumbnailerJob(QUrl::fromUserInput(id).toLocalFile(), requestedSize); + connect(job_, &ThumbnailerJob::gotPreview, [this] (QPixmap pixmap) { m_image = pixmap.toImage(); emit this->finished(); }); - connect(job, &KIO::PreviewJob::failed, [this](KFileItem) { + connect(job_, &ThumbnailerJob::failed, [this] { emit this->cancel(); emit this->finished(); }); - job->start(); + job_->start(); + + + +// QStringList plugins = KIO::PreviewJob::defaultPlugins(); +// auto job = new KIO::PreviewJob(KFileItemList() << KFileItem(QUrl::fromUserInput(id)), requestedSize, &plugins); + +// connect(job, &KIO::PreviewJob::gotPreview, [this](KFileItem, QPixmap pixmap) { +// m_image = pixmap.toImage(); +// emit this->finished(); +// }); + +// connect(job, &KIO::PreviewJob::failed, [this](KFileItem) { +// emit this->cancel(); +// emit this->finished(); +// }); + +// job->start(); } QQuickTextureFactory *AsyncImageResponse::textureFactory() const diff --git a/helper/thumbnailerjob.cpp b/helper/thumbnailerjob.cpp new file mode 100644 index 0000000..3c1c011 --- /dev/null +++ b/helper/thumbnailerjob.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "thumbnailerjob.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +ThumbnailerJob::ThumbnailerJob(const QString &fileName, const QSize &size, QObject *parent) + : QThread(parent) + , m_url(QUrl::fromUserInput(fileName)) + , m_size(size) + , shmaddr(nullptr) + , shmid(-1) +{ + // http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DIRECTORY + m_thumbnailsDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/thumbnails/"); +} + +ThumbnailerJob::~ThumbnailerJob() +{ + if (shmaddr) { + shmdt((char *)shmaddr); + shmctl(shmid, IPC_RMID, nullptr); + } +} + +void ThumbnailerJob::run() +{ + if (!QFile::exists(m_url.toLocalFile())) { + emit failed(); + return; + } + + int width = m_size.width(); + int height = m_size.height(); + int cacheSize = 0; + bool needCache = false; + + // 首先需要找到目录 + if (width <= 128 && height <= 128) + cacheSize = 128; + else if (width <= 256 && height <= 256) + cacheSize = 256; + else + cacheSize = 512; + + struct CachePool { + QString path; + int minSize; + }; + + const static auto pools = { + CachePool{QStringLiteral("/normal/"), 128}, + CachePool{QStringLiteral("/large/"), 256}, + CachePool{QStringLiteral("/x-large/"), 512}, + CachePool{QStringLiteral("/xx-large/"), 1024}, + }; + + QString thumbDir; + int wants = /*devicePixelRatio **/ cacheSize; + for (const auto &p : pools) { + if (p.minSize < wants) { + continue; + } else { + thumbDir = p.path; + break; + } + } + + m_thumbnailsPath = m_thumbnailsDir + thumbDir; + + // 不存在需要创建路径 + if (!QDir(m_thumbnailsPath).exists()) { + if (QDir().mkpath(m_thumbnailsPath)) { + QFile f(m_thumbnailsPath); + f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700 + } + } + + // 求出文件的 md5 + QByteArray origName; + const QFileInfo info(m_url.toLocalFile()); + const QString canonicalPath = info.canonicalFilePath(); + origName = QUrl::fromLocalFile(canonicalPath).toEncoded(QUrl::RemovePassword | QUrl::FullyEncoded); + + if (origName.isEmpty()) { + emit failed(); + return; + } + + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(origName); + m_thumbnailsName = QString::fromLatin1(md5.result().toHex()) + QLatin1String(".png"); + + // 是否需要生成缓存 + needCache = !QFile::exists(m_thumbnailsPath + m_thumbnailsName); + + if (needCache) { + QFile f(m_url.toLocalFile()); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QImage thumb; + thumb.loadFromData(data); + thumb = thumb.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + + // 保存 cache 文件 + QSaveFile saveFile(m_thumbnailsPath + m_thumbnailsName); + if (saveFile.open(QIODevice::WriteOnly)) { + if (thumb.save(&saveFile, "PNG")) { + saveFile.commit(); + } + } + + emitPreview(thumb); + } + } else { + QFile f(m_thumbnailsPath + m_thumbnailsName); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QImage thumb; + thumb.loadFromData(data); + emitPreview(thumb); + } + } +} + +void ThumbnailerJob::emitPreview(const QImage &image) +{ + QPixmap pixmap; + + if (image.width() > m_size.width() || image.height() > m_size.height()) { + pixmap = QPixmap::fromImage(image.scaled(m_size, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + } else { + pixmap = QPixmap::fromImage(image); + } + + emit gotPreview(pixmap); +} diff --git a/helper/thumbnailerjob.h b/helper/thumbnailerjob.h new file mode 100644 index 0000000..10ead87 --- /dev/null +++ b/helper/thumbnailerjob.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef THUMBNAILERJOB_H +#define THUMBNAILERJOB_H + +#include +#include +#include +#include + +class ThumbnailerJob : public QThread +{ + Q_OBJECT + +public: + explicit ThumbnailerJob(const QString &fileName, const QSize &size, QObject *parent = nullptr); + ~ThumbnailerJob(); + + void run() override; + +signals: + void gotPreview(const QPixmap &pixmap); + void failed(); + +private: + void emitPreview(const QImage &image); + +private: + QUrl m_url; + QSize m_size; + + // thumbnail cache folder + QString m_thumbnailsDir; + QString m_thumbnailsPath; + QString m_thumbnailsName; + + uchar *shmaddr; + size_t shmsize; + int shmid; +}; + +#endif // THUMBNAILERJOB_H