File indexing completed on 2025-01-05 03:59:36
0001 /* 0002 SPDX-FileCopyrightText: 2005-2007 Torsten Rahn <tackat@kde.org> 0003 SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org> 0004 SPDX-FileCopyrightText: 2008, 2009, 2010 Jens-Michael Hoffmann <jensmh@gmx.de> 0005 SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "StackedTileLoader.h" 0011 0012 #include <QCache> 0013 #include <QHash> 0014 #include <QReadWriteLock> 0015 #include <QImage> 0016 0017 #include "MergedLayerDecorator.h" 0018 #include "StackedTile.h" 0019 #include "TileLoader.h" 0020 #include "TileLoaderHelper.h" 0021 #include "MarbleGlobal.h" 0022 0023 #include "digikam_debug.h" 0024 0025 namespace Marble 0026 { 0027 0028 class StackedTileLoaderPrivate 0029 { 0030 public: 0031 explicit StackedTileLoaderPrivate( MergedLayerDecorator *mergedLayerDecorator ) 0032 : m_layerDecorator( mergedLayerDecorator ) 0033 { 0034 m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes 0035 } 0036 0037 MergedLayerDecorator *const m_layerDecorator; 0038 QHash <TileId, StackedTile*> m_tilesOnDisplay; 0039 QCache <TileId, StackedTile> m_tileCache; 0040 QReadWriteLock m_cacheLock; 0041 }; 0042 0043 StackedTileLoader::StackedTileLoader( MergedLayerDecorator *mergedLayerDecorator, QObject *parent ) 0044 : QObject( parent ), 0045 d( new StackedTileLoaderPrivate( mergedLayerDecorator ) ) 0046 { 0047 } 0048 0049 StackedTileLoader::~StackedTileLoader() 0050 { 0051 qDeleteAll( d->m_tilesOnDisplay ); 0052 delete d; 0053 } 0054 0055 int StackedTileLoader::tileColumnCount( int level ) const 0056 { 0057 return d->m_layerDecorator->tileColumnCount( level ); 0058 } 0059 0060 int StackedTileLoader::tileRowCount( int level ) const 0061 { 0062 return d->m_layerDecorator->tileRowCount( level ); 0063 } 0064 0065 const GeoSceneAbstractTileProjection *StackedTileLoader::tileProjection() const 0066 { 0067 return d->m_layerDecorator->tileProjection(); 0068 } 0069 0070 QSize StackedTileLoader::tileSize() const 0071 { 0072 return d->m_layerDecorator->tileSize(); 0073 } 0074 0075 void StackedTileLoader::resetTilehash() 0076 { 0077 QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin(); 0078 QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd(); 0079 for (; it != end; ++it ) { 0080 Q_ASSERT( it.value()->used() && "contained in m_tilesOnDisplay should imply used()" ); 0081 it.value()->setUsed( false ); 0082 } 0083 } 0084 0085 void StackedTileLoader::cleanupTilehash() 0086 { 0087 // Make sure that tiles which haven't been used during the last 0088 // rendering of the map at all get removed from the tile hash. 0089 0090 QHashIterator<TileId, StackedTile*> it( d->m_tilesOnDisplay ); 0091 while ( it.hasNext() ) { 0092 it.next(); 0093 if ( !it.value()->used() ) { 0094 // If insert call result is false then the cache is too small to store the tile 0095 // but the item will get deleted nevertheless and the pointer we have 0096 // doesn't get set to zero (so don't delete it in this case or it will crash!) 0097 d->m_tileCache.insert( it.key(), it.value(), it.value()->byteCount() ); 0098 d->m_tilesOnDisplay.remove( it.key() ); 0099 } 0100 } 0101 } 0102 0103 const StackedTile* StackedTileLoader::loadTile( TileId const & stackedTileId ) 0104 { 0105 // check if the tile is in the hash 0106 d->m_cacheLock.lockForRead(); 0107 StackedTile * stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 ); 0108 d->m_cacheLock.unlock(); 0109 if ( stackedTile ) { 0110 stackedTile->setUsed( true ); 0111 return stackedTile; 0112 } 0113 // here ends the performance critical section of this method 0114 0115 d->m_cacheLock.lockForWrite(); 0116 0117 // has another thread loaded our tile due to a race condition? 0118 stackedTile = d->m_tilesOnDisplay.value( stackedTileId, 0 ); 0119 if ( stackedTile ) { 0120 Q_ASSERT( stackedTile->used() && "other thread should have marked tile as used" ); 0121 d->m_cacheLock.unlock(); 0122 return stackedTile; 0123 } 0124 0125 // the tile was not in the hash so check if it is in the cache 0126 stackedTile = d->m_tileCache.take( stackedTileId ); 0127 if ( stackedTile ) { 0128 Q_ASSERT( !stackedTile->used() && "tiles in m_tileCache are invisible and should thus be marked as unused" ); 0129 stackedTile->setUsed( true ); 0130 d->m_tilesOnDisplay[ stackedTileId ] = stackedTile; 0131 d->m_cacheLock.unlock(); 0132 return stackedTile; 0133 } 0134 0135 // tile (valid) has not been found in hash or cache, so load it from disk 0136 // and place it in the hash from where it will get transferred to the cache 0137 0138 qCDebug(DIGIKAM_MARBLE_LOG) << "load tile from disk:" << stackedTileId; 0139 0140 stackedTile = d->m_layerDecorator->loadTile( stackedTileId ); 0141 Q_ASSERT( stackedTile ); 0142 stackedTile->setUsed( true ); 0143 0144 d->m_tilesOnDisplay[ stackedTileId ] = stackedTile; 0145 d->m_cacheLock.unlock(); 0146 0147 Q_EMIT tileLoaded( stackedTileId ); 0148 0149 return stackedTile; 0150 } 0151 0152 quint64 StackedTileLoader::volatileCacheLimit() const 0153 { 0154 return d->m_tileCache.maxCost() / 1024; 0155 } 0156 0157 QList<TileId> StackedTileLoader::visibleTiles() const 0158 { 0159 return d->m_tilesOnDisplay.keys(); 0160 } 0161 0162 int StackedTileLoader::tileCount() const 0163 { 0164 return d->m_tileCache.count() + d->m_tilesOnDisplay.count(); 0165 } 0166 0167 void StackedTileLoader::setVolatileCacheLimit( quint64 kiloBytes ) 0168 { 0169 qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8("Setting tile cache to %1 kilobytes.").arg( kiloBytes ); 0170 d->m_tileCache.setMaxCost( kiloBytes * 1024 ); 0171 } 0172 0173 void StackedTileLoader::updateTile( TileId const &tileId, QImage const &tileImage ) 0174 { 0175 const TileId stackedTileId( 0, tileId.zoomLevel(), tileId.x(), tileId.y() ); 0176 0177 StackedTile * displayedTile = d->m_tilesOnDisplay.take( stackedTileId ); 0178 if ( displayedTile ) { 0179 Q_ASSERT( !d->m_tileCache.contains( stackedTileId ) ); 0180 0181 StackedTile *const stackedTile = d->m_layerDecorator->updateTile( *displayedTile, tileId, tileImage ); 0182 stackedTile->setUsed( true ); 0183 d->m_tilesOnDisplay.insert( stackedTileId, stackedTile ); 0184 0185 delete displayedTile; 0186 displayedTile = nullptr; 0187 0188 Q_EMIT tileLoaded( stackedTileId ); 0189 } else { 0190 d->m_tileCache.remove( stackedTileId ); 0191 } 0192 } 0193 0194 RenderState StackedTileLoader::renderState() const 0195 { 0196 RenderState renderState( QString::fromUtf8("Stacked Tiles") ); 0197 QHash<TileId, StackedTile*>::const_iterator it = d->m_tilesOnDisplay.constBegin(); 0198 QHash<TileId, StackedTile*>::const_iterator const end = d->m_tilesOnDisplay.constEnd(); 0199 for (; it != end; ++it ) { 0200 renderState.addChild( d->m_layerDecorator->renderState( it.key() ) ); 0201 } 0202 return renderState; 0203 } 0204 0205 void StackedTileLoader::clear() 0206 { 0207 qDeleteAll( d->m_tilesOnDisplay ); 0208 d->m_tilesOnDisplay.clear(); 0209 d->m_tileCache.clear(); // clear the tile cache in physical memory 0210 0211 Q_EMIT cleared(); 0212 } 0213 0214 } 0215 0216 #include "moc_StackedTileLoader.cpp"