1 /* Copyright (C) 2008-2009 Henner Zeller <h.zeller@acm.org>
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public
5 License as published by the Free Software Foundation; either
6 version 2 of the License, or (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; see the file COPYING. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
18 #include "ThumbnailView/ThumbnailCache.h"
20 #include <QPixmapCache>
23 #include "DB/ResultId.h"
24 #include "ImageManager/Manager.h"
25 #include "Settings/SettingsData.h"
26 #include "ThumbnailView/ThumbnailRequest.h"
27 #include "DB/ImageInfo.h"
29 ThumbnailView::ThumbnailCache::ThumbnailCache()
35 _asyncWarmingTimer
= new QTimer( this );
36 _asyncWarmingTimer
->setSingleShot(true);
37 connect( _asyncWarmingTimer
, SIGNAL( timeout() ),
38 this, SLOT( slotAsyncCacheWarming() ) );
41 bool ThumbnailView::ThumbnailCache::find(const DB::ResultId
& id
,
42 QPixmap
*result
) const {
43 Q_ASSERT(result
!= NULL
);
44 return QPixmapCache::find( thumbnailPixmapCacheKey(id
), *result
);
47 void ThumbnailView::ThumbnailCache::insert(const DB::ResultId
& id
,
48 const QPixmap
&pixmap
) {
49 QPixmapCache::insert( thumbnailPixmapCacheKey(id
), pixmap
);
52 void ThumbnailView::ThumbnailCache::clear()
54 QPixmapCache::clear();
57 void ThumbnailView::ThumbnailCache::setDisplayList(const DB::Result
& list
)
62 void ThumbnailView::ThumbnailCache::setThumbnailSize(const QSize
& thumbSize
)
64 if (thumbSize
!= _thumbSize
) {
65 _thumbSize
= thumbSize
;
70 // Warm the cache around the current hot area. This slot is called
71 // asynchronously after the user has stopped scrolling.
72 void ThumbnailView::ThumbnailCache::slotAsyncCacheWarming()
74 if (!_thumbSize
.isValid())
77 const bool scrollDown
= _hotFrom
> _lastHotFrom
;
78 _lastHotFrom
= _hotFrom
;
80 const int page
= _hotTo
- _hotFrom
;
81 const int thumbSize
= 4 * _thumbSize
.width() * _thumbSize
.height();
82 const int totalCacheableThumbs
= Settings::SettingsData::instance()->thumbnailCacheBytes() / thumbSize
;
83 // lets only use a part of the thumbnail cache because right now we're
84 // using the global QPixmapCache that is used by others as well.
85 const int preloadable
= 3 * (totalCacheableThumbs
- page
) / 4;
86 _requestedImagesLock
.lock();
87 _requestedImages
.clear();
88 _requestedImagesLock
.unlock();
89 ImageManager::Manager::instance()->stop(this);
91 // First, we make sure that the closer areas are covered: next and
94 // If we don't have much preload spots to spend, favour the
95 // direction in which we're going.
96 const int likelyDir
= ((preloadable
< 2*page
)
97 ? qMin(preloadable
, page
)
99 const int unlikelyDir
= ((preloadable
< 2*page
)
100 ? preloadable
- likelyDir
104 requestRange(_hotTo
, _hotTo
+ likelyDir
);
105 requestRange(_hotFrom
- unlikelyDir
, _hotFrom
);
107 requestRange(_hotFrom
- likelyDir
, _hotFrom
);
108 requestRange(_hotTo
, _hotTo
+ unlikelyDir
);
111 // If we still have preload space to spend, load it now.
112 if (preloadable
> 2*page
) {
113 const int limit
= preloadable
/ 2;
114 requestRange(_hotTo
+ page
, _hotTo
+ limit
);
115 requestRange(_hotFrom
- limit
, _hotFrom
- page
);
119 void ThumbnailView::ThumbnailCache::setHotArea(int from
, int to
)
121 const bool anyChange
= (_hotFrom
!= from
) || (_hotTo
!= to
);
125 // Do the asynchronous warming after we've settled a bit.
126 _asyncWarmingTimer
->stop();
127 _asyncWarmingTimer
->start( 150 );
130 // TODO(hzeller) to something smart here and determine the scroll speed
131 // and the typical thumbnail generation speed and calculate with it what
132 // to do (e.g. only 1 thumbnail per page on really quick scrolling).
135 void ThumbnailView::ThumbnailCache::requestRange(int from
, int to
)
137 ImageManager::Manager
* imgManager
= ImageManager::Manager::instance();
138 if (from
< 0) from
= 0;
139 if (to
> _displayList
.size()) to
= _displayList
.size();
140 for (int i
= from
; i
< to
; ++i
) {
141 const DB::ResultId id
= _displayList
.at(i
);
142 if (QPixmapCache::find(thumbnailPixmapCacheKey(id
)) != NULL
)
144 DB::ImageInfoPtr info
= id
.fetchInfo();
145 const QString fileName
= info
->fileName(DB::AbsolutePath
);
146 _requestedImagesLock
.lock();
147 _requestedImages
.insert(fileName
, id
);
148 _requestedImagesLock
.unlock();
149 ImageManager::ImageRequest
* request
150 = new ThumbnailCacheRequest(fileName
, _thumbSize
,
151 info
->angle(), this);
152 request
->setPriority( ImageManager::ThumbnailInvisible
);
153 _requestedImagesLock
.lock();
154 _requestedImages
.insert(fileName
, id
);
155 _requestedImagesLock
.unlock();
156 imgManager
->load( request
);
160 void ThumbnailView::ThumbnailCache::pixmapLoaded( const QString
& fileName
,
162 const QSize
& fullSize
,
163 int angle
, const QImage
& image
,
169 if (!loadedOK
|| image
.isNull())
171 QPixmap
pixmap( size
);
172 pixmap
= QPixmap::fromImage( image
);
174 QMutexLocker
l(&_requestedImagesLock
);
175 RequestedMap::iterator found
= _requestedImages
.find(fileName
);
176 if (found
!= _requestedImages
.end()) {
177 insert(found
.value(), pixmap
);
178 _requestedImages
.erase(found
);
182 bool ThumbnailView::ThumbnailCache::thumbnailStillNeeded(const QString
& fileName
) const
184 QMutexLocker
l(&_requestedImagesLock
);
185 return _requestedImages
.contains(fileName
);
188 QString
ThumbnailView::ThumbnailCache::thumbnailPixmapCacheKey(const DB::ResultId
& id
)
190 return QString::fromLatin1("thumbnail:%1").arg(toInt(id
.rawId()));
193 #include "ThumbnailCache.moc"