Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thumbnailer #9

Merged
merged 2 commits into from
Jul 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_executable(cutefish-filemanager
helper/pathhistory.cpp
helper/fm.cpp
helper/shortcut.cpp
helper/thumbnailerjob.cpp

desktopiconprovider.cpp

Expand Down
29 changes: 23 additions & 6 deletions helper/thumbnailer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include <KIO/PreviewJob>

#include "thumbnailerjob.h"

#include <QDebug>
#include <QImage>

Expand All @@ -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
Expand Down
164 changes: 164 additions & 0 deletions helper/thumbnailerjob.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: Reion Wong <reionwong@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "thumbnailerjob.h"
#include <QStandardPaths>
#include <QCryptographicHash>
#include <QFile>
#include <QDir>
#include <QImage>
#include <QPixmap>
#include <QSaveFile>
#include <QDebug>
#include <QImageReader>

#include <sys/ipc.h>
#include <sys/shm.h>

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);
}
59 changes: 59 additions & 0 deletions helper/thumbnailerjob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2021 CutefishOS Team.
*
* Author: Reion Wong <reionwong@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef THUMBNAILERJOB_H
#define THUMBNAILERJOB_H

#include <QObject>
#include <QThread>
#include <QSize>
#include <QUrl>

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
6 changes: 3 additions & 3 deletions qml/FolderListItem.qml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ Item {
property bool selected: model.selected
property bool blank: model.blank

property color hoveredColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.1)
: Qt.darker(FishUI.Theme.backgroundColor, 1.05)
property color hoveredColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 2.3)
: Qt.darker(FishUI.Theme.backgroundColor, 1.05)
property color selectedColor: FishUI.Theme.darkMode ? Qt.lighter(FishUI.Theme.backgroundColor, 1.2)
: Qt.darker(FishUI.Theme.backgroundColor, 1.15)
: Qt.darker(FishUI.Theme.backgroundColor, 1.15)
// onSelectedChanged: {
// if (selected && !blank) {
// _listItem.grabToImage(function(result) {
Expand Down
Loading