void KisVLineIterator2::fetchTileDataForCache(KisTileInfo& kti, qint32 col, qint32 row)
{
    kti.tile = m_dataManager->getTile(col, row, m_writable);
    lockTile(kti.tile);
    kti.data = kti.tile->data();

    // set old data
    kti.oldtile = m_dataManager->getOldTile(col, row);
    lockOldTile(kti.oldtile);
    kti.oldData = kti.oldtile->data();
}
bool OsmAnd::OnlineMapRasterTileProvider_P::obtainTile(const TileId tileId, const ZoomLevel zoom, std::shared_ptr<const MapTile>& outTile, const IQueryController* const queryController)
{
    // Check provider can supply this zoom level
    if(zoom > owner->maxZoom || zoom < owner->minZoom)
    {
        outTile.reset();
        return true;
    }

    // Check if requested tile is already being processed, and wait until that's done
    // to mark that as being processed.
    lockTile(tileId, zoom);

    // Check if requested tile is already in local storage.
    const auto tileLocalRelativePath =
        QString::number(zoom) + QDir::separator() +
        QString::number(tileId.x) + QDir::separator() +
        QString::number(tileId.y) + QLatin1String(".tile");
    QFileInfo localFile;
    {
        QMutexLocker scopedLocker(&_localCachePathMutex);
        localFile.setFile(_localCachePath.filePath(tileLocalRelativePath));
    }
    if(localFile.exists())
    {
        // Since tile is in local storage, it's safe to unmark it as being processed
        unlockTile(tileId, zoom);

        // If local file is empty, it means that requested tile does not exist (has no data)
        if(localFile.size() == 0)
        {
            outTile.reset();
            return true;
        }

        auto bitmap = new SkBitmap();
        SkFILEStream fileStream(qPrintable(localFile.absoluteFilePath()));
        if(!SkImageDecoder::DecodeStream(&fileStream, bitmap, SkBitmap::Config::kNo_Config, SkImageDecoder::kDecodePixels_Mode))
        {
            LogPrintf(LogSeverityLevel::Error, "Failed to decode tile file '%s'", qPrintable(localFile.absoluteFilePath()));

            delete bitmap;

            return false;
        }

        assert(bitmap->width() == bitmap->height());
        assert(bitmap->width() == owner->providerTileSize);

        // Return tile
        auto tile = new MapBitmapTile(bitmap, owner->alphaChannelData);
        outTile.reset(tile);
        return true;
    }

    // Since tile is not in local cache (or cache is disabled, which is the same),
    // the tile must be downloaded from network:

    // If network access is disallowed, return failure
    if(!_networkAccessAllowed)
    {
        // Before returning, unlock tile
        unlockTile(tileId, zoom);

        return false;
    }

    // Check if there is free download slot. If all download slots are used, wait for one to be freed
    if(owner->maxConcurrentDownloads > 0)
    {
        QMutexLocker scopedLocker(&_currentDownloadsCounterMutex);

        while(_currentDownloadsCounter >= owner->maxConcurrentDownloads)
            _currentDownloadsCounterChanged.wait(&_currentDownloadsCounterMutex);

        _currentDownloadsCounter++;
    }

    // Perform synchronous download
    auto tileUrl = owner->urlPattern;
    tileUrl
        .replace(QLatin1String("${zoom}"), QString::number(zoom))
        .replace(QLatin1String("${x}"), QString::number(tileId.x))
        .replace(QLatin1String("${y}"), QString::number(tileId.y));
    const auto networkReply = Network::Downloader::download(tileUrl);

    // Free download slot
    {
        QMutexLocker scopedLocker(&_currentDownloadsCounterMutex);

        _currentDownloadsCounter--;
        _currentDownloadsCounterChanged.wakeAll();
    }

    // Ensure that all directories are created in path to local tile
    localFile.dir().mkpath(localFile.dir().absolutePath());

    // If there was error, check what the error was
    auto networkError = networkReply->error();
    if(networkError != QNetworkReply::NetworkError::NoError)
    {
        const auto httpStatus = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

        LogPrintf(LogSeverityLevel::Warning, "Failed to download tile from %s (HTTP status %d)", qPrintable(tileUrl), httpStatus);

        // 404 means that this tile does not exist, so create a zero file
        if(httpStatus == 404)
        {
            // Save to a file
            QFile tileFile(localFile.absoluteFilePath());
            if(tileFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
            {
                tileFile.close();

                // Unlock the tile
                unlockTile(tileId, zoom);
                networkReply->deleteLater();
                return true;
            }
            else
            {
                LogPrintf(LogSeverityLevel::Error, "Failed to mark tile as non-existent with empty file '%s'", qPrintable(localFile.absoluteFilePath()));

                // Unlock the tile
                unlockTile(tileId, zoom);
                return false;
            }
        }

        // Unlock the tile
        unlockTile(tileId, zoom);
        return false;
    }

    // Save data to a file
#if OSMAND_DEBUG
    LogPrintf(LogSeverityLevel::Info, "Downloaded tile from %s", qPrintable(tileUrl));
#endif
    const auto& data = networkReply->readAll();

    // Save to a file
    QFile tileFile(localFile.absoluteFilePath());
    if(tileFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        tileFile.write(data);
        tileFile.close();

#if OSMAND_DEBUG
        LogPrintf(LogSeverityLevel::Info, "Saved tile from %s to %s", qPrintable(tileUrl), qPrintable(localFile.absoluteFilePath()));
#endif
    }
    else
        LogPrintf(LogSeverityLevel::Error, "Failed to save tile to '%s'", qPrintable(localFile.absoluteFilePath()));

    // Unlock tile, since local storage work is done
    unlockTile(tileId, zoom);

    // Decode in-memory
    auto bitmap = new SkBitmap();
    if(!SkImageDecoder::DecodeMemory(data.data(), data.size(), bitmap, SkBitmap::Config::kNo_Config, SkImageDecoder::kDecodePixels_Mode))
    {
        LogPrintf(LogSeverityLevel::Error, "Failed to decode tile file from '%s'", qPrintable(tileUrl));

        delete bitmap;

        return false;
    }

    assert(bitmap->width() == bitmap->height());
    assert(bitmap->width() == owner->providerTileSize);

    // Return tile
    auto tile = new MapBitmapTile(bitmap, owner->alphaChannelData);
    outTile.reset(tile);
    return true;
}