void rasterize(std::ostream &output, const OsmAnd::EyePiece::Configuration& cfg)
#endif
{
    // Obtain and configure rasterization style context
    OsmAnd::MapStyles stylesCollection;
    for(auto itStyleFile = cfg.styleFiles.begin(); itStyleFile != cfg.styleFiles.end(); ++itStyleFile)
    {
        const auto& styleFile = *itStyleFile;

        if(!stylesCollection.registerStyle(styleFile.absoluteFilePath()))
            output << xT("Failed to parse metadata of '") << QStringToStlString(styleFile.fileName()) << xT("' or duplicate style") << std::endl;
    }
    std::shared_ptr<const OsmAnd::MapStyle> style;
    if(!stylesCollection.obtainStyle(cfg.styleName, style))
    {
        output << xT("Failed to resolve style '") << QStringToStlString(cfg.styleName) << xT("'") << std::endl;
        return;
    }
    if(cfg.dumpRules)
        style->dump();
    
    OsmAnd::ObfsCollection obfsCollection;
    obfsCollection.watchDirectory(cfg.obfsDir);

    // Collect all map objects (this should be replaced by something like RasterizerViewport/RasterizerContext)
    QList< std::shared_ptr<const OsmAnd::Model::MapObject> > mapObjects;
    OsmAnd::AreaI bbox31(
            OsmAnd::Utilities::get31TileNumberY(cfg.bbox.top),
            OsmAnd::Utilities::get31TileNumberX(cfg.bbox.left),
            OsmAnd::Utilities::get31TileNumberY(cfg.bbox.bottom),
            OsmAnd::Utilities::get31TileNumberX(cfg.bbox.right)
        );
    const auto& obfDI = obfsCollection.obtainDataInterface();
    OsmAnd::MapFoundationType mapFoundation;
    obfDI->obtainMapObjects(&mapObjects, &mapFoundation, bbox31, cfg.zoom, nullptr);
    bool basemapAvailable;
    obfDI->obtainBasemapPresenceFlag(basemapAvailable);
    
    // Calculate output size in pixels
    const auto tileWidth = OsmAnd::Utilities::getTileNumberX(cfg.zoom, cfg.bbox.right) - OsmAnd::Utilities::getTileNumberX(cfg.zoom, cfg.bbox.left);
    const auto tileHeight = OsmAnd::Utilities::getTileNumberY(cfg.zoom, cfg.bbox.bottom) - OsmAnd::Utilities::getTileNumberY(cfg.zoom, cfg.bbox.top);
    const auto pixelWidth = qCeil(tileWidth * cfg.tileSide);
    const auto pixelHeight = qCeil(tileHeight * cfg.tileSide);
    output << xT("Will rasterize ") << mapObjects.count() << xT(" objects onto ") << pixelWidth << xT("x") << pixelHeight << xT(" bitmap") << std::endl;

    // Allocate render target
    SkBitmap renderSurface;
    renderSurface.setConfig(cfg.is32bit ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config, pixelWidth, pixelHeight);
    if(!renderSurface.allocPixels())
    {
        output << xT("Failed to allocated render target ") << pixelWidth << xT("x") << pixelHeight;
        return;
    }
    SkDevice renderTarget(renderSurface);

    // Create render canvas
    SkCanvas canvas(&renderTarget);

    // Perform actual rendering
    OsmAnd::RasterizerEnvironment rasterizerEnv(style, basemapAvailable);
    OsmAnd::RasterizerContext rasterizerContext;
    OsmAnd::Rasterizer::prepareContext(rasterizerEnv, rasterizerContext, bbox31, cfg.zoom, cfg.tileSide, cfg.densityFactor, mapFoundation, mapObjects, OsmAnd::PointF(), nullptr);
    if(cfg.drawMap)
        OsmAnd::Rasterizer::rasterizeMap(rasterizerEnv, rasterizerContext, true, canvas, nullptr);
    /*if(cfg.drawText)
        OsmAnd::Rasterizer::rasterizeText(rasterizerContext, !cfg.drawMap, canvas, nullptr);*/

    // Save rendered area
    if(!cfg.output.isEmpty())
    {
        std::unique_ptr<SkImageEncoder> encoder(CreatePNGImageEncoder());
        std::unique_ptr<SkImageEncoder> outputStream(CreatePNGImageEncoder());
        encoder->encodeFile(cfg.output.toLocal8Bit(), renderSurface, 100);
    }

    return;
}
bool OsmAnd::OfflineMapRasterTileProvider_GPU_P::obtainTile(const TileId& tileId, const ZoomLevel& zoom, std::shared_ptr<MapTile>& outTile)
{
    // Get bounding box that covers this tile
    const auto tileBBox31 = Utilities::tileBoundingBox31(tileId, zoom);

    // Obtain OBF data interface
    const auto& dataInterface = owner->dataProvider->obfsCollection->obtainDataInterface();

    // Get map objects from data proxy
    QList< std::shared_ptr<const Model::MapObject> > mapObjects;
#if defined(_DEBUG) || defined(DEBUG)
    const auto dataRead_Begin = std::chrono::high_resolution_clock::now();
#endif
    bool basemapAvailable;
    MapFoundationType tileFoundation;
    dataInterface->obtainBasemapPresenceFlag(basemapAvailable, nullptr);
    dataInterface->obtainMapObjects(&mapObjects, &tileFoundation, tileBBox31, zoom, nullptr);
#if defined(_DEBUG) || defined(DEBUG)
    const auto dataRead_End = std::chrono::high_resolution_clock::now();
    const std::chrono::duration<float> dataRead_Elapsed = dataRead_End - dataRead_Begin;
#endif

#if defined(_DEBUG) || defined(DEBUG)
    const auto dataRasterization_Begin = std::chrono::high_resolution_clock::now();
#endif

    //TODO: SkGpuDevice
    // Allocate rasterization target
    auto rasterizationSurface = new SkBitmap();
    rasterizationSurface->setConfig(SkBitmap::kARGB_8888_Config, outputTileSize, outputTileSize);
    if(!rasterizationSurface->allocPixels())
    {
        delete rasterizationSurface;

        LogPrintf(LogSeverityLevel::Error, "Failed to allocate buffer for ARGB8888 rasterization surface %dx%d", outputTileSize, outputTileSize);
        return false;
    }
    SkBitmapDevice rasterizationTarget(*rasterizationSurface);

    // Create rasterization canvas
    SkCanvas canvas(&rasterizationTarget);

    // Perform actual rendering
    bool nothingToRasterize = false;
    RasterizerEnvironment rasterizerEnv(owner->dataProvider->mapStyle, basemapAvailable, density);
    RasterizerContext rasterizerContext;
    Rasterizer::prepareContext(rasterizerEnv, rasterizerContext, tileBBox31, zoom, outputTileSize, tileFoundation, mapObjects, OsmAnd::PointF(), &nothingToRasterize, nullptr);
    if(!nothingToRasterize)
        Rasterizer::rasterizeMap(rasterizerEnv, rasterizerContext, true, canvas, nullptr);

#if defined(_DEBUG) || defined(DEBUG)
    const auto dataRasterization_End = std::chrono::high_resolution_clock::now();
    const std::chrono::duration<float> dataRasterization_Elapsed = dataRasterization_End - dataRasterization_Begin;
    if(!nothingToRasterize)
    {
        LogPrintf(LogSeverityLevel::Info,
            "%d map objects from %dx%d@%d: reading %fs, rasterization %fs",
            mapObjects.count(), tileId.x, tileId.y, zoom, dataRead_Elapsed.count(), dataRasterization_Elapsed.count());
    }
    else
    {
        LogPrintf(LogSeverityLevel::Info,
            "%d map objects from %dx%d@%d: reading %fs, nothing to rasterize (%fs)",
            mapObjects.count(), tileId.x, tileId.y, zoom, dataRead_Elapsed.count(), dataRasterization_Elapsed.count());
    }
#endif

    // If there is no data to rasterize, tell that this tile is not available
    if(nothingToRasterize)
    {
        delete rasterizationSurface;

        outTile.reset();
        return true;
    }

    // Or supply newly rasterized tile
    auto tile = new MapBitmapTile(rasterizationSurface, MapBitmapTile::AlphaChannelData::NotPresent);
    outTile.reset(tile);
    return true;
}