Krita Source Code Documentation
Loading...
Searching...
No Matches
KisLayerThumbnailCache.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
7
8#include "kis_image.h"
10#include "kis_layer_utils.h"
11
14
15
16namespace {
17struct ThumbnailRecord {
18 QImage image;
19 int seqNo = -1;
20 int maxSize = 0;
21};
22} // namespace
23
25{
26 Q_OBJECT
27public:
28
29 ThumbnailsStroke(KisImageSP image, int maxSize, const QMap<KisNodeWSP, ThumbnailRecord> &cache)
30 : KisIdleTaskStrokeStrategy(QLatin1String("layer-thumbnails-stroke"), kundo2_i18n("Update layer thumbnails"))
31 , m_root(image->root())
32 , m_maxSize(maxSize)
33 , m_cache(cache)
34 {
35 // thread-safety!
36 m_cache.detach();
37 }
38
39 void initStrokeCallback() override
40 {
42
45
47 recursiveApplyNodes(m_root, [&jobs, this] (KisNodeSP node) {
48
49
50 if (!node->parent()) return;
51 if (node->isFakeNode()) return;
52
53 bool shouldRegenerateThumbnail = false;
54
55 auto it = m_cache.find(node);
56
57 if (it != m_cache.end()) {
58 ThumbnailRecord rec = *it;
59
60 if (rec.seqNo != node->thumbnailSeqNo() ||
61 rec.maxSize != m_maxSize) {
62
63 shouldRegenerateThumbnail = true;
64 }
65 } else {
66 shouldRegenerateThumbnail = true;
67 }
68
69 if (shouldRegenerateThumbnail) {
70 addJobConcurrent(jobs, [node, this] () mutable {
71 QImage image = node->createThumbnail(m_maxSize, m_maxSize, Qt::KeepAspectRatio);
72 this->sigThumbnailGenerated(node, node->thumbnailSeqNo(), m_maxSize, image);
73 });
74 }
75 });
76
78 }
79
80Q_SIGNALS:
81 void sigThumbnailGenerated(KisNodeSP node, int maxSize, int seqNo, const QImage &thumb);
82private:
83
86 QMap<KisNodeWSP, ThumbnailRecord> m_cache;
87
88};
89
91{
93
95 int maxSize = 32;
96 QMap<KisNodeWSP, ThumbnailRecord> cache;
97
99};
100
101
106
108
110{
111 if (manager) {
112 m_d->taskGuard = manager->addIdleTaskWithGuard([this] (KisImageSP image) {
113 ThumbnailsStroke *stroke = new ThumbnailsStroke(image, m_d->maxSize, m_d->cache);
114 connect(stroke, SIGNAL(sigThumbnailGenerated(KisNodeSP, int, int, QImage)), this, SLOT(slotThumbnailGenerated(KisNodeSP, int, int, QImage)));
115 return stroke;
116 });
117 } else {
118 m_d->taskGuard = KisIdleTasksManager::TaskGuard();
119 }
120}
121
123{
124 m_d->image = image;
125 m_d->cache.clear();
126
127 if (m_d->image && m_d->taskGuard.isValid()) {
128 m_d->taskGuard.trigger();
129 }
130}
131
133{
134 setIdleTaskManagerImpl(manager);
135 if (m_d->image && m_d->taskGuard.isValid()) {
136 m_d->taskGuard.trigger();
137 }
138}
139
141{
142 setIdleTaskManagerImpl(manager);
143 setImage(image);
144 // the update is triggered only by the second call, not the first one!
145}
146
148{
149 m_d->maxSize = maxSize;
150 if (m_d->image && m_d->taskGuard.isValid()) {
151 m_d->taskGuard.trigger();
152 }
153}
154
156{
157 return m_d->maxSize;
158}
159
161{
162 QImage image;
163
164 auto it = m_d->cache.find(node);
165 if (it != m_d->cache.end()) {
166 image = it->image;
167
168 if (it->maxSize > m_d->maxSize) {
169 image = image.scaled(m_d->maxSize, m_d->maxSize, Qt::KeepAspectRatio);
170 }
171 } else {
172 image = QImage(1, 1, QImage::Format_ARGB32);
173 image.fill(0);
174 }
175
176 return image;
177}
178
180{
181 for (auto it = cache.begin(); it != cache.end();) {
182 if (!it.key()) {
183 it = cache.erase(it);
184 } else {
185 ++it;
186 }
187 }
188}
189
191{
192 Q_UNUSED(node);
193 m_d->cleanupDeletedNodes();
194}
195
197{
198 Q_UNUSED(node);
199 m_d->cleanupDeletedNodes();
200
201 if (m_d->image && m_d->taskGuard.isValid()) {
202 m_d->taskGuard.trigger();
203 }
204}
205
207{
208 m_d->cache.clear();
209}
210
211void KisLayerThumbnailCache::slotThumbnailGenerated(KisNodeSP node, int seqNo, int maxSize, const QImage &thumb)
212{
213 if (node->image() != m_d->image) {
214 qWarning() << "KisLayerThumbnailCache::slotThumbnailGenerated: node does not belong to the attached image anymore!" << ppVar(node) << ppVar(m_d->image);
215 return;
216 }
217
218 m_d->cache[node] = {thumb, seqNo, maxSize};
219 Q_EMIT sigLayerThumbnailUpdated(node);
220}
221
222#include "KisLayerThumbnailCache.moc"
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
TaskGuard addIdleTaskWithGuard(KisIdleTaskStrokeStrategyFactory factory)
Registers the factory for the idle task.
void setImage(KisImageSP image)
QImage thumbnail(KisNodeSP node) const
void notifyNodeAdded(KisNodeSP node)
void sigLayerThumbnailUpdated(KisNodeSP node)
void setIdleTaskManagerImpl(KisIdleTasksManager *manager)
QScopedPointer< Private > m_d
void setIdleTaskManager(KisIdleTasksManager *manager)
void slotThumbnailGenerated(KisNodeSP node, int seqNo, int maxSize, const QImage &thumb)
void notifyNodeRemoved(KisNodeSP node)
KisRunnableStrokeJobsInterface * runnableJobsInterface() const
virtual void addRunnableJobs(const QVector< KisRunnableStrokeJobDataBase * > &list)=0
#define ppVar(var)
Definition kis_debug.h:155
KUndo2MagicString kundo2_i18n(const char *text)
void recursiveApplyNodes(NodePointer node, Functor func)
void addJobConcurrent(QVector< Job * > &jobs, Func func)
virtual QImage createThumbnail(qint32 w, qint32 h, Qt::AspectRatioMode aspectRatioMode=Qt::IgnoreAspectRatio)
virtual int thumbnailSeqNo() const
KisImageWSP image
virtual bool isFakeNode() const
KisIdleTasksManager::TaskGuard taskGuard
QMap< KisNodeWSP, ThumbnailRecord > cache
KisNodeWSP parent
Definition kis_node.cpp:86
void initStrokeCallback() override
QMap< KisNodeWSP, ThumbnailRecord > m_cache
ThumbnailsStroke(KisImageSP image, int maxSize, const QMap< KisNodeWSP, ThumbnailRecord > &cache)
void sigThumbnailGenerated(KisNodeSP node, int maxSize, int seqNo, const QImage &thumb)