Krita Source Code Documentation
Loading...
Searching...
No Matches
KisResourceThumbnailCache.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Sharaf Zaman <shzam@sdf.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#include <QMap>
10#include <QModelIndex>
11#include <QSize>
12
13#include <KisResourceLocator.h>
14#include <KisResourceModel.h>
15
16#include <kis_global.h>
17
19
21 QSize size;
22 Qt::AspectRatioMode aspectRatioMode;
23 Qt::TransformationMode transformationMode;
24
25 bool operator<(const ImageScalingParameters &other) const
26 {
27 if (size != other.size) {
28 if (size.width() != other.size.width()) {
29 return size.width() < other.size.width();
30 } else {
31 return size.height() < other.size.height();
32 }
33 } else if (aspectRatioMode != other.aspectRatioMode) {
34 return aspectRatioMode < other.aspectRatioMode;
35 } else if (transformationMode != other.transformationMode) {
37 } else {
38 // they're the same
39 return false;
40 }
41 }
42};
43
44namespace
45{
46using ResourceKey = QPair<QString, QString>;
47using ThumbnailCacheT = QMap<ImageScalingParameters, QImage>;
48} // namespace
49
51 QMap<ResourceKey, ThumbnailCacheT> scaledThumbnailCache;
52 QMap<ResourceKey, QImage> originalImageCache;
53
54 QImage getExactMatch(const ResourceKey &key, ImageScalingParameters param) const;
55 QImage getOriginal(const ResourceKey &key) const;
56 void insertOriginal(const ResourceKey &key, const QImage &image);
57 bool containsOriginal(const ResourceKey &key) const;
58
59 ResourceKey
60 key(const QString &storageLocation, const QString &resourceType, const QString &filename) const;
61};
62
64 ImageScalingParameters param) const
65{
66 const auto thumbnailEntries = scaledThumbnailCache.find(key);
67 if (thumbnailEntries != scaledThumbnailCache.end()) {
68 const auto scaledThumbnail = thumbnailEntries->find(param);
69 if (scaledThumbnail != thumbnailEntries->end()) {
70 return *scaledThumbnail;
71 }
72 }
73
74 const auto originalImage = originalImageCache.find(key);
75 if (originalImage != originalImageCache.end() && originalImage->size() == param.size) {
76 return *originalImage;
77 }
78
79 return QImage();
80}
81
82QImage KisResourceThumbnailCache::Private::getOriginal(const ResourceKey &key) const
83{
84 return originalImageCache[key];
85}
86
87void KisResourceThumbnailCache::Private::insertOriginal(const ResourceKey &key, const QImage &image)
88{
89 // Someone else has added the image to this cache, when the only path to here is from a method which
90 // checks whether this cache contains it or not.
91 KIS_ASSERT(!originalImageCache.contains(key));
92 originalImageCache.insert(key, image);
93}
94
96{
97 return originalImageCache.contains(key);
98}
99
100ResourceKey KisResourceThumbnailCache::Private::key(const QString &storageLocation,
101 const QString &resourceType,
102 const QString &filename) const
103{
104 return {storageLocation, resourceType + "/" + filename};
105}
106
111
116
120
121QImage KisResourceThumbnailCache::originalImage(const QString &storageLocation,
122 const QString &resourceType,
123 const QString &filename) const
124{
125 const ResourceKey key = m_d->key(storageLocation, resourceType, filename);
126 return m_d->containsOriginal(key) ? m_d->getOriginal(key) : QImage();
127}
128
129void KisResourceThumbnailCache::insert(const QString &storageLocation,
130 const QString &resourceType,
131 const QString &filename,
132 const QImage &image)
133{
134 if (image.isNull()) {
135 return;
136 }
137 insert(m_d->key(storageLocation, resourceType, filename), image);
138}
139
140void KisResourceThumbnailCache::insert(const QPair<QString, QString> &key, const QImage &image)
141{
142 m_d->insertOriginal(key, image);
143}
144
145void KisResourceThumbnailCache::remove(const QString &storageLocation,
146 const QString &resourceType,
147 const QString &filename)
148{
149 remove(m_d->key(storageLocation, resourceType, filename));
150}
151
152void KisResourceThumbnailCache::remove(const QPair<QString, QString> &key)
153{
154 if (m_d->originalImageCache.contains(key)) {
155 m_d->originalImageCache.remove(key);
156
157 if (m_d->scaledThumbnailCache.contains(key)) {
158 m_d->scaledThumbnailCache.remove(key);
159 }
160 } else {
161 // Something must have gone wrong for thumbnail to exist in scaledThumbnailCache but not be in
162 // original.
163 KIS_ASSERT(!m_d->scaledThumbnailCache.contains(key));
164 }
165}
166
167QImage KisResourceThumbnailCache::getImage(const QModelIndex &index,
168 const QSize size,
169 Qt::AspectRatioMode aspectMode,
170 Qt::TransformationMode transformMode)
171{
172 const QString storageLocation = KisResourceLocator::instance()->makeStorageLocationAbsolute(
173 index.data(Qt::UserRole + KisAbstractResourceModel::Location).value<QString>());
174 const QString resourceType =
175 index.data(Qt::UserRole + KisAbstractResourceModel::ResourceType).value<QString>();
176 const QString filename = index.data(Qt::UserRole + KisAbstractResourceModel::Filename).value<QString>();
177
178 const ImageScalingParameters param = {size, aspectMode, transformMode};
179
180 ResourceKey key = m_d->key(storageLocation, resourceType, filename);
181
182 QImage result = m_d->getExactMatch(key, param);
183 if (!result.isNull()) {
184 return result;
185 } else if (m_d->containsOriginal(key)) {
186 result = m_d->getOriginal(key);
187 } else {
188 result = index.data(Qt::UserRole + KisAbstractResourceModel::Thumbnail).value<QImage>();
189 // KisResourceQueryMapper should have inserted the image, so we don't have to.
190 // Why there? Because most of the API usage for Thumbnail is going to be from index.data(), so we just
191 // remove the dependency that our user has to know this class for just accessing the cached original
192 // thumbnail.
193 KIS_SAFE_ASSERT_RECOVER_NOOP(result.isNull() || m_d->containsOriginal(key));
194 }
195 // if the size that the has been demanded, we will then cache the size and then pass it.
196 if (!result.isNull() && param.size.isValid()) {
197 const QImage scaledImage = result.scaled(param.size, param.aspectRatioMode, param.transformationMode);
198 if (m_d->scaledThumbnailCache.contains(key)) {
199 m_d->scaledThumbnailCache[key].insert(param, scaledImage);
200 } else {
201 ThumbnailCacheT scaledCacheMap;
202 scaledCacheMap.insert(param, scaledImage);
203 m_d->scaledThumbnailCache.insert(key, scaledCacheMap);
204 }
205 return scaledImage;
206 } else {
207 return result;
208 }
209}
Q_GLOBAL_STATIC(KisResourceThumbnailCache, s_instance)
QString makeStorageLocationAbsolute(QString storageLocation) const
static KisResourceLocator * instance()
void insert(const QString &storageLocation, const QString &resourceType, const QString &filename, const QImage &image)
static KisResourceThumbnailCache * instance()
void remove(const QString &storageLocation, const QString &resourceType, const QString &filename)
QImage originalImage(const QString &storageLocation, const QString &resourceType, const QString &filename) const
QImage getImage(const QModelIndex &index, const QSize size=QSize(-1, -1), Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode=Qt::FastTransformation)
QScopedPointer< Private > m_d
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
Qt::TransformationMode transformationMode
bool operator<(const ImageScalingParameters &other) const
QImage getOriginal(const ResourceKey &key) const
QMap< ResourceKey, ThumbnailCacheT > scaledThumbnailCache
QImage getExactMatch(const ResourceKey &key, ImageScalingParameters param) const
void insertOriginal(const ResourceKey &key, const QImage &image)
bool containsOriginal(const ResourceKey &key) const
ResourceKey key(const QString &storageLocation, const QString &resourceType, const QString &filename) const