LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - gdalgeopackagerasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1869 2025 92.3 %
Date: 2025-01-18 12:42:00 Functions: 46 47 97.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoPackage Translator
       4             :  * Purpose:  Implements GDALGeoPackageRasterBand class
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_geopackage.h"
      14             : #include "memdataset.h"
      15             : #include "gdal_alg_priv.h"
      16             : #include "ogrsqlitevfs.h"
      17             : #include "cpl_error.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cmath>
      21             : #include <limits>
      22             : #include <set>
      23             : #include <utility>
      24             : 
      25             : #if !defined(DEBUG_VERBOSE) && defined(DEBUG_VERBOSE_GPKG)
      26             : #define DEBUG_VERBOSE
      27             : #endif
      28             : 
      29             : /************************************************************************/
      30             : /*                    GDALGPKGMBTilesLikePseudoDataset()                */
      31             : /************************************************************************/
      32             : 
      33        2526 : GDALGPKGMBTilesLikePseudoDataset::GDALGPKGMBTilesLikePseudoDataset()
      34             :     : m_bForceTempDBCompaction(
      35        2526 :           CPLTestBool(CPLGetConfigOption("GPKG_FORCE_TEMPDB_COMPACTION", "NO")))
      36             : {
      37       12630 :     for (int i = 0; i < 4; i++)
      38             :     {
      39       10104 :         m_asCachedTilesDesc[i].nRow = -1;
      40       10104 :         m_asCachedTilesDesc[i].nCol = -1;
      41       10104 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
      42       10104 :         m_asCachedTilesDesc[i].abBandDirty[0] = FALSE;
      43       10104 :         m_asCachedTilesDesc[i].abBandDirty[1] = FALSE;
      44       10104 :         m_asCachedTilesDesc[i].abBandDirty[2] = FALSE;
      45       10104 :         m_asCachedTilesDesc[i].abBandDirty[3] = FALSE;
      46             :     }
      47        2526 : }
      48             : 
      49             : /************************************************************************/
      50             : /*                 ~GDALGPKGMBTilesLikePseudoDataset()                  */
      51             : /************************************************************************/
      52             : 
      53        2526 : GDALGPKGMBTilesLikePseudoDataset::~GDALGPKGMBTilesLikePseudoDataset()
      54             : {
      55        2526 :     if (m_poParentDS == nullptr && m_hTempDB != nullptr)
      56             :     {
      57          48 :         sqlite3_close(m_hTempDB);
      58          48 :         m_hTempDB = nullptr;
      59          48 :         VSIUnlink(m_osTempDBFilename);
      60          48 :         if (m_pMyVFS)
      61             :         {
      62          29 :             sqlite3_vfs_unregister(m_pMyVFS);
      63          29 :             CPLFree(m_pMyVFS->pAppData);
      64          29 :             CPLFree(m_pMyVFS);
      65             :         }
      66             :     }
      67        2526 :     CPLFree(m_pabyCachedTiles);
      68        2526 :     delete m_poCT;
      69        2526 :     CPLFree(m_pabyHugeColorArray);
      70        2526 : }
      71             : 
      72             : /************************************************************************/
      73             : /*                            SetDataType()                             */
      74             : /************************************************************************/
      75             : 
      76         306 : void GDALGPKGMBTilesLikePseudoDataset::SetDataType(GDALDataType eDT)
      77             : {
      78         306 :     CPLAssert(eDT == GDT_Byte || eDT == GDT_Int16 || eDT == GDT_UInt16 ||
      79             :               eDT == GDT_Float32);
      80         306 :     m_eDT = eDT;
      81         306 :     m_nDTSize = GDALGetDataTypeSizeBytes(m_eDT);
      82         306 : }
      83             : 
      84             : /************************************************************************/
      85             : /*                        SetGlobalOffsetScale()                        */
      86             : /************************************************************************/
      87             : 
      88          75 : void GDALGPKGMBTilesLikePseudoDataset::SetGlobalOffsetScale(double dfOffset,
      89             :                                                             double dfScale)
      90             : {
      91          75 :     m_dfOffset = dfOffset;
      92          75 :     m_dfScale = dfScale;
      93          75 : }
      94             : 
      95             : /************************************************************************/
      96             : /*                      GDALGPKGMBTilesLikeRasterBand()                 */
      97             : /************************************************************************/
      98             : 
      99             : // Recent GCC versions complain about null dereference of m_poTPD in
     100             : // the constructor body
     101             : #ifdef __GNUC__
     102             : #pragma GCC diagnostic push
     103             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     104             : #endif
     105             : 
     106        2526 : GDALGPKGMBTilesLikeRasterBand::GDALGPKGMBTilesLikeRasterBand(
     107        2526 :     GDALGPKGMBTilesLikePseudoDataset *poTPD, int nTileWidth, int nTileHeight)
     108        2526 :     : m_poTPD(poTPD)
     109             : {
     110        2526 :     eDataType = m_poTPD->m_eDT;
     111        2526 :     m_nDTSize = m_poTPD->m_nDTSize;
     112        2526 :     nBlockXSize = nTileWidth;
     113        2526 :     nBlockYSize = nTileHeight;
     114        2526 : }
     115             : 
     116             : #ifdef __GNUC__
     117             : #pragma GCC diagnostic pop
     118             : #endif
     119             : 
     120             : /************************************************************************/
     121             : /*                              FlushCache()                            */
     122             : /************************************************************************/
     123             : 
     124        5451 : CPLErr GDALGPKGMBTilesLikeRasterBand::FlushCache(bool bAtClosing)
     125             : {
     126        5451 :     m_poTPD->m_nLastSpaceCheckTimestamp = -1;  // disable partial flushes
     127        5451 :     CPLErr eErr = GDALPamRasterBand::FlushCache(bAtClosing);
     128        5451 :     if (eErr == CE_None)
     129        5448 :         eErr = m_poTPD->IFlushCacheWithErrCode(bAtClosing);
     130        5451 :     m_poTPD->m_nLastSpaceCheckTimestamp = 0;
     131        5451 :     return eErr;
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /*                              FlushTiles()                            */
     136             : /************************************************************************/
     137             : 
     138        3248 : CPLErr GDALGPKGMBTilesLikePseudoDataset::FlushTiles()
     139             : {
     140        3248 :     CPLErr eErr = CE_None;
     141        3248 :     GDALGPKGMBTilesLikePseudoDataset *poMainDS =
     142        3248 :         m_poParentDS ? m_poParentDS : this;
     143        3248 :     if (poMainDS->m_nTileInsertionCount < 0)
     144           7 :         return CE_Failure;
     145             : 
     146        3241 :     if (IGetUpdate())
     147             :     {
     148        1932 :         if (m_nShiftXPixelsMod || m_nShiftYPixelsMod)
     149             :         {
     150         607 :             eErr = FlushRemainingShiftedTiles(false /* total flush*/);
     151             :         }
     152             :         else
     153             :         {
     154        1325 :             eErr = WriteTile();
     155             :         }
     156             :     }
     157             : 
     158        3241 :     if (poMainDS->m_nTileInsertionCount > 0)
     159             :     {
     160         180 :         if (poMainDS->ICommitTransaction() != OGRERR_NONE)
     161             :         {
     162           0 :             poMainDS->m_nTileInsertionCount = -1;
     163           0 :             eErr = CE_Failure;
     164             :         }
     165             :         else
     166             :         {
     167         180 :             poMainDS->m_nTileInsertionCount = 0;
     168             :         }
     169             :     }
     170        3241 :     return eErr;
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                             GetColorTable()                          */
     175             : /************************************************************************/
     176             : 
     177         335 : GDALColorTable *GDALGPKGMBTilesLikeRasterBand::GetColorTable()
     178             : {
     179         335 :     if (poDS->GetRasterCount() != 1)
     180         127 :         return nullptr;
     181             : 
     182         208 :     if (!m_poTPD->m_bTriedEstablishingCT)
     183             :     {
     184          60 :         m_poTPD->m_bTriedEstablishingCT = true;
     185          60 :         if (m_poTPD->m_poParentDS != nullptr)
     186             :         {
     187          16 :             m_poTPD->m_poCT =
     188           8 :                 m_poTPD->m_poParentDS->IGetRasterBand(1)->GetColorTable();
     189           8 :             if (m_poTPD->m_poCT)
     190           4 :                 m_poTPD->m_poCT = m_poTPD->m_poCT->Clone();
     191           8 :             return m_poTPD->m_poCT;
     192             :         }
     193             : 
     194          62 :         for (int i = 0; i < 2; i++)
     195             :         {
     196          57 :             bool bRetry = false;
     197          57 :             char *pszSQL = nullptr;
     198          57 :             if (i == 0)
     199             :             {
     200          52 :                 pszSQL = sqlite3_mprintf("SELECT tile_data FROM \"%w\" "
     201             :                                          "WHERE zoom_level = %d LIMIT 1",
     202          52 :                                          m_poTPD->m_osRasterTable.c_str(),
     203          52 :                                          m_poTPD->m_nZoomLevel);
     204             :             }
     205             :             else
     206             :             {
     207             :                 // Try a tile in the middle of the raster
     208           5 :                 pszSQL = sqlite3_mprintf(
     209             :                     "SELECT tile_data FROM \"%w\" "
     210             :                     "WHERE zoom_level = %d AND tile_column = %d AND tile_row = "
     211             :                     "%d",
     212           5 :                     m_poTPD->m_osRasterTable.c_str(), m_poTPD->m_nZoomLevel,
     213           5 :                     m_poTPD->m_nShiftXTiles + nRasterXSize / 2 / nBlockXSize,
     214           5 :                     m_poTPD->GetRowFromIntoTopConvention(
     215           5 :                         m_poTPD->m_nShiftYTiles +
     216           5 :                         nRasterYSize / 2 / nBlockYSize));
     217             :             }
     218          57 :             sqlite3_stmt *hStmt = nullptr;
     219          57 :             int rc = sqlite3_prepare_v2(m_poTPD->IGetDB(), pszSQL, -1, &hStmt,
     220             :                                         nullptr);
     221          57 :             if (rc == SQLITE_OK)
     222             :             {
     223          57 :                 rc = sqlite3_step(hStmt);
     224          72 :                 if (rc == SQLITE_ROW &&
     225          15 :                     sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
     226             :                 {
     227          15 :                     const int nBytes = sqlite3_column_bytes(hStmt, 0);
     228             :                     GByte *pabyRawData = reinterpret_cast<GByte *>(
     229          15 :                         const_cast<void *>(sqlite3_column_blob(hStmt, 0)));
     230             :                     const CPLString osMemFileName(
     231          30 :                         VSIMemGenerateHiddenFilename("gpkg_read_tile"));
     232          15 :                     VSILFILE *fp = VSIFileFromMemBuffer(
     233             :                         osMemFileName.c_str(), pabyRawData, nBytes, FALSE);
     234          15 :                     VSIFCloseL(fp);
     235             : 
     236             :                     // Only PNG can have color table.
     237          15 :                     const char *const apszDrivers[] = {"PNG", nullptr};
     238             :                     auto poDSTile = std::unique_ptr<GDALDataset>(
     239             :                         GDALDataset::Open(osMemFileName.c_str(),
     240             :                                           GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     241          30 :                                           apszDrivers, nullptr, nullptr));
     242          15 :                     if (poDSTile != nullptr)
     243             :                     {
     244          15 :                         if (poDSTile->GetRasterCount() == 1)
     245             :                         {
     246          10 :                             m_poTPD->m_poCT =
     247           5 :                                 poDSTile->GetRasterBand(1)->GetColorTable();
     248           5 :                             if (m_poTPD->m_poCT != nullptr)
     249           1 :                                 m_poTPD->m_poCT = m_poTPD->m_poCT->Clone();
     250             :                         }
     251             :                         else
     252             :                         {
     253          10 :                             bRetry = true;
     254             :                         }
     255             :                     }
     256             :                     else
     257           0 :                         bRetry = true;
     258             : 
     259          15 :                     VSIUnlink(osMemFileName);
     260             :                 }
     261             :             }
     262          57 :             sqlite3_free(pszSQL);
     263          57 :             sqlite3_finalize(hStmt);
     264          57 :             if (!bRetry)
     265          47 :                 break;
     266             :         }
     267             :     }
     268             : 
     269         200 :     return m_poTPD->m_poCT;
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                             SetColorTable()                          */
     274             : /************************************************************************/
     275             : 
     276          15 : CPLErr GDALGPKGMBTilesLikeRasterBand::SetColorTable(GDALColorTable *poCT)
     277             : {
     278          15 :     if (m_poTPD->m_eDT != GDT_Byte)
     279           0 :         return CE_Failure;
     280          15 :     if (poDS->GetRasterCount() != 1)
     281             :     {
     282           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     283             :                  "SetColorTable() only supported for a single band dataset");
     284           1 :         return CE_Failure;
     285             :     }
     286          14 :     if (!m_poTPD->m_bNew || m_poTPD->m_bTriedEstablishingCT)
     287             :     {
     288           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     289             :                  "SetColorTable() only supported on a newly created dataset");
     290           2 :         return CE_Failure;
     291             :     }
     292             : 
     293          12 :     AssignColorTable(poCT);
     294          12 :     return CE_None;
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                          AssignColorTable()                          */
     299             : /************************************************************************/
     300             : 
     301          15 : void GDALGPKGMBTilesLikeRasterBand::AssignColorTable(const GDALColorTable *poCT)
     302             : {
     303          15 :     m_poTPD->m_bTriedEstablishingCT = true;
     304          15 :     delete m_poTPD->m_poCT;
     305          15 :     if (poCT != nullptr)
     306          14 :         m_poTPD->m_poCT = poCT->Clone();
     307             :     else
     308           1 :         m_poTPD->m_poCT = nullptr;
     309          15 : }
     310             : 
     311             : /************************************************************************/
     312             : /*                        GetColorInterpretation()                      */
     313             : /************************************************************************/
     314             : 
     315         208 : GDALColorInterp GDALGPKGMBTilesLikeRasterBand::GetColorInterpretation()
     316             : {
     317         208 :     if (m_poTPD->m_eDT != GDT_Byte)
     318          22 :         return GCI_Undefined;
     319         186 :     if (poDS->GetRasterCount() == 1)
     320          48 :         return GetColorTable() ? GCI_PaletteIndex : GCI_GrayIndex;
     321         138 :     else if (poDS->GetRasterCount() == 2)
     322           5 :         return (nBand == 1) ? GCI_GrayIndex : GCI_AlphaBand;
     323             :     else
     324         133 :         return static_cast<GDALColorInterp>(GCI_RedBand + (nBand - 1));
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                        SetColorInterpretation()                      */
     329             : /************************************************************************/
     330             : 
     331             : CPLErr
     332          31 : GDALGPKGMBTilesLikeRasterBand::SetColorInterpretation(GDALColorInterp eInterp)
     333             : {
     334          31 :     if (eInterp == GCI_Undefined)
     335           1 :         return CE_None;
     336          32 :     if (poDS->GetRasterCount() == 1 &&
     337           2 :         (eInterp == GCI_GrayIndex || eInterp == GCI_PaletteIndex))
     338          23 :         return CE_None;
     339          11 :     if (poDS->GetRasterCount() == 2 &&
     340           4 :         ((nBand == 1 && eInterp == GCI_GrayIndex) ||
     341           3 :          (nBand == 2 && eInterp == GCI_AlphaBand)))
     342           2 :         return CE_None;
     343           5 :     if (poDS->GetRasterCount() >= 3 && eInterp == GCI_RedBand + nBand - 1)
     344           1 :         return CE_None;
     345           4 :     CPLError(CE_Warning, CPLE_NotSupported,
     346             :              "%s color interpretation not supported. Will be ignored",
     347             :              GDALGetColorInterpretationName(eInterp));
     348           4 :     return CE_Warning;
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                        GPKGFindBestEntry()                           */
     353             : /************************************************************************/
     354             : 
     355           1 : static int GPKGFindBestEntry(GDALColorTable *poCT, GByte c1, GByte c2, GByte c3,
     356             :                              GByte c4, int nTileBandCount)
     357             : {
     358           1 :     const int nEntries = std::min(256, poCT->GetColorEntryCount());
     359           1 :     int iBestIdx = 0;
     360           1 :     int nBestDistance = 4 * 256 * 256;
     361         257 :     for (int i = 0; i < nEntries; i++)
     362             :     {
     363         256 :         const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
     364         256 :         int nDistance = (psEntry->c1 - c1) * (psEntry->c1 - c1) +
     365         256 :                         (psEntry->c2 - c2) * (psEntry->c2 - c2) +
     366         256 :                         (psEntry->c3 - c3) * (psEntry->c3 - c3);
     367         256 :         if (nTileBandCount == 4)
     368         256 :             nDistance += (psEntry->c4 - c4) * (psEntry->c4 - c4);
     369         256 :         if (nDistance < nBestDistance)
     370             :         {
     371          10 :             iBestIdx = i;
     372          10 :             nBestDistance = nDistance;
     373             :         }
     374             :     }
     375           1 :     return iBestIdx;
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                             FillBuffer()                             */
     380             : /************************************************************************/
     381             : 
     382       13842 : void GDALGPKGMBTilesLikePseudoDataset::FillBuffer(GByte *pabyData,
     383             :                                                   size_t nPixels)
     384             : {
     385       13842 :     int bHasNoData = FALSE;
     386       13842 :     const double dfNoDataValue = IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
     387       13842 :     if (!bHasNoData || dfNoDataValue == 0.0)
     388             :     {
     389             :         // cppcheck-suppress nullPointer
     390       13540 :         memset(pabyData, 0, nPixels * m_nDTSize);
     391             :     }
     392             :     else
     393             :     {
     394         302 :         GDALCopyWords64(&dfNoDataValue, GDT_Float64, 0, pabyData, m_eDT,
     395             :                         m_nDTSize, nPixels);
     396             :     }
     397       13842 : }
     398             : 
     399             : /************************************************************************/
     400             : /*                           FillEmptyTile()                            */
     401             : /************************************************************************/
     402             : 
     403        2955 : void GDALGPKGMBTilesLikePseudoDataset::FillEmptyTile(GByte *pabyData)
     404             : {
     405             :     int nBlockXSize, nBlockYSize;
     406        2955 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     407        2955 :     const int nBands = IGetRasterCount();
     408        2955 :     const size_t nPixels =
     409        2955 :         static_cast<size_t>(nBands) * nBlockXSize * nBlockYSize;
     410        2955 :     FillBuffer(pabyData, nPixels);
     411        2955 : }
     412             : 
     413             : /************************************************************************/
     414             : /*                    FillEmptyTileSingleBand()                         */
     415             : /************************************************************************/
     416             : 
     417        1481 : void GDALGPKGMBTilesLikePseudoDataset::FillEmptyTileSingleBand(GByte *pabyData)
     418             : {
     419             :     int nBlockXSize, nBlockYSize;
     420        1481 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     421        1481 :     const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
     422        1481 :     FillBuffer(pabyData, nPixels);
     423        1481 : }
     424             : 
     425             : /************************************************************************/
     426             : /*                           ReadTile()                                 */
     427             : /************************************************************************/
     428             : 
     429        2581 : CPLErr GDALGPKGMBTilesLikePseudoDataset::ReadTile(
     430             :     const CPLString &osMemFileName, GByte *pabyTileData, double dfTileOffset,
     431             :     double dfTileScale, bool *pbIsLossyFormat)
     432             : {
     433        2581 :     const char *const apszDriversByte[] = {"JPEG", "PNG", "WEBP", nullptr};
     434        2581 :     const char *const apszDriversInt[] = {"PNG", nullptr};
     435        2581 :     const char *const apszDriversFloat[] = {"GTiff", nullptr};
     436             :     int nBlockXSize, nBlockYSize;
     437        2581 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     438        2581 :     const int nBands = IGetRasterCount();
     439             :     auto poDSTile = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     440             :         osMemFileName.c_str(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
     441        2581 :         (m_eDT == GDT_Byte)                   ? apszDriversByte
     442          43 :         : (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT) ? apszDriversFloat
     443             :                                               : apszDriversInt,
     444        5162 :         nullptr, nullptr));
     445        2581 :     if (poDSTile == nullptr)
     446             :     {
     447           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse tile data");
     448           2 :         FillEmptyTile(pabyTileData);
     449           2 :         return CE_Failure;
     450             :     }
     451             : 
     452        2579 :     const int nTileBandCount = poDSTile->GetRasterCount();
     453             : 
     454        5158 :     if (!(poDSTile->GetRasterXSize() == nBlockXSize &&
     455        2579 :           poDSTile->GetRasterYSize() == nBlockYSize &&
     456        7737 :           (nTileBandCount >= 1 && nTileBandCount <= 4)) ||
     457        2579 :         (m_eDT != GDT_Byte && nTileBandCount != 1))
     458             :     {
     459           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     460             :                  "Inconsistent tiles characteristics");
     461           0 :         FillEmptyTile(pabyTileData);
     462           0 :         return CE_Failure;
     463             :     }
     464             : 
     465        2579 :     GDALDataType eRequestDT = GDT_Byte;
     466        2579 :     if (m_eTF == GPKG_TF_PNG_16BIT)
     467             :     {
     468          40 :         CPLAssert(m_eDT == GDT_Int16 || m_eDT == GDT_UInt16 ||
     469             :                   m_eDT == GDT_Float32);
     470          40 :         eRequestDT = GDT_UInt16;
     471             :     }
     472        2539 :     else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
     473             :     {
     474           3 :         CPLAssert(m_eDT == GDT_Float32);
     475           3 :         eRequestDT = GDT_Float32;
     476             :     }
     477             : 
     478        2579 :     if (poDSTile->RasterIO(GF_Read, 0, 0, nBlockXSize, nBlockYSize,
     479             :                            pabyTileData, nBlockXSize, nBlockYSize, eRequestDT,
     480             :                            poDSTile->GetRasterCount(), nullptr, 0, 0, 0,
     481        2579 :                            nullptr) != CE_None)
     482             :     {
     483           0 :         FillEmptyTile(pabyTileData);
     484           0 :         return CE_Failure;
     485             :     }
     486             : 
     487        2579 :     if (m_eDT != GDT_Byte)
     488             :     {
     489          43 :         int bHasNoData = FALSE;
     490             :         const double dfNoDataValue =
     491          43 :             IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
     492          43 :         if (m_eDT == GDT_Int16)
     493             :         {
     494          29 :             CPLAssert(eRequestDT == GDT_UInt16);
     495          29 :             for (size_t i = 0;
     496     1900570 :                  i < static_cast<size_t>(nBlockXSize) * nBlockYSize; i++)
     497             :             {
     498             :                 GUInt16 usVal;
     499     1900540 :                 memcpy(&usVal, pabyTileData + i * sizeof(GUInt16),
     500             :                        sizeof(usVal));
     501             :                 double dfVal =
     502     1900540 :                     floor((usVal * dfTileScale + dfTileOffset) * m_dfScale +
     503     1900540 :                           m_dfOffset + 0.5);
     504     1900540 :                 if (bHasNoData && usVal == m_usGPKGNull)
     505      195804 :                     dfVal = dfNoDataValue;
     506     1900540 :                 if (dfVal > 32767)
     507           0 :                     dfVal = 32767;
     508     1900540 :                 else if (dfVal < -32768)
     509           0 :                     dfVal = -32768;
     510     1900540 :                 GInt16 sVal = static_cast<GInt16>(dfVal);
     511     1900540 :                 memcpy(pabyTileData + i * sizeof(GUInt16), &sVal, sizeof(sVal));
     512             :             }
     513             :         }
     514          14 :         else if (m_eDT == GDT_UInt16 &&
     515           6 :                  (m_dfOffset != 0.0 || m_dfScale != 1.0 ||
     516           6 :                   dfTileOffset != 0.0 || dfTileScale != 1.0))
     517             :         {
     518           0 :             CPLAssert(eRequestDT == GDT_UInt16);
     519           0 :             GUInt16 *psVal = reinterpret_cast<GUInt16 *>(pabyTileData);
     520           0 :             for (size_t i = 0;
     521           0 :                  i < static_cast<size_t>(nBlockXSize) * nBlockYSize; i++)
     522             :             {
     523           0 :                 const GUInt16 nVal = psVal[i];
     524             :                 double dfVal =
     525           0 :                     floor((nVal * dfTileScale + dfTileOffset) * m_dfScale +
     526           0 :                           m_dfOffset + 0.5);
     527           0 :                 if (bHasNoData && nVal == m_usGPKGNull)
     528           0 :                     dfVal = dfNoDataValue;
     529           0 :                 if (dfVal > 65535)
     530           0 :                     dfVal = 65535;
     531           0 :                 else if (dfVal < 0)
     532           0 :                     dfVal = 0;
     533           0 :                 psVal[i] = static_cast<GUInt16>(dfVal);
     534           0 :             }
     535             :         }
     536          14 :         else if (m_eDT == GDT_Float32 && eRequestDT == GDT_UInt16)
     537             :         {
     538             :             // Due to non identical data type size, we need to start from the
     539             :             // end of the buffer.
     540      327685 :             for (GPtrDiff_t i =
     541           5 :                      static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize - 1;
     542      327685 :                  i >= 0; i--)
     543             :             {
     544             :                 // Use memcpy() and not reinterpret_cast<GUInt16*> and
     545             :                 // reinterpret_cast<float*>, otherwise compilers such as ICC
     546             :                 // may (ab)use rules about aliasing to generate wrong code!
     547             :                 GUInt16 usVal;
     548      327680 :                 memcpy(&usVal, pabyTileData + i * sizeof(GUInt16),
     549             :                        sizeof(usVal));
     550      327680 :                 double dfVal =
     551      327680 :                     (usVal * dfTileScale + dfTileOffset) * m_dfScale +
     552      327680 :                     m_dfOffset;
     553      327680 :                 if (m_dfPrecision == 1.0)
     554      327680 :                     dfVal = floor(dfVal + 0.5);
     555      327680 :                 if (bHasNoData && usVal == m_usGPKGNull)
     556      196206 :                     dfVal = dfNoDataValue;
     557      327680 :                 const float fVal = static_cast<float>(dfVal);
     558      327680 :                 memcpy(pabyTileData + i * sizeof(float), &fVal, sizeof(fVal));
     559             :             }
     560             :         }
     561             : 
     562          43 :         return CE_None;
     563             :     }
     564             : 
     565        2536 :     GDALColorTable *poCT = nullptr;
     566        2536 :     if (nBands == 1 || nTileBandCount == 1)
     567             :     {
     568         152 :         poCT = poDSTile->GetRasterBand(1)->GetColorTable();
     569         152 :         IGetRasterBand(1)->GetColorTable();
     570             :     }
     571             : 
     572        2536 :     if (pbIsLossyFormat)
     573           9 :         *pbIsLossyFormat =
     574           9 :             !EQUAL(poDSTile->GetDriver()->GetDescription(), "PNG") ||
     575           0 :             (poCT != nullptr && poCT->GetColorEntryCount() == 256) /* PNG8 */;
     576             : 
     577             :     /* Map RGB(A) tile to single-band color indexed */
     578        2536 :     const GPtrDiff_t nBlockPixels =
     579        2536 :         static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
     580        2536 :     if (nBands == 1 && m_poCT != nullptr && nTileBandCount != 1)
     581             :     {
     582           1 :         std::map<GUInt32, int> oMapEntryToIndex;
     583           1 :         const int nEntries = std::min(256, m_poCT->GetColorEntryCount());
     584         257 :         for (int i = 0; i < nEntries; i++)
     585             :         {
     586         256 :             const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
     587         256 :             GByte c1 = static_cast<GByte>(psEntry->c1);
     588         256 :             GByte c2 = static_cast<GByte>(psEntry->c2);
     589         256 :             GByte c3 = static_cast<GByte>(psEntry->c3);
     590         256 :             GUInt32 nVal = c1 + (c2 << 8) + (c3 << 16);
     591         256 :             if (nTileBandCount == 4)
     592         256 :                 nVal += (static_cast<GUInt32>(psEntry->c4) << 24);
     593         256 :             oMapEntryToIndex[nVal] = i;
     594             :         }
     595             :         int iBestEntryFor0 =
     596           1 :             GPKGFindBestEntry(m_poCT, 0, 0, 0, 0, nTileBandCount);
     597       10001 :         for (GPtrDiff_t i = 0; i < nBlockPixels; i++)
     598             :         {
     599       10000 :             const GByte c1 = pabyTileData[i];
     600       10000 :             const GByte c2 = pabyTileData[i + nBlockPixels];
     601       10000 :             const GByte c3 = pabyTileData[i + 2 * nBlockPixels];
     602       10000 :             const GByte c4 = pabyTileData[i + 3 * nBlockPixels];
     603       10000 :             GUInt32 nVal = c1 + (c2 << 8) + (c3 << 16);
     604       10000 :             if (nTileBandCount == 4)
     605       10000 :                 nVal += (c4 << 24);
     606       10000 :             if (nVal == 0)
     607             :                 // In most cases we will reach that point at partial tiles.
     608        5000 :                 pabyTileData[i] = static_cast<GByte>(iBestEntryFor0);
     609             :             else
     610             :             {
     611             :                 std::map<GUInt32, int>::iterator oMapEntryToIndexIter =
     612        5000 :                     oMapEntryToIndex.find(nVal);
     613        5000 :                 if (oMapEntryToIndexIter == oMapEntryToIndex.end())
     614             :                     /* Could happen with JPEG tiles */
     615           0 :                     pabyTileData[i] = static_cast<GByte>(GPKGFindBestEntry(
     616             :                         m_poCT, c1, c2, c3, c4, nTileBandCount));
     617             :                 else
     618        5000 :                     pabyTileData[i] =
     619        5000 :                         static_cast<GByte>(oMapEntryToIndexIter->second);
     620             :             }
     621             :         }
     622           1 :         return CE_None;
     623             :     }
     624             : 
     625          30 :     if (nBands == 1 && nTileBandCount == 1 && poCT != nullptr &&
     626        2565 :         m_poCT != nullptr && !poCT->IsSame(m_poCT))
     627             :     {
     628           0 :         CPLError(CE_Warning, CPLE_NotSupported,
     629             :                  "Different color tables. Unhandled for now");
     630             :     }
     631        2535 :     else if ((nBands == 1 && nTileBandCount >= 3) ||
     632          30 :              (nBands == 1 && nTileBandCount == 1 && m_poCT != nullptr &&
     633        2535 :               poCT == nullptr) ||
     634        2535 :              ((nBands == 1 || nBands == 2) && nTileBandCount == 1 &&
     635          35 :               m_poCT == nullptr && poCT != nullptr))
     636             :     {
     637           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     638             :                  "Inconsistent dataset and tiles band characteristics");
     639             :     }
     640             : 
     641        2535 :     if (nBands == 2)
     642             :     {
     643             :         // assuming that the RGB is Grey,Grey,Grey
     644         307 :         if (nTileBandCount == 1 || nTileBandCount == 3)
     645             :         {
     646             :             /* Create fully opaque alpha */
     647          97 :             memset(pabyTileData + 1 * nBlockPixels, 255, nBlockPixels);
     648             :         }
     649         210 :         else if (nTileBandCount == 4)
     650             :         {
     651             :             /* Transfer alpha band */
     652          67 :             memcpy(pabyTileData + 1 * nBlockPixels,
     653          67 :                    pabyTileData + 3 * nBlockPixels, nBlockPixels);
     654             :         }
     655             :     }
     656        2228 :     else if (nTileBandCount == 2)
     657             :     {
     658             :         /* Do Grey+Alpha -> RGBA */
     659         361 :         memcpy(pabyTileData + 3 * nBlockPixels, pabyTileData + 1 * nBlockPixels,
     660             :                nBlockPixels);
     661         361 :         memcpy(pabyTileData + 1 * nBlockPixels, pabyTileData, nBlockPixels);
     662         361 :         memcpy(pabyTileData + 2 * nBlockPixels, pabyTileData, nBlockPixels);
     663             :     }
     664        1867 :     else if (nTileBandCount == 1 && !(nBands == 1 && m_poCT != nullptr))
     665             :     {
     666             :         /* Expand color indexed to RGB(A) */
     667         122 :         if (poCT != nullptr)
     668             :         {
     669             :             GByte abyCT[4 * 256];
     670          15 :             const int nEntries = std::min(256, poCT->GetColorEntryCount());
     671        3600 :             for (int i = 0; i < nEntries; i++)
     672             :             {
     673        3585 :                 const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
     674        3585 :                 abyCT[4 * i] = static_cast<GByte>(psEntry->c1);
     675        3585 :                 abyCT[4 * i + 1] = static_cast<GByte>(psEntry->c2);
     676        3585 :                 abyCT[4 * i + 2] = static_cast<GByte>(psEntry->c3);
     677        3585 :                 abyCT[4 * i + 3] = static_cast<GByte>(psEntry->c4);
     678             :             }
     679         270 :             for (int i = nEntries; i < 256; i++)
     680             :             {
     681         255 :                 abyCT[4 * i] = 0;
     682         255 :                 abyCT[4 * i + 1] = 0;
     683         255 :                 abyCT[4 * i + 2] = 0;
     684         255 :                 abyCT[4 * i + 3] = 0;
     685             :             }
     686      967175 :             for (GPtrDiff_t i = 0; i < nBlockPixels; i++)
     687             :             {
     688      967160 :                 const GByte byVal = pabyTileData[i];
     689      967160 :                 pabyTileData[i] = abyCT[4 * byVal];
     690      967160 :                 pabyTileData[i + 1 * nBlockPixels] = abyCT[4 * byVal + 1];
     691      967160 :                 pabyTileData[i + 2 * nBlockPixels] = abyCT[4 * byVal + 2];
     692      967160 :                 pabyTileData[i + 3 * nBlockPixels] = abyCT[4 * byVal + 3];
     693             :             }
     694             :         }
     695             :         else
     696             :         {
     697         107 :             memcpy(pabyTileData + 1 * nBlockPixels, pabyTileData, nBlockPixels);
     698         107 :             memcpy(pabyTileData + 2 * nBlockPixels, pabyTileData, nBlockPixels);
     699         107 :             if (nBands == 4)
     700             :             {
     701          96 :                 memset(pabyTileData + 3 * nBlockPixels, 255, nBlockPixels);
     702             :             }
     703         122 :         }
     704             :     }
     705        1745 :     else if (nTileBandCount == 3 && nBands == 4)
     706             :     {
     707             :         /* Create fully opaque alpha */
     708         224 :         memset(pabyTileData + 3 * nBlockPixels, 255, nBlockPixels);
     709             :     }
     710             : 
     711        2535 :     return CE_None;
     712             : }
     713             : 
     714             : /************************************************************************/
     715             : /*                           ReadTile()                                 */
     716             : /************************************************************************/
     717             : 
     718        7541 : GByte *GDALGPKGMBTilesLikePseudoDataset::ReadTile(int nRow, int nCol)
     719             : {
     720             :     int nBlockXSize, nBlockYSize;
     721        7541 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     722        7541 :     const int nBands = IGetRasterCount();
     723        7541 :     const size_t nBandBlockSize =
     724        7541 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
     725        7541 :     const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
     726        7541 :     if (m_nShiftXPixelsMod || m_nShiftYPixelsMod)
     727             :     {
     728        4803 :         GByte *pabyData = nullptr;
     729        4803 :         int i = 0;
     730       11981 :         for (; i < 4; i++)
     731             :         {
     732       11981 :             if (m_asCachedTilesDesc[i].nRow == nRow &&
     733        7181 :                 m_asCachedTilesDesc[i].nCol == nCol)
     734             :             {
     735        4803 :                 if (m_asCachedTilesDesc[i].nIdxWithinTileData >= 0)
     736             :                 {
     737        2040 :                     return m_pabyCachedTiles +
     738        2040 :                            nBandBlockSize *
     739        2040 :                                m_asCachedTilesDesc[i].nIdxWithinTileData *
     740        2040 :                                nTileBands;
     741             :                 }
     742             :                 else
     743             :                 {
     744        2763 :                     if (i == 0)
     745         194 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     746         194 :                             (m_asCachedTilesDesc[1].nIdxWithinTileData == 0)
     747         194 :                                 ? 1
     748             :                                 : 0;
     749        2569 :                     else if (i == 1)
     750        1189 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     751        1189 :                             (m_asCachedTilesDesc[0].nIdxWithinTileData == 0)
     752        1189 :                                 ? 1
     753             :                                 : 0;
     754        1380 :                     else if (i == 2)
     755         191 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     756         191 :                             (m_asCachedTilesDesc[3].nIdxWithinTileData == 2)
     757         191 :                                 ? 3
     758             :                                 : 2;
     759             :                     else
     760        1189 :                         m_asCachedTilesDesc[i].nIdxWithinTileData =
     761        1189 :                             (m_asCachedTilesDesc[2].nIdxWithinTileData == 2)
     762        1189 :                                 ? 3
     763             :                                 : 2;
     764        2763 :                     pabyData = m_pabyCachedTiles +
     765        2763 :                                nBandBlockSize *
     766        2763 :                                    m_asCachedTilesDesc[i].nIdxWithinTileData *
     767        2763 :                                    nTileBands;
     768        2763 :                     break;
     769             :                 }
     770             :             }
     771             :         }
     772        2763 :         CPLAssert(i < 4);
     773        2763 :         return ReadTile(nRow, nCol, pabyData);
     774             :     }
     775             :     else
     776             :     {
     777        2738 :         GByte *pabyDest = m_pabyCachedTiles + 2 * nTileBands * nBandBlockSize;
     778        2738 :         bool bAllNonDirty = true;
     779       11225 :         for (int i = 0; i < nBands; i++)
     780             :         {
     781        8487 :             if (m_asCachedTilesDesc[0].abBandDirty[i])
     782           2 :                 bAllNonDirty = false;
     783             :         }
     784        2738 :         if (bAllNonDirty)
     785             :         {
     786        2736 :             return ReadTile(nRow, nCol, pabyDest);
     787             :         }
     788             : 
     789             :         /* If some bands of the blocks are dirty/written we need to fetch */
     790             :         /* the tile in a temporary buffer in order not to override dirty bands*/
     791           2 :         GByte *pabyTemp = m_pabyCachedTiles + 3 * nTileBands * nBandBlockSize;
     792           2 :         if (ReadTile(nRow, nCol, pabyTemp) != nullptr)
     793             :         {
     794           8 :             for (int i = 0; i < nBands; i++)
     795             :             {
     796           6 :                 if (!m_asCachedTilesDesc[0].abBandDirty[i])
     797             :                 {
     798           4 :                     memcpy(pabyDest + i * nBandBlockSize,
     799           4 :                            pabyTemp + i * nBandBlockSize, nBandBlockSize);
     800             :                 }
     801             :             }
     802             :         }
     803           2 :         return pabyDest;
     804             :     }
     805             : }
     806             : 
     807             : /************************************************************************/
     808             : /*                         GetTileOffsetAndScale()                      */
     809             : /************************************************************************/
     810             : 
     811        2581 : void GDALGPKGMBTilesLikePseudoDataset::GetTileOffsetAndScale(
     812             :     GIntBig nTileId, double &dfTileOffset, double &dfTileScale)
     813             : {
     814        2581 :     dfTileOffset = 0.0;
     815        2581 :     dfTileScale = 1.0;
     816             : 
     817        2581 :     if (m_eTF == GPKG_TF_PNG_16BIT)
     818             :     {
     819          40 :         char *pszSQL = sqlite3_mprintf(
     820             :             "SELECT offset, scale FROM gpkg_2d_gridded_tile_ancillary WHERE "
     821             :             "tpudt_name = '%q' AND tpudt_id = ?",
     822             :             m_osRasterTable.c_str());
     823          40 :         sqlite3_stmt *hStmt = nullptr;
     824          40 :         int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
     825          40 :         if (rc == SQLITE_OK)
     826             :         {
     827          40 :             sqlite3_bind_int64(hStmt, 1, nTileId);
     828          40 :             rc = sqlite3_step(hStmt);
     829          40 :             if (rc == SQLITE_ROW)
     830             :             {
     831          40 :                 if (sqlite3_column_type(hStmt, 0) == SQLITE_FLOAT)
     832          40 :                     dfTileOffset = sqlite3_column_double(hStmt, 0);
     833          40 :                 if (sqlite3_column_type(hStmt, 1) == SQLITE_FLOAT)
     834          40 :                     dfTileScale = sqlite3_column_double(hStmt, 1);
     835             :             }
     836          40 :             sqlite3_finalize(hStmt);
     837             :         }
     838          40 :         sqlite3_free(pszSQL);
     839             :     }
     840        2581 : }
     841             : 
     842             : /************************************************************************/
     843             : /*                           ReadTile()                                 */
     844             : /************************************************************************/
     845             : 
     846        5519 : GByte *GDALGPKGMBTilesLikePseudoDataset::ReadTile(int nRow, int nCol,
     847             :                                                   GByte *pabyData,
     848             :                                                   bool *pbIsLossyFormat)
     849             : {
     850        5519 :     int nBlockXSize = 0;
     851        5519 :     int nBlockYSize = 0;
     852        5519 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     853        5519 :     const int nBands = IGetRasterCount();
     854             : 
     855        5519 :     if (pbIsLossyFormat)
     856          18 :         *pbIsLossyFormat = false;
     857             : 
     858        5519 :     const size_t nBandBlockSize =
     859        5519 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
     860        5519 :     if (nRow < 0 || nCol < 0 || nRow >= m_nTileMatrixHeight ||
     861        5462 :         nCol >= m_nTileMatrixWidth)
     862             :     {
     863          73 :         FillEmptyTile(pabyData);
     864          73 :         return pabyData;
     865             :     }
     866             : 
     867             : #ifdef DEBUG_VERBOSE
     868             :     CPLDebug("GPKG", "ReadTile(row=%d, col=%d)", nRow, nCol);
     869             : #endif
     870             : 
     871       16338 :     char *pszSQL = sqlite3_mprintf(
     872             :         "SELECT tile_data%s FROM \"%w\" "
     873             :         "WHERE zoom_level = %d AND tile_row = %d AND tile_column = %d%s",
     874        5446 :         m_eDT != GDT_Byte ? ", id" : "",  // MBTiles do not have an id
     875             :         m_osRasterTable.c_str(), m_nZoomLevel,
     876        5446 :         GetRowFromIntoTopConvention(nRow), nCol,
     877        5446 :         !m_osWHERE.empty() ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str()) : "");
     878             : 
     879             : #ifdef DEBUG_VERBOSE
     880             :     CPLDebug("GPKG", "%s", pszSQL);
     881             : #endif
     882             : 
     883        5446 :     sqlite3_stmt *hStmt = nullptr;
     884        5446 :     int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
     885        5446 :     if (rc != SQLITE_OK)
     886             :     {
     887           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s",
     888           0 :                  pszSQL, sqlite3_errmsg(IGetDB()));
     889           0 :         sqlite3_free(pszSQL);
     890           0 :         return nullptr;
     891             :     }
     892        5446 :     sqlite3_free(pszSQL);
     893        5446 :     rc = sqlite3_step(hStmt);
     894             : 
     895        5446 :     if (rc == SQLITE_ROW && sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
     896             :     {
     897        2564 :         const int nBytes = sqlite3_column_bytes(hStmt, 0);
     898             :         GIntBig nTileId =
     899        2564 :             (m_eDT == GDT_Byte) ? 0 : sqlite3_column_int64(hStmt, 1);
     900             :         GByte *pabyRawData = static_cast<GByte *>(
     901        2564 :             const_cast<void *>(sqlite3_column_blob(hStmt, 0)));
     902             :         const CPLString osMemFileName(
     903        5128 :             VSIMemGenerateHiddenFilename("gpkg_read_tile"));
     904        2564 :         VSILFILE *fp = VSIFileFromMemBuffer(osMemFileName.c_str(), pabyRawData,
     905             :                                             nBytes, FALSE);
     906        2564 :         VSIFCloseL(fp);
     907             : 
     908        2564 :         double dfTileOffset = 0.0;
     909        2564 :         double dfTileScale = 1.0;
     910        2564 :         GetTileOffsetAndScale(nTileId, dfTileOffset, dfTileScale);
     911        2564 :         ReadTile(osMemFileName, pabyData, dfTileOffset, dfTileScale,
     912             :                  pbIsLossyFormat);
     913        2564 :         VSIUnlink(osMemFileName);
     914        2564 :         sqlite3_finalize(hStmt);
     915             :     }
     916        2882 :     else if (rc == SQLITE_BUSY)
     917             :     {
     918           0 :         FillEmptyTile(pabyData);
     919           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     920             :                  "sqlite3_step(%s) failed (SQLITE_BUSY): %s",
     921           0 :                  sqlite3_sql(hStmt), sqlite3_errmsg(IGetDB()));
     922           0 :         sqlite3_finalize(hStmt);
     923           0 :         return pabyData;
     924             :     }
     925             :     else
     926             :     {
     927        2882 :         sqlite3_finalize(hStmt);
     928        2882 :         hStmt = nullptr;
     929             : 
     930        2882 :         if (m_hTempDB && (m_nShiftXPixelsMod || m_nShiftYPixelsMod))
     931             :         {
     932         602 :             const char *pszSQLNew = CPLSPrintf(
     933             :                 "SELECT partial_flag, tile_data_band_1, tile_data_band_2, "
     934             :                 "tile_data_band_3, tile_data_band_4 FROM partial_tiles WHERE "
     935             :                 "zoom_level = %d AND tile_row = %d AND tile_column = %d",
     936             :                 m_nZoomLevel, nRow, nCol);
     937             : 
     938             : #ifdef DEBUG_VERBOSE
     939             :             CPLDebug("GPKG", "%s", pszSQLNew);
     940             : #endif
     941             : 
     942         602 :             rc = sqlite3_prepare_v2(m_hTempDB, pszSQLNew, -1, &hStmt, nullptr);
     943         602 :             if (rc != SQLITE_OK)
     944             :             {
     945           0 :                 FillEmptyTile(pabyData);
     946           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     947             :                          "sqlite3_prepare_v2(%s) failed: %s", pszSQLNew,
     948             :                          sqlite3_errmsg(m_hTempDB));
     949           0 :                 return pabyData;
     950             :             }
     951             : 
     952         602 :             rc = sqlite3_step(hStmt);
     953         602 :             if (rc == SQLITE_ROW)
     954             :             {
     955           2 :                 const int nPartialFlag = sqlite3_column_int(hStmt, 0);
     956          10 :                 for (int iBand = 1; iBand <= nBands; iBand++)
     957             :                 {
     958           8 :                     GByte *pabyDestBand =
     959           8 :                         pabyData + (iBand - 1) * nBandBlockSize;
     960           8 :                     if (nPartialFlag & (((1 << 4) - 1) << (4 * (iBand - 1))))
     961             :                     {
     962           2 :                         CPLAssert(sqlite3_column_bytes(hStmt, iBand) ==
     963             :                                   static_cast<int>(nBandBlockSize));
     964           2 :                         memcpy(pabyDestBand, sqlite3_column_blob(hStmt, iBand),
     965             :                                nBandBlockSize);
     966             :                     }
     967             :                     else
     968             :                     {
     969           6 :                         FillEmptyTileSingleBand(pabyDestBand);
     970             :                     }
     971             :                 }
     972             :             }
     973             :             else
     974             :             {
     975         600 :                 FillEmptyTile(pabyData);
     976             :             }
     977         602 :             sqlite3_finalize(hStmt);
     978             :         }
     979             :         else
     980             :         {
     981        2280 :             FillEmptyTile(pabyData);
     982             :         }
     983             :     }
     984             : 
     985        5446 :     return pabyData;
     986             : }
     987             : 
     988             : /************************************************************************/
     989             : /*                         IReadBlock()                                 */
     990             : /************************************************************************/
     991             : 
     992        3949 : CPLErr GDALGPKGMBTilesLikeRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     993             :                                                  void *pData)
     994             : {
     995             : #ifdef DEBUG_VERBOSE
     996             :     CPLDebug("GPKG",
     997             :              "IReadBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d,m_nZoomLevel=%d)",
     998             :              nBand, nBlockXOff, nBlockYOff, m_poTPD->m_nZoomLevel);
     999             : #endif
    1000             : 
    1001        3949 :     if (m_poTPD->m_pabyCachedTiles == nullptr)
    1002           0 :         return CE_Failure;
    1003             : 
    1004        3949 :     const int nRowMin = nBlockYOff + m_poTPD->m_nShiftYTiles;
    1005        3949 :     int nRowMax = nRowMin;
    1006        3949 :     if (m_poTPD->m_nShiftYPixelsMod)
    1007        1211 :         nRowMax++;
    1008             : 
    1009        3949 :     const int nColMin = nBlockXOff + m_poTPD->m_nShiftXTiles;
    1010        3949 :     int nColMax = nColMin;
    1011        3949 :     if (m_poTPD->m_nShiftXPixelsMod)
    1012        1189 :         nColMax++;
    1013             : 
    1014        2760 : retry:
    1015             :     /* Optimize for left to right reading at constant row */
    1016        3952 :     if (m_poTPD->m_nShiftXPixelsMod || m_poTPD->m_nShiftYPixelsMod)
    1017             :     {
    1018        1214 :         if (nRowMin == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    1019        1036 :             nColMin == m_poTPD->m_asCachedTilesDesc[0].nCol + 1 &&
    1020        1030 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData >= 0)
    1021             :         {
    1022        1030 :             CPLAssert(nRowMin == m_poTPD->m_asCachedTilesDesc[1].nRow);
    1023        1030 :             CPLAssert(nColMin == m_poTPD->m_asCachedTilesDesc[1].nCol);
    1024        1030 :             CPLAssert(m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0 ||
    1025             :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 1);
    1026             : 
    1027             :             /* 0 1  --> 1 -1 */
    1028             :             /* 2 3      3 -1 */
    1029             :             /* or */
    1030             :             /* 1 0  --> 0 -1 */
    1031             :             /* 3 2      2 -1 */
    1032        1030 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData =
    1033        1030 :                 m_poTPD->m_asCachedTilesDesc[1].nIdxWithinTileData;
    1034        1030 :             m_poTPD->m_asCachedTilesDesc[2].nIdxWithinTileData =
    1035        1030 :                 m_poTPD->m_asCachedTilesDesc[3].nIdxWithinTileData;
    1036             :         }
    1037             :         else
    1038             :         {
    1039         184 :             m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    1040         184 :             m_poTPD->m_asCachedTilesDesc[2].nIdxWithinTileData = -1;
    1041             :         }
    1042        1214 :         m_poTPD->m_asCachedTilesDesc[0].nRow = nRowMin;
    1043        1214 :         m_poTPD->m_asCachedTilesDesc[0].nCol = nColMin;
    1044        1214 :         m_poTPD->m_asCachedTilesDesc[1].nRow = nRowMin;
    1045        1214 :         m_poTPD->m_asCachedTilesDesc[1].nCol = nColMin + 1;
    1046        1214 :         m_poTPD->m_asCachedTilesDesc[2].nRow = nRowMin + 1;
    1047        1214 :         m_poTPD->m_asCachedTilesDesc[2].nCol = nColMin;
    1048        1214 :         m_poTPD->m_asCachedTilesDesc[3].nRow = nRowMin + 1;
    1049        1214 :         m_poTPD->m_asCachedTilesDesc[3].nCol = nColMin + 1;
    1050        1214 :         m_poTPD->m_asCachedTilesDesc[1].nIdxWithinTileData = -1;
    1051        1214 :         m_poTPD->m_asCachedTilesDesc[3].nIdxWithinTileData = -1;
    1052             :     }
    1053             : 
    1054        9112 :     for (int nRow = nRowMin; nRow <= nRowMax; nRow++)
    1055             :     {
    1056       12701 :         for (int nCol = nColMin; nCol <= nColMax; nCol++)
    1057             :         {
    1058        7541 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    1059        2785 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    1060             :             {
    1061        2738 :                 if (!(nRow == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    1062           7 :                       nCol == m_poTPD->m_asCachedTilesDesc[0].nCol &&
    1063           3 :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    1064             :                 {
    1065        2736 :                     if (m_poTPD->WriteTile() != CE_None)
    1066           0 :                         return CE_Failure;
    1067             :                 }
    1068             :             }
    1069             : 
    1070        7541 :             GByte *pabyTileData = m_poTPD->ReadTile(nRow, nCol);
    1071        7541 :             if (pabyTileData == nullptr)
    1072           0 :                 return CE_Failure;
    1073             : 
    1074       34598 :             for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
    1075             :             {
    1076       27060 :                 GDALRasterBlock *poBlock = nullptr;
    1077       27060 :                 GByte *pabyDest = nullptr;
    1078       27060 :                 if (iBand == nBand)
    1079             :                 {
    1080        7541 :                     pabyDest = static_cast<GByte *>(pData);
    1081             :                 }
    1082             :                 else
    1083             :                 {
    1084       19519 :                     poBlock = poDS->GetRasterBand(iBand)->GetLockedBlockRef(
    1085       19519 :                         nBlockXOff, nBlockYOff, TRUE);
    1086       19519 :                     if (poBlock == nullptr)
    1087           0 :                         continue;
    1088       19519 :                     if (poBlock->GetDirty())
    1089             :                     {
    1090           2 :                         poBlock->DropLock();
    1091           2 :                         continue;
    1092             :                     }
    1093             :                     /* if we are short of GDAL cache max and there are dirty
    1094             :                      * blocks */
    1095             :                     /* of our dataset, the above GetLockedBlockRef() might have
    1096             :                      * reset */
    1097             :                     /* (at least part of) the 4 tiles we want to cache and have
    1098             :                      */
    1099             :                     /* already read */
    1100             :                     // FIXME this is way too fragile.
    1101       19517 :                     if ((m_poTPD->m_nShiftXPixelsMod != 0 ||
    1102        5857 :                          m_poTPD->m_nShiftYPixelsMod != 0) &&
    1103       13770 :                         (m_poTPD->m_asCachedTilesDesc[0].nRow != nRowMin ||
    1104       13767 :                          m_poTPD->m_asCachedTilesDesc[0].nCol != nColMin))
    1105             :                     {
    1106           3 :                         poBlock->DropLock();
    1107           3 :                         goto retry;
    1108             :                     }
    1109       19514 :                     pabyDest = static_cast<GByte *>(poBlock->GetDataRef());
    1110             :                 }
    1111             : 
    1112             :                 // Composite tile data into block data
    1113       27055 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    1114        8639 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    1115             :                 {
    1116        8485 :                     const size_t nBandBlockSize =
    1117        8485 :                         static_cast<size_t>(nBlockXSize) * nBlockYSize *
    1118        8485 :                         m_nDTSize;
    1119        8485 :                     memcpy(pabyDest,
    1120        8485 :                            pabyTileData + (iBand - 1) * nBandBlockSize,
    1121        8485 :                            nBandBlockSize);
    1122             : #ifdef DEBUG_VERBOSE
    1123             :                     if (eDataType == GDT_Byte &&
    1124             :                         (nBlockXOff + 1) * nBlockXSize <= nRasterXSize &&
    1125             :                         (nBlockYOff + 1) * nBlockYSize > nRasterYSize)
    1126             :                     {
    1127             :                         bool bFoundNonZero = false;
    1128             :                         for (int y = nRasterYSize - nBlockYOff * nBlockYSize;
    1129             :                              y < nBlockYSize; y++)
    1130             :                         {
    1131             :                             for (int x = 0; x < nBlockXSize; x++)
    1132             :                             {
    1133             :                                 if (pabyDest[static_cast<GPtrDiff_t>(y) *
    1134             :                                                  nBlockXSize +
    1135             :                                              x] != 0 &&
    1136             :                                     !bFoundNonZero)
    1137             :                                 {
    1138             :                                     CPLDebug("GPKG",
    1139             :                                              "IReadBlock(): Found non-zero "
    1140             :                                              "content in ghost part of "
    1141             :                                              "tile(nBand=%d,nBlockXOff=%d,"
    1142             :                                              "nBlockYOff=%d,m_nZoomLevel=%d)\n",
    1143             :                                              iBand, nBlockXOff, nBlockYOff,
    1144             :                                              m_poTPD->m_nZoomLevel);
    1145             :                                     bFoundNonZero = true;
    1146             :                                 }
    1147             :                             }
    1148             :                         }
    1149             :                     }
    1150             : #endif
    1151             :                 }
    1152             :                 else
    1153             :                 {
    1154       18570 :                     int nSrcXOffset = 0;
    1155       18570 :                     int nSrcXSize = 0;
    1156       18570 :                     int nDstXOffset = 0;
    1157       18570 :                     if (nCol == nColMin)
    1158             :                     {
    1159        9362 :                         nSrcXOffset = m_poTPD->m_nShiftXPixelsMod;
    1160        9362 :                         nSrcXSize = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    1161        9362 :                         nDstXOffset = 0;
    1162             :                     }
    1163             :                     else
    1164             :                     {
    1165        9208 :                         nSrcXOffset = 0;
    1166        9208 :                         nSrcXSize = m_poTPD->m_nShiftXPixelsMod;
    1167        9208 :                         nDstXOffset = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    1168             :                     }
    1169       18570 :                     int nSrcYOffset = 0;
    1170       18570 :                     int nSrcYSize = 0;
    1171       18570 :                     int nDstYOffset = 0;
    1172       18570 :                     if (nRow == nRowMin)
    1173             :                     {
    1174        9288 :                         nSrcYOffset = m_poTPD->m_nShiftYPixelsMod;
    1175        9288 :                         nSrcYSize = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    1176        9288 :                         nDstYOffset = 0;
    1177             :                     }
    1178             :                     else
    1179             :                     {
    1180        9282 :                         nSrcYOffset = 0;
    1181        9282 :                         nSrcYSize = m_poTPD->m_nShiftYPixelsMod;
    1182        9282 :                         nDstYOffset = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    1183             :                     }
    1184             : 
    1185             : #ifdef DEBUG_VERBOSE
    1186             :                     CPLDebug("GPKG",
    1187             :                              "Copy source tile x=%d,w=%d,y=%d,h=%d into "
    1188             :                              "buffer at x=%d,y=%d",
    1189             :                              nSrcXOffset, nSrcXSize, nSrcYOffset, nSrcYSize,
    1190             :                              nDstXOffset, nDstYOffset);
    1191             : #endif
    1192             : 
    1193     2368610 :                     for (GPtrDiff_t y = 0; y < nSrcYSize; y++)
    1194             :                     {
    1195     2350040 :                         GByte *pSrc =
    1196             :                             pabyTileData +
    1197     2350040 :                             (static_cast<GPtrDiff_t>(iBand - 1) * nBlockXSize *
    1198     2350040 :                                  nBlockYSize +
    1199     2350040 :                              (y + nSrcYOffset) * nBlockXSize + nSrcXOffset) *
    1200     2350040 :                                 m_nDTSize;
    1201     2350040 :                         GByte *pDst =
    1202             :                             pabyDest +
    1203     2350040 :                             ((y + nDstYOffset) * nBlockXSize + nDstXOffset) *
    1204     2350040 :                                 m_nDTSize;
    1205     2350040 :                         GDALCopyWords(pSrc, eDataType, m_nDTSize, pDst,
    1206             :                                       eDataType, m_nDTSize, nSrcXSize);
    1207             :                     }
    1208             :                 }
    1209             : 
    1210       27055 :                 if (poBlock)
    1211       19514 :                     poBlock->DropLock();
    1212             :             }
    1213             :         }
    1214             :     }
    1215             : 
    1216        3949 :     return CE_None;
    1217             : }
    1218             : 
    1219             : /************************************************************************/
    1220             : /*                       IGetDataCoverageStatus()                       */
    1221             : /************************************************************************/
    1222             : 
    1223           8 : int GDALGPKGMBTilesLikeRasterBand::IGetDataCoverageStatus(int nXOff, int nYOff,
    1224             :                                                           int nXSize,
    1225             :                                                           int nYSize,
    1226             :                                                           int nMaskFlagStop,
    1227             :                                                           double *pdfDataPct)
    1228             : {
    1229           8 :     if (eAccess == GA_Update)
    1230           0 :         FlushCache(false);
    1231             : 
    1232           8 :     const int iColMin = nXOff / nBlockXSize + m_poTPD->m_nShiftXTiles;
    1233          16 :     const int iColMax = (nXOff + nXSize - 1) / nBlockXSize +
    1234           8 :                         m_poTPD->m_nShiftXTiles +
    1235           8 :                         (m_poTPD->m_nShiftXPixelsMod ? 1 : 0);
    1236           8 :     const int iRowMin = nYOff / nBlockYSize + m_poTPD->m_nShiftYTiles;
    1237          16 :     const int iRowMax = (nYOff + nYSize - 1) / nBlockYSize +
    1238           8 :                         m_poTPD->m_nShiftYTiles +
    1239           8 :                         (m_poTPD->m_nShiftYPixelsMod ? 1 : 0);
    1240             : 
    1241           8 :     int iDBRowMin = m_poTPD->GetRowFromIntoTopConvention(iRowMin);
    1242           8 :     int iDBRowMax = m_poTPD->GetRowFromIntoTopConvention(iRowMax);
    1243           8 :     if (iDBRowMin > iDBRowMax)
    1244           0 :         std::swap(iDBRowMin, iDBRowMax);
    1245             : 
    1246          16 :     char *pszSQL = sqlite3_mprintf(
    1247             :         "SELECT tile_row, tile_column FROM \"%w\" "
    1248             :         "WHERE zoom_level = %d AND "
    1249             :         "(tile_row BETWEEN %d AND %d) AND "
    1250             :         "(tile_column BETWEEN %d AND %d)"
    1251             :         "%s",
    1252           8 :         m_poTPD->m_osRasterTable.c_str(), m_poTPD->m_nZoomLevel, iDBRowMin,
    1253             :         iDBRowMax, iColMin, iColMax,
    1254           8 :         !m_poTPD->m_osWHERE.empty()
    1255           0 :             ? CPLSPrintf(" AND (%s)", m_poTPD->m_osWHERE.c_str())
    1256             :             : "");
    1257             : 
    1258             : #ifdef DEBUG_VERBOSE
    1259             :     CPLDebug("GPKG", "%s", pszSQL);
    1260             : #endif
    1261             : 
    1262           8 :     sqlite3_stmt *hStmt = nullptr;
    1263           8 :     int rc = sqlite3_prepare_v2(m_poTPD->IGetDB(), pszSQL, -1, &hStmt, nullptr);
    1264           8 :     if (rc != SQLITE_OK)
    1265             :     {
    1266           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s",
    1267           0 :                  pszSQL, sqlite3_errmsg(m_poTPD->IGetDB()));
    1268           0 :         sqlite3_free(pszSQL);
    1269             :         return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
    1270           0 :                GDAL_DATA_COVERAGE_STATUS_DATA;
    1271             :     }
    1272           8 :     sqlite3_free(pszSQL);
    1273           8 :     rc = sqlite3_step(hStmt);
    1274          16 :     std::set<std::pair<int, int>> oSetTiles;  // (row, col)
    1275          12 :     while (rc == SQLITE_ROW)
    1276             :     {
    1277           0 :         oSetTiles.insert(std::pair(
    1278           4 :             m_poTPD->GetRowFromIntoTopConvention(sqlite3_column_int(hStmt, 0)),
    1279           8 :             sqlite3_column_int(hStmt, 1)));
    1280           4 :         rc = sqlite3_step(hStmt);
    1281             :     }
    1282           8 :     sqlite3_finalize(hStmt);
    1283           8 :     if (rc != SQLITE_DONE)
    1284             :     {
    1285             :         return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
    1286           0 :                GDAL_DATA_COVERAGE_STATUS_DATA;
    1287             :     }
    1288           8 :     if (oSetTiles.empty())
    1289             :     {
    1290           4 :         if (pdfDataPct)
    1291           4 :             *pdfDataPct = 0.0;
    1292           4 :         return GDAL_DATA_COVERAGE_STATUS_EMPTY;
    1293             :     }
    1294             : 
    1295           8 :     if (m_poTPD->m_nShiftXPixelsMod == 0 && m_poTPD->m_nShiftYPixelsMod == 0 &&
    1296           4 :         oSetTiles.size() == static_cast<size_t>(iRowMax - iRowMin + 1) *
    1297           4 :                                 (iColMax - iColMin + 1))
    1298             :     {
    1299           2 :         if (pdfDataPct)
    1300           2 :             *pdfDataPct = 100.0;
    1301           2 :         return GDAL_DATA_COVERAGE_STATUS_DATA;
    1302             :     }
    1303             : 
    1304           2 :     if (m_poTPD->m_nShiftXPixelsMod == 0 && m_poTPD->m_nShiftYPixelsMod == 0)
    1305             :     {
    1306           2 :         int nStatus = 0;
    1307           2 :         GIntBig nPixelsData = 0;
    1308           7 :         for (int iY = iRowMin; iY <= iRowMax; ++iY)
    1309             :         {
    1310          21 :             for (int iX = iColMin; iX <= iColMax; ++iX)
    1311             :             {
    1312             :                 // coverity[swapped_arguments]
    1313          16 :                 if (oSetTiles.find(std::pair(iY, iX)) == oSetTiles.end())
    1314             :                 {
    1315          14 :                     nStatus |= GDAL_DATA_COVERAGE_STATUS_EMPTY;
    1316             :                 }
    1317             :                 else
    1318             :                 {
    1319           2 :                     const int iXGDAL = iX - m_poTPD->m_nShiftXTiles;
    1320           2 :                     const int iYGDAL = iY - m_poTPD->m_nShiftYTiles;
    1321           2 :                     const int nXBlockRight =
    1322           2 :                         (iXGDAL * nBlockXSize > INT_MAX - nBlockXSize)
    1323           2 :                             ? INT_MAX
    1324           2 :                             : (iXGDAL + 1) * nBlockXSize;
    1325           2 :                     const int nYBlockBottom =
    1326           2 :                         (iYGDAL * nBlockYSize > INT_MAX - nBlockYSize)
    1327           2 :                             ? INT_MAX
    1328           2 :                             : (iYGDAL + 1) * nBlockYSize;
    1329             : 
    1330           4 :                     nPixelsData += (static_cast<GIntBig>(std::min(
    1331           2 :                                         nXBlockRight, nXOff + nXSize)) -
    1332           2 :                                     std::max(iXGDAL * nBlockXSize, nXOff)) *
    1333           2 :                                    (std::min(nYBlockBottom, nYOff + nYSize) -
    1334           2 :                                     std::max(iYGDAL * nBlockYSize, nYOff));
    1335           2 :                     nStatus |= GDAL_DATA_COVERAGE_STATUS_DATA;
    1336             :                 }
    1337          16 :                 if (nMaskFlagStop != 0 && (nMaskFlagStop & nStatus) != 0)
    1338             :                 {
    1339           0 :                     if (pdfDataPct)
    1340           0 :                         *pdfDataPct = -1.0;
    1341           0 :                     return nStatus;
    1342             :                 }
    1343             :             }
    1344             :         }
    1345             : 
    1346           2 :         if (pdfDataPct)
    1347             :         {
    1348           2 :             *pdfDataPct =
    1349           2 :                 100.0 * nPixelsData / (static_cast<GIntBig>(nXSize) * nYSize);
    1350             :         }
    1351           2 :         return nStatus;
    1352             :     }
    1353             : 
    1354             :     return GDAL_DATA_COVERAGE_STATUS_UNIMPLEMENTED |
    1355           0 :            GDAL_DATA_COVERAGE_STATUS_DATA;
    1356             : }
    1357             : 
    1358             : /************************************************************************/
    1359             : /*                       WEBPSupports4Bands()                           */
    1360             : /************************************************************************/
    1361             : 
    1362          83 : static bool WEBPSupports4Bands()
    1363             : {
    1364             :     static int bRes = -1;
    1365          83 :     if (bRes < 0)
    1366             :     {
    1367           1 :         GDALDriver *poDrv = GDALDriver::FromHandle(GDALGetDriverByName("WEBP"));
    1368           2 :         if (poDrv == nullptr ||
    1369           1 :             CPLTestBool(CPLGetConfigOption("GPKG_SIMUL_WEBP_3BAND", "FALSE")))
    1370           0 :             bRes = false;
    1371             :         else
    1372             :         {
    1373             :             // LOSSLESS and RGBA support appeared in the same version
    1374           1 :             bRes = strstr(poDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    1375           1 :                           "LOSSLESS") != nullptr;
    1376             :         }
    1377           1 :         if (poDrv != nullptr && !bRes)
    1378             :         {
    1379           0 :             CPLError(
    1380             :                 CE_Warning, CPLE_AppDefined,
    1381             :                 "The version of WEBP available does not support 4-band RGBA");
    1382             :         }
    1383             :     }
    1384          83 :     return CPL_TO_BOOL(bRes);
    1385             : }
    1386             : 
    1387             : /************************************************************************/
    1388             : /*                         GetTileId()                                  */
    1389             : /************************************************************************/
    1390             : 
    1391          55 : GIntBig GDALGPKGMBTilesLikePseudoDataset::GetTileId(int nRow, int nCol)
    1392             : {
    1393             :     char *pszSQL =
    1394          55 :         sqlite3_mprintf("SELECT id FROM \"%w\" WHERE zoom_level = %d AND "
    1395             :                         "tile_row = %d AND tile_column = %d",
    1396             :                         m_osRasterTable.c_str(), m_nZoomLevel,
    1397          55 :                         GetRowFromIntoTopConvention(nRow), nCol);
    1398          55 :     GIntBig nRes = SQLGetInteger64(IGetDB(), pszSQL, nullptr);
    1399          55 :     sqlite3_free(pszSQL);
    1400          55 :     return nRes;
    1401             : }
    1402             : 
    1403             : /************************************************************************/
    1404             : /*                           DeleteTile()                               */
    1405             : /************************************************************************/
    1406             : 
    1407         458 : bool GDALGPKGMBTilesLikePseudoDataset::DeleteTile(int nRow, int nCol)
    1408             : {
    1409             :     char *pszSQL =
    1410         458 :         sqlite3_mprintf("DELETE FROM \"%w\" "
    1411             :                         "WHERE zoom_level = %d AND tile_row = %d AND "
    1412             :                         "tile_column = %d",
    1413             :                         m_osRasterTable.c_str(), m_nZoomLevel,
    1414         458 :                         GetRowFromIntoTopConvention(nRow), nCol);
    1415             : #ifdef DEBUG_VERBOSE
    1416             :     CPLDebug("GPKG", "%s", pszSQL);
    1417             : #endif
    1418         458 :     char *pszErrMsg = nullptr;
    1419         458 :     int rc = sqlite3_exec(IGetDB(), pszSQL, nullptr, nullptr, &pszErrMsg);
    1420         458 :     if (rc != SQLITE_OK)
    1421             :     {
    1422           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1423             :                  "Failure when deleting tile (row=%d,col=%d) "
    1424             :                  "at zoom_level=%d : %s",
    1425           0 :                  GetRowFromIntoTopConvention(nRow), nCol, m_nZoomLevel,
    1426           0 :                  pszErrMsg ? pszErrMsg : "");
    1427             :     }
    1428         458 :     sqlite3_free(pszSQL);
    1429         458 :     sqlite3_free(pszErrMsg);
    1430         458 :     return rc == SQLITE_OK;
    1431             : }
    1432             : 
    1433             : /************************************************************************/
    1434             : /*                   DeleteFromGriddedTileAncillary()                   */
    1435             : /************************************************************************/
    1436             : 
    1437          55 : bool GDALGPKGMBTilesLikePseudoDataset::DeleteFromGriddedTileAncillary(
    1438             :     GIntBig nTileId)
    1439             : {
    1440             :     char *pszSQL =
    1441          55 :         sqlite3_mprintf("DELETE FROM gpkg_2d_gridded_tile_ancillary WHERE "
    1442             :                         "tpudt_name = '%q' AND tpudt_id = ?",
    1443             :                         m_osRasterTable.c_str());
    1444          55 :     sqlite3_stmt *hStmt = nullptr;
    1445          55 :     int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
    1446          55 :     if (rc == SQLITE_OK)
    1447             :     {
    1448          55 :         sqlite3_bind_int64(hStmt, 1, nTileId);
    1449          55 :         rc = sqlite3_step(hStmt);
    1450          55 :         sqlite3_finalize(hStmt);
    1451             :     }
    1452          55 :     sqlite3_free(pszSQL);
    1453          55 :     return rc == SQLITE_OK;
    1454             : }
    1455             : 
    1456             : /************************************************************************/
    1457             : /*                      ProcessInt16UInt16Tile()                        */
    1458             : /************************************************************************/
    1459             : 
    1460             : template <class T>
    1461          46 : static void ProcessInt16UInt16Tile(
    1462             :     const void *pabyData, GPtrDiff_t nPixels, bool bIsInt16, bool bHasNoData,
    1463             :     double dfNoDataValue, GUInt16 usGPKGNull, double m_dfOffset,
    1464             :     double m_dfScale, GUInt16 *pTempTileBuffer, double &dfTileOffset,
    1465             :     double &dfTileScale, double &dfTileMin, double &dfTileMax,
    1466             :     double &dfTileMean, double &dfTileStdDev, GPtrDiff_t &nValidPixels)
    1467             : {
    1468          46 :     const T *pSrc = reinterpret_cast<const T *>(pabyData);
    1469          46 :     T nMin = 0;
    1470          46 :     T nMax = 0;
    1471          46 :     double dfM2 = 0.0;
    1472     3014700 :     for (int i = 0; i < nPixels; i++)
    1473             :     {
    1474     3014660 :         const T nVal = pSrc[i];
    1475     3014660 :         if (bHasNoData && nVal == dfNoDataValue)
    1476      718161 :             continue;
    1477             : 
    1478     2296499 :         if (nValidPixels == 0)
    1479             :         {
    1480          45 :             nMin = nVal;
    1481          45 :             nMax = nVal;
    1482             :         }
    1483             :         else
    1484             :         {
    1485     2296450 :             nMin = std::min(nMin, nVal);
    1486     2296450 :             nMax = std::max(nMax, nVal);
    1487             :         }
    1488     2296499 :         nValidPixels++;
    1489     2296499 :         const double dfDelta = nVal - dfTileMean;
    1490     2296499 :         dfTileMean += dfDelta / nValidPixels;
    1491     2296499 :         dfM2 += dfDelta * (nVal - dfTileMean);
    1492             :     }
    1493          46 :     dfTileMin = nMin;
    1494          46 :     dfTileMax = nMax;
    1495          46 :     if (nValidPixels)
    1496          45 :         dfTileStdDev = sqrt(dfM2 / nValidPixels);
    1497             : 
    1498          46 :     double dfGlobalMin = (nMin - m_dfOffset) / m_dfScale;
    1499          46 :     double dfGlobalMax = (nMax - m_dfOffset) / m_dfScale;
    1500          46 :     double dfRange = 65535.0;
    1501          46 :     if (bHasNoData && usGPKGNull == 65535 &&
    1502           8 :         dfGlobalMax - dfGlobalMin >= dfRange)
    1503             :     {
    1504           0 :         dfRange = 65534.0;
    1505             :     }
    1506             : 
    1507          46 :     if (dfGlobalMax - dfGlobalMin > dfRange)
    1508             :     {
    1509           0 :         dfTileScale = (dfGlobalMax - dfGlobalMin) / dfRange;
    1510             :     }
    1511          46 :     if (dfGlobalMin < 0.0)
    1512             :     {
    1513           0 :         dfTileOffset = -dfGlobalMin;
    1514             :     }
    1515          46 :     else if (dfGlobalMax / dfTileScale > dfRange)
    1516             :     {
    1517           0 :         dfTileOffset = dfGlobalMax - dfRange * dfTileScale;
    1518             :     }
    1519             : 
    1520          46 :     if (bHasNoData && std::numeric_limits<T>::min() == 0 && m_dfOffset == 0.0 &&
    1521             :         m_dfScale == 1.0)
    1522             :     {
    1523           7 :         dfTileOffset = 0.0;
    1524           7 :         dfTileScale = 1.0;
    1525             :     }
    1526          39 :     else if (bHasNoData && bIsInt16 && dfNoDataValue == -32768.0 &&
    1527           1 :              usGPKGNull == 65535 && m_dfOffset == -32768.0 && m_dfScale == 1.0)
    1528             :     {
    1529           1 :         dfTileOffset = 1.0;
    1530           1 :         dfTileScale = 1.0;
    1531             :     }
    1532             : 
    1533     3014700 :     for (GPtrDiff_t i = 0; i < nPixels; i++)
    1534             :     {
    1535     3014660 :         const T nVal = pSrc[i];
    1536     3014660 :         if (bHasNoData && nVal == dfNoDataValue)
    1537      718161 :             pTempTileBuffer[i] = usGPKGNull;
    1538             :         else
    1539             :         {
    1540     2296499 :             double dfVal =
    1541     2296499 :                 ((nVal - m_dfOffset) / m_dfScale - dfTileOffset) / dfTileScale;
    1542     2296499 :             CPLAssert(dfVal >= 0.0 && dfVal < 65535.5);
    1543     2296499 :             pTempTileBuffer[i] = static_cast<GUInt16>(dfVal + 0.5);
    1544     2296499 :             if (bHasNoData && pTempTileBuffer[i] == usGPKGNull)
    1545             :             {
    1546           0 :                 if (usGPKGNull > 0)
    1547           0 :                     pTempTileBuffer[i]--;
    1548             :                 else
    1549           0 :                     pTempTileBuffer[i]++;
    1550             :                 ;
    1551             :             }
    1552             :         }
    1553             :     }
    1554          46 : }
    1555             : 
    1556             : /************************************************************************/
    1557             : /*                         WriteTile()                                  */
    1558             : /************************************************************************/
    1559             : 
    1560       13350 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteTile()
    1561             : {
    1562       13350 :     GDALGPKGMBTilesLikePseudoDataset *poMainDS =
    1563       13350 :         m_poParentDS ? m_poParentDS : this;
    1564       13350 :     if (poMainDS->m_nTileInsertionCount < 0)
    1565         501 :         return CE_Failure;
    1566             : 
    1567       12849 :     if (m_bInWriteTile)
    1568             :     {
    1569             :         // Shouldn't happen in practice, but #7022 shows that the unexpected
    1570             :         // can happen sometimes.
    1571           0 :         CPLError(
    1572             :             CE_Failure, CPLE_AppDefined,
    1573             :             "Recursive call to GDALGPKGMBTilesLikePseudoDataset::WriteTile()");
    1574           0 :         return CE_Failure;
    1575             :     }
    1576       12849 :     GDALRasterBlock::EnterDisableDirtyBlockFlush();
    1577       12849 :     m_bInWriteTile = true;
    1578       12849 :     CPLErr eErr = WriteTileInternal();
    1579       12849 :     m_bInWriteTile = false;
    1580       12849 :     GDALRasterBlock::LeaveDisableDirtyBlockFlush();
    1581       12849 :     return eErr;
    1582             : }
    1583             : 
    1584             : /* should only be called by WriteTile() */
    1585       12849 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteTileInternal()
    1586             : {
    1587       17463 :     if (!(IGetUpdate() && m_asCachedTilesDesc[0].nRow >= 0 &&
    1588        4614 :           m_asCachedTilesDesc[0].nCol >= 0 &&
    1589        4614 :           m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    1590        8235 :         return CE_None;
    1591             : 
    1592        4614 :     int nRow = m_asCachedTilesDesc[0].nRow;
    1593        4614 :     int nCol = m_asCachedTilesDesc[0].nCol;
    1594             : 
    1595        4614 :     bool bAllDirty = true;
    1596        4614 :     bool bAllNonDirty = true;
    1597        4614 :     const int nBands = IGetRasterCount();
    1598       17980 :     for (int i = 0; i < nBands; i++)
    1599             :     {
    1600       13366 :         if (m_asCachedTilesDesc[0].abBandDirty[i])
    1601       13333 :             bAllNonDirty = false;
    1602             :         else
    1603          33 :             bAllDirty = false;
    1604             :     }
    1605        4614 :     if (bAllNonDirty)
    1606           0 :         return CE_None;
    1607             : 
    1608             :     int nBlockXSize, nBlockYSize;
    1609        4614 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1610             : 
    1611             :     /* If all bands for that block are not dirty/written, we need to */
    1612             :     /* fetch the missing ones if the tile exists */
    1613        4614 :     bool bIsLossyFormat = false;
    1614        4614 :     const size_t nBandBlockSize =
    1615        4614 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    1616        4614 :     if (!bAllDirty)
    1617             :     {
    1618          72 :         for (int i = 1; i <= 3; i++)
    1619             :         {
    1620          54 :             m_asCachedTilesDesc[i].nRow = -1;
    1621          54 :             m_asCachedTilesDesc[i].nCol = -1;
    1622          54 :             m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    1623             :         }
    1624          18 :         const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    1625          18 :         GByte *pabyTemp = m_pabyCachedTiles + nTileBands * nBandBlockSize;
    1626          18 :         ReadTile(nRow, nCol, pabyTemp, &bIsLossyFormat);
    1627          82 :         for (int i = 0; i < nBands; i++)
    1628             :         {
    1629          64 :             if (!m_asCachedTilesDesc[0].abBandDirty[i])
    1630             :             {
    1631          33 :                 memcpy(m_pabyCachedTiles + i * nBandBlockSize,
    1632          33 :                        pabyTemp + i * nBandBlockSize, nBandBlockSize);
    1633             :             }
    1634             :         }
    1635             :     }
    1636             : 
    1637             :     /* Compute origin of tile in GDAL raster space */
    1638        4614 :     int nXOff = (nCol - m_nShiftXTiles) * nBlockXSize - m_nShiftXPixelsMod;
    1639        4614 :     int nYOff = (nRow - m_nShiftYTiles) * nBlockYSize - m_nShiftYPixelsMod;
    1640             : 
    1641             :     /* Assert that the tile at least intersects some of the GDAL raster space */
    1642        4614 :     CPLAssert(nXOff > -nBlockXSize);
    1643        4614 :     CPLAssert(nYOff > -nBlockYSize);
    1644             :     /* Can happen if the tile of the raster is less than the block size */
    1645        4614 :     const int nRasterXSize = IGetRasterBand(1)->GetXSize();
    1646        4614 :     const int nRasterYSize = IGetRasterBand(1)->GetYSize();
    1647        4614 :     if (nXOff >= nRasterXSize || nYOff >= nRasterYSize)
    1648           0 :         return CE_None;
    1649             : 
    1650             : #ifdef DEBUG_VERBOSE
    1651             :     if (m_nShiftXPixelsMod == 0 && m_nShiftYPixelsMod == 0 && m_eDT == GDT_Byte)
    1652             :     {
    1653             :         int nBlockXOff = nCol;
    1654             :         int nBlockYOff = nRow;
    1655             :         if (nBlockXOff * nBlockXSize <= nRasterXSize - nBlockXSize &&
    1656             :             nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    1657             :         {
    1658             :             for (int i = 0; i < nBands; i++)
    1659             :             {
    1660             :                 bool bFoundNonZero = false;
    1661             :                 for (GPtrDiff_t y = nRasterYSize - nBlockYOff * nBlockYSize;
    1662             :                      y < nBlockYSize; y++)
    1663             :                 {
    1664             :                     for (int x = 0; x < nBlockXSize; x++)
    1665             :                     {
    1666             :                         if (m_pabyCachedTiles[y * nBlockXSize + x +
    1667             :                                               i * nBandBlockSize] != 0 &&
    1668             :                             !bFoundNonZero)
    1669             :                         {
    1670             :                             CPLDebug("GPKG",
    1671             :                                      "WriteTileInternal(): Found non-zero "
    1672             :                                      "content in ghost part of "
    1673             :                                      "tile(band=%d,nBlockXOff=%d,nBlockYOff=%d,"
    1674             :                                      "m_nZoomLevel=%d)\n",
    1675             :                                      i + 1, nBlockXOff, nBlockYOff,
    1676             :                                      m_nZoomLevel);
    1677             :                             bFoundNonZero = true;
    1678             :                         }
    1679             :                     }
    1680             :                 }
    1681             :             }
    1682             :         }
    1683             :     }
    1684             : #endif
    1685             : 
    1686             :     /* Validity area of tile data in intra-tile coordinate space */
    1687        4614 :     int iXOff = 0;
    1688        4614 :     GPtrDiff_t iYOff = 0;
    1689        4614 :     int iXCount = nBlockXSize;
    1690        4614 :     int iYCount = nBlockYSize;
    1691             : 
    1692        4614 :     bool bPartialTile = false;
    1693        4614 :     int nAlphaBand = (nBands == 2) ? 2 : (nBands == 4) ? 4 : 0;
    1694        4614 :     if (nAlphaBand == 0)
    1695             :     {
    1696        3734 :         if (nXOff < 0)
    1697             :         {
    1698           4 :             bPartialTile = true;
    1699           4 :             iXOff = -nXOff;
    1700           4 :             iXCount += nXOff;
    1701             :         }
    1702        3734 :         if (nXOff > nRasterXSize - nBlockXSize)
    1703             :         {
    1704          85 :             bPartialTile = true;
    1705          85 :             iXCount -= static_cast<int>(static_cast<GIntBig>(nXOff) +
    1706          85 :                                         nBlockXSize - nRasterXSize);
    1707             :         }
    1708        3734 :         if (nYOff < 0)
    1709             :         {
    1710           8 :             bPartialTile = true;
    1711           8 :             iYOff = -nYOff;
    1712           8 :             iYCount += nYOff;
    1713             :         }
    1714        3734 :         if (nYOff > nRasterYSize - nBlockYSize)
    1715             :         {
    1716          99 :             bPartialTile = true;
    1717          99 :             iYCount -= static_cast<int>(static_cast<GIntBig>(nYOff) +
    1718          99 :                                         nBlockYSize - nRasterYSize);
    1719             :         }
    1720        3734 :         CPLAssert(iXOff >= 0);
    1721        3734 :         CPLAssert(iYOff >= 0);
    1722        3734 :         CPLAssert(iXCount > 0);
    1723        3734 :         CPLAssert(iYCount > 0);
    1724        3734 :         CPLAssert(iXOff + iXCount <= nBlockXSize);
    1725        3734 :         CPLAssert(iYOff + iYCount <= nBlockYSize);
    1726             :     }
    1727             : 
    1728        4614 :     m_asCachedTilesDesc[0].nRow = -1;
    1729        4614 :     m_asCachedTilesDesc[0].nCol = -1;
    1730        4614 :     m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    1731        4614 :     m_asCachedTilesDesc[0].abBandDirty[0] = false;
    1732        4614 :     m_asCachedTilesDesc[0].abBandDirty[1] = false;
    1733        4614 :     m_asCachedTilesDesc[0].abBandDirty[2] = false;
    1734        4614 :     m_asCachedTilesDesc[0].abBandDirty[3] = false;
    1735             : 
    1736        4614 :     CPLErr eErr = CE_Failure;
    1737             : 
    1738        4614 :     int bHasNoData = FALSE;
    1739        4614 :     double dfNoDataValue = IGetRasterBand(1)->GetNoDataValue(&bHasNoData);
    1740        4614 :     const bool bHasNanNoData = bHasNoData && std::isnan(dfNoDataValue);
    1741             : 
    1742        4614 :     bool bAllOpaque = true;
    1743             :     // Detect fully transparent tiles, but only if all bands are dirty (that is
    1744             :     // the user wrote content into them) !
    1745        4614 :     if (bAllDirty && m_eDT == GDT_Byte && m_poCT == nullptr && nAlphaBand != 0)
    1746             :     {
    1747         870 :         GByte byFirstAlphaVal =
    1748         870 :             m_pabyCachedTiles[(nAlphaBand - 1) * nBlockXSize * nBlockYSize];
    1749         870 :         GPtrDiff_t i = 1;
    1750    23974800 :         for (; i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    1751             :         {
    1752    23974300 :             if (m_pabyCachedTiles[static_cast<GPtrDiff_t>(nAlphaBand - 1) *
    1753    23974300 :                                       nBlockXSize * nBlockYSize +
    1754    23974300 :                                   i] != byFirstAlphaVal)
    1755         367 :                 break;
    1756             :         }
    1757         870 :         if (i == static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize)
    1758             :         {
    1759             :             // If tile is fully transparent, don't serialize it and remove it if
    1760             :             // it exists
    1761         503 :             if (byFirstAlphaVal == 0)
    1762             :             {
    1763         438 :                 DeleteTile(nRow, nCol);
    1764             : 
    1765         438 :                 return CE_None;
    1766             :             }
    1767          65 :             bAllOpaque = (byFirstAlphaVal == 255);
    1768             :         }
    1769             :         else
    1770         432 :             bAllOpaque = false;
    1771             :     }
    1772        3744 :     else if (bAllDirty && m_eDT == GDT_Byte && m_poCT == nullptr &&
    1773        3649 :              (!bHasNoData || dfNoDataValue == 0.0))
    1774             :     {
    1775        3649 :         bool bAllEmpty = true;
    1776        3649 :         const auto nPixels =
    1777        3649 :             static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize * nBands;
    1778     1207860 :         for (GPtrDiff_t i = 0; i < nPixels; i++)
    1779             :         {
    1780     1207840 :             if (m_pabyCachedTiles[i] != 0)
    1781             :             {
    1782        3632 :                 bAllEmpty = false;
    1783        3632 :                 break;
    1784             :             }
    1785             :         }
    1786        3649 :         if (bAllEmpty)
    1787             :         {
    1788             :             // If tile is fully transparent, don't serialize it and remove it if
    1789             :             // it exists
    1790          17 :             DeleteTile(nRow, nCol);
    1791             : 
    1792          17 :             return CE_None;
    1793        3632 :         }
    1794             :     }
    1795          95 :     else if (bAllDirty && m_eDT == GDT_Float32)
    1796             :     {
    1797          11 :         const float *pSrc = reinterpret_cast<float *>(m_pabyCachedTiles);
    1798             :         GPtrDiff_t i;
    1799          11 :         const float fNoDataValueOrZero =
    1800          11 :             bHasNoData ? static_cast<float>(dfNoDataValue) : 0.0f;
    1801      131083 :         for (i = 0; i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    1802             :         {
    1803      131081 :             const float fVal = pSrc[i];
    1804      131081 :             if (bHasNanNoData)
    1805             :             {
    1806           0 :                 if (std::isnan(fVal))
    1807           0 :                     continue;
    1808             :             }
    1809      131081 :             else if (fVal == fNoDataValueOrZero)
    1810             :             {
    1811      131072 :                 continue;
    1812             :             }
    1813           9 :             break;
    1814             :         }
    1815          11 :         if (i == static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize)
    1816             :         {
    1817             :             // If tile is fully transparent, don't serialize it and remove it if
    1818             :             // it exists
    1819           2 :             DeleteTile(nRow, nCol);
    1820             : 
    1821           2 :             return CE_None;
    1822             :         }
    1823             :     }
    1824             : 
    1825        4157 :     if (bIsLossyFormat)
    1826             :     {
    1827           1 :         CPLDebug("GPKG",
    1828             :                  "Had to read tile (row=%d,col=%d) at zoom_level=%d, "
    1829             :                  "stored in a lossy format, before rewriting it, causing "
    1830             :                  "potential extra quality loss",
    1831             :                  nRow, nCol, m_nZoomLevel);
    1832             :     }
    1833             : 
    1834             :     const CPLString osMemFileName(
    1835        8314 :         VSIMemGenerateHiddenFilename("gpkg_write_tile"));
    1836        4157 :     const char *pszDriverName = "PNG";
    1837        4157 :     CPL_IGNORE_RET_VAL(pszDriverName);  // Make CSA happy
    1838        4157 :     bool bTileDriverSupports1Band = false;
    1839        4157 :     bool bTileDriverSupports2Bands = false;
    1840        4157 :     bool bTileDriverSupports4Bands = false;
    1841        4157 :     bool bTileDriverSupportsCT = false;
    1842             : 
    1843        4157 :     if (nBands == 1 && m_eDT == GDT_Byte)
    1844          72 :         IGetRasterBand(1)->GetColorTable();
    1845             : 
    1846        4157 :     GDALDataType eTileDT = GDT_Byte;
    1847             :     // If not all bands are dirty, then (temporarily) use a lossless format
    1848        4157 :     if (m_eTF == GPKG_TF_PNG_JPEG || (!bAllDirty && m_eTF == GPKG_TF_JPEG))
    1849             :     {
    1850          96 :         bTileDriverSupports1Band = true;
    1851          96 :         if (bPartialTile || !bAllDirty || (nBands == 2 && !bAllOpaque) ||
    1852          19 :             (nBands == 4 && !bAllOpaque) || m_poCT != nullptr)
    1853             :         {
    1854          88 :             pszDriverName = "PNG";
    1855          88 :             bTileDriverSupports2Bands = m_bPNGSupports2Bands;
    1856          88 :             bTileDriverSupports4Bands = true;
    1857          88 :             bTileDriverSupportsCT = m_bPNGSupportsCT;
    1858             :         }
    1859             :         else
    1860           8 :             pszDriverName = "JPEG";
    1861             :     }
    1862        4061 :     else if (m_eTF == GPKG_TF_PNG || m_eTF == GPKG_TF_PNG8)
    1863             :     {
    1864        3841 :         pszDriverName = "PNG";
    1865        3841 :         bTileDriverSupports1Band = true;
    1866        3841 :         bTileDriverSupports2Bands = m_bPNGSupports2Bands;
    1867        3841 :         bTileDriverSupports4Bands = true;
    1868        3841 :         bTileDriverSupportsCT = m_bPNGSupportsCT;
    1869             :     }
    1870         220 :     else if (m_eTF == GPKG_TF_JPEG)
    1871             :     {
    1872          82 :         pszDriverName = "JPEG";
    1873          82 :         bTileDriverSupports1Band = true;
    1874             :     }
    1875         138 :     else if (m_eTF == GPKG_TF_WEBP)
    1876             :     {
    1877          83 :         pszDriverName = "WEBP";
    1878          83 :         bTileDriverSupports4Bands = WEBPSupports4Bands();
    1879             :     }
    1880          55 :     else if (m_eTF == GPKG_TF_PNG_16BIT)
    1881             :     {
    1882          51 :         pszDriverName = "PNG";
    1883          51 :         eTileDT = GDT_UInt16;
    1884          51 :         bTileDriverSupports1Band = true;
    1885             :     }
    1886           4 :     else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    1887             :     {
    1888           4 :         pszDriverName = "GTiff";
    1889           4 :         eTileDT = GDT_Float32;
    1890           4 :         bTileDriverSupports1Band = true;
    1891             :     }
    1892             :     else
    1893             :     {
    1894           0 :         CPLAssert(false);
    1895             :     }
    1896             : 
    1897             :     GDALDriver *l_poDriver =
    1898        4157 :         GDALDriver::FromHandle(GDALGetDriverByName(pszDriverName));
    1899        4157 :     if (l_poDriver != nullptr)
    1900             :     {
    1901        4156 :         auto poMEMDS = MEMDataset::Create("", nBlockXSize, nBlockYSize, 0,
    1902             :                                           eTileDT, nullptr);
    1903        4156 :         int nTileBands = nBands;
    1904        4156 :         if (bPartialTile && nBands == 1 && m_poCT == nullptr &&
    1905             :             bTileDriverSupports2Bands)
    1906          27 :             nTileBands = 2;
    1907        4129 :         else if (bPartialTile && bTileDriverSupports4Bands)
    1908          39 :             nTileBands = 4;
    1909             :         // only use (somewhat lossy) PNG8 if all bands are dirty
    1910        4090 :         else if (bAllDirty && m_eTF == GPKG_TF_PNG8 && nBands >= 3 &&
    1911          10 :                  bAllOpaque && !bPartialTile)
    1912          10 :             nTileBands = 1;
    1913        4080 :         else if (nBands == 2)
    1914             :         {
    1915         383 :             if (bAllOpaque)
    1916             :             {
    1917          51 :                 if (bTileDriverSupports2Bands)
    1918          30 :                     nTileBands = 1;
    1919             :                 else
    1920          21 :                     nTileBands = 3;
    1921             :             }
    1922         332 :             else if (!bTileDriverSupports2Bands)
    1923             :             {
    1924         135 :                 if (bTileDriverSupports4Bands)
    1925          68 :                     nTileBands = 4;
    1926             :                 else
    1927          67 :                     nTileBands = 3;
    1928             :             }
    1929             :         }
    1930        3697 :         else if (nBands == 4 && (bAllOpaque || !bTileDriverSupports4Bands))
    1931          21 :             nTileBands = 3;
    1932        3676 :         else if (nBands == 1 && m_poCT != nullptr && !bTileDriverSupportsCT)
    1933             :         {
    1934           1 :             nTileBands = 3;
    1935           1 :             if (bTileDriverSupports4Bands)
    1936             :             {
    1937           0 :                 for (int i = 0; i < m_poCT->GetColorEntryCount(); i++)
    1938             :                 {
    1939           0 :                     const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
    1940           0 :                     if (psEntry->c4 == 0)
    1941             :                     {
    1942           0 :                         nTileBands = 4;
    1943           0 :                         break;
    1944             :                     }
    1945             :                 }
    1946           1 :             }
    1947             :         }
    1948        3675 :         else if (nBands == 1 && m_poCT == nullptr && !bTileDriverSupports1Band)
    1949           1 :             nTileBands = 3;
    1950             : 
    1951        4156 :         if (bPartialTile && (nTileBands == 2 || nTileBands == 4))
    1952             :         {
    1953          66 :             int nTargetAlphaBand = nTileBands;
    1954          66 :             memset(m_pabyCachedTiles + (nTargetAlphaBand - 1) * nBandBlockSize,
    1955             :                    0, nBandBlockSize);
    1956        4065 :             for (GPtrDiff_t iY = iYOff; iY < iYOff + iYCount; iY++)
    1957             :             {
    1958        3999 :                 memset(m_pabyCachedTiles +
    1959        3999 :                            (static_cast<size_t>(nTargetAlphaBand - 1) *
    1960        3999 :                                 nBlockYSize +
    1961        3999 :                             iY) *
    1962        3999 :                                nBlockXSize +
    1963        3999 :                            iXOff,
    1964             :                        255, iXCount);
    1965             :             }
    1966             :         }
    1967             : 
    1968        4156 :         GUInt16 *pTempTileBuffer = nullptr;
    1969        4156 :         GPtrDiff_t nValidPixels = 0;
    1970        4156 :         double dfTileMin = 0.0;
    1971        4156 :         double dfTileMax = 0.0;
    1972        4156 :         double dfTileMean = 0.0;
    1973        4156 :         double dfTileStdDev = 0.0;
    1974        4156 :         double dfTileOffset = 0.0;
    1975        4156 :         double dfTileScale = 1.0;
    1976        4156 :         if (m_eTF == GPKG_TF_PNG_16BIT)
    1977             :         {
    1978             :             pTempTileBuffer = static_cast<GUInt16 *>(
    1979          51 :                 VSI_MALLOC3_VERBOSE(2, nBlockXSize, nBlockYSize));
    1980             : 
    1981          51 :             if (m_eDT == GDT_Int16)
    1982             :             {
    1983          72 :                 ProcessInt16UInt16Tile<GInt16>(
    1984          36 :                     m_pabyCachedTiles,
    1985          36 :                     static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize, true,
    1986          36 :                     CPL_TO_BOOL(bHasNoData), dfNoDataValue, m_usGPKGNull,
    1987             :                     m_dfOffset, m_dfScale, pTempTileBuffer, dfTileOffset,
    1988             :                     dfTileScale, dfTileMin, dfTileMax, dfTileMean, dfTileStdDev,
    1989             :                     nValidPixels);
    1990             :             }
    1991          15 :             else if (m_eDT == GDT_UInt16)
    1992             :             {
    1993          20 :                 ProcessInt16UInt16Tile<GUInt16>(
    1994          10 :                     m_pabyCachedTiles,
    1995          10 :                     static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize, false,
    1996          10 :                     CPL_TO_BOOL(bHasNoData), dfNoDataValue, m_usGPKGNull,
    1997             :                     m_dfOffset, m_dfScale, pTempTileBuffer, dfTileOffset,
    1998             :                     dfTileScale, dfTileMin, dfTileMax, dfTileMean, dfTileStdDev,
    1999             :                     nValidPixels);
    2000             :             }
    2001           5 :             else if (m_eDT == GDT_Float32)
    2002             :             {
    2003           5 :                 const float *pSrc =
    2004             :                     reinterpret_cast<float *>(m_pabyCachedTiles);
    2005           5 :                 float fMin = 0.0f;
    2006           5 :                 float fMax = 0.0f;
    2007           5 :                 double dfM2 = 0.0;
    2008      327685 :                 for (GPtrDiff_t i = 0;
    2009      327685 :                      i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
    2010             :                      i++)
    2011             :                 {
    2012      327680 :                     const float fVal = pSrc[i];
    2013      327680 :                     if (bHasNanNoData)
    2014             :                     {
    2015           0 :                         if (std::isnan(fVal))
    2016      196206 :                             continue;
    2017             :                     }
    2018      720494 :                     else if (bHasNoData &&
    2019      327680 :                              fVal == static_cast<float>(dfNoDataValue))
    2020             :                     {
    2021      196206 :                         continue;
    2022             :                     }
    2023      131474 :                     if (std::isinf(fVal))
    2024           0 :                         continue;
    2025             : 
    2026      131474 :                     if (nValidPixels == 0)
    2027             :                     {
    2028           5 :                         fMin = fVal;
    2029           5 :                         fMax = fVal;
    2030             :                     }
    2031             :                     else
    2032             :                     {
    2033      131469 :                         fMin = std::min(fMin, fVal);
    2034      131469 :                         fMax = std::max(fMax, fVal);
    2035             :                     }
    2036      131474 :                     nValidPixels++;
    2037      131474 :                     const double dfDelta = fVal - dfTileMean;
    2038      131474 :                     dfTileMean += dfDelta / nValidPixels;
    2039      131474 :                     dfM2 += dfDelta * (fVal - dfTileMean);
    2040             :                 }
    2041           5 :                 dfTileMin = fMin;
    2042           5 :                 dfTileMax = fMax;
    2043           5 :                 if (nValidPixels)
    2044           5 :                     dfTileStdDev = sqrt(dfM2 / nValidPixels);
    2045             : 
    2046           5 :                 double dfGlobalMin = (fMin - m_dfOffset) / m_dfScale;
    2047           5 :                 double dfGlobalMax = (fMax - m_dfOffset) / m_dfScale;
    2048           5 :                 if (dfGlobalMax > dfGlobalMin)
    2049             :                 {
    2050           4 :                     if (bHasNoData && m_usGPKGNull == 65535 &&
    2051           2 :                         dfGlobalMax - dfGlobalMin >= 65534.0)
    2052             :                     {
    2053           1 :                         dfTileOffset = dfGlobalMin;
    2054           1 :                         dfTileScale = (dfGlobalMax - dfGlobalMin) / 65534.0;
    2055             :                     }
    2056           3 :                     else if (bHasNoData && m_usGPKGNull == 0 &&
    2057           0 :                              (dfNoDataValue - m_dfOffset) / m_dfScale != 0)
    2058             :                     {
    2059           0 :                         dfTileOffset =
    2060           0 :                             (65535.0 * dfGlobalMin - dfGlobalMax) / 65534.0;
    2061           0 :                         dfTileScale = dfGlobalMin - dfTileOffset;
    2062             :                     }
    2063             :                     else
    2064             :                     {
    2065           3 :                         dfTileOffset = dfGlobalMin;
    2066           3 :                         dfTileScale = (dfGlobalMax - dfGlobalMin) / 65535.0;
    2067             :                     }
    2068             :                 }
    2069             :                 else
    2070             :                 {
    2071           1 :                     dfTileOffset = dfGlobalMin;
    2072             :                 }
    2073             : 
    2074      327685 :                 for (GPtrDiff_t i = 0;
    2075      327685 :                      i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
    2076             :                      i++)
    2077             :                 {
    2078      327680 :                     const float fVal = pSrc[i];
    2079      327680 :                     if (bHasNanNoData)
    2080             :                     {
    2081           0 :                         if (std::isnan(fVal))
    2082             :                         {
    2083           0 :                             pTempTileBuffer[i] = m_usGPKGNull;
    2084           0 :                             continue;
    2085             :                         }
    2086             :                     }
    2087      327680 :                     else if (bHasNoData)
    2088             :                     {
    2089      196608 :                         if (fVal == static_cast<float>(dfNoDataValue))
    2090             :                         {
    2091      196206 :                             pTempTileBuffer[i] = m_usGPKGNull;
    2092      196206 :                             continue;
    2093             :                         }
    2094             :                     }
    2095             :                     double dfVal =
    2096      131474 :                         std::isfinite(fVal)
    2097      131474 :                             ? ((fVal - m_dfOffset) / m_dfScale - dfTileOffset) /
    2098             :                                   dfTileScale
    2099             :                         : (fVal > 0) ? 65535
    2100      131474 :                                      : 0;
    2101      131474 :                     CPLAssert(dfVal >= 0.0 && dfVal < 65535.5);
    2102      131474 :                     pTempTileBuffer[i] = static_cast<GUInt16>(dfVal + 0.5);
    2103      131474 :                     if (bHasNoData && pTempTileBuffer[i] == m_usGPKGNull)
    2104             :                     {
    2105           1 :                         if (m_usGPKGNull > 0)
    2106           1 :                             pTempTileBuffer[i]--;
    2107             :                         else
    2108           0 :                             pTempTileBuffer[i]++;
    2109             :                     }
    2110             :                 }
    2111             :             }
    2112             : 
    2113          51 :             auto hBand = MEMCreateRasterBandEx(
    2114             :                 poMEMDS, 1, reinterpret_cast<GByte *>(pTempTileBuffer),
    2115             :                 GDT_UInt16, 0, 0, false);
    2116          51 :             poMEMDS->AddMEMBand(hBand);
    2117             :         }
    2118        4105 :         else if (m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    2119             :         {
    2120           4 :             const float *pSrc = reinterpret_cast<float *>(m_pabyCachedTiles);
    2121           4 :             float fMin = 0.0f;
    2122           4 :             float fMax = 0.0f;
    2123           4 :             double dfM2 = 0.0;
    2124      262148 :             for (GPtrDiff_t i = 0;
    2125      262148 :                  i < static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize; i++)
    2126             :             {
    2127      262144 :                 const float fVal = pSrc[i];
    2128      262144 :                 if (bHasNanNoData)
    2129             :                 {
    2130           0 :                     if (std::isnan(fVal))
    2131       65136 :                         continue;
    2132             :                 }
    2133      392816 :                 else if (bHasNoData &&
    2134      262144 :                          fVal == static_cast<float>(dfNoDataValue))
    2135             :                 {
    2136       65136 :                     continue;
    2137             :                 }
    2138             : 
    2139      197008 :                 if (nValidPixels == 0)
    2140             :                 {
    2141           4 :                     fMin = fVal;
    2142           4 :                     fMax = fVal;
    2143             :                 }
    2144             :                 else
    2145             :                 {
    2146      197004 :                     fMin = std::min(fMin, fVal);
    2147      197004 :                     fMax = std::max(fMax, fVal);
    2148             :                 }
    2149      197008 :                 nValidPixels++;
    2150      197008 :                 const double dfDelta = fVal - dfTileMean;
    2151      197008 :                 dfTileMean += dfDelta / nValidPixels;
    2152      197008 :                 dfM2 += dfDelta * (fVal - dfTileMean);
    2153             :             }
    2154           4 :             dfTileMin = fMin;
    2155           4 :             dfTileMax = fMax;
    2156           4 :             if (nValidPixels)
    2157           4 :                 dfTileStdDev = sqrt(dfM2 / nValidPixels);
    2158             : 
    2159           4 :             auto hBand = MEMCreateRasterBandEx(poMEMDS, 1, m_pabyCachedTiles,
    2160             :                                                GDT_Float32, 0, 0, false);
    2161           4 :             poMEMDS->AddMEMBand(hBand);
    2162             :         }
    2163             :         else
    2164             :         {
    2165        4101 :             CPLAssert(m_eDT == GDT_Byte);
    2166       16173 :             for (int i = 0; i < nTileBands; i++)
    2167             :             {
    2168       12072 :                 int iSrc = i;
    2169       12072 :                 if (nBands == 1 && m_poCT == nullptr && nTileBands == 3)
    2170           3 :                     iSrc = 0;
    2171       12069 :                 else if (nBands == 1 && m_poCT == nullptr && bPartialTile &&
    2172             :                          nTileBands == 4)
    2173           8 :                     iSrc = (i < 3) ? 0 : 3;
    2174       12061 :                 else if (nBands == 2 && nTileBands >= 3)
    2175         536 :                     iSrc = (i < 3) ? 0 : 1;
    2176             : 
    2177       24144 :                 auto hBand = MEMCreateRasterBandEx(
    2178             :                     poMEMDS, i + 1,
    2179       12072 :                     m_pabyCachedTiles + iSrc * nBlockXSize * nBlockYSize,
    2180             :                     GDT_Byte, 0, 0, false);
    2181       12072 :                 poMEMDS->AddMEMBand(hBand);
    2182             : 
    2183       12072 :                 if (i == 0 && nTileBands == 1 && m_poCT != nullptr)
    2184          15 :                     poMEMDS->GetRasterBand(1)->SetColorTable(m_poCT);
    2185             :             }
    2186             :         }
    2187             : 
    2188        4156 :         if ((m_eTF == GPKG_TF_PNG_16BIT || m_eTF == GPKG_TF_TIFF_32BIT_FLOAT) &&
    2189          55 :             nValidPixels == 0)
    2190             :         {
    2191             :             // If tile is fully transparent, don't serialize it and remove
    2192             :             // it if it exists.
    2193           1 :             GIntBig nId = GetTileId(nRow, nCol);
    2194           1 :             if (nId > 0)
    2195             :             {
    2196           1 :                 DeleteTile(nRow, nCol);
    2197             : 
    2198           1 :                 DeleteFromGriddedTileAncillary(nId);
    2199             :             }
    2200             : 
    2201           1 :             CPLFree(pTempTileBuffer);
    2202           1 :             delete poMEMDS;
    2203           2 :             return CE_None;
    2204             :         }
    2205             : 
    2206        4155 :         if (m_eTF == GPKG_TF_PNG8 && nTileBands == 1 && nBands >= 3)
    2207             :         {
    2208          10 :             CPLAssert(bAllDirty);
    2209             : 
    2210          10 :             auto poMEM_RGB_DS = MEMDataset::Create("", nBlockXSize, nBlockYSize,
    2211             :                                                    0, GDT_Byte, nullptr);
    2212          40 :             for (int i = 0; i < 3; i++)
    2213             :             {
    2214          60 :                 auto hBand = MEMCreateRasterBandEx(
    2215          30 :                     poMEMDS, i + 1, m_pabyCachedTiles + i * nBandBlockSize,
    2216             :                     GDT_Byte, 0, 0, false);
    2217          30 :                 poMEM_RGB_DS->AddMEMBand(hBand);
    2218             :             }
    2219             : 
    2220          10 :             if (m_pabyHugeColorArray == nullptr)
    2221             :             {
    2222           5 :                 if (nBlockXSize <= 65536 / nBlockYSize)
    2223           4 :                     m_pabyHugeColorArray =
    2224           4 :                         VSIMalloc(MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536);
    2225             :                 else
    2226           1 :                     m_pabyHugeColorArray =
    2227           1 :                         VSIMalloc2(256 * 256 * 256, sizeof(GUInt32));
    2228             :             }
    2229             : 
    2230          10 :             GDALColorTable *poCT = new GDALColorTable();
    2231          20 :             GDALComputeMedianCutPCTInternal(
    2232          10 :                 poMEM_RGB_DS->GetRasterBand(1), poMEM_RGB_DS->GetRasterBand(2),
    2233          10 :                 poMEM_RGB_DS->GetRasterBand(3),
    2234             :                 /*NULL, NULL, NULL,*/
    2235          10 :                 m_pabyCachedTiles, m_pabyCachedTiles + nBandBlockSize,
    2236          10 :                 m_pabyCachedTiles + 2 * nBandBlockSize, nullptr,
    2237             :                 256, /* max colors */
    2238             :                 8,   /* bit depth */
    2239             :                 static_cast<GUInt32 *>(
    2240          10 :                     m_pabyHugeColorArray), /* preallocated histogram */
    2241             :                 poCT, nullptr, nullptr);
    2242             : 
    2243          20 :             GDALDitherRGB2PCTInternal(
    2244          10 :                 poMEM_RGB_DS->GetRasterBand(1), poMEM_RGB_DS->GetRasterBand(2),
    2245          10 :                 poMEM_RGB_DS->GetRasterBand(3), poMEMDS->GetRasterBand(1), poCT,
    2246             :                 8, /* bit depth */
    2247             :                 static_cast<GInt16 *>(
    2248          10 :                     m_pabyHugeColorArray), /* pasDynamicColorMap */
    2249          10 :                 m_bDither, nullptr, nullptr);
    2250          10 :             poMEMDS->GetRasterBand(1)->SetColorTable(poCT);
    2251          10 :             delete poCT;
    2252          10 :             GDALClose(poMEM_RGB_DS);
    2253             :         }
    2254        4145 :         else if (nBands == 1 && m_poCT != nullptr && nTileBands > 1)
    2255             :         {
    2256             :             GByte abyCT[4 * 256];
    2257           7 :             const int nEntries = std::min(256, m_poCT->GetColorEntryCount());
    2258        1799 :             for (int i = 0; i < nEntries; i++)
    2259             :             {
    2260        1792 :                 const GDALColorEntry *psEntry = m_poCT->GetColorEntry(i);
    2261        1792 :                 abyCT[4 * i] = static_cast<GByte>(psEntry->c1);
    2262        1792 :                 abyCT[4 * i + 1] = static_cast<GByte>(psEntry->c2);
    2263        1792 :                 abyCT[4 * i + 2] = static_cast<GByte>(psEntry->c3);
    2264        1792 :                 abyCT[4 * i + 3] = static_cast<GByte>(psEntry->c4);
    2265             :             }
    2266           7 :             for (int i = nEntries; i < 256; i++)
    2267             :             {
    2268           0 :                 abyCT[4 * i] = 0;
    2269           0 :                 abyCT[4 * i + 1] = 0;
    2270           0 :                 abyCT[4 * i + 2] = 0;
    2271           0 :                 abyCT[4 * i + 3] = 0;
    2272             :             }
    2273           7 :             if (iYOff > 0)
    2274             :             {
    2275           0 :                 memset(m_pabyCachedTiles + 0 * nBandBlockSize, 0,
    2276           0 :                        nBlockXSize * iYOff);
    2277           0 :                 memset(m_pabyCachedTiles + 1 * nBandBlockSize, 0,
    2278           0 :                        nBlockXSize * iYOff);
    2279           0 :                 memset(m_pabyCachedTiles + 2 * nBandBlockSize, 0,
    2280           0 :                        nBlockXSize * iYOff);
    2281           0 :                 memset(m_pabyCachedTiles + 3 * nBandBlockSize, 0,
    2282           0 :                        nBlockXSize * iYOff);
    2283             :             }
    2284        1057 :             for (GPtrDiff_t iY = iYOff; iY < iYOff + iYCount; iY++)
    2285             :             {
    2286        1050 :                 if (iXOff > 0)
    2287             :                 {
    2288           0 :                     const GPtrDiff_t i = iY * nBlockXSize;
    2289           0 :                     memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2290             :                            iXOff);
    2291           0 :                     memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2292             :                            iXOff);
    2293           0 :                     memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2294             :                            iXOff);
    2295           0 :                     memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2296             :                            iXOff);
    2297             :                 }
    2298      183250 :                 for (int iX = iXOff; iX < iXOff + iXCount; iX++)
    2299             :                 {
    2300      182200 :                     const GPtrDiff_t i = iY * nBlockXSize + iX;
    2301      182200 :                     GByte byVal = m_pabyCachedTiles[i];
    2302      182200 :                     m_pabyCachedTiles[i] = abyCT[4 * byVal];
    2303      182200 :                     m_pabyCachedTiles[i + 1 * nBandBlockSize] =
    2304      182200 :                         abyCT[4 * byVal + 1];
    2305      182200 :                     m_pabyCachedTiles[i + 2 * nBandBlockSize] =
    2306      182200 :                         abyCT[4 * byVal + 2];
    2307      182200 :                     m_pabyCachedTiles[i + 3 * nBandBlockSize] =
    2308      182200 :                         abyCT[4 * byVal + 3];
    2309             :                 }
    2310        1050 :                 if (iXOff + iXCount < nBlockXSize)
    2311             :                 {
    2312         800 :                     const GPtrDiff_t i = iY * nBlockXSize + iXOff + iXCount;
    2313         800 :                     memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2314         800 :                            nBlockXSize - (iXOff + iXCount));
    2315         800 :                     memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2316         800 :                            nBlockXSize - (iXOff + iXCount));
    2317         800 :                     memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2318         800 :                            nBlockXSize - (iXOff + iXCount));
    2319         800 :                     memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2320         800 :                            nBlockXSize - (iXOff + iXCount));
    2321             :                 }
    2322             :             }
    2323           7 :             if (iYOff + iYCount < nBlockYSize)
    2324             :             {
    2325           7 :                 const GPtrDiff_t i = (iYOff + iYCount) * nBlockXSize;
    2326           7 :                 memset(m_pabyCachedTiles + 0 * nBandBlockSize + i, 0,
    2327           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2328           7 :                 memset(m_pabyCachedTiles + 1 * nBandBlockSize + i, 0,
    2329           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2330           7 :                 memset(m_pabyCachedTiles + 2 * nBandBlockSize + i, 0,
    2331           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2332           7 :                 memset(m_pabyCachedTiles + 3 * nBandBlockSize + i, 0,
    2333           7 :                        nBlockXSize * (nBlockYSize - (iYOff + iYCount)));
    2334             :             }
    2335             :         }
    2336             : 
    2337             :         char **papszDriverOptions =
    2338        4155 :             CSLSetNameValue(nullptr, "_INTERNAL_DATASET", "YES");
    2339        4155 :         if (EQUAL(pszDriverName, "JPEG") || EQUAL(pszDriverName, "WEBP"))
    2340             :         {
    2341             :             // If not all bands are dirty, then use lossless WEBP
    2342         172 :             if (!bAllDirty && EQUAL(pszDriverName, "WEBP"))
    2343             :             {
    2344           2 :                 papszDriverOptions =
    2345           2 :                     CSLSetNameValue(papszDriverOptions, "LOSSLESS", "YES");
    2346             :             }
    2347             :             else
    2348             :             {
    2349             :                 papszDriverOptions =
    2350         170 :                     CSLSetNameValue(papszDriverOptions, "QUALITY",
    2351             :                                     CPLSPrintf("%d", m_nQuality));
    2352             :             }
    2353             :         }
    2354        3983 :         else if (EQUAL(pszDriverName, "PNG"))
    2355             :         {
    2356        3979 :             papszDriverOptions = CSLSetNameValue(papszDriverOptions, "ZLEVEL",
    2357             :                                                  CPLSPrintf("%d", m_nZLevel));
    2358             :         }
    2359           4 :         else if (EQUAL(pszDriverName, "GTiff"))
    2360             :         {
    2361             :             papszDriverOptions =
    2362           4 :                 CSLSetNameValue(papszDriverOptions, "COMPRESS", "LZW");
    2363           4 :             if (nBlockXSize * nBlockYSize <= 512 * 512)
    2364             :             {
    2365             :                 // If tile is not too big, create it as single-strip TIFF
    2366             :                 papszDriverOptions =
    2367           4 :                     CSLSetNameValue(papszDriverOptions, "BLOCKYSIZE",
    2368             :                                     CPLSPrintf("%d", nBlockYSize));
    2369             :             }
    2370             :         }
    2371             : #ifdef DEBUG
    2372             :         VSIStatBufL sStat;
    2373        4155 :         CPLAssert(VSIStatL(osMemFileName, &sStat) != 0);
    2374             : #endif
    2375             :         GDALDataset *poOutDS =
    2376        4155 :             l_poDriver->CreateCopy(osMemFileName, poMEMDS, FALSE,
    2377             :                                    papszDriverOptions, nullptr, nullptr);
    2378        4155 :         CSLDestroy(papszDriverOptions);
    2379        4155 :         CPLFree(pTempTileBuffer);
    2380             : 
    2381        4155 :         if (poOutDS)
    2382             :         {
    2383        4155 :             GDALClose(poOutDS);
    2384        4155 :             vsi_l_offset nBlobSize = 0;
    2385             :             GByte *pabyBlob =
    2386        4155 :                 VSIGetMemFileBuffer(osMemFileName, &nBlobSize, TRUE);
    2387             : 
    2388             :             /* Create or commit and recreate transaction */
    2389        4155 :             GDALGPKGMBTilesLikePseudoDataset *poMainDS =
    2390        4155 :                 m_poParentDS ? m_poParentDS : this;
    2391        4155 :             if (poMainDS->m_nTileInsertionCount == 0)
    2392             :             {
    2393         181 :                 poMainDS->IStartTransaction();
    2394             :             }
    2395        3974 :             else if (poMainDS->m_nTileInsertionCount == 1000)
    2396             :             {
    2397           3 :                 if (poMainDS->ICommitTransaction() != OGRERR_NONE)
    2398             :                 {
    2399           1 :                     poMainDS->m_nTileInsertionCount = -1;
    2400           1 :                     CPLFree(pabyBlob);
    2401           1 :                     VSIUnlink(osMemFileName);
    2402           1 :                     delete poMEMDS;
    2403           1 :                     return CE_Failure;
    2404             :                 }
    2405           2 :                 poMainDS->IStartTransaction();
    2406           2 :                 poMainDS->m_nTileInsertionCount = 0;
    2407             :             }
    2408        4154 :             poMainDS->m_nTileInsertionCount++;
    2409             : 
    2410             :             char *pszSQL =
    2411        4154 :                 sqlite3_mprintf("INSERT OR REPLACE INTO \"%w\" "
    2412             :                                 "(zoom_level, tile_row, tile_column, "
    2413             :                                 "tile_data) VALUES (%d, %d, %d, ?)",
    2414             :                                 m_osRasterTable.c_str(), m_nZoomLevel,
    2415        4154 :                                 GetRowFromIntoTopConvention(nRow), nCol);
    2416             : #ifdef DEBUG_VERBOSE
    2417             :             CPLDebug("GPKG", "%s", pszSQL);
    2418             : #endif
    2419        4154 :             sqlite3_stmt *hStmt = nullptr;
    2420        4154 :             int rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt, nullptr);
    2421        4154 :             if (rc != SQLITE_OK)
    2422             :             {
    2423           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2424             :                          "failed to prepare SQL %s: %s", pszSQL,
    2425           0 :                          sqlite3_errmsg(IGetDB()));
    2426           0 :                 CPLFree(pabyBlob);
    2427             :             }
    2428             :             else
    2429             :             {
    2430        4154 :                 sqlite3_bind_blob(hStmt, 1, pabyBlob,
    2431             :                                   static_cast<int>(nBlobSize), CPLFree);
    2432        4154 :                 rc = sqlite3_step(hStmt);
    2433        4154 :                 if (rc == SQLITE_DONE)
    2434        4154 :                     eErr = CE_None;
    2435             :                 else
    2436             :                 {
    2437           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2438             :                              "Failure when inserting tile (row=%d,col=%d) at "
    2439             :                              "zoom_level=%d : %s",
    2440           0 :                              GetRowFromIntoTopConvention(nRow), nCol,
    2441           0 :                              m_nZoomLevel, sqlite3_errmsg(IGetDB()));
    2442             :                 }
    2443             :             }
    2444        4154 :             sqlite3_finalize(hStmt);
    2445        4154 :             sqlite3_free(pszSQL);
    2446             : 
    2447        4154 :             if (m_eTF == GPKG_TF_PNG_16BIT || m_eTF == GPKG_TF_TIFF_32BIT_FLOAT)
    2448             :             {
    2449          54 :                 GIntBig nTileId = GetTileId(nRow, nCol);
    2450          54 :                 if (nTileId == 0)
    2451           0 :                     eErr = CE_Failure;
    2452             :                 else
    2453             :                 {
    2454          54 :                     DeleteFromGriddedTileAncillary(nTileId);
    2455             : 
    2456          54 :                     pszSQL = sqlite3_mprintf(
    2457             :                         "INSERT INTO gpkg_2d_gridded_tile_ancillary "
    2458             :                         "(tpudt_name, tpudt_id, scale, offset, min, max, "
    2459             :                         "mean, std_dev) VALUES "
    2460             :                         "('%q', ?, %.17g, %.17g, ?, ?, ?, ?)",
    2461             :                         m_osRasterTable.c_str(), dfTileScale, dfTileOffset);
    2462             : #ifdef DEBUG_VERBOSE
    2463             :                     CPLDebug("GPKG", "%s", pszSQL);
    2464             : #endif
    2465          54 :                     hStmt = nullptr;
    2466          54 :                     rc = sqlite3_prepare_v2(IGetDB(), pszSQL, -1, &hStmt,
    2467             :                                             nullptr);
    2468          54 :                     if (rc != SQLITE_OK)
    2469             :                     {
    2470           0 :                         eErr = CE_Failure;
    2471           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2472             :                                  "failed to prepare SQL %s: %s", pszSQL,
    2473           0 :                                  sqlite3_errmsg(IGetDB()));
    2474             :                     }
    2475             :                     else
    2476             :                     {
    2477          54 :                         sqlite3_bind_int64(hStmt, 1, nTileId);
    2478          54 :                         sqlite3_bind_double(hStmt, 2, dfTileMin);
    2479          54 :                         sqlite3_bind_double(hStmt, 3, dfTileMax);
    2480          54 :                         sqlite3_bind_double(hStmt, 4, dfTileMean);
    2481          54 :                         sqlite3_bind_double(hStmt, 5, dfTileStdDev);
    2482          54 :                         rc = sqlite3_step(hStmt);
    2483          54 :                         if (rc == SQLITE_DONE)
    2484             :                         {
    2485          54 :                             eErr = CE_None;
    2486             :                         }
    2487             :                         else
    2488             :                         {
    2489           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    2490             :                                      "Cannot insert into "
    2491             :                                      "gpkg_2d_gridded_tile_ancillary");
    2492           0 :                             eErr = CE_Failure;
    2493             :                         }
    2494             :                     }
    2495          54 :                     sqlite3_finalize(hStmt);
    2496          54 :                     sqlite3_free(pszSQL);
    2497             :                 }
    2498             :             }
    2499             :         }
    2500             : 
    2501        4154 :         VSIUnlink(osMemFileName);
    2502        4154 :         delete poMEMDS;
    2503             :     }
    2504             :     else
    2505             :     {
    2506           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot find driver %s",
    2507             :                  pszDriverName);
    2508             :     }
    2509             : 
    2510        4155 :     return eErr;
    2511             : }
    2512             : 
    2513             : /************************************************************************/
    2514             : /*                     FlushRemainingShiftedTiles()                     */
    2515             : /************************************************************************/
    2516             : 
    2517             : CPLErr
    2518         609 : GDALGPKGMBTilesLikePseudoDataset::FlushRemainingShiftedTiles(bool bPartialFlush)
    2519             : {
    2520         609 :     if (m_hTempDB == nullptr)
    2521         460 :         return CE_None;
    2522             : 
    2523         745 :     for (int i = 0; i <= 3; i++)
    2524             :     {
    2525         596 :         m_asCachedTilesDesc[i].nRow = -1;
    2526         596 :         m_asCachedTilesDesc[i].nCol = -1;
    2527         596 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    2528             :     }
    2529             : 
    2530             :     int nBlockXSize, nBlockYSize;
    2531         149 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2532         149 :     const int nBands = IGetRasterCount();
    2533         149 :     const int nRasterXSize = IGetRasterBand(1)->GetXSize();
    2534         149 :     const int nRasterYSize = IGetRasterBand(1)->GetYSize();
    2535         149 :     const int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
    2536         149 :     const int nYBlocks = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
    2537             : 
    2538         149 :     int nPartialActiveTiles = 0;
    2539         149 :     if (bPartialFlush)
    2540             :     {
    2541           2 :         sqlite3_stmt *hStmt = nullptr;
    2542           4 :         CPLString osSQL;
    2543             :         osSQL.Printf("SELECT COUNT(*) FROM partial_tiles WHERE zoom_level = %d "
    2544             :                      "AND partial_flag != 0",
    2545           2 :                      m_nZoomLevel);
    2546           2 :         if (sqlite3_prepare_v2(m_hTempDB, osSQL.c_str(), -1, &hStmt, nullptr) ==
    2547             :             SQLITE_OK)
    2548             :         {
    2549           2 :             if (sqlite3_step(hStmt) == SQLITE_ROW)
    2550             :             {
    2551           2 :                 nPartialActiveTiles = sqlite3_column_int(hStmt, 0);
    2552           2 :                 CPLDebug("GPKG", "Active partial tiles before flush: %d",
    2553             :                          nPartialActiveTiles);
    2554             :             }
    2555           2 :             sqlite3_finalize(hStmt);
    2556             :         }
    2557             :     }
    2558             : 
    2559         298 :     CPLString osSQL = "SELECT tile_row, tile_column, partial_flag";
    2560         616 :     for (int nBand = 1; nBand <= nBands; nBand++)
    2561             :     {
    2562         467 :         osSQL += CPLSPrintf(", tile_data_band_%d", nBand);
    2563             :     }
    2564             :     osSQL += CPLSPrintf(" FROM partial_tiles WHERE "
    2565             :                         "zoom_level = %d AND partial_flag != 0",
    2566         149 :                         m_nZoomLevel);
    2567         149 :     if (bPartialFlush)
    2568             :     {
    2569           2 :         osSQL += " ORDER BY age";
    2570             :     }
    2571         149 :     const char *pszSQL = osSQL.c_str();
    2572             : 
    2573             : #ifdef DEBUG_VERBOSE
    2574             :     CPLDebug("GPKG", "%s", pszSQL);
    2575             : #endif
    2576         149 :     sqlite3_stmt *hStmt = nullptr;
    2577         149 :     int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    2578         149 :     if (rc != SQLITE_OK)
    2579             :     {
    2580           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2581             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    2582             :                  sqlite3_errmsg(m_hTempDB));
    2583           0 :         return CE_Failure;
    2584             :     }
    2585             : 
    2586         149 :     CPLErr eErr = CE_None;
    2587         149 :     bool bGotPartialTiles = false;
    2588         149 :     int nCountFlushedTiles = 0;
    2589         149 :     const size_t nBandBlockSize =
    2590         149 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    2591         175 :     do
    2592             :     {
    2593         324 :         rc = sqlite3_step(hStmt);
    2594         324 :         if (rc == SQLITE_ROW)
    2595             :         {
    2596         177 :             bGotPartialTiles = true;
    2597             : 
    2598         177 :             int nRow = sqlite3_column_int(hStmt, 0);
    2599         177 :             int nCol = sqlite3_column_int(hStmt, 1);
    2600         177 :             int nPartialFlags = sqlite3_column_int(hStmt, 2);
    2601             : 
    2602         177 :             if (bPartialFlush)
    2603             :             {
    2604             :                 // This method assumes that there are no dirty blocks alive
    2605             :                 // so check this assumption.
    2606             :                 // When called with bPartialFlush = false, FlushCache() has
    2607             :                 // already been called, so no need to check.
    2608          16 :                 bool bFoundDirtyBlock = false;
    2609          16 :                 int nBlockXOff = nCol - m_nShiftXTiles;
    2610          16 :                 int nBlockYOff = nRow - m_nShiftYTiles;
    2611          96 :                 for (int iX = 0; !bFoundDirtyBlock &&
    2612          48 :                                  iX < ((m_nShiftXPixelsMod != 0) ? 2 : 1);
    2613             :                      iX++)
    2614             :                 {
    2615          32 :                     if (nBlockXOff + iX < 0 || nBlockXOff + iX >= nXBlocks)
    2616          12 :                         continue;
    2617         120 :                     for (int iY = 0; !bFoundDirtyBlock &&
    2618          60 :                                      iY < ((m_nShiftYPixelsMod != 0) ? 2 : 1);
    2619             :                          iY++)
    2620             :                     {
    2621          40 :                         if (nBlockYOff + iY < 0 || nBlockYOff + iY >= nYBlocks)
    2622          17 :                             continue;
    2623          69 :                         for (int iBand = 1;
    2624          69 :                              !bFoundDirtyBlock && iBand <= nBands; iBand++)
    2625             :                         {
    2626             :                             GDALRasterBlock *poBlock =
    2627             :                                 cpl::down_cast<GDALGPKGMBTilesLikeRasterBand *>(
    2628          46 :                                     IGetRasterBand(iBand))
    2629          46 :                                     ->AccessibleTryGetLockedBlockRef(
    2630             :                                         nBlockXOff + iX, nBlockYOff + iY);
    2631          46 :                             if (poBlock)
    2632             :                             {
    2633           0 :                                 if (poBlock->GetDirty())
    2634           0 :                                     bFoundDirtyBlock = true;
    2635           0 :                                 poBlock->DropLock();
    2636             :                             }
    2637             :                         }
    2638             :                     }
    2639             :                 }
    2640          16 :                 if (bFoundDirtyBlock)
    2641             :                 {
    2642             : #ifdef DEBUG_VERBOSE
    2643             :                     CPLDebug("GPKG",
    2644             :                              "Skipped flushing tile row = %d, column = %d "
    2645             :                              "because it has dirty block(s) in GDAL cache",
    2646             :                              nRow, nCol);
    2647             : #endif
    2648           0 :                     continue;
    2649             :                 }
    2650             :             }
    2651             : 
    2652         177 :             nCountFlushedTiles++;
    2653         177 :             if (bPartialFlush && nCountFlushedTiles >= nPartialActiveTiles / 2)
    2654             :             {
    2655           2 :                 CPLDebug("GPKG", "Flushed %d tiles", nCountFlushedTiles);
    2656           2 :                 break;
    2657             :             }
    2658             : 
    2659         705 :             for (int nBand = 1; nBand <= nBands; nBand++)
    2660             :             {
    2661         530 :                 if (nPartialFlags & (((1 << 4) - 1) << (4 * (nBand - 1))))
    2662             :                 {
    2663         498 :                     CPLAssert(sqlite3_column_bytes(hStmt, 2 + nBand) ==
    2664             :                               static_cast<int>(nBandBlockSize));
    2665         498 :                     memcpy(m_pabyCachedTiles + (nBand - 1) * nBandBlockSize,
    2666             :                            sqlite3_column_blob(hStmt, 2 + nBand),
    2667             :                            nBandBlockSize);
    2668             :                 }
    2669             :                 else
    2670             :                 {
    2671          32 :                     FillEmptyTileSingleBand(m_pabyCachedTiles +
    2672          32 :                                             (nBand - 1) * nBandBlockSize);
    2673             :                 }
    2674             :             }
    2675             : 
    2676         175 :             int nFullFlags = (1 << (4 * nBands)) - 1;
    2677             : 
    2678             :             // In case the partial flags indicate that there's some quadrant
    2679             :             // missing, check in the main database if there is already a tile
    2680             :             // In which case, use the parts of that tile that aren't in the
    2681             :             // temporary database
    2682         175 :             if (nPartialFlags != nFullFlags)
    2683             :             {
    2684         525 :                 char *pszNewSQL = sqlite3_mprintf(
    2685             :                     "SELECT tile_data%s FROM \"%w\" "
    2686             :                     "WHERE zoom_level = %d AND tile_row = %d AND tile_column = "
    2687             :                     "%d%s",
    2688         175 :                     m_eDT != GDT_Byte ? ", id"
    2689             :                                       : "",  // MBTiles do not have an id
    2690             :                     m_osRasterTable.c_str(), m_nZoomLevel,
    2691         175 :                     GetRowFromIntoTopConvention(nRow), nCol,
    2692         175 :                     !m_osWHERE.empty()
    2693           0 :                         ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str())
    2694             :                         : "");
    2695             : #ifdef DEBUG_VERBOSE
    2696             :                 CPLDebug("GPKG", "%s", pszNewSQL);
    2697             : #endif
    2698         175 :                 sqlite3_stmt *hNewStmt = nullptr;
    2699         175 :                 rc = sqlite3_prepare_v2(IGetDB(), pszNewSQL, -1, &hNewStmt,
    2700             :                                         nullptr);
    2701         175 :                 if (rc == SQLITE_OK)
    2702             :                 {
    2703         175 :                     rc = sqlite3_step(hNewStmt);
    2704         192 :                     if (rc == SQLITE_ROW &&
    2705          17 :                         sqlite3_column_type(hNewStmt, 0) == SQLITE_BLOB)
    2706             :                     {
    2707          17 :                         const int nBytes = sqlite3_column_bytes(hNewStmt, 0);
    2708             :                         GIntBig nTileId =
    2709          17 :                             (m_eDT == GDT_Byte)
    2710          17 :                                 ? 0
    2711           0 :                                 : sqlite3_column_int64(hNewStmt, 1);
    2712             :                         GByte *pabyRawData =
    2713             :                             const_cast<GByte *>(static_cast<const GByte *>(
    2714          17 :                                 sqlite3_column_blob(hNewStmt, 0)));
    2715             :                         const CPLString osMemFileName(
    2716          34 :                             VSIMemGenerateHiddenFilename("gpkg_read_tile"));
    2717          17 :                         VSILFILE *fp = VSIFileFromMemBuffer(
    2718             :                             osMemFileName.c_str(), pabyRawData, nBytes, FALSE);
    2719          17 :                         VSIFCloseL(fp);
    2720             : 
    2721          17 :                         double dfTileOffset = 0.0;
    2722          17 :                         double dfTileScale = 1.0;
    2723          17 :                         GetTileOffsetAndScale(nTileId, dfTileOffset,
    2724             :                                               dfTileScale);
    2725          17 :                         const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    2726          17 :                         GByte *pabyTemp =
    2727          17 :                             m_pabyCachedTiles + nTileBands * nBandBlockSize;
    2728          17 :                         ReadTile(osMemFileName, pabyTemp, dfTileOffset,
    2729             :                                  dfTileScale);
    2730          17 :                         VSIUnlink(osMemFileName);
    2731             : 
    2732          17 :                         int iYQuadrantMax = (m_nShiftYPixelsMod) ? 1 : 0;
    2733          17 :                         int iXQuadrantMax = (m_nShiftXPixelsMod) ? 1 : 0;
    2734          51 :                         for (int iYQuadrant = 0; iYQuadrant <= iYQuadrantMax;
    2735             :                              iYQuadrant++)
    2736             :                         {
    2737         100 :                             for (int iXQuadrant = 0;
    2738         100 :                                  iXQuadrant <= iXQuadrantMax; iXQuadrant++)
    2739             :                             {
    2740         226 :                                 for (int nBand = 1; nBand <= nBands; nBand++)
    2741             :                                 {
    2742         160 :                                     int iQuadrantFlag = 0;
    2743         160 :                                     if (iXQuadrant == 0 && iYQuadrant == 0)
    2744          42 :                                         iQuadrantFlag |= (1 << 0);
    2745         160 :                                     if (iXQuadrant == iXQuadrantMax &&
    2746             :                                         iYQuadrant == 0)
    2747          42 :                                         iQuadrantFlag |= (1 << 1);
    2748         160 :                                     if (iXQuadrant == 0 &&
    2749             :                                         iYQuadrant == iYQuadrantMax)
    2750          42 :                                         iQuadrantFlag |= (1 << 2);
    2751         160 :                                     if (iXQuadrant == iXQuadrantMax &&
    2752             :                                         iYQuadrant == iYQuadrantMax)
    2753          42 :                                         iQuadrantFlag |= (1 << 3);
    2754         160 :                                     int nLocalFlag = iQuadrantFlag
    2755         160 :                                                      << (4 * (nBand - 1));
    2756         160 :                                     if (!(nPartialFlags & nLocalFlag))
    2757             :                                     {
    2758             :                                         int nXOff, nYOff, nXSize, nYSize;
    2759         118 :                                         if (iXQuadrant == 0 &&
    2760          60 :                                             m_nShiftXPixelsMod != 0)
    2761             :                                         {
    2762          56 :                                             nXOff = 0;
    2763          56 :                                             nXSize = m_nShiftXPixelsMod;
    2764             :                                         }
    2765             :                                         else
    2766             :                                         {
    2767          62 :                                             nXOff = m_nShiftXPixelsMod;
    2768          62 :                                             nXSize = nBlockXSize -
    2769          62 :                                                      m_nShiftXPixelsMod;
    2770             :                                         }
    2771         118 :                                         if (iYQuadrant == 0 &&
    2772          63 :                                             m_nShiftYPixelsMod != 0)
    2773             :                                         {
    2774          63 :                                             nYOff = 0;
    2775          63 :                                             nYSize = m_nShiftYPixelsMod;
    2776             :                                         }
    2777             :                                         else
    2778             :                                         {
    2779          55 :                                             nYOff = m_nShiftYPixelsMod;
    2780          55 :                                             nYSize = nBlockYSize -
    2781          55 :                                                      m_nShiftYPixelsMod;
    2782             :                                         }
    2783         118 :                                         for (int iY = nYOff;
    2784       13888 :                                              iY < nYOff + nYSize; iY++)
    2785             :                                         {
    2786       13770 :                                             memcpy(m_pabyCachedTiles +
    2787       13770 :                                                        ((static_cast<size_t>(
    2788       13770 :                                                              nBand - 1) *
    2789       13770 :                                                              nBlockYSize +
    2790       13770 :                                                          iY) *
    2791       13770 :                                                             nBlockXSize +
    2792       13770 :                                                         nXOff) *
    2793       13770 :                                                            m_nDTSize,
    2794       13770 :                                                    pabyTemp +
    2795       13770 :                                                        ((static_cast<size_t>(
    2796       13770 :                                                              nBand - 1) *
    2797       13770 :                                                              nBlockYSize +
    2798       13770 :                                                          iY) *
    2799       13770 :                                                             nBlockXSize +
    2800       13770 :                                                         nXOff) *
    2801       13770 :                                                            m_nDTSize,
    2802       13770 :                                                    static_cast<size_t>(nXSize) *
    2803       13770 :                                                        m_nDTSize);
    2804             :                                         }
    2805             :                                     }
    2806             :                                 }
    2807             :                             }
    2808             :                         }
    2809             :                     }
    2810         158 :                     else if (rc != SQLITE_DONE)
    2811             :                     {
    2812           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2813             :                                  "sqlite3_step(%s) failed: %s", pszNewSQL,
    2814             :                                  sqlite3_errmsg(m_hTempDB));
    2815             :                     }
    2816         175 :                     sqlite3_finalize(hNewStmt);
    2817             :                 }
    2818             :                 else
    2819             :                 {
    2820           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2821             :                              "sqlite3_prepare_v2(%s) failed: %s", pszNewSQL,
    2822             :                              sqlite3_errmsg(m_hTempDB));
    2823             :                 }
    2824         175 :                 sqlite3_free(pszNewSQL);
    2825             :             }
    2826             : 
    2827         175 :             m_asCachedTilesDesc[0].nRow = nRow;
    2828         175 :             m_asCachedTilesDesc[0].nCol = nCol;
    2829         175 :             m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    2830         175 :             m_asCachedTilesDesc[0].abBandDirty[0] = true;
    2831         175 :             m_asCachedTilesDesc[0].abBandDirty[1] = true;
    2832         175 :             m_asCachedTilesDesc[0].abBandDirty[2] = true;
    2833         175 :             m_asCachedTilesDesc[0].abBandDirty[3] = true;
    2834             : 
    2835         175 :             eErr = WriteTile();
    2836             : 
    2837         175 :             if (eErr == CE_None && bPartialFlush)
    2838             :             {
    2839             :                 pszSQL =
    2840          14 :                     CPLSPrintf("DELETE FROM partial_tiles WHERE zoom_level = "
    2841             :                                "%d AND tile_row = %d AND tile_column = %d",
    2842             :                                m_nZoomLevel, nRow, nCol);
    2843             : #ifdef DEBUG_VERBOSE
    2844             :                 CPLDebug("GPKG", "%s", pszSQL);
    2845             : #endif
    2846          14 :                 if (SQLCommand(m_hTempDB, pszSQL) != OGRERR_NONE)
    2847           0 :                     eErr = CE_None;
    2848             :             }
    2849             :         }
    2850             :         else
    2851             :         {
    2852         147 :             if (rc != SQLITE_DONE)
    2853             :             {
    2854           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2855             :                          "sqlite3_step(%s) failed: %s", pszSQL,
    2856             :                          sqlite3_errmsg(m_hTempDB));
    2857             :             }
    2858         147 :             break;
    2859             :         }
    2860         175 :     } while (eErr == CE_None);
    2861             : 
    2862         149 :     sqlite3_finalize(hStmt);
    2863             : 
    2864         149 :     if (bPartialFlush && nCountFlushedTiles < nPartialActiveTiles / 2)
    2865             :     {
    2866           0 :         CPLDebug("GPKG", "Flushed %d tiles. Target was %d", nCountFlushedTiles,
    2867             :                  nPartialActiveTiles / 2);
    2868             :     }
    2869             : 
    2870         149 :     if (bGotPartialTiles && !bPartialFlush)
    2871             :     {
    2872             : #ifdef DEBUG_VERBOSE
    2873             :         pszSQL = CPLSPrintf("SELECT p1.id, p1.tile_row, p1.tile_column FROM "
    2874             :                             "partial_tiles p1, partial_tiles p2 "
    2875             :                             "WHERE p1.zoom_level = %d AND p2.zoom_level = %d "
    2876             :                             "AND p1.tile_row = p2.tile_row AND p1.tile_column "
    2877             :                             "= p2.tile_column AND p2.partial_flag != 0",
    2878             :                             -1 - m_nZoomLevel, m_nZoomLevel);
    2879             :         rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    2880             :         CPLAssert(rc == SQLITE_OK);
    2881             :         while ((rc = sqlite3_step(hStmt)) == SQLITE_ROW)
    2882             :         {
    2883             :             CPLDebug("GPKG",
    2884             :                      "Conflict: existing id = %d, tile_row = %d, tile_column = "
    2885             :                      "%d, zoom_level = %d",
    2886             :                      sqlite3_column_int(hStmt, 0), sqlite3_column_int(hStmt, 1),
    2887             :                      sqlite3_column_int(hStmt, 2), m_nZoomLevel);
    2888             :         }
    2889             :         sqlite3_finalize(hStmt);
    2890             : #endif
    2891             : 
    2892         110 :         pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, "
    2893             :                             "partial_flag = 0, age = -1 WHERE zoom_level = %d "
    2894             :                             "AND partial_flag != 0",
    2895          55 :                             -1 - m_nZoomLevel, m_nZoomLevel);
    2896             : #ifdef DEBUG_VERBOSE
    2897             :         CPLDebug("GPKG", "%s", pszSQL);
    2898             : #endif
    2899          55 :         SQLCommand(m_hTempDB, pszSQL);
    2900             :     }
    2901             : 
    2902         149 :     return eErr;
    2903             : }
    2904             : 
    2905             : /************************************************************************/
    2906             : /*                DoPartialFlushOfPartialTilesIfNecessary()             */
    2907             : /************************************************************************/
    2908             : 
    2909             : CPLErr
    2910        4556 : GDALGPKGMBTilesLikePseudoDataset::DoPartialFlushOfPartialTilesIfNecessary()
    2911             : {
    2912        4556 :     time_t nCurTimeStamp = time(nullptr);
    2913        4556 :     if (m_nLastSpaceCheckTimestamp == 0)
    2914           3 :         m_nLastSpaceCheckTimestamp = nCurTimeStamp;
    2915        4556 :     if (m_nLastSpaceCheckTimestamp > 0 &&
    2916          81 :         (m_bForceTempDBCompaction ||
    2917          13 :          nCurTimeStamp - m_nLastSpaceCheckTimestamp > 10))
    2918             :     {
    2919          68 :         m_nLastSpaceCheckTimestamp = nCurTimeStamp;
    2920             :         GIntBig nFreeSpace =
    2921          68 :             VSIGetDiskFreeSpace(CPLGetDirnameSafe(m_osTempDBFilename).c_str());
    2922          68 :         bool bTryFreeing = false;
    2923          68 :         if (nFreeSpace >= 0 && nFreeSpace < 1024 * 1024 * 1024)
    2924             :         {
    2925           0 :             CPLDebug("GPKG",
    2926             :                      "Free space below 1GB. Flushing part of partial tiles");
    2927           0 :             bTryFreeing = true;
    2928             :         }
    2929             :         else
    2930             :         {
    2931             :             VSIStatBufL sStat;
    2932          68 :             if (VSIStatL(m_osTempDBFilename, &sStat) == 0)
    2933             :             {
    2934          68 :                 GIntBig nTempSpace = sStat.st_size;
    2935         136 :                 if (VSIStatL((m_osTempDBFilename + "-journal").c_str(),
    2936          68 :                              &sStat) == 0)
    2937           0 :                     nTempSpace += sStat.st_size;
    2938         136 :                 else if (VSIStatL((m_osTempDBFilename + "-wal").c_str(),
    2939          68 :                                   &sStat) == 0)
    2940           0 :                     nTempSpace += sStat.st_size;
    2941             : 
    2942             :                 int nBlockXSize, nBlockYSize;
    2943          68 :                 IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2944          68 :                 const int nBands = IGetRasterCount();
    2945             : 
    2946          68 :                 if (nTempSpace >
    2947          68 :                     4 * static_cast<GIntBig>(IGetRasterBand(1)->GetXSize()) *
    2948          68 :                         nBlockYSize * nBands * m_nDTSize)
    2949             :                 {
    2950           2 :                     CPLDebug("GPKG",
    2951             :                              "Partial tiles DB is " CPL_FRMT_GIB
    2952             :                              " bytes. Flushing part of partial tiles",
    2953             :                              nTempSpace);
    2954           2 :                     bTryFreeing = true;
    2955             :                 }
    2956             :             }
    2957             :         }
    2958          68 :         if (bTryFreeing)
    2959             :         {
    2960           2 :             if (FlushRemainingShiftedTiles(true /* partial flush*/) != CE_None)
    2961             :             {
    2962           0 :                 return CE_Failure;
    2963             :             }
    2964           2 :             SQLCommand(m_hTempDB,
    2965             :                        "DELETE FROM partial_tiles WHERE zoom_level < 0");
    2966           2 :             SQLCommand(m_hTempDB, "VACUUM");
    2967             :         }
    2968             :     }
    2969        4556 :     return CE_None;
    2970             : }
    2971             : 
    2972             : /************************************************************************/
    2973             : /*                         WriteShiftedTile()                           */
    2974             : /************************************************************************/
    2975             : 
    2976        4556 : CPLErr GDALGPKGMBTilesLikePseudoDataset::WriteShiftedTile(
    2977             :     int nRow, int nCol, int nBand, int nDstXOffset, int nDstYOffset,
    2978             :     int nDstXSize, int nDstYSize)
    2979             : {
    2980        4556 :     CPLAssert(m_nShiftXPixelsMod || m_nShiftYPixelsMod);
    2981        4556 :     CPLAssert(nRow >= 0);
    2982        4556 :     CPLAssert(nCol >= 0);
    2983        4556 :     CPLAssert(nRow < m_nTileMatrixHeight);
    2984        4556 :     CPLAssert(nCol < m_nTileMatrixWidth);
    2985             : 
    2986        4556 :     if (m_hTempDB == nullptr &&
    2987          55 :         (m_poParentDS == nullptr || m_poParentDS->m_hTempDB == nullptr))
    2988             :     {
    2989             :         const char *pszBaseFilename =
    2990          48 :             m_poParentDS ? m_poParentDS->IGetFilename() : IGetFilename();
    2991             :         m_osTempDBFilename =
    2992          48 :             CPLResetExtensionSafe(pszBaseFilename, "partial_tiles.db");
    2993          48 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2994          48 :         VSIUnlink(m_osTempDBFilename);
    2995          48 :         CPLPopErrorHandler();
    2996          48 :         m_hTempDB = nullptr;
    2997          48 :         int rc = 0;
    2998          48 :         if (STARTS_WITH(m_osTempDBFilename, "/vsi"))
    2999             :         {
    3000          33 :             m_pMyVFS = OGRSQLiteCreateVFS(nullptr, nullptr);
    3001          33 :             sqlite3_vfs_register(m_pMyVFS, 0);
    3002          33 :             rc = sqlite3_open_v2(m_osTempDBFilename, &m_hTempDB,
    3003             :                                  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
    3004             :                                      SQLITE_OPEN_NOMUTEX,
    3005          33 :                                  m_pMyVFS->zName);
    3006             :         }
    3007             :         else
    3008             :         {
    3009          15 :             rc = sqlite3_open(m_osTempDBFilename, &m_hTempDB);
    3010             :         }
    3011          48 :         if (rc != SQLITE_OK || m_hTempDB == nullptr)
    3012             :         {
    3013           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3014             :                      "Cannot create temporary database %s",
    3015             :                      m_osTempDBFilename.c_str());
    3016           0 :             return CE_Failure;
    3017             :         }
    3018          48 :         SQLCommand(m_hTempDB, "PRAGMA synchronous = OFF");
    3019          48 :         SQLCommand(m_hTempDB, "PRAGMA journal_mode = OFF");
    3020          48 :         SQLCommand(m_hTempDB, "CREATE TABLE partial_tiles("
    3021             :                               "id INTEGER PRIMARY KEY AUTOINCREMENT,"
    3022             :                               "zoom_level INTEGER NOT NULL,"
    3023             :                               "tile_column INTEGER NOT NULL,"
    3024             :                               "tile_row INTEGER NOT NULL,"
    3025             :                               "tile_data_band_1 BLOB,"
    3026             :                               "tile_data_band_2 BLOB,"
    3027             :                               "tile_data_band_3 BLOB,"
    3028             :                               "tile_data_band_4 BLOB,"
    3029             :                               "partial_flag INTEGER NOT NULL,"
    3030             :                               "age INTEGER NOT NULL,"
    3031             :                               "UNIQUE (zoom_level, tile_column, tile_row))");
    3032          48 :         SQLCommand(m_hTempDB, "CREATE INDEX partial_tiles_partial_flag_idx "
    3033             :                               "ON partial_tiles(partial_flag)");
    3034          48 :         SQLCommand(m_hTempDB, "CREATE INDEX partial_tiles_age_idx "
    3035             :                               "ON partial_tiles(age)");
    3036             : 
    3037          48 :         if (m_poParentDS != nullptr)
    3038             :         {
    3039           4 :             m_poParentDS->m_osTempDBFilename = m_osTempDBFilename;
    3040           4 :             m_poParentDS->m_hTempDB = m_hTempDB;
    3041             :         }
    3042             :     }
    3043             : 
    3044        4556 :     if (m_poParentDS != nullptr)
    3045        4020 :         m_hTempDB = m_poParentDS->m_hTempDB;
    3046             : 
    3047             :     int nBlockXSize, nBlockYSize;
    3048        4556 :     IGetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    3049        4556 :     const int nBands = IGetRasterCount();
    3050        4556 :     const size_t nBandBlockSize =
    3051        4556 :         static_cast<size_t>(nBlockXSize) * nBlockYSize * m_nDTSize;
    3052             : 
    3053        4556 :     int iQuadrantFlag = 0;
    3054        4556 :     if (nDstXOffset == 0 && nDstYOffset == 0)
    3055        1031 :         iQuadrantFlag |= (1 << 0);
    3056        4556 :     if ((nDstXOffset != 0 || nDstXOffset + nDstXSize == nBlockXSize) &&
    3057             :         nDstYOffset == 0)
    3058        1125 :         iQuadrantFlag |= (1 << 1);
    3059        4556 :     if (nDstXOffset == 0 &&
    3060        1031 :         (nDstYOffset != 0 || nDstYOffset + nDstYSize == nBlockYSize))
    3061        1135 :         iQuadrantFlag |= (1 << 2);
    3062        4556 :     if ((nDstXOffset != 0 || nDstXOffset + nDstXSize == nBlockXSize) &&
    3063        1125 :         (nDstYOffset != 0 || nDstYOffset + nDstYSize == nBlockYSize))
    3064        1323 :         iQuadrantFlag |= (1 << 3);
    3065        4556 :     int l_nFlags = iQuadrantFlag << (4 * (nBand - 1));
    3066        4556 :     int nFullFlags = (1 << (4 * nBands)) - 1;
    3067        4556 :     int nOldFlags = 0;
    3068             : 
    3069       18224 :     for (int i = 1; i <= 3; i++)
    3070             :     {
    3071       13668 :         m_asCachedTilesDesc[i].nRow = -1;
    3072       13668 :         m_asCachedTilesDesc[i].nCol = -1;
    3073       13668 :         m_asCachedTilesDesc[i].nIdxWithinTileData = -1;
    3074             :     }
    3075             : 
    3076        4556 :     int nExistingId = 0;
    3077        4556 :     const char *pszSQL = CPLSPrintf(
    3078             :         "SELECT id, partial_flag, tile_data_band_%d FROM partial_tiles WHERE "
    3079             :         "zoom_level = %d AND tile_row = %d AND tile_column = %d",
    3080             :         nBand, m_nZoomLevel, nRow, nCol);
    3081             : #ifdef DEBUG_VERBOSE
    3082             :     CPLDebug("GPKG", "%s", pszSQL);
    3083             : #endif
    3084        4556 :     sqlite3_stmt *hStmt = nullptr;
    3085        4556 :     int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    3086        4556 :     if (rc != SQLITE_OK)
    3087             :     {
    3088           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3089             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    3090             :                  sqlite3_errmsg(m_hTempDB));
    3091           0 :         return CE_Failure;
    3092             :     }
    3093             : 
    3094        4556 :     rc = sqlite3_step(hStmt);
    3095        4556 :     const int nTileBands = m_eDT == GDT_Byte ? 4 : 1;
    3096        4556 :     GByte *pabyTemp = m_pabyCachedTiles + nTileBands * nBandBlockSize;
    3097        4556 :     if (rc == SQLITE_ROW)
    3098             :     {
    3099        4134 :         nExistingId = sqlite3_column_int(hStmt, 0);
    3100             : #ifdef DEBUG_VERBOSE
    3101             :         CPLDebug("GPKG", "Using partial_tile id=%d", nExistingId);
    3102             : #endif
    3103        4134 :         nOldFlags = sqlite3_column_int(hStmt, 1);
    3104        4134 :         CPLAssert(nOldFlags != 0);
    3105        4134 :         if ((nOldFlags & (((1 << 4) - 1) << (4 * (nBand - 1)))) == 0)
    3106             :         {
    3107        1021 :             FillEmptyTileSingleBand(pabyTemp + (nBand - 1) * nBandBlockSize);
    3108             :         }
    3109             :         else
    3110             :         {
    3111        3113 :             CPLAssert(sqlite3_column_bytes(hStmt, 2) ==
    3112             :                       static_cast<int>(nBandBlockSize));
    3113        3113 :             memcpy(pabyTemp + (nBand - 1) * nBandBlockSize,
    3114             :                    sqlite3_column_blob(hStmt, 2), nBandBlockSize);
    3115             :         }
    3116             :     }
    3117             :     else
    3118             :     {
    3119         422 :         FillEmptyTileSingleBand(pabyTemp + (nBand - 1) * nBandBlockSize);
    3120             :     }
    3121        4556 :     sqlite3_finalize(hStmt);
    3122        4556 :     hStmt = nullptr;
    3123             : 
    3124             :     /* Copy the updated rectangle into the full tile */
    3125      556108 :     for (int iY = nDstYOffset; iY < nDstYOffset + nDstYSize; iY++)
    3126             :     {
    3127      551552 :         memcpy(pabyTemp +
    3128      551552 :                    (static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize +
    3129      551552 :                     static_cast<size_t>(iY) * nBlockXSize + nDstXOffset) *
    3130      551552 :                        m_nDTSize,
    3131      551552 :                m_pabyCachedTiles +
    3132      551552 :                    (static_cast<size_t>(nBand - 1) * nBlockXSize * nBlockYSize +
    3133      551552 :                     static_cast<size_t>(iY) * nBlockXSize + nDstXOffset) *
    3134      551552 :                        m_nDTSize,
    3135      551552 :                static_cast<size_t>(nDstXSize) * m_nDTSize);
    3136             :     }
    3137             : 
    3138             : #ifdef notdef
    3139             :     static int nCounter = 1;
    3140             :     GDALDataset *poLogDS =
    3141             :         ((GDALDriver *)GDALGetDriverByName("GTiff"))
    3142             :             ->Create(CPLSPrintf("/tmp/partial_band_%d_%d.tif", 1, nCounter++),
    3143             :                      nBlockXSize, nBlockYSize, nBands, m_eDT, NULL);
    3144             :     poLogDS->RasterIO(GF_Write, 0, 0, nBlockXSize, nBlockYSize,
    3145             :                       pabyTemp + (nBand - 1) * nBandBlockSize, nBlockXSize,
    3146             :                       nBlockYSize, m_eDT, 1, NULL, 0, 0, 0, NULL);
    3147             :     GDALClose(poLogDS);
    3148             : #endif
    3149             : 
    3150        4556 :     if ((nOldFlags & l_nFlags) != 0)
    3151             :     {
    3152           0 :         CPLDebug("GPKG",
    3153             :                  "Rewriting quadrant %d of band %d of tile (row=%d,col=%d)",
    3154             :                  iQuadrantFlag, nBand, nRow, nCol);
    3155             :     }
    3156             : 
    3157        4556 :     l_nFlags |= nOldFlags;
    3158        4556 :     if (l_nFlags == nFullFlags)
    3159             :     {
    3160             : #ifdef DEBUG_VERBOSE
    3161             :         CPLDebug("GPKG", "Got all quadrants for that tile");
    3162             : #endif
    3163        1192 :         for (int iBand = 1; iBand <= nBands; iBand++)
    3164             :         {
    3165         945 :             if (iBand != nBand)
    3166             :             {
    3167         698 :                 pszSQL = CPLSPrintf(
    3168             :                     "SELECT tile_data_band_%d FROM partial_tiles WHERE "
    3169             :                     "id = %d",
    3170             :                     iBand, nExistingId);
    3171             : #ifdef DEBUG_VERBOSE
    3172             :                 CPLDebug("GPKG", "%s", pszSQL);
    3173             : #endif
    3174         698 :                 hStmt = nullptr;
    3175         698 :                 rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    3176         698 :                 if (rc != SQLITE_OK)
    3177             :                 {
    3178           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3179             :                              "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
    3180             :                              sqlite3_errmsg(m_hTempDB));
    3181           0 :                     return CE_Failure;
    3182             :                 }
    3183             : 
    3184         698 :                 rc = sqlite3_step(hStmt);
    3185         698 :                 if (rc == SQLITE_ROW)
    3186             :                 {
    3187         698 :                     CPLAssert(sqlite3_column_bytes(hStmt, 0) ==
    3188             :                               static_cast<int>(nBandBlockSize));
    3189         698 :                     memcpy(m_pabyCachedTiles + (iBand - 1) * nBandBlockSize,
    3190             :                            sqlite3_column_blob(hStmt, 0), nBandBlockSize);
    3191             :                 }
    3192         698 :                 sqlite3_finalize(hStmt);
    3193         698 :                 hStmt = nullptr;
    3194             :             }
    3195             :             else
    3196             :             {
    3197         247 :                 memcpy(m_pabyCachedTiles + (iBand - 1) * nBandBlockSize,
    3198         247 :                        pabyTemp + (iBand - 1) * nBandBlockSize, nBandBlockSize);
    3199             :             }
    3200             :         }
    3201             : 
    3202         247 :         m_asCachedTilesDesc[0].nRow = nRow;
    3203         247 :         m_asCachedTilesDesc[0].nCol = nCol;
    3204         247 :         m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    3205         247 :         m_asCachedTilesDesc[0].abBandDirty[0] = true;
    3206         247 :         m_asCachedTilesDesc[0].abBandDirty[1] = true;
    3207         247 :         m_asCachedTilesDesc[0].abBandDirty[2] = true;
    3208         247 :         m_asCachedTilesDesc[0].abBandDirty[3] = true;
    3209             : 
    3210         494 :         pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, "
    3211             :                             "partial_flag = 0, age = -1 WHERE id = %d",
    3212         247 :                             -1 - m_nZoomLevel, nExistingId);
    3213         247 :         SQLCommand(m_hTempDB, pszSQL);
    3214             : #ifdef DEBUG_VERBOSE
    3215             :         CPLDebug("GPKG", "%s", pszSQL);
    3216             : #endif
    3217         247 :         CPLErr eErr = WriteTile();
    3218             : 
    3219             :         // Call DoPartialFlushOfPartialTilesIfNecessary() after using
    3220             :         // m_pabyCachedTiles as it is going to mess with it.
    3221         247 :         if (DoPartialFlushOfPartialTilesIfNecessary() != CE_None)
    3222           0 :             eErr = CE_None;
    3223             : 
    3224         247 :         return eErr;
    3225             :     }
    3226             : 
    3227        4309 :     if (nExistingId == 0)
    3228             :     {
    3229             :         OGRErr err;
    3230         844 :         pszSQL = CPLSPrintf("SELECT id FROM partial_tiles WHERE "
    3231             :                             "partial_flag = 0 AND zoom_level = %d "
    3232             :                             "AND tile_row = %d AND tile_column = %d",
    3233         422 :                             -1 - m_nZoomLevel, nRow, nCol);
    3234             : #ifdef DEBUG_VERBOSE
    3235             :         CPLDebug("GPKG", "%s", pszSQL);
    3236             : #endif
    3237         422 :         nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err);
    3238         422 :         if (nExistingId == 0)
    3239             :         {
    3240         418 :             pszSQL =
    3241             :                 "SELECT id FROM partial_tiles WHERE partial_flag = 0 LIMIT 1";
    3242             : #ifdef DEBUG_VERBOSE
    3243             :             CPLDebug("GPKG", "%s", pszSQL);
    3244             : #endif
    3245         418 :             nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err);
    3246             :         }
    3247             :     }
    3248             : 
    3249        4309 :     const GIntBig nAge = (m_poParentDS) ? m_poParentDS->m_nAge : m_nAge;
    3250        4309 :     if (nExistingId == 0)
    3251             :     {
    3252         338 :         pszSQL = CPLSPrintf(
    3253             :             "INSERT INTO partial_tiles "
    3254             :             "(zoom_level, tile_row, tile_column, tile_data_band_%d, "
    3255             :             "partial_flag, age) VALUES (%d, %d, %d, ?, %d, " CPL_FRMT_GIB ")",
    3256             :             nBand, m_nZoomLevel, nRow, nCol, l_nFlags, nAge);
    3257             :     }
    3258             :     else
    3259             :     {
    3260        3971 :         pszSQL = CPLSPrintf(
    3261             :             "UPDATE partial_tiles SET zoom_level = %d, "
    3262             :             "tile_row = %d, tile_column = %d, "
    3263             :             "tile_data_band_%d = ?, partial_flag = %d, age = " CPL_FRMT_GIB
    3264             :             " WHERE id = %d",
    3265             :             m_nZoomLevel, nRow, nCol, nBand, l_nFlags, nAge, nExistingId);
    3266             :     }
    3267        4309 :     if (m_poParentDS)
    3268        3801 :         m_poParentDS->m_nAge++;
    3269             :     else
    3270         508 :         m_nAge++;
    3271             : 
    3272             : #ifdef DEBUG_VERBOSE
    3273             :     CPLDebug("GPKG", "%s", pszSQL);
    3274             : #endif
    3275             : 
    3276        4309 :     hStmt = nullptr;
    3277        4309 :     rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, nullptr);
    3278        4309 :     if (rc != SQLITE_OK)
    3279             :     {
    3280           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s",
    3281             :                  pszSQL, sqlite3_errmsg(m_hTempDB));
    3282           0 :         return CE_Failure;
    3283             :     }
    3284             : 
    3285        4309 :     sqlite3_bind_blob(hStmt, 1, pabyTemp + (nBand - 1) * nBandBlockSize,
    3286             :                       static_cast<int>(nBandBlockSize), SQLITE_TRANSIENT);
    3287        4309 :     rc = sqlite3_step(hStmt);
    3288        4309 :     CPLErr eErr = CE_Failure;
    3289        4309 :     if (rc == SQLITE_DONE)
    3290        4309 :         eErr = CE_None;
    3291             :     else
    3292             :     {
    3293           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3294             :                  "Failure when inserting partial tile (row=%d,col=%d) at "
    3295             :                  "zoom_level=%d : %s",
    3296             :                  nRow, nCol, m_nZoomLevel, sqlite3_errmsg(m_hTempDB));
    3297             :     }
    3298             : 
    3299        4309 :     sqlite3_finalize(hStmt);
    3300             : 
    3301             :     // Call DoPartialFlushOfPartialTilesIfNecessary() after using
    3302             :     // m_pabyCachedTiles as it is going to mess with it.
    3303        4309 :     if (DoPartialFlushOfPartialTilesIfNecessary() != CE_None)
    3304           0 :         eErr = CE_None;
    3305             : 
    3306        4309 :     return eErr;
    3307             : }
    3308             : 
    3309             : /************************************************************************/
    3310             : /*                         IWriteBlock()                                */
    3311             : /************************************************************************/
    3312             : 
    3313        5839 : CPLErr GDALGPKGMBTilesLikeRasterBand::IWriteBlock(int nBlockXOff,
    3314             :                                                   int nBlockYOff, void *pData)
    3315             : {
    3316             : #ifdef DEBUG_VERBOSE
    3317             :     CPLDebug(
    3318             :         "GPKG",
    3319             :         "IWriteBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d,m_nZoomLevel=%d)",
    3320             :         nBand, nBlockXOff, nBlockYOff, m_poTPD->m_nZoomLevel);
    3321             : #endif
    3322        5839 :     if (!m_poTPD->ICanIWriteBlock())
    3323             :     {
    3324           0 :         return CE_Failure;
    3325             :     }
    3326        5839 :     if (m_poTPD->m_poParentDS)
    3327        1139 :         m_poTPD->m_poParentDS->m_bHasModifiedTiles = true;
    3328             :     else
    3329        4700 :         m_poTPD->m_bHasModifiedTiles = true;
    3330             : 
    3331        5839 :     int nRow = nBlockYOff + m_poTPD->m_nShiftYTiles;
    3332        5839 :     int nCol = nBlockXOff + m_poTPD->m_nShiftXTiles;
    3333             : 
    3334        5839 :     int nRowMin = nRow;
    3335        5839 :     int nRowMax = nRowMin;
    3336        5839 :     if (m_poTPD->m_nShiftYPixelsMod)
    3337        1360 :         nRowMax++;
    3338             : 
    3339        5839 :     int nColMin = nCol;
    3340        5839 :     int nColMax = nColMin;
    3341        5839 :     if (m_poTPD->m_nShiftXPixelsMod)
    3342        1322 :         nColMax++;
    3343             : 
    3344        5839 :     CPLErr eErr = CE_None;
    3345             : 
    3346       13038 :     for (nRow = nRowMin; eErr == CE_None && nRow <= nRowMax; nRow++)
    3347             :     {
    3348       17042 :         for (nCol = nColMin; eErr == CE_None && nCol <= nColMax; nCol++)
    3349             :         {
    3350        9843 :             if (nRow < 0 || nCol < 0 || nRow >= m_poTPD->m_nTileMatrixHeight ||
    3351        9703 :                 nCol >= m_poTPD->m_nTileMatrixWidth)
    3352             :             {
    3353         167 :                 continue;
    3354             :             }
    3355             : 
    3356        9676 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3357        4505 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    3358             :             {
    3359        4447 :                 if (!(nRow == m_poTPD->m_asCachedTilesDesc[0].nRow &&
    3360         250 :                       nCol == m_poTPD->m_asCachedTilesDesc[0].nCol &&
    3361           5 :                       m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData == 0))
    3362             :                 {
    3363        4442 :                     eErr = m_poTPD->WriteTile();
    3364             : 
    3365        4442 :                     m_poTPD->m_asCachedTilesDesc[0].nRow = nRow;
    3366        4442 :                     m_poTPD->m_asCachedTilesDesc[0].nCol = nCol;
    3367        4442 :                     m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = 0;
    3368             :                 }
    3369             :             }
    3370             : 
    3371             :             // Composite block data into tile, and check if all bands for this
    3372             :             // block are dirty, and if so write the tile
    3373        9676 :             bool bAllDirty = true;
    3374       42207 :             for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
    3375             :             {
    3376       32531 :                 GDALRasterBlock *poBlock = nullptr;
    3377       32531 :                 GByte *pabySrc = nullptr;
    3378       32531 :                 if (iBand == nBand)
    3379             :                 {
    3380        9676 :                     pabySrc = static_cast<GByte *>(pData);
    3381             :                 }
    3382             :                 else
    3383             :                 {
    3384       22855 :                     if (!(m_poTPD->m_nShiftXPixelsMod == 0 &&
    3385        8353 :                           m_poTPD->m_nShiftYPixelsMod == 0))
    3386       14646 :                         continue;
    3387             : 
    3388             :                     // If the block for this band is not dirty, it might be
    3389             :                     // dirty in cache
    3390        8209 :                     if (m_poTPD->m_asCachedTilesDesc[0].abBandDirty[iBand - 1])
    3391         505 :                         continue;
    3392             :                     else
    3393             :                     {
    3394             :                         poBlock =
    3395        7704 :                             cpl::down_cast<GDALGPKGMBTilesLikeRasterBand *>(
    3396        7704 :                                 poDS->GetRasterBand(iBand))
    3397        7704 :                                 ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    3398        7704 :                         if (poBlock && poBlock->GetDirty())
    3399             :                         {
    3400             :                             pabySrc =
    3401        7664 :                                 static_cast<GByte *>(poBlock->GetDataRef());
    3402        7664 :                             poBlock->MarkClean();
    3403             :                         }
    3404             :                         else
    3405             :                         {
    3406          40 :                             if (poBlock)
    3407           2 :                                 poBlock->DropLock();
    3408          40 :                             bAllDirty = false;
    3409          40 :                             continue;
    3410             :                         }
    3411             :                     }
    3412             :                 }
    3413             : 
    3414       17340 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3415       12169 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    3416       12111 :                     m_poTPD->m_asCachedTilesDesc[0].abBandDirty[iBand - 1] =
    3417             :                         true;
    3418             : 
    3419       17340 :                 int nDstXOffset = 0;
    3420       17340 :                 int nDstXSize = nBlockXSize;
    3421       17340 :                 int nDstYOffset = 0;
    3422       17340 :                 int nDstYSize = nBlockYSize;
    3423             :                 // Composite block data into tile data
    3424       17340 :                 if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3425       12169 :                     m_poTPD->m_nShiftYPixelsMod == 0)
    3426             :                 {
    3427             : 
    3428             : #ifdef DEBUG_VERBOSE
    3429             :                     if (eDataType == GDT_Byte &&
    3430             :                         nBlockXOff * nBlockXSize <=
    3431             :                             nRasterXSize - nBlockXSize &&
    3432             :                         nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    3433             :                     {
    3434             :                         bool bFoundNonZero = false;
    3435             :                         for (int y = nRasterYSize - nBlockYOff * nBlockYSize;
    3436             :                              y < nBlockYSize; y++)
    3437             :                         {
    3438             :                             for (int x = 0; x < nBlockXSize; x++)
    3439             :                             {
    3440             :                                 if (pabySrc[static_cast<GPtrDiff_t>(y) *
    3441             :                                                 nBlockXSize +
    3442             :                                             x] != 0 &&
    3443             :                                     !bFoundNonZero)
    3444             :                                 {
    3445             :                                     CPLDebug("GPKG",
    3446             :                                              "IWriteBlock(): Found non-zero "
    3447             :                                              "content in ghost part of "
    3448             :                                              "tile(nBand=%d,nBlockXOff=%d,"
    3449             :                                              "nBlockYOff=%d,m_nZoomLevel=%d)\n",
    3450             :                                              iBand, nBlockXOff, nBlockYOff,
    3451             :                                              m_poTPD->m_nZoomLevel);
    3452             :                                     bFoundNonZero = true;
    3453             :                                 }
    3454             :                             }
    3455             :                         }
    3456             :                     }
    3457             : #endif
    3458             : 
    3459       12111 :                     const size_t nBandBlockSize =
    3460       12111 :                         static_cast<size_t>(nBlockXSize) * nBlockYSize *
    3461       12111 :                         m_nDTSize;
    3462       12111 :                     memcpy(m_poTPD->m_pabyCachedTiles +
    3463       12111 :                                (iBand - 1) * nBandBlockSize,
    3464             :                            pabySrc, nBandBlockSize);
    3465             : 
    3466             :                     // Make sure partial blocks are zero'ed outside of the
    3467             :                     // validity area but do that only when know that JPEG will
    3468             :                     // not be used so as to avoid edge effects (although we
    3469             :                     // should probably repeat last pixels if we really want to
    3470             :                     // do that, but that only makes sense if readers only clip
    3471             :                     // to the gpkg_contents extent). Well, ere on the safe side
    3472             :                     // for now
    3473       12111 :                     if (m_poTPD->m_eTF != GPKG_TF_JPEG &&
    3474       11863 :                         (nBlockXOff * nBlockXSize >=
    3475       11863 :                              nRasterXSize - nBlockXSize ||
    3476       11338 :                          nBlockYOff * nBlockYSize >=
    3477       11338 :                              nRasterYSize - nBlockYSize))
    3478             :                     {
    3479        1021 :                         int nXEndValidity =
    3480        1021 :                             nRasterXSize - nBlockXOff * nBlockXSize;
    3481        1021 :                         if (nXEndValidity > nBlockXSize)
    3482         496 :                             nXEndValidity = nBlockXSize;
    3483        1021 :                         int nYEndValidity =
    3484        1021 :                             nRasterYSize - nBlockYOff * nBlockYSize;
    3485        1021 :                         if (nYEndValidity > nBlockYSize)
    3486         294 :                             nYEndValidity = nBlockYSize;
    3487        1021 :                         if (nXEndValidity < nBlockXSize)
    3488             :                         {
    3489        9366 :                             for (int iY = 0; iY < nYEndValidity; iY++)
    3490             :                             {
    3491        9187 :                                 m_poTPD->FillBuffer(
    3492        9187 :                                     m_poTPD->m_pabyCachedTiles +
    3493        9187 :                                         ((static_cast<size_t>(iBand - 1) *
    3494        9187 :                                               nBlockYSize +
    3495        9187 :                                           iY) *
    3496        9187 :                                              nBlockXSize +
    3497        9187 :                                          nXEndValidity) *
    3498        9187 :                                             m_nDTSize,
    3499        9187 :                                     nBlockXSize - nXEndValidity);
    3500             :                             }
    3501             :                         }
    3502        1021 :                         if (nYEndValidity < nBlockYSize)
    3503             :                         {
    3504         219 :                             m_poTPD->FillBuffer(
    3505         219 :                                 m_poTPD->m_pabyCachedTiles +
    3506         219 :                                     (static_cast<size_t>(iBand - 1) *
    3507         219 :                                          nBlockYSize +
    3508         219 :                                      nYEndValidity) *
    3509         219 :                                         nBlockXSize * m_nDTSize,
    3510         219 :                                 static_cast<size_t>(nBlockYSize -
    3511             :                                                     nYEndValidity) *
    3512         219 :                                     nBlockXSize);
    3513             :                         }
    3514       12111 :                     }
    3515             :                 }
    3516             :                 else
    3517             :                 {
    3518        5229 :                     const int nXValid =
    3519        5229 :                         (nBlockXOff * nBlockXSize > nRasterXSize - nBlockXSize)
    3520        5229 :                             ? (nRasterXSize - nBlockXOff * nBlockXSize)
    3521             :                             : nBlockXSize;
    3522        5229 :                     const int nYValid =
    3523        5229 :                         (nBlockYOff * nBlockYSize > nRasterYSize - nBlockYSize)
    3524        5229 :                             ? (nRasterYSize - nBlockYOff * nBlockYSize)
    3525             :                             : nBlockYSize;
    3526             : 
    3527        5229 :                     int nSrcXOffset = 0;
    3528        5229 :                     if (nCol == nColMin)
    3529             :                     {
    3530        2643 :                         nDstXOffset = m_poTPD->m_nShiftXPixelsMod;
    3531        2643 :                         nDstXSize = std::min(
    3532        2643 :                             nXValid, nBlockXSize - m_poTPD->m_nShiftXPixelsMod);
    3533             :                     }
    3534             :                     else
    3535             :                     {
    3536        2586 :                         nDstXOffset = 0;
    3537        2586 :                         if (nXValid > nBlockXSize - m_poTPD->m_nShiftXPixelsMod)
    3538             :                         {
    3539        2212 :                             nDstXSize = nXValid - (nBlockXSize -
    3540        2212 :                                                    m_poTPD->m_nShiftXPixelsMod);
    3541             :                         }
    3542             :                         else
    3543         374 :                             nDstXSize = 0;
    3544        2586 :                         nSrcXOffset = nBlockXSize - m_poTPD->m_nShiftXPixelsMod;
    3545             :                     }
    3546             : 
    3547        5229 :                     int nSrcYOffset = 0;
    3548        5229 :                     if (nRow == nRowMin)
    3549             :                     {
    3550        2607 :                         nDstYOffset = m_poTPD->m_nShiftYPixelsMod;
    3551        2607 :                         nDstYSize = std::min(
    3552        2607 :                             nYValid, nBlockYSize - m_poTPD->m_nShiftYPixelsMod);
    3553             :                     }
    3554             :                     else
    3555             :                     {
    3556        2622 :                         nDstYOffset = 0;
    3557        2622 :                         if (nYValid > nBlockYSize - m_poTPD->m_nShiftYPixelsMod)
    3558             :                         {
    3559        2232 :                             nDstYSize = nYValid - (nBlockYSize -
    3560        2232 :                                                    m_poTPD->m_nShiftYPixelsMod);
    3561             :                         }
    3562             :                         else
    3563         390 :                             nDstYSize = 0;
    3564        2622 :                         nSrcYOffset = nBlockYSize - m_poTPD->m_nShiftYPixelsMod;
    3565             :                     }
    3566             : 
    3567             : #ifdef DEBUG_VERBOSE
    3568             :                     CPLDebug("GPKG",
    3569             :                              "Copy source tile x=%d,w=%d,y=%d,h=%d into "
    3570             :                              "buffer at x=%d,y=%d",
    3571             :                              nDstXOffset, nDstXSize, nDstYOffset, nDstYSize,
    3572             :                              nSrcXOffset, nSrcYOffset);
    3573             : #endif
    3574             : 
    3575        5229 :                     if (nDstXSize > 0 && nDstYSize > 0)
    3576             :                     {
    3577      556108 :                         for (GPtrDiff_t y = 0; y < nDstYSize; y++)
    3578             :                         {
    3579      551552 :                             GByte *pDst = m_poTPD->m_pabyCachedTiles +
    3580      551552 :                                           (static_cast<size_t>(iBand - 1) *
    3581      551552 :                                                nBlockXSize * nBlockYSize +
    3582      551552 :                                            (y + nDstYOffset) * nBlockXSize +
    3583      551552 :                                            nDstXOffset) *
    3584      551552 :                                               m_nDTSize;
    3585      551552 :                             GByte *pSrc =
    3586      551552 :                                 pabySrc + ((y + nSrcYOffset) * nBlockXSize +
    3587      551552 :                                            nSrcXOffset) *
    3588      551552 :                                               m_nDTSize;
    3589      551552 :                             GDALCopyWords(pSrc, eDataType, m_nDTSize, pDst,
    3590             :                                           eDataType, m_nDTSize, nDstXSize);
    3591             :                         }
    3592             :                     }
    3593             :                 }
    3594             : 
    3595       17340 :                 if (poBlock)
    3596        7664 :                     poBlock->DropLock();
    3597             : 
    3598       17340 :                 if (!(m_poTPD->m_nShiftXPixelsMod == 0 &&
    3599       12169 :                       m_poTPD->m_nShiftYPixelsMod == 0))
    3600             :                 {
    3601        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nRow = -1;
    3602        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nCol = -1;
    3603        5229 :                     m_poTPD->m_asCachedTilesDesc[0].nIdxWithinTileData = -1;
    3604        5229 :                     if (nDstXSize > 0 && nDstYSize > 0)
    3605             :                     {
    3606        4556 :                         eErr = m_poTPD->WriteShiftedTile(
    3607             :                             nRow, nCol, iBand, nDstXOffset, nDstYOffset,
    3608             :                             nDstXSize, nDstYSize);
    3609             :                     }
    3610             :                 }
    3611             :             }
    3612             : 
    3613        9676 :             if (m_poTPD->m_nShiftXPixelsMod == 0 &&
    3614        4505 :                 m_poTPD->m_nShiftYPixelsMod == 0)
    3615             :             {
    3616        4447 :                 if (bAllDirty)
    3617             :                 {
    3618        4425 :                     eErr = m_poTPD->WriteTile();
    3619             :                 }
    3620             :             }
    3621             :         }
    3622             :     }
    3623             : 
    3624        5839 :     return eErr;
    3625             : }
    3626             : 
    3627             : /************************************************************************/
    3628             : /*                           GetNoDataValue()                           */
    3629             : /************************************************************************/
    3630             : 
    3631       19474 : double GDALGPKGMBTilesLikeRasterBand::GetNoDataValue(int *pbSuccess)
    3632             : {
    3633       19474 :     if (m_bHasNoData)
    3634             :     {
    3635         401 :         if (pbSuccess)
    3636         400 :             *pbSuccess = TRUE;
    3637         401 :         return m_dfNoDataValue;
    3638             :     }
    3639       19073 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
    3640             : }
    3641             : 
    3642             : /************************************************************************/
    3643             : /*                        SetNoDataValueInternal()                      */
    3644             : /************************************************************************/
    3645             : 
    3646          75 : void GDALGPKGMBTilesLikeRasterBand::SetNoDataValueInternal(double dfNoDataValue)
    3647             : {
    3648          75 :     m_bHasNoData = true;
    3649          75 :     m_dfNoDataValue = dfNoDataValue;
    3650          75 : }
    3651             : 
    3652             : /************************************************************************/
    3653             : /*                      GDALGeoPackageRasterBand()                      */
    3654             : /************************************************************************/
    3655             : 
    3656        1792 : GDALGeoPackageRasterBand::GDALGeoPackageRasterBand(
    3657        1792 :     GDALGeoPackageDataset *poDSIn, int nTileWidth, int nTileHeight)
    3658        1792 :     : GDALGPKGMBTilesLikeRasterBand(poDSIn, nTileWidth, nTileHeight)
    3659             : {
    3660        1792 :     poDS = poDSIn;
    3661        1792 : }
    3662             : 
    3663             : /************************************************************************/
    3664             : /*                         GetOverviewCount()                           */
    3665             : /************************************************************************/
    3666             : 
    3667           8 : int GDALGeoPackageRasterBand::GetOverviewCount()
    3668             : {
    3669             :     GDALGeoPackageDataset *poGDS =
    3670           8 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3671           8 :     return poGDS->m_nOverviewCount;
    3672             : }
    3673             : 
    3674             : /************************************************************************/
    3675             : /*                         GetOverviewCount()                           */
    3676             : /************************************************************************/
    3677             : 
    3678          41 : GDALRasterBand *GDALGeoPackageRasterBand::GetOverview(int nIdx)
    3679             : {
    3680          41 :     GDALGeoPackageDataset *poGDS =
    3681             :         reinterpret_cast<GDALGeoPackageDataset *>(poDS);
    3682          41 :     if (nIdx < 0 || nIdx >= poGDS->m_nOverviewCount)
    3683           1 :         return nullptr;
    3684          40 :     return poGDS->m_papoOverviewDS[nIdx]->GetRasterBand(nBand);
    3685             : }
    3686             : 
    3687             : /************************************************************************/
    3688             : /*                           SetNoDataValue()                           */
    3689             : /************************************************************************/
    3690             : 
    3691          23 : CPLErr GDALGeoPackageRasterBand::SetNoDataValue(double dfNoDataValue)
    3692             : {
    3693             :     GDALGeoPackageDataset *poGDS =
    3694          23 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3695             : 
    3696          23 :     if (eDataType == GDT_Byte)
    3697             :     {
    3698           6 :         if (!(dfNoDataValue >= 0 && dfNoDataValue <= 255 &&
    3699           4 :               static_cast<int>(dfNoDataValue) == dfNoDataValue))
    3700             :         {
    3701           2 :             CPLError(CE_Failure, CPLE_NotSupported,
    3702             :                      "Invalid nodata value for a Byte band: %.17g",
    3703             :                      dfNoDataValue);
    3704           2 :             return CE_Failure;
    3705             :         }
    3706             : 
    3707           8 :         for (int i = 1; i <= poGDS->nBands; ++i)
    3708             :         {
    3709           5 :             if (i != nBand)
    3710             :             {
    3711           2 :                 int bHasNoData = FALSE;
    3712             :                 double dfOtherNoDataValue =
    3713           2 :                     poGDS->GetRasterBand(i)->GetNoDataValue(&bHasNoData);
    3714           2 :                 if (bHasNoData && dfOtherNoDataValue != dfNoDataValue)
    3715             :                 {
    3716           1 :                     CPLError(
    3717             :                         CE_Failure, CPLE_NotSupported,
    3718             :                         "Only the same nodata value can be set on all bands");
    3719           1 :                     return CE_Failure;
    3720             :                 }
    3721             :             }
    3722             :         }
    3723             : 
    3724           3 :         SetNoDataValueInternal(dfNoDataValue);
    3725           3 :         poGDS->m_bMetadataDirty = true;
    3726           3 :         return CE_None;
    3727             :     }
    3728             : 
    3729          17 :     if (std::isnan(dfNoDataValue))
    3730             :     {
    3731           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    3732             :                  "A NaN nodata value cannot be recorded in "
    3733             :                  "gpkg_2d_gridded_coverage_ancillary table");
    3734             :     }
    3735             : 
    3736          17 :     SetNoDataValueInternal(dfNoDataValue);
    3737             : 
    3738          17 :     char *pszSQL = sqlite3_mprintf(
    3739             :         "UPDATE gpkg_2d_gridded_coverage_ancillary SET data_null = ? "
    3740             :         "WHERE tile_matrix_set_name = '%q'",
    3741             :         poGDS->m_osRasterTable.c_str());
    3742          17 :     sqlite3_stmt *hStmt = nullptr;
    3743          17 :     int rc = sqlite3_prepare_v2(poGDS->IGetDB(), pszSQL, -1, &hStmt, nullptr);
    3744          17 :     if (rc == SQLITE_OK)
    3745             :     {
    3746          17 :         if (poGDS->m_eTF == GPKG_TF_PNG_16BIT)
    3747             :         {
    3748          15 :             if (eDataType == GDT_UInt16 && poGDS->m_dfOffset == 0.0 &&
    3749           6 :                 poGDS->m_dfScale == 1.0 && dfNoDataValue >= 0 &&
    3750           5 :                 dfNoDataValue <= 65535 &&
    3751           5 :                 static_cast<GUInt16>(dfNoDataValue) == dfNoDataValue)
    3752             :             {
    3753           5 :                 poGDS->m_usGPKGNull = static_cast<GUInt16>(dfNoDataValue);
    3754             :             }
    3755             :             else
    3756             :             {
    3757          10 :                 poGDS->m_usGPKGNull = 65535;
    3758             :             }
    3759          15 :             sqlite3_bind_double(hStmt, 1, poGDS->m_usGPKGNull);
    3760             :         }
    3761             :         else
    3762             :         {
    3763           2 :             sqlite3_bind_double(hStmt, 1, static_cast<float>(dfNoDataValue));
    3764             :         }
    3765          17 :         rc = sqlite3_step(hStmt);
    3766          17 :         sqlite3_finalize(hStmt);
    3767             :     }
    3768          17 :     sqlite3_free(pszSQL);
    3769             : 
    3770          17 :     return (rc == SQLITE_OK) ? CE_None : CE_Failure;
    3771             : }
    3772             : 
    3773             : /************************************************************************/
    3774             : /*                         LoadBandMetadata()                           */
    3775             : /************************************************************************/
    3776             : 
    3777        1206 : void GDALGeoPackageRasterBand::LoadBandMetadata()
    3778             : {
    3779             :     GDALGeoPackageDataset *poGDS =
    3780        1206 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    3781             : 
    3782        1206 :     if (m_bHasReadMetadataFromStorage)
    3783        1105 :         return;
    3784             : 
    3785         464 :     m_bHasReadMetadataFromStorage = true;
    3786             : 
    3787         464 :     poGDS->TryLoadXML();
    3788             : 
    3789         464 :     if (!poGDS->HasMetadataTables())
    3790         363 :         return;
    3791             : 
    3792         101 :     char *pszSQL = sqlite3_mprintf(
    3793             :         "SELECT md.metadata, md.md_standard_uri, md.mime_type "
    3794             :         "FROM gpkg_metadata md "
    3795             :         "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
    3796             :         "WHERE "
    3797             :         "mdr.reference_scope = 'table' AND lower(mdr.table_name) = "
    3798             :         "lower('%q') ORDER BY md.id "
    3799             :         "LIMIT 1000",  // to avoid denial of service
    3800             :         poGDS->m_osRasterTable.c_str());
    3801             : 
    3802         101 :     auto oResult = SQLQuery(poGDS->hDB, pszSQL);
    3803         101 :     sqlite3_free(pszSQL);
    3804         101 :     if (!oResult)
    3805             :     {
    3806           0 :         return;
    3807             :     }
    3808             : 
    3809             :     /* GDAL metadata */
    3810         198 :     for (int i = 0; i < oResult->RowCount(); i++)
    3811             :     {
    3812          97 :         const char *pszMetadata = oResult->GetValue(0, i);
    3813          97 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    3814          97 :         const char *pszMimeType = oResult->GetValue(2, i);
    3815          97 :         if (pszMetadata && pszMDStandardURI && pszMimeType &&
    3816          97 :             EQUAL(pszMDStandardURI, "http://gdal.org") &&
    3817          73 :             EQUAL(pszMimeType, "text/xml"))
    3818             :         {
    3819          73 :             CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
    3820          73 :             if (psXMLNode)
    3821             :             {
    3822         146 :                 GDALMultiDomainMetadata oLocalMDMD;
    3823          73 :                 oLocalMDMD.XMLInit(psXMLNode, FALSE);
    3824             : 
    3825          73 :                 CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
    3826         206 :                 for (CSLConstList papszIter = papszDomainList;
    3827         206 :                      papszIter && *papszIter; ++papszIter)
    3828             :                 {
    3829         133 :                     if (STARTS_WITH(*papszIter, "BAND_"))
    3830             :                     {
    3831           5 :                         int l_nBand = atoi(*papszIter + strlen("BAND_"));
    3832           5 :                         if (l_nBand >= 1 && l_nBand <= poGDS->GetRasterCount())
    3833             :                         {
    3834             :                             auto l_poBand =
    3835           5 :                                 cpl::down_cast<GDALGeoPackageRasterBand *>(
    3836             :                                     poGDS->GetRasterBand(l_nBand));
    3837           5 :                             l_poBand->m_bHasReadMetadataFromStorage = true;
    3838             : 
    3839          10 :                             char **papszMD = CSLDuplicate(
    3840           5 :                                 oLocalMDMD.GetMetadata(*papszIter));
    3841          10 :                             papszMD = CSLMerge(
    3842             :                                 papszMD,
    3843           5 :                                 GDALGPKGMBTilesLikeRasterBand::GetMetadata(""));
    3844           5 :                             l_poBand->GDALPamRasterBand::SetMetadata(papszMD);
    3845           5 :                             CSLDestroy(papszMD);
    3846             :                         }
    3847             :                     }
    3848             :                 }
    3849             : 
    3850          73 :                 CPLDestroyXMLNode(psXMLNode);
    3851             :             }
    3852             :         }
    3853             :     }
    3854             : }
    3855             : 
    3856             : /************************************************************************/
    3857             : /*                            GetMetadata()                             */
    3858             : /************************************************************************/
    3859             : 
    3860         921 : char **GDALGeoPackageRasterBand::GetMetadata(const char *pszDomain)
    3861             : {
    3862         921 :     GDALGeoPackageDataset *poGDS =
    3863             :         reinterpret_cast<GDALGeoPackageDataset *>(poDS);
    3864         921 :     LoadBandMetadata(); /* force loading from storage if needed */
    3865             : 
    3866          15 :     if (poGDS->eAccess == GA_ReadOnly && eDataType != GDT_Byte &&
    3867          11 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3868          11 :         !m_bMinMaxComputedFromTileAncillary &&
    3869         944 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MINIMUM") &&
    3870           8 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MAXIMUM"))
    3871             :     {
    3872           8 :         m_bMinMaxComputedFromTileAncillary = true;
    3873             : 
    3874           8 :         const int nColMin = poGDS->m_nShiftXTiles;
    3875           8 :         const int nColMax =
    3876           8 :             (nRasterXSize - 1 + poGDS->m_nShiftXPixelsMod) / nBlockXSize +
    3877           8 :             poGDS->m_nShiftXTiles;
    3878           8 :         const int nRowMin = poGDS->m_nShiftYTiles;
    3879           8 :         const int nRowMax =
    3880           8 :             (nRasterYSize - 1 + poGDS->m_nShiftYPixelsMod) / nBlockYSize +
    3881           8 :             poGDS->m_nShiftYTiles;
    3882             : 
    3883           8 :         bool bOK = false;
    3884           8 :         if (poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 &&
    3885           8 :             (nRasterXSize % nBlockXSize) == 0 &&
    3886           2 :             (nRasterYSize % nBlockYSize) == 0)
    3887             :         {
    3888             :             // If the area of interest matches entire tiles, then we can
    3889             :             // use tile statistics
    3890           2 :             bOK = true;
    3891             :         }
    3892           6 :         else if (m_bHasNoData)
    3893             :         {
    3894             :             // Otherwise, in the case where we have nodata, we assume that
    3895             :             // if the area of interest is at least larger than the existing
    3896             :             // tiles, the tile statistics will be reliable.
    3897           2 :             char *pszSQL = sqlite3_mprintf(
    3898             :                 "SELECT MIN(tile_column), MAX(tile_column), "
    3899             :                 "MIN(tile_row), MAX(tile_row) FROM \"%w\" "
    3900             :                 "WHERE zoom_level = %d",
    3901             :                 poGDS->m_osRasterTable.c_str(), poGDS->m_nZoomLevel);
    3902           4 :             auto sResult = SQLQuery(poGDS->IGetDB(), pszSQL);
    3903           2 :             if (sResult && sResult->RowCount() == 1)
    3904             :             {
    3905           2 :                 const char *pszMinX = sResult->GetValue(0, 0);
    3906           2 :                 const char *pszMaxX = sResult->GetValue(1, 0);
    3907           2 :                 const char *pszMinY = sResult->GetValue(2, 0);
    3908           2 :                 const char *pszMaxY = sResult->GetValue(3, 0);
    3909           2 :                 if (pszMinX && pszMaxX && pszMinY && pszMaxY)
    3910             :                 {
    3911           2 :                     bOK = atoi(pszMinX) >= nColMin &&
    3912           2 :                           atoi(pszMaxX) <= nColMax &&
    3913           4 :                           atoi(pszMinY) >= nRowMin && atoi(pszMaxY) <= nRowMax;
    3914             :                 }
    3915             :             }
    3916           2 :             sqlite3_free(pszSQL);
    3917             :         }
    3918             : 
    3919           8 :         if (bOK)
    3920             :         {
    3921           4 :             char *pszSQL = sqlite3_mprintf(
    3922             :                 "SELECT MIN(min), MAX(max) FROM "
    3923             :                 "gpkg_2d_gridded_tile_ancillary WHERE tpudt_id "
    3924             :                 "IN (SELECT id FROM \"%w\" WHERE "
    3925             :                 "zoom_level = %d AND "
    3926             :                 "tile_column >= %d AND tile_column <= %d AND "
    3927             :                 "tile_row >= %d AND tile_row <= %d)",
    3928             :                 poGDS->m_osRasterTable.c_str(), poGDS->m_nZoomLevel, nColMin,
    3929             :                 nColMax, nRowMin, nRowMax);
    3930           8 :             auto sResult = SQLQuery(poGDS->IGetDB(), pszSQL);
    3931           4 :             CPLDebug("GPKG", "%s", pszSQL);
    3932           4 :             if (sResult && sResult->RowCount() == 1)
    3933             :             {
    3934           4 :                 const char *pszMin = sResult->GetValue(0, 0);
    3935           4 :                 const char *pszMax = sResult->GetValue(1, 0);
    3936           4 :                 if (pszMin)
    3937             :                 {
    3938           4 :                     m_dfStatsMinFromTileAncillary = CPLAtof(pszMin);
    3939             :                 }
    3940           4 :                 if (pszMax)
    3941             :                 {
    3942           4 :                     m_dfStatsMaxFromTileAncillary = CPLAtof(pszMax);
    3943             :                 }
    3944             :             }
    3945           4 :             sqlite3_free(pszSQL);
    3946             :         }
    3947             :     }
    3948             : 
    3949         685 :     if (m_bAddImplicitStatistics && m_bMinMaxComputedFromTileAncillary &&
    3950           8 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3951        1614 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MINIMUM") &&
    3952           8 :         !GDALGPKGMBTilesLikeRasterBand::GetMetadataItem("STATISTICS_MAXIMUM"))
    3953             :     {
    3954             :         m_aosMD.Assign(CSLDuplicate(
    3955           8 :             GDALGPKGMBTilesLikeRasterBand::GetMetadata(pszDomain)));
    3956           8 :         if (!std::isnan(m_dfStatsMinFromTileAncillary))
    3957             :         {
    3958             :             m_aosMD.SetNameValue(
    3959             :                 "STATISTICS_MINIMUM",
    3960           4 :                 CPLSPrintf("%.14g", m_dfStatsMinFromTileAncillary));
    3961             :         }
    3962           8 :         if (!std::isnan(m_dfStatsMaxFromTileAncillary))
    3963             :         {
    3964             :             m_aosMD.SetNameValue(
    3965             :                 "STATISTICS_MAXIMUM",
    3966           4 :                 CPLSPrintf("%.14g", m_dfStatsMaxFromTileAncillary));
    3967             :         }
    3968           8 :         return m_aosMD.List();
    3969             :     }
    3970             : 
    3971         913 :     return GDALGPKGMBTilesLikeRasterBand::GetMetadata(pszDomain);
    3972             : }
    3973             : 
    3974             : /************************************************************************/
    3975             : /*                          GetMetadataItem()                           */
    3976             : /************************************************************************/
    3977             : 
    3978         251 : const char *GDALGeoPackageRasterBand::GetMetadataItem(const char *pszName,
    3979             :                                                       const char *pszDomain)
    3980             : {
    3981         251 :     LoadBandMetadata(); /* force loading from storage if needed */
    3982             : 
    3983         251 :     if (m_bAddImplicitStatistics && eDataType != GDT_Byte &&
    3984           7 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    3985           4 :         (EQUAL(pszName, "STATISTICS_MINIMUM") ||
    3986           2 :          EQUAL(pszName, "STATISTICS_MAXIMUM")))
    3987             :     {
    3988           2 :         return CSLFetchNameValue(GetMetadata(), pszName);
    3989             :     }
    3990             : 
    3991         249 :     return GDALGPKGMBTilesLikeRasterBand::GetMetadataItem(pszName, pszDomain);
    3992             : }
    3993             : 
    3994             : /************************************************************************/
    3995             : /*                            SetMetadata()                             */
    3996             : /************************************************************************/
    3997             : 
    3998          14 : CPLErr GDALGeoPackageRasterBand::SetMetadata(char **papszMetadata,
    3999             :                                              const char *pszDomain)
    4000             : {
    4001             :     GDALGeoPackageDataset *poGDS =
    4002          14 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    4003          14 :     LoadBandMetadata(); /* force loading from storage if needed */
    4004          14 :     poGDS->m_bMetadataDirty = true;
    4005          64 :     for (CSLConstList papszIter = papszMetadata; papszIter && *papszIter;
    4006             :          ++papszIter)
    4007             :     {
    4008          50 :         if (STARTS_WITH(*papszIter, "STATISTICS_"))
    4009          46 :             m_bStatsMetadataSetInThisSession = true;
    4010             :     }
    4011          14 :     return GDALPamRasterBand::SetMetadata(papszMetadata, pszDomain);
    4012             : }
    4013             : 
    4014             : /************************************************************************/
    4015             : /*                          SetMetadataItem()                           */
    4016             : /************************************************************************/
    4017             : 
    4018          20 : CPLErr GDALGeoPackageRasterBand::SetMetadataItem(const char *pszName,
    4019             :                                                  const char *pszValue,
    4020             :                                                  const char *pszDomain)
    4021             : {
    4022             :     GDALGeoPackageDataset *poGDS =
    4023          20 :         cpl::down_cast<GDALGeoPackageDataset *>(poDS);
    4024          20 :     LoadBandMetadata(); /* force loading from storage if needed */
    4025          20 :     poGDS->m_bMetadataDirty = true;
    4026          20 :     if (STARTS_WITH(pszName, "STATISTICS_"))
    4027          20 :         m_bStatsMetadataSetInThisSession = true;
    4028          20 :     return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
    4029             : }
    4030             : 
    4031             : /************************************************************************/
    4032             : /*                         InvalidateStatistics()                       */
    4033             : /************************************************************************/
    4034             : 
    4035         334 : void GDALGeoPackageRasterBand::InvalidateStatistics()
    4036             : {
    4037         334 :     bool bModified = false;
    4038         668 :     CPLStringList aosMD(CSLDuplicate(GetMetadata()));
    4039         354 :     for (CSLConstList papszIter = GetMetadata(); papszIter && *papszIter;
    4040             :          ++papszIter)
    4041             :     {
    4042          20 :         if (STARTS_WITH(*papszIter, "STATISTICS_"))
    4043             :         {
    4044          20 :             char *pszKey = nullptr;
    4045          20 :             CPLParseNameValue(*papszIter, &pszKey);
    4046          20 :             CPLAssert(pszKey);
    4047          20 :             aosMD.SetNameValue(pszKey, nullptr);
    4048          20 :             CPLFree(pszKey);
    4049          20 :             bModified = true;
    4050             :         }
    4051             :     }
    4052         334 :     if (bModified)
    4053           4 :         SetMetadata(aosMD.List());
    4054         334 : }

Generated by: LCOV version 1.14