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"