1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2007-06-05
 * Description : Thumbnail loading
 *
 * SPDX-FileCopyrightText: 2006-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QPixmap>
#include <QImage>

// Local includes

#include "managedloadsavethread.h"
#include "thumbnailinfo.h"

namespace Digikam
{

class DbEngineParameters;
class ThumbnailCreator;
class ThumbnailInfoProvider;

class DIGIKAM_EXPORT ThumbnailLoadThread : public ManagedLoadSaveThread
{
    Q_OBJECT

public:

    explicit ThumbnailLoadThread(QObject* const parent = nullptr);
    ~ThumbnailLoadThread() override;

    /**
     * Return application-wide default thumbnail threads.
     * It is perfectly all right to create an extra object of the class,
     * but it is useful to have default object
     */
    static ThumbnailLoadThread* defaultThread();
    static ThumbnailLoadThread* defaultIconViewThread();

    static void cleanUp();

    /**
     * Enable loading of thumbnails from a thumbnail database.
     * This shall be called once at application startup.
     * This need not be called, then the FreeDesktop standard is used.
     * You can optionally provide a thumbnail info provider.
     */
    static void initializeThumbnailDatabase(const DbEngineParameters& params, ThumbnailInfoProvider* const provider = nullptr);

    /**
     * For color management, this sets the widget the thumbnails will be color managed for.
     * (currently it is only possible to set one global widget)
     */
    static void setDisplayingWidget(QWidget* const widget);

    /**
     * Find a thumbnail.
     * If the pixmap is found in the cache, returns true and sets pixmap
     * to the found QPixmap.
     * If the pixmap is not found in the cache, load() is called to start the loading process,
     * false is returned and pixmap is not touched.
     */
    bool find(const ThumbnailIdentifier& identifier, QPixmap& pixmap);

    /**
     * Same as above, but does not use the global size, but an extra specified size.
     */
    bool find(const ThumbnailIdentifier& identifier,
              QPixmap& pixmap, int size, bool onlyStorage = false);

    /**
     * Find a thumbnail.
     * This method sends the signals and does not return values like the method above.
     * If you certainly need asynchronous return, connect with Qt::QueuedConnection to the signals.
     * If you connect directly, the signals may be sent from within the method call.
     */
    void find(const ThumbnailIdentifier& identifier);

    /**
     * Same as above, but does not use the global size, but an extra specified size.
     */
    void find(const ThumbnailIdentifier& identifier, int size);

    /**
     * Find a group of thumbnails. The items will be loaded in order and signals will be sent.
     * Can be used to ensure that thumbnails are loaded in a particular order
     */
    void findGroup(QList<ThumbnailIdentifier>& identifiers);
    void findGroup(QList<ThumbnailIdentifier>& identifiers, int size);

    /**
     * All tastes of find() methods, for loading the thumbnail of a detail
     */
    bool find(const ThumbnailIdentifier& identifier, const QRect& rect, QPixmap& pixmap);
    bool find(const ThumbnailIdentifier& identifier,
              const QRect& rect, QPixmap& pixmap, int size, bool onlyStorage = false);
    void find(const ThumbnailIdentifier& identifier, const QRect& rect);
    void find(const ThumbnailIdentifier& identifier, const QRect& rect, int size);
    void findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& filePathAndRects);
    void findGroup(const QList<QPair<ThumbnailIdentifier, QRect> >& filePathsAndRects, int size);

    /**
     * Find the thumbnail pixmap in the buffered cache to
     * avoid flickering while loading a new thumbnail.
     */
    bool findBuffered(const ThumbnailIdentifier& identifier,
                      const QRect& rect, QPixmap& pixmap, int size);

    /**
     * Preload the thumbnail or thumbnail group.
     * This is essentially the same as loading, but with a lower priority.
     */
    void preload(const ThumbnailIdentifier& identifier);
    void preload(const ThumbnailIdentifier& identifier, int size);
    void preloadGroup(QList<ThumbnailIdentifier>& identifiers);
    void preloadGroup(QList<ThumbnailIdentifier>& identifiers, int size);

    /**
     * Pregenerate the thumbnail group.
     * No signals will be emitted when these are loaded.
     */
    void pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers);
    void pregenerateGroup(const QList<ThumbnailIdentifier>& identifiers, int size);

    /**
     * Load a thumbnail.
     * You do not need to use this method directly, it will not access the pixmap cache. Use find().
     * The LoadingDescription shall be constructed with the constructor for preview/thumbnail jobs.
     * (in the description constructor, you need to specify file path, thumbnail size and Exif rotation)
     */
    void load(const LoadingDescription& description);<--- Derived function 'ThumbnailLoadThread::load'<--- Derived function 'ThumbnailLoadThread::load'

    /**
     * Returns the descriptions used by the last call to any of the above methods.
     * After calling single-thumbnail methods (find, preload) the list will have size 1,
     * after the group methods (findGroup, preloadGroup, pregenerateGroup) the list can
     * be larger than 1.
     * There is no information if the description was ever scheduled in the thread,
     * already processed, skipped or canceled.
     */
    QList<LoadingDescription> lastDescriptions() const;

    /**
     * NOTE: If the thread is currently loading thumbnails, there is no guarantee as to when
     * the property change by one of the following methods takes effect.
     */

    /**
     * Set the requested thumbnail size.
     * Default value: 128
     */
    void setThumbnailSize(int size, bool forFace = false);

    /**
     * Returns the maximum possible size of a thumbnail.
     * If you request a larger size, the thumbnail will not load.
     * The size of the pixmap can slightly differ, especially when highlighting.
     */
    static int maximumThumbnailSize();
    static int maximumThumbnailPixmapSize(bool withHighlighting);

    /**
     * If you enable this, the signal thumbnailLoaded(LoadingDescription, QPixmap) will be emitted.
     * If you do not enable this, only the QImage-based signal (see LoadSaveThread) will be emitted.
     *
     * If you disable this, pay attention to the (global) setting of the LoadingCache, which per default
     * does not cache the images !!
     *
     * Default value: Enabled.
     */
    void setPixmapRequested(bool wantPixmap);

    /**
     * If you enable this, a highlighting border will be drawn around the pixmap.
     * This option has only an effect if pixmapRequested is true.
     * Default value: Enabled.
     */
    void setHighlightPixmap(bool highlight);

    /**
     * Computes the pixmap size for the give thumbnail size.
     * These can differ when highlighting is turned on.
     */
    int        thumbnailToPixmapSize(int size) const;
    static int thumbnailToPixmapSize(bool withHighlight, int size);

    /**
     * Computes the thumbnail size for the give pixmap size.
     */
    int pixmapToThumbnailSize(int size) const;

    /**
     * If you enable this, the thread will try hard to send a pixmap if thumbnail loading failed.
     * It will use standard system icons to replace the real thumbnail.
     * If you disable this, a null QPixmap will be sent.
     * This does not influence the QImage-based signal; this signal will be emitted with a null
     * QImage regardless of this setting here, if the loading failed.
     * Default value: Enabled.
     */
    void setSendSurrogatePixmap(bool send);

    /**
     * Stores the given detail thumbnail on disk.
     * Use this if possible because generation of detail thumbnails
     * is potentially slower.
     * The image should at least have storedSize().
     */
    void storeDetailThumbnail(const QString& filePath,
                              const QRect& detailRect,
                              const QImage& image,
                              bool isFace = false);
    int  storedSize() const;

    /**
     * This is a tool to force regeneration of thumbnails.
     * All thumbnail files for the given file will be removed from disk,
     * and the cached instances will be removed as well.
     * Use this method if you know that the contents of the file has changed.
     * This method works independently from the multithreaded thumbnail loading.
     */
    static void deleteThumbnail(const QString& filePath);

Q_SIGNALS:

    /// NOTE: See LoadSaveThread for a QImage-based thumbnailLoaded() signal.
    void signalThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& pix);

public:

    /// NOTE: For internal use - may only be used from the thread
    ThumbnailCreator* thumbnailCreator() const;

protected:

    void thumbnailLoaded(const LoadingDescription& loadingDescription, const QImage& img) override;

private:

    bool find(const ThumbnailIdentifier& identifier, int size, QPixmap* retPixmap,
              bool emitSignal, const QRect& detailRect, bool onlyStorage = false);
    void load(const LoadingDescription& description, bool pregenerate);
    bool checkSize(int size);
    QPixmap surrogatePixmap(const LoadingDescription& loadingDescription);

Q_SIGNALS:

    /// NOTE: For internal use only.
    void thumbnailsAvailable();
    void ThumbnailLoaded(const LoadingDescription&, const QImage&);

private Q_SLOTS:

    void slotThumbnailsAvailable();
    void slotThumbnailLoaded(const LoadingDescription&, const QImage&);

private:

    // Disable
    ThumbnailLoadThread(const ThumbnailLoadThread&)            = delete;
    ThumbnailLoadThread& operator=(const ThumbnailLoadThread&) = delete;

private:

    class Private;
    Private* const d = nullptr;
};

// --------------------------------------------------------------------------------------------------

class DIGIKAM_EXPORT ThumbnailImageCatcher : public QObject
{
    Q_OBJECT

public:

    /**
     *  Use this class to get a thumbnail synchronously.
     *  1. Create the ThumbnailImageCatcher object with your ThumbnailLoadThread
     *  2. a) Request a thumbnail
     *     b) Call enqueue()
     *  3. Call waitForThumbnails which returns the thumbnail QImage(s).
     *
     *  Note: Not meant for loading QPixmap thumbnails.
     */

    explicit ThumbnailImageCatcher(QObject* const parent = nullptr);
    explicit ThumbnailImageCatcher(ThumbnailLoadThread* const thread,
                                   QObject* const parent = nullptr);
    ~ThumbnailImageCatcher() override;

    ThumbnailLoadThread* thread() const;
    void setThumbnailLoadThread(ThumbnailLoadThread* const thread);

    /**
     * After requesting a thumbnail from the thread, call enqueue()
     * each time. Enqueue records the requested loading operation in an internal list.
     * A loading operation can result in the return of more than one thumbnail,
     * so enqueue() returns the number of expected results.
     * Then call waitForThumbnails. The returned list is the sum of previous calls
     * to enqueue, one entry per expected result, in order.
     * If stopped prematurely or loading failed, the respective entries will be null.
     */
    int           enqueue();
    QList<QImage> waitForThumbnails();

public Q_SLOTS:

    /**
     * The catcher is active per default after construction.
     * Deactivate it if you use the catcher as a longer-lived
     * object and do not use it for some time,
     * then activate it before you request a thumbnail from the thread again.
     */
    void setActive(bool active);

    /**
     * If the catcher is waiting in waitForThumbnails() in a different thread,
     * cancels the waiting. The results will be returned as received so far.
     */
    void cancel();

protected Q_SLOTS:

    void slotThumbnailLoaded(const LoadingDescription&, const QImage&);

private:

    // Disable
    ThumbnailImageCatcher(const ThumbnailImageCatcher&)            = delete;
    ThumbnailImageCatcher& operator=(const ThumbnailImageCatcher&) = delete;

private:

    class Private;
    Private* const d = nullptr;
};

} // namespace Digikam