Skip to content

Commit 4e1ec7e

Browse files
committed
Show image icons from embedded thumbnails
1 parent ab06a23 commit 4e1ec7e

11 files changed

+255
-13
lines changed

Startrailer.desktop

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[Desktop Entry]
2+
Type=Application
3+
Terminal=false
4+
Name=Startrailer
5+
Icon=Startrailer
6+
Exec=startrailer
7+
Categories=Graphics;2DGraphics;RasterGraphics;Qt;

src/iconproxy.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include "iconproxy.h"
2+
#include "qiconthumbextractor.h"
3+
4+
QIcon getThumbnailIcon(const QString & path){
5+
QIconThumbExtractor ex;
6+
return ex.icon(path);
7+
}
8+
9+
10+
IconProxy::~IconProxy() {
11+
if (runnersPool) {
12+
delete runnersPool;
13+
}
14+
}

src/iconproxy.h

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#ifndef ICONPROXY_H
2+
#define ICONPROXY_H
3+
4+
#include <QIdentityProxyModel>
5+
#include <QFileSystemModel>
6+
#include <QtWidgets>
7+
#include <QtConcurrent/QtConcurrent>
8+
#include <QThreadPool>
9+
#include <QDebug>
10+
11+
/// A thread-safe function that returns an icon for an item with a given path.
12+
/// If the icon is not known, a null icon is returned.
13+
QIcon getThumbnailIcon(const QString & path);
14+
15+
class IconProxy : public QIdentityProxyModel {
16+
Q_OBJECT
17+
QMap<QString, QIcon> m_icons;
18+
Q_SIGNAL void hasIcon(const QString&, const QIcon&, const QPersistentModelIndex& index) const;
19+
20+
void onIcon(const QString& path, const QIcon& icon, const QPersistentModelIndex& index) {
21+
m_icons.insert(path, icon);
22+
emit dataChanged(index, index, QVector<int>{QFileSystemModel::FileIconRole});
23+
}
24+
QThreadPool *runnersPool=0;
25+
26+
public:
27+
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override {
28+
// QIdentityProxyModel::data(index, QFileSystemModel::FileIconRole);
29+
30+
if (role == QFileSystemModel::FileIconRole && index.column() == 0) {
31+
auto path = index.data(QFileSystemModel::FilePathRole).toString();
32+
auto it = m_icons.find(path);
33+
if (it != m_icons.end()) {
34+
if (! it->isNull()) return *it;
35+
return QIdentityProxyModel::data(index, role);
36+
}
37+
QPersistentModelIndex pIndex{index};
38+
QtConcurrent::run(runnersPool, [this,path,pIndex]{
39+
emit hasIcon(path, getThumbnailIcon(path), pIndex);
40+
});
41+
return QVariant{};
42+
}
43+
return QIdentityProxyModel::data(index, role);
44+
}
45+
46+
IconProxy(QObject * parent = nullptr) : QIdentityProxyModel{parent} {
47+
connect(this, &IconProxy::hasIcon, this, &IconProxy::onIcon);
48+
49+
runnersPool = new QThreadPool();
50+
int half_of_cpus = (QThread::idealThreadCount() / 2);
51+
int threads_count = half_of_cpus > 1 ? half_of_cpus : 1;
52+
qDebug() << "Thread pool size for thumbnails creation: " << threads_count;
53+
runnersPool->setMaxThreadCount(threads_count);
54+
}
55+
56+
virtual ~IconProxy();
57+
58+
protected:
59+
std::atomic_bool cancelled;
60+
};
61+
62+
#endif // ICONPROXY_H

src/mainwindow.cpp

+23-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "mainwindow.h"
1818
#include "ui_mainwindow.h"
1919
#include "playbackreader.h"
20-
20+
#include "iconproxy.h"
2121
#include "compositetrailstask.h"
2222

2323
MainWindow::MainWindow(QWidget *parent) :
@@ -43,7 +43,6 @@ MainWindow::MainWindow(QWidget *parent) :
4343
start_path = cmdline_args.first();
4444
}
4545

46-
4746
model->setRootPath(start_path);
4847
model->setReadOnly(true);
4948

@@ -59,10 +58,15 @@ MainWindow::MainWindow(QWidget *parent) :
5958
model->setNameFilters(filters);
6059
model->setNameFilterDisables(false);
6160

61+
//icon_provider = new QImageFileIconProvider();
62+
//model->setIconProvider(icon_provider);
6263

63-
ui->filesList->setModel(model);
64-
ui->filesList->setRootIndex(model->index(start_path));
65-
model->sort(0);
64+
icon_proxy = new IconProxy;
65+
icon_proxy->setSourceModel(model);
66+
67+
ui->filesList->setModel(icon_proxy);
68+
ui->filesList->setRootIndex(icon_proxy->mapFromSource(model->index(start_path)));
69+
icon_proxy->sort(0);
6670

6771
item = new QGraphicsPixmapItem();
6872
item->setTransformationMode(Qt::SmoothTransformation);
@@ -124,6 +128,12 @@ MainWindow::~MainWindow()
124128
delete gl;
125129
delete model;
126130
delete preview_each_n_group;
131+
132+
if (icon_provider)
133+
delete icon_provider;
134+
135+
if (icon_proxy)
136+
delete icon_proxy;
127137
}
128138

129139
//void MainWindow::handleFinished()
@@ -166,9 +176,10 @@ void MainWindow::on_filesList_doubleClicked(const QModelIndex &index)
166176

167177
if (model->fileInfo(index).isDir())
168178
{
169-
ui->filesList->setRootIndex(index);
179+
ui->filesList->setRootIndex(icon_proxy->mapFromSource(index));
170180
model->setRootPath(model->filePath(index));
171181
model->sort(0);
182+
icon_proxy->sort(0);
172183
}
173184
}
174185

@@ -177,10 +188,11 @@ void MainWindow::on_actionBack_triggered()
177188
{
178189
stopCompositing();
179190

180-
QModelIndex parent_index = model->parent(ui->filesList->rootIndex());
181-
ui->filesList->setRootIndex(parent_index);
191+
QModelIndex parent_index = icon_proxy->parent(ui->filesList->rootIndex());
192+
ui->filesList->setRootIndex(icon_proxy->mapFromSource(parent_index));
182193
model->setRootPath(model->filePath(parent_index));
183194
model->sort(0);
195+
icon_proxy->sort(0);
184196
}
185197

186198

@@ -373,9 +385,10 @@ void MainWindow::openDir(QString dir)
373385

374386
model->setRootPath(dir);
375387

376-
ui->filesList->setModel(model);
377-
ui->filesList->setRootIndex(model->index(dir));
388+
ui->filesList->setModel(icon_proxy);
389+
ui->filesList->setRootIndex(icon_proxy->mapFromSource(model->index(dir)));
378390
model->sort(0);
391+
icon_proxy->sort(0);
379392

380393
QuteImage *new_preview_image = new QuteImage();
381394
QuteImage *old_preview_image = preview_image;

src/mainwindow.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include "image.h"
1818
#include "quteimage.h"
1919
#include <QElapsedTimer>
20+
#include "qimagefileiconprovider.h"
21+
#include "qiconthumbextractor.h"
22+
#include "iconproxy.h"
2023

2124
namespace Ui {
2225
class MainWindow;
@@ -141,7 +144,8 @@ private slots:
141144
Ui::MainWindow *ui;
142145
QOpenGLWidget *gl=0;
143146
bool use_opengl=false;
144-
QFileSystemModel *model;
147+
QFileSystemModel *model=0;
148+
IconProxy *icon_proxy=0;
145149

146150
QGraphicsScene* scene;
147151
QGraphicsPixmapItem* item;
@@ -182,6 +186,8 @@ private slots:
182186
return result;
183187
}
184188

189+
QImageFileIconProvider *icon_provider=0;
190+
185191
void openDir(QString dir);
186192

187193
void stopCompositing();

src/qiconthumbextractor.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "qiconthumbextractor.h"
2+
#include <QDebug>
3+
4+
QIconThumbExtractor::QIconThumbExtractor() {
5+
exif_loader = exif_loader_new();
6+
}
7+
8+
9+
QIconThumbExtractor::~QIconThumbExtractor() {
10+
if (exif_loader) {
11+
exif_loader_unref(exif_loader);
12+
exif_loader = 0;
13+
}
14+
}
15+
16+
17+
QIcon QIconThumbExtractor::icon(const QFileInfo &info) const {
18+
const QString &file_path = info.absoluteFilePath();
19+
20+
//return icon_provider.icon(info);
21+
22+
if (!file_path.isEmpty() && info.isFile()) {
23+
if (QFile(file_path).exists()) {
24+
ExifData *ed = exif_loader_get_data(exif_loader);
25+
exif_loader_write_file(exif_loader, file_path.toStdString().c_str());
26+
ed = exif_loader_get_data(exif_loader);
27+
28+
if (ed) {
29+
QPixmap p;
30+
if (ed->data && ed->size) {
31+
p.loadFromData(ed->data, ed->size);
32+
} else {
33+
qDebug() << "No EXIF thumbnail in file " << file_path;
34+
// return QIcon(file_path); // try to make icon from whole image
35+
}
36+
exif_data_unref(ed);
37+
return QIcon(p);
38+
}
39+
}
40+
}
41+
42+
return QIcon(); //icon_provider.icon(info);
43+
}
44+
45+
QIcon QIconThumbExtractor::icon(const QString &path) const{
46+
return icon(QFileInfo(path));
47+
}

src/qiconthumbextractor.h

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef QICONTHUMBEXTRACTOR_H
2+
#define QICONTHUMBEXTRACTOR_H
3+
#include <libexif/exif-loader.h>
4+
#include <QFileInfo>
5+
#include <QIcon>
6+
#include <QFileIconProvider>
7+
8+
class QIconThumbExtractor
9+
{
10+
public:
11+
QIconThumbExtractor();
12+
virtual ~QIconThumbExtractor();
13+
14+
QIcon icon(const QFileInfo &info) const;
15+
QIcon icon(const QString &path) const;
16+
17+
protected:
18+
ExifLoader *exif_loader=0;
19+
QFileIconProvider icon_provider;
20+
};
21+
22+
#endif // QICONTHUMBEXTRACTOR_H

src/qimagefileiconprovider.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include "qimagefileiconprovider.h"
2+
#include <QDebug>
3+
4+
QImageFileIconProvider::QImageFileIconProvider() : QFileIconProvider() {
5+
exif_loader = exif_loader_new();
6+
}
7+
8+
9+
QImageFileIconProvider::~QImageFileIconProvider() {
10+
if (exif_loader) {
11+
exif_loader_unref(exif_loader);
12+
exif_loader = 0;
13+
}
14+
15+
}
16+
17+
18+
QIcon QImageFileIconProvider::icon(const QFileInfo &info) const
19+
{
20+
const QString &file_path = info.absoluteFilePath();
21+
22+
//return QFileIconProvider::icon(info);
23+
24+
if (!file_path.isEmpty() && info.isFile()) {
25+
if (QFile(file_path).exists()) {
26+
27+
ExifData *ed = exif_loader_get_data(exif_loader);
28+
exif_loader_write_file(exif_loader, file_path.toStdString().c_str());
29+
ed = exif_loader_get_data(exif_loader);
30+
31+
if (ed) {
32+
QPixmap p;
33+
if (ed->data && ed->size) {
34+
p.loadFromData(ed->data, ed->size);
35+
} else {
36+
qDebug() << "No EXIF thumbnail in file " << file_path;
37+
// return QIcon(file_path); // try to make icon from whole image
38+
}
39+
exif_data_unref(ed);
40+
return QIcon(p);
41+
}
42+
}
43+
}
44+
45+
return QFileIconProvider::icon(info);
46+
}

src/qimagefileiconprovider.h

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef QIMAGEFILEICONPROVIDER_H
2+
#define QIMAGEFILEICONPROVIDER_H
3+
4+
#include <QFileIconProvider>
5+
#include <libexif/exif-loader.h>
6+
7+
class QImageFileIconProvider : public QFileIconProvider
8+
{
9+
public:
10+
QImageFileIconProvider();
11+
QIcon icon(const QFileInfo &info) const;
12+
virtual ~QImageFileIconProvider();
13+
14+
protected:
15+
ExifLoader *exif_loader=0;
16+
};
17+
18+
#endif // QIMAGEFILEICONPROVIDER_H

src/src.pro

+9-2
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@ TEMPLATE = app
1616
#INCLUDEPATH += src
1717

1818
SOURCES += main.cpp\
19+
iconproxy.cpp \
1920
mainwindow.cpp \
2021
compositetrailstask.cpp \
22+
qiconthumbextractor.cpp \
23+
qimagefileiconprovider.cpp \
2124
view.cpp \
2225
playbackreader.cpp \
2326
image.cpp \
2427
quteimage.cpp
2528

2629
HEADERS += mainwindow.h \
2730
compositetrailstask.h \
31+
iconproxy.h \
32+
qiconthumbextractor.h \
33+
qimagefileiconprovider.h \
2834
view.h \
2935
playbackreader.h \
3036
image.h \
@@ -43,12 +49,11 @@ UI_DIR = $$DESTDIR/ui
4349
#LIBS += `Magick++-config --cppflags --cxxflags --ldflags --libs`
4450
CONFIG += link_pkgconfig
4551
#PKGCONFIG += Magick++
46-
CONFIG += link_pkgconfig
4752
PKGCONFIG = GraphicsMagick++
4853

4954
#LIBS+=-lgomp
5055

51-
#QMAKE_CXXFLAGS_RELEASE += -O2
56+
QMAKE_CXXFLAGS_RELEASE += -O3
5257

5358
OTHER_FILES += \
5459
README.md
@@ -61,8 +66,10 @@ BUILDDATE = $$system(date "+%Y%m%d%H%M")
6166
DEFINES += BUILDDATE=\\\"\"$$BUILDDATE\"\\\"
6267

6368
PKGCONFIG += libraw_r
69+
PKGCONFIG += libexif
6470
#LIBS+=-lraw_r
6571
unix: QMAKE_CXXFLAGS += -std=c++11
6672
unix: QMAKE_CXXFLAGS_DEBUG += -fprofile-arcs -ftest-coverage
73+
release: QMAKE_CXXFLAGS += -march=native -mtune=native -O3
6774
LIBS += \
6875
-lgcov

startrailer.png

14.6 KB
Loading

0 commit comments

Comments
 (0)