File indexing completed on 2025-01-19 03:57:35

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2013-07-22
0007  * Description : Qt item model for Showfoto thumbnails entries
0008  *
0009  * SPDX-FileCopyrightText: 2013 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "showfotothumbnailmodel.h"
0016 
0017 // Qt includes
0018 
0019 #include <QFileInfo>
0020 #include <QScopedPointer>
0021 
0022 // KDE includes
0023 
0024 #include <ksharedconfig.h>
0025 #include <kconfiggroup.h>
0026 
0027 // Local includes
0028 
0029 #include "drawdecoder.h"
0030 #include "digikam_debug.h"
0031 #include "dmetadata.h"
0032 
0033 using namespace Digikam;
0034 
0035 namespace ShowFoto
0036 {
0037 
0038 class Q_DECL_HIDDEN ShowfotoThumbnailModel::Private
0039 {
0040 public:
0041 
0042     explicit Private()
0043       : displayWidget       (nullptr),
0044         loadingThread       (nullptr),
0045         preloadThread       (nullptr),
0046         thumbSize           (0),
0047         lastGlobalThumbSize (0),
0048         preloadThumbSize    (0),
0049         maxThumbSize        (ThumbnailSize::Huge),
0050         emitDataChanged     (true)
0051     {
0052     }
0053 
0054     int preloadThumbnailSize() const
0055     {
0056         if (preloadThumbSize.size())
0057         {
0058             return preloadThumbSize.size();
0059         }
0060 
0061         return thumbSize.size();
0062     }
0063 
0064 public:
0065 
0066     QWidget*             displayWidget;
0067 
0068     ThumbnailLoadThread* loadingThread;
0069     ThumbnailLoadThread* preloadThread;
0070     ThumbnailSize        thumbSize;
0071     ThumbnailSize        lastGlobalThumbSize;
0072     ThumbnailSize        preloadThumbSize;
0073     QRect                detailRect;
0074     int                  maxThumbSize;
0075     bool                 emitDataChanged;
0076 };
0077 
0078 ShowfotoThumbnailModel::ShowfotoThumbnailModel(QWidget* const parent)
0079     : ShowfotoItemModel(parent),
0080       d                (new Private)
0081 {
0082     d->displayWidget = parent;
0083 
0084     connect(this, &ShowfotoThumbnailModel::signalThumbInfo,
0085             this, &ShowfotoThumbnailModel::slotThumbInfoLoaded);
0086 }
0087 
0088 ShowfotoThumbnailModel::~ShowfotoThumbnailModel()
0089 {
0090     this->showfotoItemInfosCleared();
0091 
0092     delete d->preloadThread;
0093     delete d;
0094 }
0095 
0096 void ShowfotoThumbnailModel::setThumbnailLoadThread(ThumbnailLoadThread* thread)
0097 {
0098     d->loadingThread = thread;
0099 
0100     connect(d->loadingThread, &ThumbnailLoadThread::signalThumbnailLoaded,
0101             this, &ShowfotoThumbnailModel::slotThumbnailLoaded);
0102 }
0103 
0104 ThumbnailLoadThread* ShowfotoThumbnailModel::thumbnailLoadThread() const
0105 {
0106     return d->loadingThread;
0107 }
0108 
0109 ThumbnailSize ShowfotoThumbnailModel::thumbnailSize() const
0110 {
0111     return d->thumbSize;
0112 }
0113 
0114 void ShowfotoThumbnailModel::setThumbnailSize(const ThumbnailSize& size)
0115 {
0116     d->lastGlobalThumbSize = size;
0117     d->thumbSize           = size;
0118 }
0119 
0120 void ShowfotoThumbnailModel::setPreloadThumbnailSize(const ThumbnailSize& size)
0121 {
0122     d->preloadThumbSize = size;
0123 }
0124 
0125 void ShowfotoThumbnailModel::setEmitDataChanged(bool emitSignal)
0126 {
0127     d->emitDataChanged = emitSignal;
0128 }
0129 
0130 void ShowfotoThumbnailModel::showfotoItemInfosCleared()
0131 {
0132     if (d->preloadThread)
0133     {
0134         d->preloadThread->stopAllTasks();
0135     }
0136 }
0137 
0138 QVariant ShowfotoThumbnailModel::data(const QModelIndex& index, int role) const
0139 {
0140     if ((role == ThumbnailRole) && (d->loadingThread && index.isValid()))
0141     {
0142         QImage    thumbnailImage;
0143         QPixmap   pixmap;
0144         ShowfotoItemInfo info = showfotoItemInfo(index);
0145         QString url           = info.url.toDisplayString();
0146         QString path          = info.folder + QLatin1Char('/') + info.name;
0147 
0148         if (info.isNull() || url.isEmpty())
0149         {
0150 
0151 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0152 
0153             QVariant var = QPixmap();
0154             return var;
0155 
0156 #else
0157 
0158             return QVariant(QVariant::Pixmap);
0159 
0160 #endif
0161 
0162         }
0163 
0164         if (pixmapForItem(path, pixmap))
0165         {
0166             return pixmap;
0167         }
0168 
0169         double dpr    = d->displayWidget->devicePixelRatio();
0170         int thumbSize = qRound((double)d->thumbSize.size() * dpr);
0171 
0172         // if pixmapForItem Failed
0173 
0174         if (getThumbnail(info, thumbnailImage))
0175         {
0176             thumbnailImage = thumbnailImage.scaled(thumbSize, thumbSize,
0177                                                    Qt::KeepAspectRatio,
0178                                                    Qt::SmoothTransformation);
0179             Q_EMIT signalThumbInfo(info, thumbnailImage);
0180 
0181             return thumbnailImage;
0182         }
0183 
0184 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0185 
0186             QVariant var = QPixmap();
0187             return var;
0188 
0189 #else
0190 
0191             return QVariant(QVariant::Pixmap);
0192 
0193 #endif
0194 
0195     }
0196 
0197     return ShowfotoItemModel::data(index, role);
0198 }
0199 
0200 bool ShowfotoThumbnailModel::setData(const QModelIndex& index, const QVariant& value, int role)
0201 {
0202     if (role == ThumbnailRole)
0203     {
0204 
0205 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0206 
0207         switch (value.typeId())
0208 
0209 #else
0210 
0211         switch (value.type())
0212 
0213 #endif
0214 
0215         {
0216             case QVariant::Invalid:
0217             {
0218                 d->thumbSize  = d->lastGlobalThumbSize;
0219                 d->detailRect = QRect();
0220 
0221                 break;
0222             }
0223 
0224             case QVariant::Int:
0225             {
0226                 if (value.isNull())
0227                 {
0228                     d->thumbSize = ThumbnailSize(d->lastGlobalThumbSize);
0229                 }
0230                 else
0231                 {
0232                     d->thumbSize = ThumbnailSize(value.toInt());
0233                 }
0234 
0235                 break;
0236             }
0237 
0238             case QVariant::Rect:
0239             {
0240                 if (value.isNull())
0241                 {
0242                     d->detailRect = QRect();
0243                 }
0244                 else
0245                 {
0246                     d->detailRect = value.toRect();
0247                 }
0248 
0249                 break;
0250             }
0251 
0252             default:
0253             {
0254                 break;
0255             }
0256         }
0257     }
0258 
0259     return ShowfotoItemModel::setData(index, value, role);
0260 }
0261 
0262 void ShowfotoThumbnailModel::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
0263 {
0264     if (thumb.isNull())
0265     {
0266         return;
0267     }
0268 
0269     // In case of multiple occurrence, we currently do not know which thumbnail is this. Signal change on all.
0270 
0271     Q_FOREACH (const QModelIndex& index, indexesForUrl(QUrl::fromLocalFile(loadingDescription.filePath)))
0272     {
0273         if (thumb.isNull())
0274         {
0275             Q_EMIT thumbnailFailed(index, loadingDescription.previewParameters.size);
0276         }
0277         else
0278         {
0279             Q_EMIT thumbnailAvailable(index, loadingDescription.previewParameters.size);
0280 
0281             if (d->emitDataChanged)
0282             {
0283                 Q_EMIT dataChanged(index, index);
0284             }
0285 
0286             Q_EMIT signalItemThumbnail(showfotoItemInfo(index), thumb);
0287         }
0288     }
0289 }
0290 
0291 bool ShowfotoThumbnailModel::getThumbnail(const ShowfotoItemInfo& itemInfo, QImage& thumbnail) const
0292 {
0293     QString path = itemInfo.folder + QLatin1Char('/') + itemInfo.name;
0294 
0295     // Try to get preview from Exif data (good quality). Can work with Raw files
0296 
0297     QScopedPointer<DMetadata> metadata(new DMetadata(path));
0298     metadata->getItemPreview(thumbnail);
0299 
0300     if (!thumbnail.isNull())
0301     {
0302         return true;
0303     }
0304 
0305     // RAW files : try to extract embedded thumbnail using RawEngine
0306 
0307     DRawDecoder::loadRawPreview(thumbnail, path);
0308 
0309     if (!thumbnail.isNull())
0310     {
0311         return true;
0312     }
0313 
0314     KSharedConfig::Ptr config  = KSharedConfig::openConfig();
0315     KConfigGroup group         = config->group(QLatin1String("Camera Settings"));
0316     bool turnHighQualityThumbs = group.readEntry(QLatin1String("TurnHighQualityThumbs"), false);
0317 
0318     // Try to get thumbnail from Exif data (poor quality).
0319 
0320     if (!turnHighQualityThumbs)
0321     {
0322         thumbnail = metadata->getExifThumbnail(true);
0323 
0324         if (!thumbnail.isNull())
0325         {
0326             return true;
0327         }
0328     }
0329 
0330     // THM files: try to get thumbnail from '.thm' files if we didn't manage to get
0331     // thumbnail from Exif. Any cameras provides *.thm files like JPEG files with RAW files.
0332     // Using this way is always speed up than ultimate loading using DImg.
0333     // Note: the thumbnail extracted with this method can be in poor quality.
0334     // 2006/27/01 - Gilles - Tested with my Minolta Dynax 5D USM camera.
0335 
0336     QFileInfo fi(path);
0337 
0338     if      (thumbnail.load(itemInfo.folder + QLatin1Char('/') + fi.baseName() + QLatin1String(".thm")))        // Lowercase
0339     {
0340         if (!thumbnail.isNull())
0341         {
0342             return true;
0343         }
0344     }
0345     else if (thumbnail.load(itemInfo.folder + QLatin1Char('/') + fi.baseName() + QLatin1String(".THM")))   // Uppercase
0346     {
0347         if (!thumbnail.isNull())
0348         {
0349             return true;
0350         }
0351     }
0352 
0353     // Finally, we trying to get thumbnail using DImg API (slow).
0354 /*
0355     qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Use DImg loader to get thumbnail from : " << path;
0356 
0357     DImg dimgThumb(path);
0358 
0359     if (!dimgThumb.isNull())
0360     {
0361         thumbnail = dimgThumb.copyQImage();
0362         return true;
0363     }
0364 */
0365     return false;
0366 }
0367 
0368 bool ShowfotoThumbnailModel::pixmapForItem(const QString& url, QPixmap& pix) const
0369 {
0370     double dpr    = d->displayWidget->devicePixelRatio();
0371     int thumbSize = qRound((double)d->thumbSize.size() * dpr);
0372 
0373     if (thumbSize > d->maxThumbSize)
0374     {
0375         // TODO: Install a widget maximum size to prevent this situation
0376 
0377         bool hasPixmap = d->loadingThread->find(ThumbnailIdentifier(url), pix, d->maxThumbSize);
0378 
0379         if (hasPixmap)
0380         {
0381 /*
0382             qCWarning(DIGIKAM_GENERAL_LOG) << "Thumbbar: Requested thumbnail size" << d->thumbSize.size()
0383                                            << "is larger than the maximum thumbnail size" << d->maxThumbSize
0384                                            << ". Returning a scaled-up image.";
0385 */
0386             pix = pix.scaled(thumbSize, thumbSize,
0387                              Qt::KeepAspectRatio, Qt::SmoothTransformation);
0388 
0389             return true;
0390         }
0391         else
0392         {
0393             return false;
0394         }
0395     }
0396     else
0397     {
0398         return d->loadingThread->find(ThumbnailIdentifier(url), pix, thumbSize);
0399     }
0400 }
0401 
0402 void ShowfotoThumbnailModel::slotThumbInfoLoaded(const ShowfotoItemInfo& info, const QImage& thumbnailImage)
0403 {
0404     QImage thumbnail = thumbnailImage;
0405 
0406     if (thumbnail.isNull())
0407     {
0408         thumbnail = QImage();
0409     }
0410 
0411     Q_FOREACH (const QModelIndex& index, indexesForUrl(info.url))
0412     {
0413         if (thumbnail.isNull())
0414         {
0415             Q_EMIT thumbnailFailed(index, d->thumbSize.size());
0416         }
0417         else
0418         {
0419             Q_EMIT thumbnailAvailable(index, d->thumbSize.size());
0420 
0421             if (d->emitDataChanged)
0422             {
0423                 Q_EMIT dataChanged(index, index);
0424             }
0425         }
0426     }
0427 }
0428 
0429 } // namespace ShowFoto
0430 
0431 #include "moc_showfotothumbnailmodel.cpp"