• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

digikam

albumthumbnailloader.cpp

Go to the documentation of this file.
00001 /* ============================================================
00002  *
00003  * This file is a part of digiKam project
00004  * http://www.digikam.org
00005  *
00006  * Date        : 2006-04-14
00007  * Description : Load and cache tag thumbnails
00008  *
00009  * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
00010  *
00011  * This program is free software; you can redistribute it
00012  * and/or modify it under the terms of the GNU General
00013  * Public License as published by the Free Software Foundation;
00014  * either version 2, or (at your option)
00015  * any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * ============================================================ */
00023 
00024 // C includes.
00025 
00026 #include <math.h>
00027 
00028 // Qt includes.
00029 
00030 #include <QList>
00031 #include <QMap>
00032 #include <QPainter>
00033 #include <QEvent>
00034 #include <QPixmap>
00035 #include <QEvent>
00036 
00037 // KDE includes.
00038 
00039 #include <kapplication.h>
00040 #include <kiconloader.h>
00041 #include <kdebug.h>
00042 
00043 // Local includes.
00044 
00045 #include "thumbnailloadthread.h"
00046 #include "thumbnailsize.h"
00047 #include "album.h"
00048 #include "albummanager.h"
00049 #include "albumsettings.h"
00050 #include "albumthumbnailloader.h"
00051 #include "albumthumbnailloader.moc"
00052 
00053 namespace Digikam
00054 {
00055 
00056 typedef QMap<QString, QList<int> > PathAlbumMap;
00057 typedef QMap<int, QPixmap> AlbumThumbnailMap;
00058 
00059 class AlbumThumbnailLoaderPrivate
00060 {
00061 public:
00062     AlbumThumbnailLoaderPrivate()
00063     {
00064         iconSize             = AlbumSettings::instance()->getDefaultTreeIconSize();
00065         minBlendSize         = 20;
00066         iconAlbumThumbThread = 0;
00067         iconTagThumbThread   = 0;
00068     }
00069 
00070     int                     iconSize;
00071     int                     minBlendSize;
00072 
00073     ThumbnailLoadThread    *iconTagThumbThread;
00074     ThumbnailLoadThread    *iconAlbumThumbThread;
00075 
00076     PathAlbumMap            pathAlbumMap;
00077 
00078     AlbumThumbnailMap       thumbnailMap;
00079 };
00080 
00081 class AlbumThumbnailLoaderCreator { public: AlbumThumbnailLoader object; };
00082 K_GLOBAL_STATIC(AlbumThumbnailLoaderCreator, creator)
00083 
00084 AlbumThumbnailLoader *AlbumThumbnailLoader::instance()
00085 {
00086     return &creator->object;
00087 }
00088 
00089 AlbumThumbnailLoader::AlbumThumbnailLoader()
00090 {
00091     d = new AlbumThumbnailLoaderPrivate;
00092 
00093     connect(this, SIGNAL(signalDispatchThumbnailInternal(int, const QPixmap &)),
00094             this, SLOT(slotDispatchThumbnailInternal(int, const QPixmap &)));
00095 
00096     connect(AlbumManager::instance(), SIGNAL(signalAlbumIconChanged(Album*)),
00097             this, SLOT(slotIconChanged(Album*)));
00098 
00099     connect(AlbumManager::instance(), SIGNAL(signalAlbumDeleted(Album*)),
00100             this, SLOT(slotIconChanged(Album*)));
00101 }
00102 
00103 AlbumThumbnailLoader::~AlbumThumbnailLoader()
00104 {
00105     delete d->iconTagThumbThread;
00106     delete d->iconAlbumThumbThread;
00107 
00108     delete d;
00109 }
00110 
00111 QPixmap AlbumThumbnailLoader::getStandardTagIcon(RelativeSize relativeSize)
00112 {
00113     return loadIcon("tag", computeIconSize(relativeSize));
00114 }
00115 
00116 QPixmap AlbumThumbnailLoader::getStandardTagRootIcon(RelativeSize relativeSize)
00117 {
00118     return loadIcon("tag-folder", computeIconSize(relativeSize));
00119 }
00120 
00121 QPixmap AlbumThumbnailLoader::getStandardTagIcon(TAlbum *album, RelativeSize relativeSize)
00122 {
00123     if (album->isRoot())
00124         return getStandardTagRootIcon(relativeSize);
00125     else
00126         return getStandardTagIcon(relativeSize);
00127 }
00128 
00129 QPixmap AlbumThumbnailLoader::getStandardAlbumIcon(RelativeSize relativeSize)
00130 {
00131     return loadIcon("folder", computeIconSize(relativeSize));
00132 }
00133 
00134 QPixmap AlbumThumbnailLoader::getStandardAlbumRootIcon(RelativeSize relativeSize)
00135 {
00136     return loadIcon("folder-image", computeIconSize(relativeSize));
00137 }
00138 
00139 QPixmap AlbumThumbnailLoader::getStandardAlbumIcon(PAlbum *album, RelativeSize relativeSize)
00140 {
00141     if (album->isRoot() || album->isAlbumRoot())
00142         return getStandardAlbumRootIcon(relativeSize);
00143     else
00144         return getStandardAlbumIcon(relativeSize);
00145 }
00146 
00147 int AlbumThumbnailLoader::computeIconSize(RelativeSize relativeSize)
00148 {
00149     if (relativeSize == SmallerSize)
00150     {
00151         // when size was 32 smaller was 20. Scale.
00152         return lround(20.0 / 32.0 * (double)d->iconSize);
00153     }
00154     return d->iconSize;
00155 }
00156 
00157 QRect AlbumThumbnailLoader::computeBlendRect(int iconSize)
00158 {
00159     // when drawing a 20x20 thumbnail in a 32x32 icon, starting point was (6,9). Scale.
00160     double largerSize = iconSize;
00161     double x = 6.0 / 32.0 * largerSize;
00162     double y = 9.0 / 32.0 * largerSize;
00163     double size = 20.0 / 32.0 * largerSize;
00164     return QRect(lround(x), lround(y), lround(size), lround(size));
00165 }
00166 
00167 QPixmap AlbumThumbnailLoader::loadIcon(const QString &name, int size)
00168 {
00169     KIconLoader *iconLoader = KIconLoader::global();
00170     return iconLoader->loadIcon(name, KIconLoader::NoGroup, size);
00171 }
00172 
00173 bool AlbumThumbnailLoader::getTagThumbnail(TAlbum *album, QPixmap &icon)
00174 {
00175     int size = computeIconSize(SmallerSize);
00176     /*
00177     if (size >= d->minBlendSize)
00178     {
00179         QRect rect = computeBlendRect(size);
00180         size = rect.width();
00181     }
00182     */
00183 
00184     if(!album->icon().isEmpty())
00185     {
00186         if(album->icon().startsWith('/'))
00187         {
00188             KUrl iconKURL;
00189             iconKURL.setPath(album->icon());
00190             addUrl(album, iconKURL);
00191             icon = QPixmap();
00192             return true;
00193         }
00194         else
00195         {
00196             icon = loadIcon(album->icon(), size);
00197             return false;
00198         }
00199     }
00200     else
00201     {
00202         icon = QPixmap();
00203         return false;
00204     }
00205 }
00206 
00207 QPixmap AlbumThumbnailLoader::getTagThumbnailDirectly(TAlbum *album, bool blendIcon)
00208 {
00209     int size = computeIconSize(SmallerSize);
00210 
00211     if(!album->icon().isEmpty())
00212     {
00213         // icon cached?
00214         AlbumThumbnailMap::iterator it = d->thumbnailMap.find(album->globalID());
00215         if (it != d->thumbnailMap.end())
00216         {
00217             if (blendIcon)
00218                 return blendIcons(getStandardTagIcon(), *it);
00219             else
00220                 return *it;
00221         }
00222 
00223         if(album->icon().startsWith('/'))
00224         {
00225             KUrl iconKURL;
00226             iconKURL.setPath(album->icon());
00227             addUrl(album, iconKURL);
00228         }
00229         else
00230         {
00231             return loadIcon(album->icon(), size);
00232         }
00233     }
00234 
00235     return getStandardTagIcon(album);
00236 }
00237 
00238 bool AlbumThumbnailLoader::getAlbumThumbnail(PAlbum *album)
00239 {
00240     if(!album->icon().isEmpty() && d->iconSize > d->minBlendSize)
00241     {
00242         addUrl(album, album->iconKURL());
00243     }
00244     else
00245     {
00246         return false;
00247     }
00248 
00249     return true;
00250 }
00251 
00252 QPixmap AlbumThumbnailLoader::getAlbumThumbnailDirectly(PAlbum *album)
00253 {
00254     if(!album->icon().isEmpty() && d->iconSize > d->minBlendSize)
00255     {
00256         // icon cached?
00257         AlbumThumbnailMap::iterator it = d->thumbnailMap.find(album->globalID());
00258         if (it != d->thumbnailMap.end())
00259             return *it;
00260 
00261         // schedule for loading
00262         addUrl(album, album->iconKURL());
00263     }
00264 
00265     return getStandardAlbumIcon(album);
00266 }
00267 
00268 void AlbumThumbnailLoader::addUrl(Album *album, const KUrl &url)
00269 {
00270     /*
00271     QPixmap* pix = d->cache->find(album->iconKURL().path());
00272     if (pix)
00273     return pix;
00274     */
00275 
00276     // First check cached thumbnails.
00277     // We use a private cache which is actually a map to be sure to cache _all_ album thumbnails.
00278     // At startup, this is not relevant, as the views will add their requests in a row.
00279     // This is to speed up context menu and IE imagedescedit
00280     AlbumThumbnailMap::iterator ttit = d->thumbnailMap.find(album->globalID());
00281     if (ttit != d->thumbnailMap.end())
00282     {
00283         // It is not necessary to return cached icon asynchronously - they could be
00284         // returned by getTagThumbnail already - but this would make the API
00285         // less elegant, it feels much better this way.
00286         emit signalDispatchThumbnailInternal(album->globalID(), *ttit);
00287         return;
00288     }
00289 
00290     // Check if the URL has already been added
00291     PathAlbumMap::iterator it = d->pathAlbumMap.find(url.path());
00292 
00293     if (it == d->pathAlbumMap.end())
00294     {
00295         // use two IOslaves so that tag and album thumbnails are loaded
00296         // in parallel and not first album, then tag thumbnails
00297         if (album->type() == Album::TAG)
00298         {
00299             if(!d->iconTagThumbThread)
00300             {
00301                 d->iconTagThumbThread = new ThumbnailLoadThread();
00302                 d->iconTagThumbThread->setThumbnailSize(d->iconSize);
00303                 d->iconTagThumbThread->setSendSurrogatePixmap(false);
00304                 d->iconTagThumbThread->setExifRotate(AlbumSettings::instance()->getExifRotate());
00305                 connect(d->iconTagThumbThread,
00306                         SIGNAL(signalThumbnailLoaded(const LoadingDescription &, const QPixmap&)),
00307                         SLOT(slotGotThumbnailFromIcon(const LoadingDescription &, const QPixmap&)),
00308                         Qt::QueuedConnection);
00309             }
00310 
00311             // use the asynchronous version - with queued connections, see above
00312             d->iconTagThumbThread->find(url.path());
00313         }
00314         else
00315         {
00316             if(!d->iconAlbumThumbThread)
00317             {
00318                 d->iconAlbumThumbThread = new ThumbnailLoadThread();
00319                 d->iconAlbumThumbThread->setThumbnailSize(d->iconSize);
00320                 d->iconAlbumThumbThread->setSendSurrogatePixmap(false);
00321                 d->iconAlbumThumbThread->setExifRotate(AlbumSettings::instance()->getExifRotate());
00322                 connect(d->iconAlbumThumbThread,
00323                         SIGNAL(signalThumbnailLoaded(const LoadingDescription &, const QPixmap&)),
00324                         SLOT(slotGotThumbnailFromIcon(const LoadingDescription &, const QPixmap&)),
00325                         Qt::QueuedConnection);
00326             }
00327 
00328             d->iconAlbumThumbThread->find(url.path());
00329         }
00330 
00331         // insert new entry to map, add album globalID
00332         QList<int> &list = d->pathAlbumMap[url.path()];
00333         list.removeAll(album->globalID());
00334         list.push_back(album->globalID());
00335     }
00336     else
00337     {
00338         // only add album global ID to list which is already inserted in map
00339         (*it).removeAll(album->globalID());
00340         (*it).push_back(album->globalID());
00341     }
00342 }
00343 
00344 void AlbumThumbnailLoader::setThumbnailSize(int size)
00345 {
00346     if (d->iconSize == size)
00347         return;
00348 
00349     d->iconSize = size;
00350 
00351     // clear task list
00352     d->pathAlbumMap.clear();
00353     // clear cached thumbnails
00354     d->thumbnailMap.clear();
00355 
00356     if (d->iconAlbumThumbThread)
00357     {
00358         d->iconAlbumThumbThread->stopLoading();
00359     }
00360     if (d->iconTagThumbThread)
00361     {
00362         d->iconTagThumbThread->stopLoading();
00363     }
00364 
00365     emit signalReloadThumbnails();
00366 }
00367 
00368 int AlbumThumbnailLoader::thumbnailSize() const
00369 {
00370     return d->iconSize;
00371 }
00372 
00373 void AlbumThumbnailLoader::slotGotThumbnailFromIcon(const LoadingDescription &loadingDescription, const QPixmap &thumbnail)
00374 {
00375     // We need to find all albums for which the given url has been requested,
00376     // and emit a signal for each album.
00377 
00378     PathAlbumMap::iterator it = d->pathAlbumMap.find(loadingDescription.filePath);
00379 
00380     if (it != d->pathAlbumMap.end())
00381     {
00382         AlbumManager *manager = AlbumManager::instance();
00383 
00384         if (thumbnail.isNull())
00385         {
00386             // Loading failed
00387             for (QList<int>::iterator vit = (*it).begin(); vit != (*it).end(); ++vit)
00388             {
00389                 Album *album = manager->findAlbum(*vit);
00390                 if (album)
00391                     emit signalFailed(album);
00392             }
00393         }
00394         else
00395         {
00396             // Loading succeeded
00397 
00398             QPixmap tagThumbnail;
00399 
00400             for (QList<int>::iterator vit = (*it).begin(); vit != (*it).end(); ++vit)
00401             {
00402                 // look up with global id
00403                 Album *album = manager->findAlbum(*vit);
00404                 if (album)
00405                 {
00406                     if (album->type() == Album::TAG)
00407                     {
00408                         // create tag thumbnail if needed
00409                         if (tagThumbnail.isNull())
00410                         {
00411                             tagThumbnail = createTagThumbnail(thumbnail);
00412                             d->thumbnailMap.insert(album->globalID(), tagThumbnail);
00413                         }
00414 
00415                         emit signalThumbnail(album, tagThumbnail);
00416                     }
00417                     else
00418                     {
00419                         d->thumbnailMap.insert(album->globalID(), thumbnail);
00420                         emit signalThumbnail(album, thumbnail);
00421                     }
00422                 }
00423             }
00424         }
00425 
00426         d->pathAlbumMap.erase(it);
00427     }
00428 }
00429 
00430 void AlbumThumbnailLoader::slotDispatchThumbnailInternal(int albumID, const QPixmap &thumbnail)
00431 {
00432     // for cached thumbnails
00433 
00434     AlbumManager *manager = AlbumManager::instance();
00435     Album *album          = manager->findAlbum(albumID);
00436     if (album)
00437     {
00438         if (thumbnail.isNull())
00439             emit signalFailed(album);
00440         else
00441             emit signalThumbnail(album, thumbnail);
00442     }
00443 }
00444 
00445 void AlbumThumbnailLoader::slotIconChanged(Album* album)
00446 {
00447     if(!album || album->type() != Album::TAG)
00448         return;
00449 
00450     d->thumbnailMap.remove(album->globalID());
00451 }
00452 
00453 QPixmap AlbumThumbnailLoader::createTagThumbnail(const QPixmap &albumThumbnail)
00454 {
00455     // tag thumbnails are cropped
00456 
00457     QPixmap tagThumbnail;
00458     int thumbSize = qMax(albumThumbnail.width(), albumThumbnail.height());
00459 
00460     if(!albumThumbnail.isNull() && thumbSize >= d->minBlendSize)
00461     {
00462         QRect rect   = computeBlendRect(thumbSize);
00463         int w1       = albumThumbnail.width();
00464         int w2       = rect.width();
00465         int h1       = albumThumbnail.height();
00466         int h2       = rect.height();
00467         tagThumbnail = QPixmap(w2, h2);
00468         QPainter p(&tagThumbnail);
00469         p.drawPixmap(0, 0, albumThumbnail, (w1-w2)/2, (h1-h2)/2, w2, h2);
00470         p.end();
00471     }
00472     else
00473     {
00474         tagThumbnail = albumThumbnail;
00475     }
00476 
00477     return tagThumbnail;
00478 }
00479 
00480 QPixmap AlbumThumbnailLoader::blendIcons(QPixmap dstIcon, const QPixmap &tagIcon)
00481 {
00482     int dstIconSize = qMax(dstIcon.width(), dstIcon.height());
00483 
00484     if (dstIconSize >= d->minBlendSize)
00485     {
00486         if(!tagIcon.isNull())
00487         {
00488             QRect rect = computeBlendRect(dstIconSize);
00489             QPainter p(&dstIcon);
00490             p.drawPixmap(rect.x(), rect.y(), tagIcon, 0, 0, rect.width(), rect.height());
00491             p.end();
00492         }
00493         return dstIcon;
00494     }
00495     else
00496     {
00497         return tagIcon;
00498     }
00499 }
00500 
00501 } // namespace Digikam

digikam

Skip menu "digikam"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • digikam
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal