ossimRefPtr<ossimImageData> ossimGeneralRasterTileSource::getTile(
   const ossimIrect& tile_rect, ossim_uint32 resLevel)
{
   if ( m_tile.valid() == false )
   {
      allocateTile(); // First time through...
   }
   
   if (m_tile.valid())
   {
      // Image rectangle must be set prior to calling getTile.
      m_tile->setImageRectangle(tile_rect);
      
      if ( getTile( m_tile.get(), resLevel ) == false )
      {
         if (m_tile->getDataObjectStatus() != OSSIM_NULL)
         {
            m_tile->makeBlank();
         }
      }
   }
   
   return m_tile;
}
bool OsmAnd::GPUAPI_OpenGL_Common::uploadTileAsTextureToGPU(const std::shared_ptr< const MapTile >& tile, std::shared_ptr< const ResourceInGPU >& resourceInGPU)
{
    GL_CHECK_PRESENT(glGenTextures);
    GL_CHECK_PRESENT(glBindTexture);
    GL_CHECK_PRESENT(glGenerateMipmap);
    GL_CHECK_PRESENT(glTexParameteri);

    // Depending on tile type, determine texture properties:
    GLsizei sourcePixelByteSize = 0;
    bool mipmapGenerationSupported = false;
    bool tileUsesPalette = false;
    if(tile->dataType == MapTileDataType::Bitmap)
    {
        const auto bitmapTile = std::static_pointer_cast<const MapBitmapTile>(tile);

        switch(bitmapTile->bitmap->getConfig())
        {
        case SkBitmap::Config::kARGB_8888_Config:
            sourcePixelByteSize = 4;
            break;
        case SkBitmap::Config::kARGB_4444_Config:
        case SkBitmap::Config::kRGB_565_Config:
            sourcePixelByteSize = 2;
            break;
        case SkBitmap::Config::kIndex8_Config:
            sourcePixelByteSize = 1;
            tileUsesPalette = true;
            break;
        default:
            assert(false);
            return false;
        }

        // No need to generate mipmaps if textureLod is not supported
        mipmapGenerationSupported = isSupported_textureLod;
    }
    else if(tile->dataType == MapTileDataType::ElevationData)
    {
        sourcePixelByteSize = 4;
        mipmapGenerationSupported = false;
    }
    else
    {
        assert(false);
        return false;
    }
    const auto textureFormat = getTextureFormat(tile);
        
    // Calculate texture size. Tiles are always stored in square textures.
    // Also, since atlas-texture support for tiles was deprecated, only 1 tile per texture is allowed.

    // If tile has NPOT size, then it needs to be rounded-up to nearest POT value
    const auto tileSizePOT = Utilities::getNextPowerOfTwo(tile->size);
    const auto textureSize = (tileSizePOT != tile->size && !isSupported_texturesNPOT) ? tileSizePOT : tile->size;
    const bool useAtlasTexture = (textureSize != tile->size);

    // Get number of mipmap levels
    auto mipmapLevels = 1u;
    if(mipmapGenerationSupported)
        mipmapLevels += qLn(textureSize) / M_LN2;

    // If tile size matches size of texture, upload is quite straightforward
    if(!useAtlasTexture)
    {
        // Create texture id
        GLuint texture;
        glGenTextures(1, &texture);
        GL_CHECK_RESULT;
        assert(texture != 0);

        // Activate texture
        glBindTexture(GL_TEXTURE_2D, texture);
        GL_CHECK_RESULT;

        if(!tileUsesPalette)
        {
            // Allocate square 2D texture
            allocateTexture2D(GL_TEXTURE_2D, mipmapLevels, textureSize, textureSize, textureFormat);

            // Upload data
            uploadDataToTexture2D(GL_TEXTURE_2D, 0,
                0, 0, (GLsizei)tile->size, (GLsizei)tile->size,
                tile->data, tile->rowLength / sourcePixelByteSize, sourcePixelByteSize,
                getSourceFormat(tile));

            // Set maximal mipmap level
            setMipMapLevelsLimit(GL_TEXTURE_2D, mipmapLevels - 1);

            // Generate mipmap levels
            if(mipmapLevels > 1)
            {
                glGenerateMipmap(GL_TEXTURE_2D);
                GL_CHECK_RESULT;
            }
        }
        else
        {
            assert(false);
            /*
            auto bitmapTile = static_cast<IMapBitmapTileProvider::Tile*>(tile.get());

            // Decompress to 32bit color texture
            SkBitmap decompressedTexture;
            bitmapTile->bitmap->deepCopyTo(&decompressedTexture, SkBitmap::Config::kARGB_8888_Config);

            // Generate mipmaps
            decompressedTexture.buildMipMap();
            auto generatedLevels = decompressedTexture.extractMipLevel(nullptr, SK_FixedMax, SK_FixedMax);
            if(mipmapLevels > generatedLevels)
            mipmapLevels = generatedLevels;

            LogPrintf(LogSeverityLevel::Info, "colors = %d", bitmapTile->bitmap->getColorTable()->count());

            // For each mipmap level starting from 0, copy data to packed array
            // and prepare merged palette
            for(auto mipmapLevel = 1; mipmapLevel < mipmapLevels; mipmapLevel++)
            {
            SkBitmap mipmapLevelData;

            auto test22 = decompressedTexture.extractMipLevel(&mipmapLevelData, SK_Fixed1<<mipmapLevel, SK_Fixed1<<mipmapLevel);

            SkBitmap recomressed;
            recomressed.setConfig(SkBitmap::kIndex8_Config, tileSize >> mipmapLevel, tileSize >> mipmapLevel);
            recomressed.allocPixels();

            LogPrintf(LogSeverityLevel::Info, "\tcolors = %d", recomressed.getColorTable()->count());
            }

            //TEST:
            auto datasize = 256*sizeof(uint32_t) + bitmapTile->bitmap->getSize();
            uint8_t* buf = new uint8_t[datasize];
            for(auto colorIdx = 0; colorIdx < 256; colorIdx++)
            {
            buf[colorIdx*4 + 0] = colorIdx;
            buf[colorIdx*4 + 1] = colorIdx;
            buf[colorIdx*4 + 2] = colorIdx;
            buf[colorIdx*4 + 3] = 0xFF;
            }
            for(auto pixelIdx = 0; pixelIdx < tileSize*tileSize; pixelIdx++)
            buf[256*4 + pixelIdx] = pixelIdx % 256;
            glCompressedTexImage2D(
            GL_TEXTURE_2D, 0, GL_PALETTE8_RGBA8_OES,
            tileSize, tileSize, 0,
            datasize, buf);
            GL_CHECK_RESULT;
            delete[] buf;
            */
            //TODO:
            // 1. convert to full RGBA8
            // 2. generate required amount of mipmap levels
            // 3. glCompressedTexImage2D to load all mipmap levels at once
        }

        // Deselect atlas as active texture
        glBindTexture(GL_TEXTURE_2D, 0);
        GL_CHECK_RESULT;

        // Create resource-in-GPU descriptor
        const auto textureInGPU = new TextureInGPU(this, reinterpret_cast<RefInGPU>(texture), textureSize, textureSize, mipmapLevels);
        resourceInGPU.reset(static_cast<ResourceInGPU*>(textureInGPU));

        return true;
    }

    // Otherwise, create an atlas texture with 1 slot only:
    //NOTE: No support for palette atlas textures
    assert(!tileUsesPalette);

    // Find proper atlas textures pool by format of texture and full size of tile (including padding)
    AtlasTypeId atlasTypeId;
    atlasTypeId.format = getTextureFormat(tile);
    atlasTypeId.tileSize = tile->size;
    atlasTypeId.tilePadding = 0;
    const auto atlasTexturesPool = obtainAtlasTexturesPool(atlasTypeId);
    if(!atlasTexturesPool)
        return false;

    // Get free slot from that pool
    const auto slotInGPU = allocateTile(atlasTexturesPool,
        [this, textureSize, mipmapLevels, atlasTexturesPool, tile]() -> AtlasTextureInGPU*
        {
            // Allocate texture id
            GLuint texture;
            glGenTextures(1, &texture);
            GL_CHECK_RESULT;
            assert(texture != 0);

            // Select this texture
            glBindTexture(GL_TEXTURE_2D, texture);
            GL_CHECK_RESULT;

            // Allocate space for this texture
            allocateTexture2D(GL_TEXTURE_2D, mipmapLevels, textureSize, textureSize, getTextureFormat(tile));
            GL_CHECK_RESULT;

            // Set maximal mipmap level
            setMipMapLevelsLimit(GL_TEXTURE_2D, mipmapLevels - 1);

            // Deselect texture
            glBindTexture(GL_TEXTURE_2D, 0);
            GL_CHECK_RESULT;

            return new AtlasTextureInGPU(this, reinterpret_cast<RefInGPU>(texture), textureSize, mipmapLevels, atlasTexturesPool);
        });

    // Upload tile to allocated slot in atlas texture

    // Select atlas as active texture
    glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(reinterpret_cast<intptr_t>(slotInGPU->atlasTexture->refInGPU)));
    GL_CHECK_RESULT;

    // Upload data
    uploadDataToTexture2D(GL_TEXTURE_2D, 0,
        0, 0, (GLsizei)tile->size, (GLsizei)tile->size,
        tile->data, tile->rowLength / sourcePixelByteSize, sourcePixelByteSize,
        getSourceFormat(tile));
    GL_CHECK_RESULT;

    // Generate mipmap
    if(slotInGPU->atlasTexture->mipmapLevels > 1)
    {
        glGenerateMipmap(GL_TEXTURE_2D);
        GL_CHECK_RESULT;
    }

    // Deselect atlas as active texture
    glBindTexture(GL_TEXTURE_2D, 0);
    GL_CHECK_RESULT;

    resourceInGPU = slotInGPU;

    return true;
}