| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
1 |
/* |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
2 |
MIT License |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
3 |
|
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
4 |
Copyright (c) 2026 Poul Sander |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
5 |
|
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
6 |
Permission is hereby granted, free of charge, to any person obtaining a copy |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
7 |
of this software and associated documentation files (the "Software"), to deal |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
8 |
in the Software without restriction, including without limitation the rights |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
9 |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
10 |
copies of the Software, and to permit persons to whom the Software is |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
11 |
furnished to do so, subject to the following conditions: |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
12 |
|
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
13 |
The above copyright notice and this permission notice shall be included in all |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
14 |
copies or substantial portions of the Software. |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
15 |
|
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
16 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
17 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
18 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
19 |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
20 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
21 |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
22 |
SOFTWARE. |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
23 |
|
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
24 |
*/ |
| 14cf904f |
Poul Sander |
2026-06-22 18:18 |
25 |
|
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
26 |
#include "thumbnailworker.h" |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
27 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
28 |
#include "thumbnailcache.h" |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
29 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
30 |
#include <QFile> |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
31 |
#include <QImageReader> |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
32 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
33 |
#include <fcntl.h> |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
34 |
#include <unistd.h> |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
35 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
36 |
namespace |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
37 |
{ |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
38 |
constexpr int kDisplaySize = 128; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
39 |
|
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
40 |
// THREADING: Pure function called from a worker thread. No shared mutable state. |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
41 |
QImage downscaleForDisplay(const QImage &src) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
42 |
{ |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
43 |
if (src.isNull()) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
44 |
return src; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
45 |
if (src.width() <= kDisplaySize && src.height() <= kDisplaySize) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
46 |
return src; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
47 |
return src.scaled(kDisplaySize, kDisplaySize, |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
48 |
Qt::KeepAspectRatio, |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
49 |
Qt::SmoothTransformation); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
50 |
} |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
51 |
} // namespace |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
52 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
53 |
ThumbnailWorker::ThumbnailWorker(const QByteArray &path, |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
54 |
int row, |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
55 |
ThumbnailCache::Size size, |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
56 |
std::atomic_bool *cancelFlag) |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
57 |
: m_path(path), |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
58 |
m_row(row), |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
59 |
m_size(size), |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
60 |
m_cancelFlag(cancelFlag) |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
61 |
{ |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
62 |
setAutoDelete(true); |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
63 |
} |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
64 |
|
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
65 |
// THREADING: This function runs on a QThreadPool worker thread, NOT the main |
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
66 |
// thread. It must not access any ImageModel state. The only shared data it |
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
67 |
// touches is the atomic m_cancelFlag (for cooperative cancellation). All other |
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
68 |
// members (m_path, m_row, m_size) are immutable after construction. Results |
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
69 |
// are delivered to the main thread via the finished() signal connected with |
| 4d6b7476 |
Poul Sander |
2026-05-30 16:39 |
70 |
// Qt::QueuedConnection. |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
71 |
void ThumbnailWorker::run() |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
72 |
{ |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
73 |
if (*m_cancelFlag) |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
74 |
return; |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
75 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
76 |
// Per the spec, never thumbnail files that live inside the cache itself. |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
77 |
if (ThumbnailCache::isInsideCache(m_path)) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
78 |
return; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
79 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
80 |
const int target = static_cast<int>(m_size); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
81 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
82 |
QImage cached = ThumbnailCache::load(m_path, m_size); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
83 |
if (*m_cancelFlag) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
84 |
return; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
85 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
86 |
if (!cached.isNull()) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
87 |
{ |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
88 |
emit finished(m_row, downscaleForDisplay(cached)); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
89 |
return; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
90 |
} |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
91 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
92 |
// Open the file via POSIX open() so that paths with non-UTF-8 bytes |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
93 |
// (e.g. Latin-1 encoded filenames) are handled correctly. |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
94 |
int fd = ::open(m_path.constData(), O_RDONLY | O_CLOEXEC); |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
95 |
if (fd < 0) |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
96 |
return; |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
97 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
98 |
QFile file; |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
99 |
if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
100 |
{ |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
101 |
::close(fd); |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
102 |
return; |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
103 |
} |
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
104 |
|
| 80a9926d |
Poul Sander |
2026-05-19 20:28 |
105 |
QImageReader reader(&file); |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
106 |
reader.setAutoTransform(true); |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
107 |
|
| 7889168c |
Poul Sander |
2026-02-26 18:27 |
108 |
// Scale during decoding to avoid hitting the allocation limit |
| 7889168c |
Poul Sander |
2026-02-26 18:27 |
109 |
// on very large images (Qt 6 defaults to 128 MB). |
| 7889168c |
Poul Sander |
2026-02-26 18:27 |
110 |
QSize fullSize = reader.size(); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
111 |
if (fullSize.isValid()) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
112 |
{ |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
113 |
QSize scaled = fullSize.scaled(target, target, Qt::KeepAspectRatio); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
114 |
reader.setScaledSize(scaled); |
| 7889168c |
Poul Sander |
2026-02-26 18:27 |
115 |
} |
| 7889168c |
Poul Sander |
2026-02-26 18:27 |
116 |
|
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
117 |
QImage image = reader.read(); |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
118 |
|
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
119 |
if (*m_cancelFlag) |
| 6fcd6c90 |
Poul Sander |
2026-02-19 21:12 |
120 |
return; |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
121 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
122 |
if (image.isNull()) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
123 |
return; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
124 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
125 |
// Ensure the longest side is exactly the target (the decoder hint may have |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
126 |
// been ignored for some formats). Aspect ratio preserved, no padding canvas. |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
127 |
QImage thumb; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
128 |
if (image.width() > target || image.height() > target) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
129 |
thumb = image.scaled(target, target, |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
130 |
Qt::KeepAspectRatio, |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
131 |
Qt::SmoothTransformation); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
132 |
else |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
133 |
thumb = image; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
134 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
135 |
ThumbnailCache::save(m_path, m_size, thumb); |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
136 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
137 |
if (*m_cancelFlag) |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
138 |
return; |
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
139 |
|
| ac476ca8 |
Poul Sander |
2026-05-18 16:47 |
140 |
emit finished(m_row, downscaleForDisplay(thumb)); |
| cec3b5da |
Poul Sander |
2026-02-19 19:17 |
141 |
} |
|
|
1970-01-01 00:00 |
142 |
|