| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtLocation module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL3$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at http://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or later as published by the Free | 
| 28 | ** Software Foundation and appearing in the file LICENSE.GPL included in | 
| 29 | ** the packaging of this file. Please review the following information to | 
| 30 | ** ensure the GNU General Public License version 2.0 requirements will be | 
| 31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. | 
| 32 | ** | 
| 33 | ** $QT_END_LICENSE$ | 
| 34 | ** | 
| 35 | ****************************************************************************/ | 
| 36 | #include "qgeofiletilecache_p.h" | 
| 37 |  | 
| 38 | #include "qgeotilespec_p.h" | 
| 39 |  | 
| 40 | #include "qgeomappingmanager_p.h" | 
| 41 |  | 
| 42 | #include <QDir> | 
| 43 | #include <QStandardPaths> | 
| 44 | #include <QMetaType> | 
| 45 | #include <QPixmap> | 
| 46 | #include <QDebug> | 
| 47 |  | 
| 48 | Q_DECLARE_METATYPE(QList<QGeoTileSpec>) | 
| 49 | Q_DECLARE_METATYPE(QSet<QGeoTileSpec>) | 
| 50 |  | 
| 51 | QT_BEGIN_NAMESPACE | 
| 52 |  | 
| 53 | class QGeoCachedTileMemory | 
| 54 | { | 
| 55 | public: | 
| 56 |     ~QGeoCachedTileMemory() | 
| 57 |     { | 
| 58 |         if (cache) | 
| 59 |             cache->evictFromMemoryCache(tm: this); | 
| 60 |     } | 
| 61 |  | 
| 62 |     QGeoTileSpec spec; | 
| 63 |     QGeoFileTileCache *cache; | 
| 64 |     QByteArray bytes; | 
| 65 |     QString format; | 
| 66 | }; | 
| 67 |  | 
| 68 | void QCache3QTileEvictionPolicy::aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj) | 
| 69 | { | 
| 70 |     Q_UNUSED(key); | 
| 71 |     // set the cache pointer to zero so we can't call evictFromDiskCache | 
| 72 |     obj->cache = 0; | 
| 73 | } | 
| 74 |  | 
| 75 | void QCache3QTileEvictionPolicy::aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer<QGeoCachedTileDisk> obj) | 
| 76 | { | 
| 77 |     Q_UNUSED(key); | 
| 78 |     Q_UNUSED(obj); | 
| 79 |     // leave the pointer set if it's a real eviction | 
| 80 | } | 
| 81 |  | 
| 82 | QGeoCachedTileDisk::~QGeoCachedTileDisk() | 
| 83 | { | 
| 84 |     if (cache) | 
| 85 |         cache->evictFromDiskCache(td: this); | 
| 86 | } | 
| 87 |  | 
| 88 | QGeoFileTileCache::QGeoFileTileCache(const QString &directory, QObject *parent) | 
| 89 |     : QAbstractGeoTileCache(parent), directory_(directory), minTextureUsage_(0), extraTextureUsage_(0) | 
| 90 |     ,costStrategyDisk_(ByteSize), costStrategyMemory_(ByteSize), costStrategyTexture_(ByteSize) | 
| 91 |     ,isDiskCostSet_(false), isMemoryCostSet_(false), isTextureCostSet_(false) | 
| 92 | { | 
| 93 |  | 
| 94 | } | 
| 95 |  | 
| 96 | void QGeoFileTileCache::init() | 
| 97 | { | 
| 98 |     const QString basePath = baseCacheDirectory() + QLatin1String("QtLocation/" ); | 
| 99 |  | 
| 100 |     // delete old tiles from QtLocation 5.7 or prior | 
| 101 |     // Newer version use plugin-specific subdirectories, versioned with qt version so those are not affected. | 
| 102 |     // TODO Remove cache cleanup in Qt 6 | 
| 103 |     QDir baseDir(basePath); | 
| 104 |     if (baseDir.exists()) { | 
| 105 |         const QStringList oldCacheFiles = baseDir.entryList(filters: QDir::Files); | 
| 106 |         foreach (const QString& file, oldCacheFiles) | 
| 107 |             baseDir.remove(fileName: file); | 
| 108 |         const QStringList oldCacheDirs = { QStringLiteral("osm" ), QStringLiteral("mapbox" ), QStringLiteral("here" ) }; | 
| 109 |         foreach (const QString& d, oldCacheDirs) { | 
| 110 |             QDir oldCacheDir(basePath + QLatin1Char('/') + d); | 
| 111 |             if (oldCacheDir.exists()) | 
| 112 |                 oldCacheDir.removeRecursively(); | 
| 113 |         } | 
| 114 |     } | 
| 115 |  | 
| 116 |     if (directory_.isEmpty()) { | 
| 117 |         directory_ = baseLocationCacheDirectory(); | 
| 118 |         qWarning() << "Plugin uses uninitialized QGeoFileTileCache directory which was deleted during startup" ; | 
| 119 |     } | 
| 120 |  | 
| 121 |     const bool directoryCreated = QDir::root().mkpath(dirPath: directory_); | 
| 122 |     if (!directoryCreated) | 
| 123 |         qWarning() << "Failed to create cache directory "  << directory_; | 
| 124 |  | 
| 125 |     // default values | 
| 126 |     if (!isDiskCostSet_) { // If setMaxDiskUsage has not been called yet | 
| 127 |         if (costStrategyDisk_ == ByteSize) | 
| 128 |             setMaxDiskUsage(50 * 1024 * 1024); | 
| 129 |         else | 
| 130 |             setMaxDiskUsage(1000); | 
| 131 |     } | 
| 132 |  | 
| 133 |     if (!isMemoryCostSet_) { // If setMaxMemoryUsage has not been called yet | 
| 134 |         if (costStrategyMemory_ == ByteSize) | 
| 135 |             setMaxMemoryUsage(3 * 1024 * 1024); | 
| 136 |         else | 
| 137 |             setMaxMemoryUsage(100); | 
| 138 |     } | 
| 139 |  | 
| 140 |     if (!isTextureCostSet_) { // If setExtraTextureUsage has not been called yet | 
| 141 |         if (costStrategyTexture_ == ByteSize) | 
| 142 |             setExtraTextureUsage(6 * 1024 * 1024); | 
| 143 |         else | 
| 144 |             setExtraTextureUsage(30); // byte size of texture is >> compressed image, hence unitary cost should be lower | 
| 145 |     } | 
| 146 |  | 
| 147 |     loadTiles(); | 
| 148 | } | 
| 149 |  | 
| 150 | void QGeoFileTileCache::loadTiles() | 
| 151 | { | 
| 152 |     QStringList formats; | 
| 153 |     formats << QLatin1String("*.*" ); | 
| 154 |  | 
| 155 |     QDir dir(directory_); | 
| 156 |     QStringList files = dir.entryList(nameFilters: formats, filters: QDir::Files); | 
| 157 | #if 0 // workaround for QTBUG-60581 | 
| 158 |     // Method: | 
| 159 |     // 1. read each queue file then, if each file exists, deserialize the data into the appropriate | 
| 160 |     // cache queue. | 
| 161 |     for (int i = 1; i<=4; i++) { | 
| 162 |         QString filename = dir.filePath(QString::fromLatin1("queue" ) + QString::number(i)); | 
| 163 |         QFile file(filename); | 
| 164 |         if (!file.open(QIODevice::ReadOnly)) | 
| 165 |             continue; | 
| 166 |         QList<QSharedPointer<QGeoCachedTileDisk> > queue; | 
| 167 |         QList<QGeoTileSpec> specs; | 
| 168 |         QList<int> costs; | 
| 169 |         while (!file.atEnd()) { | 
| 170 |             QByteArray line = file.readLine().trimmed(); | 
| 171 |             QString filename = QString::fromLatin1(line.constData(), line.length()); | 
| 172 |             if (dir.exists(filename)){ | 
| 173 |                 files.removeOne(filename); | 
| 174 |                 QGeoTileSpec spec = filenameToTileSpec(filename); | 
| 175 |                 if (spec.zoom() == -1) | 
| 176 |                     continue; | 
| 177 |                 QSharedPointer<QGeoCachedTileDisk> tileDisk(new QGeoCachedTileDisk); | 
| 178 |                 tileDisk->filename = dir.filePath(filename); | 
| 179 |                 tileDisk->cache = this; | 
| 180 |                 tileDisk->spec = spec; | 
| 181 |                 QFileInfo fi(tileDisk->filename); | 
| 182 |                 specs.append(spec); | 
| 183 |                 queue.append(tileDisk); | 
| 184 |                 if (costStrategyDisk_ == ByteSize) | 
| 185 |                     costs.append(fi.size()); | 
| 186 |                 else | 
| 187 |                     costs.append(1); | 
| 188 |  | 
| 189 |             } | 
| 190 |         } | 
| 191 |  | 
| 192 |         diskCache_.deserializeQueue(i, specs, queue, costs); | 
| 193 |         file.close(); | 
| 194 |     } | 
| 195 | #endif | 
| 196 |     // 2. remaining tiles that aren't registered in a queue get pushed into cache here | 
| 197 |     // this is a backup, in case the queue manifest files get deleted or out of sync due to | 
| 198 |     // the application not closing down properly | 
| 199 |     for (int i = 0; i < files.size(); ++i) { | 
| 200 |         QGeoTileSpec spec = filenameToTileSpec(filename: files.at(i)); | 
| 201 |         if (spec.zoom() == -1) | 
| 202 |             continue; | 
| 203 |         QString filename = dir.filePath(fileName: files.at(i)); | 
| 204 |         addToDiskCache(spec, filename); | 
| 205 |     } | 
| 206 | } | 
| 207 |  | 
| 208 | QGeoFileTileCache::~QGeoFileTileCache() | 
| 209 | { | 
| 210 | #if 0 // workaround for QTBUG-60581 | 
| 211 |     // write disk cache queues to disk | 
| 212 |     QDir dir(directory_); | 
| 213 |     for (int i = 1; i<=4; i++) { | 
| 214 |         QString filename = dir.filePath(QString::fromLatin1("queue" ) + QString::number(i)); | 
| 215 |         QFile file(filename); | 
| 216 |         if (!file.open(QIODevice::WriteOnly)){ | 
| 217 |             qWarning() << "Unable to write tile cache file "  << filename; | 
| 218 |             continue; | 
| 219 |         } | 
| 220 |         QList<QSharedPointer<QGeoCachedTileDisk> > queue; | 
| 221 |         diskCache_.serializeQueue(i, queue); | 
| 222 |         foreach (const QSharedPointer<QGeoCachedTileDisk> &tile, queue) { | 
| 223 |             if (tile.isNull()) | 
| 224 |                 continue; | 
| 225 |  | 
| 226 |             // we just want the filename here, not the full path | 
| 227 |             int index = tile->filename.lastIndexOf(QLatin1Char('/')); | 
| 228 |             QByteArray filename = tile->filename.mid(index + 1).toLatin1() + '\n'; | 
| 229 |             file.write(filename); | 
| 230 |         } | 
| 231 |         file.close(); | 
| 232 |     } | 
| 233 | #endif | 
| 234 | } | 
| 235 |  | 
| 236 | void QGeoFileTileCache::printStats() | 
| 237 | { | 
| 238 |     textureCache_.printStats(); | 
| 239 |     memoryCache_.printStats(); | 
| 240 |     diskCache_.printStats(); | 
| 241 | } | 
| 242 |  | 
| 243 | void QGeoFileTileCache::setMaxDiskUsage(int diskUsage) | 
| 244 | { | 
| 245 |     diskCache_.setMaxCost(maxCost: diskUsage); | 
| 246 |     isDiskCostSet_ = true; | 
| 247 | } | 
| 248 |  | 
| 249 | int QGeoFileTileCache::maxDiskUsage() const | 
| 250 | { | 
| 251 |     return diskCache_.maxCost(); | 
| 252 | } | 
| 253 |  | 
| 254 | int QGeoFileTileCache::diskUsage() const | 
| 255 | { | 
| 256 |     return diskCache_.totalCost(); | 
| 257 | } | 
| 258 |  | 
| 259 | void QGeoFileTileCache::setMaxMemoryUsage(int memoryUsage) | 
| 260 | { | 
| 261 |     memoryCache_.setMaxCost(maxCost: memoryUsage); | 
| 262 |     isMemoryCostSet_ = true; | 
| 263 | } | 
| 264 |  | 
| 265 | int QGeoFileTileCache::maxMemoryUsage() const | 
| 266 | { | 
| 267 |     return memoryCache_.maxCost(); | 
| 268 | } | 
| 269 |  | 
| 270 | int QGeoFileTileCache::memoryUsage() const | 
| 271 | { | 
| 272 |     return memoryCache_.totalCost(); | 
| 273 | } | 
| 274 |  | 
| 275 | void QGeoFileTileCache::(int textureUsage) | 
| 276 | { | 
| 277 |     extraTextureUsage_ = textureUsage; | 
| 278 |     textureCache_.setMaxCost(maxCost: minTextureUsage_ + extraTextureUsage_); | 
| 279 |     isTextureCostSet_ = true; | 
| 280 | } | 
| 281 |  | 
| 282 | void QGeoFileTileCache::setMinTextureUsage(int textureUsage) | 
| 283 | { | 
| 284 |     minTextureUsage_ = textureUsage; | 
| 285 |     textureCache_.setMaxCost(maxCost: minTextureUsage_ + extraTextureUsage_); | 
| 286 | } | 
| 287 |  | 
| 288 | int QGeoFileTileCache::maxTextureUsage() const | 
| 289 | { | 
| 290 |     return textureCache_.maxCost(); | 
| 291 | } | 
| 292 |  | 
| 293 | int QGeoFileTileCache::minTextureUsage() const | 
| 294 | { | 
| 295 |     return minTextureUsage_; | 
| 296 | } | 
| 297 |  | 
| 298 |  | 
| 299 | int QGeoFileTileCache::textureUsage() const | 
| 300 | { | 
| 301 |     return textureCache_.totalCost(); | 
| 302 | } | 
| 303 |  | 
| 304 | void QGeoFileTileCache::clearAll() | 
| 305 | { | 
| 306 |     textureCache_.clear(); | 
| 307 |     memoryCache_.clear(); | 
| 308 |     diskCache_.clear(); | 
| 309 |     QDir dir(directory_); | 
| 310 |     dir.setNameFilters(QStringList() << QLatin1String("*-*-*-*.*" )); | 
| 311 |     dir.setFilter(QDir::Files); | 
| 312 |     foreach (QString dirFile, dir.entryList()) { | 
| 313 |         dir.remove(fileName: dirFile); | 
| 314 |     } | 
| 315 | } | 
| 316 |  | 
| 317 | void QGeoFileTileCache::clearMapId(const int mapId) | 
| 318 | { | 
| 319 |     for (const QGeoTileSpec &k : diskCache_.keys()) | 
| 320 |         if (k.mapId() == mapId) | 
| 321 |             diskCache_.remove(key: k, force: true); | 
| 322 |     for (const QGeoTileSpec &k : memoryCache_.keys()) | 
| 323 |         if (k.mapId() == mapId) | 
| 324 |             memoryCache_.remove(key: k); | 
| 325 |     for (const QGeoTileSpec &k : textureCache_.keys()) | 
| 326 |         if (k.mapId() == mapId) | 
| 327 |             textureCache_.remove(key: k); | 
| 328 |  | 
| 329 |     // TODO: It seems the cache leaves residues, like some tiles do not get picked up. | 
| 330 |     // After the above calls, files that shouldnt be left behind are still on disk. | 
| 331 |     // Do an additional pass and make sure what has to be deleted gets deleted. | 
| 332 |     QDir dir(directory_); | 
| 333 |     QStringList formats; | 
| 334 |     formats << QLatin1String("*.*" ); | 
| 335 |     QStringList files = dir.entryList(nameFilters: formats, filters: QDir::Files); | 
| 336 |     qWarning() << "Old tile data detected. Cache eviction left out " << files.size() << "tiles" ; | 
| 337 |     for (const QString &tileFileName : files) { | 
| 338 |         QGeoTileSpec spec = filenameToTileSpec(filename: tileFileName); | 
| 339 |         if (spec.mapId() != mapId) | 
| 340 |             continue; | 
| 341 |         QFile::remove(fileName: dir.filePath(fileName: tileFileName)); | 
| 342 |     } | 
| 343 | } | 
| 344 |  | 
| 345 | void QGeoFileTileCache::setCostStrategyDisk(QAbstractGeoTileCache::CostStrategy costStrategy) | 
| 346 | { | 
| 347 |     costStrategyDisk_ = costStrategy; | 
| 348 | } | 
| 349 |  | 
| 350 | QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyDisk() const | 
| 351 | { | 
| 352 |     return costStrategyDisk_; | 
| 353 | } | 
| 354 |  | 
| 355 | void QGeoFileTileCache::setCostStrategyMemory(QAbstractGeoTileCache::CostStrategy costStrategy) | 
| 356 | { | 
| 357 |     costStrategyMemory_ = costStrategy; | 
| 358 | } | 
| 359 |  | 
| 360 | QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyMemory() const | 
| 361 | { | 
| 362 |     return costStrategyMemory_; | 
| 363 | } | 
| 364 |  | 
| 365 | void QGeoFileTileCache::setCostStrategyTexture(QAbstractGeoTileCache::CostStrategy costStrategy) | 
| 366 | { | 
| 367 |     costStrategyTexture_ = costStrategy; | 
| 368 | } | 
| 369 |  | 
| 370 | QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyTexture() const | 
| 371 | { | 
| 372 |     return costStrategyTexture_; | 
| 373 | } | 
| 374 |  | 
| 375 | QSharedPointer<QGeoTileTexture> QGeoFileTileCache::get(const QGeoTileSpec &spec) | 
| 376 | { | 
| 377 |     QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec); | 
| 378 |     if (tt) | 
| 379 |         return tt; | 
| 380 |     return getFromDisk(spec); | 
| 381 | } | 
| 382 |  | 
| 383 | void QGeoFileTileCache::insert(const QGeoTileSpec &spec, | 
| 384 |                            const QByteArray &bytes, | 
| 385 |                            const QString &format, | 
| 386 |                            QAbstractGeoTileCache::CacheAreas areas) | 
| 387 | { | 
| 388 |     if (bytes.isEmpty()) | 
| 389 |         return; | 
| 390 |  | 
| 391 |     if (areas & QAbstractGeoTileCache::DiskCache) { | 
| 392 |         QString filename = tileSpecToFilename(spec, format, directory: directory_); | 
| 393 |         addToDiskCache(spec, filename, bytes); | 
| 394 |     } | 
| 395 |  | 
| 396 |     if (areas & QAbstractGeoTileCache::MemoryCache) { | 
| 397 |         addToMemoryCache(spec, bytes, format); | 
| 398 |     } | 
| 399 |  | 
| 400 |     /* inserts do not hit the texture cache -- this actually reduces overall | 
| 401 |      * cache hit rates because many tiles come too late to be useful | 
| 402 |      * and act as a poison */ | 
| 403 | } | 
| 404 |  | 
| 405 | QString QGeoFileTileCache::tileSpecToFilenameDefault(const QGeoTileSpec &spec, const QString &format, const QString &directory) | 
| 406 | { | 
| 407 |     QString filename = spec.plugin(); | 
| 408 |     filename += QLatin1String("-" ); | 
| 409 |     filename += QString::number(spec.mapId()); | 
| 410 |     filename += QLatin1String("-" ); | 
| 411 |     filename += QString::number(spec.zoom()); | 
| 412 |     filename += QLatin1String("-" ); | 
| 413 |     filename += QString::number(spec.x()); | 
| 414 |     filename += QLatin1String("-" ); | 
| 415 |     filename += QString::number(spec.y()); | 
| 416 |  | 
| 417 |     //Append version if real version number to ensure backwards compatibility and eviction of old tiles | 
| 418 |     if (spec.version() != -1) { | 
| 419 |         filename += QLatin1String("-" ); | 
| 420 |         filename += QString::number(spec.version()); | 
| 421 |     } | 
| 422 |  | 
| 423 |     filename += QLatin1String("." ); | 
| 424 |     filename += format; | 
| 425 |  | 
| 426 |     QDir dir = QDir(directory); | 
| 427 |  | 
| 428 |     return dir.filePath(fileName: filename); | 
| 429 | } | 
| 430 |  | 
| 431 | QGeoTileSpec QGeoFileTileCache::filenameToTileSpecDefault(const QString &filename) | 
| 432 | { | 
| 433 |     QGeoTileSpec emptySpec; | 
| 434 |  | 
| 435 |     QStringList parts = filename.split(sep: '.'); | 
| 436 |  | 
| 437 |     if (parts.length() != 2) | 
| 438 |         return emptySpec; | 
| 439 |  | 
| 440 |     QString name = parts.at(i: 0); | 
| 441 |     QStringList fields = name.split(sep: '-'); | 
| 442 |  | 
| 443 |     int length = fields.length(); | 
| 444 |     if (length != 5 && length != 6) | 
| 445 |         return emptySpec; | 
| 446 |  | 
| 447 |     QList<int> numbers; | 
| 448 |  | 
| 449 |     bool ok = false; | 
| 450 |     for (int i = 1; i < length; ++i) { | 
| 451 |         ok = false; | 
| 452 |         int value = fields.at(i).toInt(ok: &ok); | 
| 453 |         if (!ok) | 
| 454 |             return emptySpec; | 
| 455 |         numbers.append(t: value); | 
| 456 |     } | 
| 457 |  | 
| 458 |     //File name without version, append default | 
| 459 |     if (numbers.length() < 5) | 
| 460 |         numbers.append(t: -1); | 
| 461 |  | 
| 462 |     return QGeoTileSpec(fields.at(i: 0), | 
| 463 |                     numbers.at(i: 0), | 
| 464 |                     numbers.at(i: 1), | 
| 465 |                     numbers.at(i: 2), | 
| 466 |                     numbers.at(i: 3), | 
| 467 |                     numbers.at(i: 4)); | 
| 468 | } | 
| 469 |  | 
| 470 | void QGeoFileTileCache::evictFromDiskCache(QGeoCachedTileDisk *td) | 
| 471 | { | 
| 472 |     QFile::remove(fileName: td->filename); | 
| 473 | } | 
| 474 |  | 
| 475 | void QGeoFileTileCache::evictFromMemoryCache(QGeoCachedTileMemory * /* tm  */) | 
| 476 | { | 
| 477 | } | 
| 478 |  | 
| 479 | QSharedPointer<QGeoCachedTileDisk> QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename) | 
| 480 | { | 
| 481 |     QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk); | 
| 482 |     td->spec = spec; | 
| 483 |     td->filename = filename; | 
| 484 |     td->cache = this; | 
| 485 |  | 
| 486 |     int cost = 1; | 
| 487 |     if (costStrategyDisk_ == ByteSize) { | 
| 488 |         QFileInfo fi(filename); | 
| 489 |         cost = fi.size(); | 
| 490 |     } | 
| 491 |     diskCache_.insert(key: spec, object: td, cost); | 
| 492 |     return td; | 
| 493 | } | 
| 494 |  | 
| 495 | bool QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename, const QByteArray &bytes) | 
| 496 | { | 
| 497 |     QSharedPointer<QGeoCachedTileDisk> td(new QGeoCachedTileDisk); | 
| 498 |     td->spec = spec; | 
| 499 |     td->filename = filename; | 
| 500 |     td->cache = this; | 
| 501 |  | 
| 502 |     int cost = 1; | 
| 503 |     if (costStrategyDisk_ == ByteSize) | 
| 504 |         cost = bytes.size(); | 
| 505 |  | 
| 506 |     if (diskCache_.insert(key: spec, object: td, cost)) { | 
| 507 |         QFile file(filename); | 
| 508 |         file.open(flags: QIODevice::WriteOnly); | 
| 509 |         file.write(data: bytes); | 
| 510 |         file.close(); | 
| 511 |         return true; | 
| 512 |     } | 
| 513 |     return false; | 
| 514 | } | 
| 515 |  | 
| 516 | void QGeoFileTileCache::addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format) | 
| 517 | { | 
| 518 |     if (isTileBogus(bytes)) | 
| 519 |         return; | 
| 520 |  | 
| 521 |     QSharedPointer<QGeoCachedTileMemory> tm(new QGeoCachedTileMemory); | 
| 522 |     tm->spec = spec; | 
| 523 |     tm->cache = this; | 
| 524 |     tm->bytes = bytes; | 
| 525 |     tm->format = format; | 
| 526 |  | 
| 527 |     int cost = 1; | 
| 528 |     if (costStrategyMemory_ == ByteSize) | 
| 529 |         cost = bytes.size(); | 
| 530 |     memoryCache_.insert(key: spec, object: tm, cost); | 
| 531 | } | 
| 532 |  | 
| 533 | QSharedPointer<QGeoTileTexture> QGeoFileTileCache::addToTextureCache(const QGeoTileSpec &spec, const QImage &image) | 
| 534 | { | 
| 535 |     QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture); | 
| 536 |     tt->spec = spec; | 
| 537 |     tt->image = image; | 
| 538 |  | 
| 539 |     int cost = 1; | 
| 540 |     if (costStrategyTexture_ == ByteSize) | 
| 541 |         cost = image.width() * image.height() * image.depth() / 8; | 
| 542 |     textureCache_.insert(key: spec, object: tt, cost); | 
| 543 |  | 
| 544 |     return tt; | 
| 545 | } | 
| 546 |  | 
| 547 | QSharedPointer<QGeoTileTexture> QGeoFileTileCache::getFromMemory(const QGeoTileSpec &spec) | 
| 548 | { | 
| 549 |     QSharedPointer<QGeoTileTexture> tt = textureCache_.object(key: spec); | 
| 550 |     if (tt) | 
| 551 |         return tt; | 
| 552 |  | 
| 553 |     QSharedPointer<QGeoCachedTileMemory> tm = memoryCache_.object(key: spec); | 
| 554 |     if (tm) { | 
| 555 |         QImage image; | 
| 556 |         if (!image.loadFromData(data: tm->bytes)) { | 
| 557 |             handleError(spec, errorString: QLatin1String("Problem with tile image" )); | 
| 558 |             return QSharedPointer<QGeoTileTexture>(0); | 
| 559 |         } | 
| 560 |         QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, image); | 
| 561 |         if (tt) | 
| 562 |             return tt; | 
| 563 |     } | 
| 564 |     return QSharedPointer<QGeoTileTexture>(); | 
| 565 | } | 
| 566 |  | 
| 567 | QSharedPointer<QGeoTileTexture> QGeoFileTileCache::getFromDisk(const QGeoTileSpec &spec) | 
| 568 | { | 
| 569 |     QSharedPointer<QGeoCachedTileDisk> td = diskCache_.object(key: spec); | 
| 570 |     if (td) { | 
| 571 |         const QString format = QFileInfo(td->filename).suffix(); | 
| 572 |         QFile file(td->filename); | 
| 573 |         file.open(flags: QIODevice::ReadOnly); | 
| 574 |         QByteArray bytes = file.readAll(); | 
| 575 |         file.close(); | 
| 576 |  | 
| 577 |         QImage image; | 
| 578 |         // Some tiles from the servers could be valid images but the tile fetcher | 
| 579 |         // might be able to recognize them as tiles that should not be shown. | 
| 580 |         // If that's the case, the tile fetcher should write "NoRetry" inside the file. | 
| 581 |         if (isTileBogus(bytes)) { | 
| 582 |             QSharedPointer<QGeoTileTexture> tt(new QGeoTileTexture); | 
| 583 |             tt->spec = spec; | 
| 584 |             tt->image = image; | 
| 585 |             return tt; | 
| 586 |         } | 
| 587 |  | 
| 588 |         // This is a truly invalid image. The fetcher should try again. | 
| 589 |         if (!image.loadFromData(data: bytes)) { | 
| 590 |             handleError(spec, errorString: QLatin1String("Problem with tile image" )); | 
| 591 |             return QSharedPointer<QGeoTileTexture>(0); | 
| 592 |         } | 
| 593 |  | 
| 594 |         // Converting it here, instead of in each QSGTexture::bind() | 
| 595 |         if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) | 
| 596 |             image = image.convertToFormat(f: QImage::Format_ARGB32_Premultiplied); | 
| 597 |  | 
| 598 |         addToMemoryCache(spec, bytes, format); | 
| 599 |         QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec: td->spec, image); | 
| 600 |         if (tt) | 
| 601 |             return tt; | 
| 602 |     } | 
| 603 |  | 
| 604 |     return QSharedPointer<QGeoTileTexture>(); | 
| 605 | } | 
| 606 |  | 
| 607 | bool QGeoFileTileCache::isTileBogus(const QByteArray &bytes) const | 
| 608 | { | 
| 609 |     if (bytes.size() == 7 && bytes == QByteArrayLiteral("NoRetry" )) | 
| 610 |         return true; | 
| 611 |     return false; | 
| 612 | } | 
| 613 |  | 
| 614 | QString QGeoFileTileCache::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const | 
| 615 | { | 
| 616 |     return tileSpecToFilenameDefault(spec, format, directory); | 
| 617 | } | 
| 618 |  | 
| 619 | QGeoTileSpec QGeoFileTileCache::filenameToTileSpec(const QString &filename) const | 
| 620 | { | 
| 621 |     return filenameToTileSpecDefault(filename); | 
| 622 | } | 
| 623 |  | 
| 624 | QString QGeoFileTileCache::directory() const | 
| 625 | { | 
| 626 |     return directory_; | 
| 627 | } | 
| 628 |  | 
| 629 | QT_END_NAMESPACE | 
| 630 |  |