LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - gdalopenfilegdbrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 672 920 73.0 %
Date: 2024-05-02 22:57:13 Functions: 13 19 68.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB raster driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : #include "cpl_conv.h"
      31             : #include "cpl_minixml.h"
      32             : 
      33             : #include "ogr_openfilegdb.h"
      34             : 
      35             : #include "gdal_rat.h"
      36             : #include "filegdbtable_priv.h"
      37             : 
      38             : #include <algorithm>
      39             : #include <cassert>
      40             : #include <limits>
      41             : #include <new>
      42             : #include <utility>
      43             : 
      44             : using namespace OpenFileGDB;
      45             : 
      46             : /***********************************************************************/
      47             : /*                         OpenRaster()                                */
      48             : /***********************************************************************/
      49             : 
      50          29 : bool OGROpenFileGDBDataSource::OpenRaster(const GDALOpenInfo *poOpenInfo,
      51             :                                           const std::string &osLayerName,
      52             :                                           const std::string &osDefinition,
      53             :                                           const std::string &osDocumentation)
      54             : {
      55          29 :     m_osRasterLayerName = osLayerName;
      56             : 
      57             :     const std::string osBndTableName(
      58          87 :         std::string("fras_bnd_").append(osLayerName).c_str());
      59          29 :     const auto oIter = m_osMapNameToIdx.find(osBndTableName);
      60          29 :     if (oIter == m_osMapNameToIdx.end())
      61             :     {
      62           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
      63             :                  osBndTableName.c_str());
      64           0 :         return false;
      65             :     }
      66          29 :     const int nBndIdx = oIter->second;
      67             : 
      68          58 :     FileGDBTable oTable;
      69             : 
      70             :     const CPLString osBndFilename(CPLFormFilename(
      71          58 :         m_osDirName, CPLSPrintf("a%08x.gdbtable", nBndIdx), nullptr));
      72          29 :     if (!oTable.Open(osBndFilename, false))
      73             :     {
      74           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open table %s",
      75             :                  osBndTableName.c_str());
      76           0 :         return false;
      77             :     }
      78             : 
      79          29 :     const int i_rasterband_id = oTable.GetFieldIdx("rasterband_id");
      80          29 :     const int i_sequence_nbr = oTable.GetFieldIdx("sequence_nbr");
      81          29 :     const int i_raster_id = oTable.GetFieldIdx("raster_id");
      82          29 :     const int i_band_width = oTable.GetFieldIdx("band_width");
      83          29 :     const int i_band_height = oTable.GetFieldIdx("band_height");
      84          29 :     const int i_band_types = oTable.GetFieldIdx("band_types");
      85          29 :     const int i_block_width = oTable.GetFieldIdx("block_width");
      86          29 :     const int i_block_height = oTable.GetFieldIdx("block_height");
      87          29 :     const int i_block_origin_x = oTable.GetFieldIdx("block_origin_x");
      88          29 :     const int i_block_origin_y = oTable.GetFieldIdx("block_origin_y");
      89          29 :     const int i_eminx = oTable.GetFieldIdx("eminx");
      90          29 :     const int i_eminy = oTable.GetFieldIdx("eminy");
      91          29 :     const int i_emaxx = oTable.GetFieldIdx("emaxx");
      92          29 :     const int i_emaxy = oTable.GetFieldIdx("emaxy");
      93          29 :     const int i_srid = oTable.GetFieldIdx("srid");
      94          29 :     if (i_rasterband_id < 0 || i_sequence_nbr < 0 || i_raster_id < 0 ||
      95          29 :         i_band_width < 0 || i_band_height < 0 || i_band_types < 0 ||
      96          29 :         i_block_width < 0 || i_block_height < 0 || i_block_origin_x < 0 ||
      97          29 :         i_block_origin_y < 0 || i_eminx < 0 || i_eminy < 0 || i_emaxx < 0 ||
      98          58 :         i_emaxy < 0 || i_srid < 0 ||
      99          58 :         oTable.GetField(i_rasterband_id)->GetType() != FGFT_OBJECTID ||
     100          58 :         oTable.GetField(i_sequence_nbr)->GetType() != FGFT_INT32 ||
     101          58 :         oTable.GetField(i_raster_id)->GetType() != FGFT_INT32 ||
     102          58 :         oTable.GetField(i_band_width)->GetType() != FGFT_INT32 ||
     103          58 :         oTable.GetField(i_band_height)->GetType() != FGFT_INT32 ||
     104          58 :         oTable.GetField(i_band_types)->GetType() != FGFT_INT32 ||
     105          58 :         oTable.GetField(i_block_width)->GetType() != FGFT_INT32 ||
     106          58 :         oTable.GetField(i_block_height)->GetType() != FGFT_INT32 ||
     107          58 :         oTable.GetField(i_block_origin_x)->GetType() != FGFT_FLOAT64 ||
     108          58 :         oTable.GetField(i_block_origin_y)->GetType() != FGFT_FLOAT64 ||
     109          58 :         oTable.GetField(i_eminx)->GetType() != FGFT_FLOAT64 ||
     110          58 :         oTable.GetField(i_eminy)->GetType() != FGFT_FLOAT64 ||
     111          58 :         oTable.GetField(i_emaxx)->GetType() != FGFT_FLOAT64 ||
     112          87 :         oTable.GetField(i_emaxy)->GetType() != FGFT_FLOAT64 ||
     113          29 :         oTable.GetField(i_srid)->GetType() != FGFT_INT32)
     114             :     {
     115           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
     116             :                  osBndTableName.c_str());
     117           0 :         return false;
     118             :     }
     119             : 
     120          29 :     int iRow = 0;
     121         111 :     while (iRow < oTable.GetTotalRecordCount() &&
     122          41 :            (iRow = oTable.GetAndSelectNextNonEmptyRow(iRow)) >= 0)
     123             :     {
     124          41 :         auto psField = oTable.GetFieldValue(i_raster_id);
     125          41 :         if (!psField)
     126             :         {
     127           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     128             :                      "Cannot read field %s in %s table", "raster_id",
     129             :                      osBndTableName.c_str());
     130           0 :             return false;
     131             :         }
     132          41 :         if (psField->Integer != 1)
     133             :         {
     134           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     135             :                      "Raster with raster_id = %d (!= 1) ignored",
     136           0 :                      psField->Integer);
     137           0 :             continue;
     138             :         }
     139             : 
     140          41 :         const int nGDBRasterBandId = iRow + 1;
     141             : 
     142          41 :         psField = oTable.GetFieldValue(i_sequence_nbr);
     143          41 :         if (!psField)
     144             :         {
     145           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     146             :                      "Cannot read field %s in %s table", "sequence_nbr",
     147             :                      osBndTableName.c_str());
     148           0 :             return false;
     149             :         }
     150          41 :         const int nSequenceNr = psField->Integer;
     151             : 
     152          41 :         m_oMapGDALBandToGDBBandId[nSequenceNr] = nGDBRasterBandId;
     153             : 
     154          41 :         ++iRow;
     155             :     }
     156             : 
     157          29 :     if (m_oMapGDALBandToGDBBandId.empty())
     158             :     {
     159           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record in %s table",
     160             :                  osBndTableName.c_str());
     161           0 :         return false;
     162             :     }
     163             : 
     164          29 :     auto psField = oTable.GetFieldValue(i_band_width);
     165          29 :     if (!psField)
     166             :     {
     167           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     168             :                  "Cannot read field %s in %s table", "band_width",
     169             :                  osBndTableName.c_str());
     170           0 :         return false;
     171             :     }
     172          29 :     int nWidth = psField->Integer;
     173             : 
     174          29 :     psField = oTable.GetFieldValue(i_band_height);
     175          29 :     if (!psField)
     176             :     {
     177           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     178             :                  "Cannot read field %s in %s table", "band_height",
     179             :                  osBndTableName.c_str());
     180           0 :         return false;
     181             :     }
     182          29 :     int nHeight = psField->Integer;
     183             : 
     184          29 :     const int l_nBands = static_cast<int>(m_oMapGDALBandToGDBBandId.size());
     185          58 :     if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
     186          29 :         !GDALCheckBandCount(l_nBands, /*bIsZeroAllowed=*/false))
     187             :     {
     188           0 :         return false;
     189             :     }
     190             : 
     191          29 :     psField = oTable.GetFieldValue(i_block_width);
     192          29 :     if (!psField)
     193             :     {
     194           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     195             :                  "Cannot read field %s in %s table", "block_width",
     196             :                  osBndTableName.c_str());
     197           0 :         return false;
     198             :     }
     199          29 :     const int nBlockWidth = psField->Integer;
     200             : 
     201             :     // 32768 somewhat arbitrary
     202          29 :     if (nBlockWidth <= 0 || nBlockWidth > 32768)
     203             :     {
     204           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
     205             :                  "block_width", osBndTableName.c_str());
     206           0 :         return false;
     207             :     }
     208             : 
     209          29 :     psField = oTable.GetFieldValue(i_block_height);
     210          29 :     if (!psField)
     211             :     {
     212           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     213             :                  "Cannot read field %s in %s table", "block_height",
     214             :                  osBndTableName.c_str());
     215           0 :         return false;
     216             :     }
     217          29 :     const int nBlockHeight = psField->Integer;
     218             : 
     219             :     // 32768 somewhat arbitrary
     220          29 :     if (nBlockHeight <= 0 || nBlockHeight > 32768)
     221             :     {
     222           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
     223             :                  "block_height", osBndTableName.c_str());
     224           0 :         return false;
     225             :     }
     226             : 
     227          29 :     psField = oTable.GetFieldValue(i_band_types);
     228          29 :     if (!psField)
     229             :     {
     230           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     231             :                  "Cannot read field %s in %s table", "band_types",
     232             :                  osBndTableName.c_str());
     233           0 :         return false;
     234             :     }
     235          29 :     const int nBandTypes = psField->Integer;
     236             : 
     237          29 :     psField = oTable.GetFieldValue(i_eminx);
     238          29 :     if (!psField)
     239             :     {
     240           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     241             :                  "Cannot read field %s in %s table", "eminx",
     242             :                  osBndTableName.c_str());
     243           0 :         return false;
     244             :     }
     245          29 :     const double dfMinX = psField->Real;
     246             : 
     247          29 :     psField = oTable.GetFieldValue(i_eminy);
     248          29 :     if (!psField)
     249             :     {
     250           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     251             :                  "Cannot read field %s in %s table", "eminy",
     252             :                  osBndTableName.c_str());
     253           0 :         return false;
     254             :     }
     255          29 :     const double dfMinY = psField->Real;
     256             : 
     257          29 :     psField = oTable.GetFieldValue(i_emaxx);
     258          29 :     if (!psField)
     259             :     {
     260           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     261             :                  "Cannot read field %s in %s table", "emaxx",
     262             :                  osBndTableName.c_str());
     263           0 :         return false;
     264             :     }
     265          29 :     const double dfMaxX = psField->Real;
     266             : 
     267          29 :     psField = oTable.GetFieldValue(i_emaxy);
     268          29 :     if (!psField)
     269             :     {
     270           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     271             :                  "Cannot read field %s in %s table", "emaxy",
     272             :                  osBndTableName.c_str());
     273           0 :         return false;
     274             :     }
     275          29 :     const double dfMaxY = psField->Real;
     276             : 
     277          29 :     psField = oTable.GetFieldValue(i_block_origin_x);
     278          29 :     if (!psField)
     279             :     {
     280           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     281             :                  "Cannot read field %s in %s table", "block_origin_x",
     282             :                  osBndTableName.c_str());
     283           0 :         return false;
     284             :     }
     285          29 :     const double dfBlockOriginX = psField->Real;
     286             : 
     287          29 :     psField = oTable.GetFieldValue(i_block_origin_y);
     288          29 :     if (!psField)
     289             :     {
     290           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     291             :                  "Cannot read field %s in %s table", "block_origin_y",
     292             :                  osBndTableName.c_str());
     293           0 :         return false;
     294             :     }
     295          29 :     const double dfBlockOriginY = psField->Real;
     296             : 
     297             :     // Figure out data type
     298          29 :     GDALDataType eDT = GDT_Byte;
     299          29 :     const int nBitWidth = (nBandTypes >> 19) & ((1 << 7) - 1);
     300          29 :     const int nBitType = (nBandTypes >> 16) & ((1 << 2) - 1);
     301          29 :     constexpr int IS_UNSIGNED = 0;
     302          29 :     constexpr int IS_SIGNED = 1;
     303          29 :     constexpr int IS_FLOATING_POINT = 2;
     304          29 :     if ((nBitWidth >= 1 && nBitWidth < 8) && nBitType == IS_UNSIGNED)
     305             :     {
     306           2 :         eDT = GDT_Byte;
     307             :     }
     308          27 :     else if (nBitWidth == 8 && nBitType <= IS_SIGNED)
     309             :     {
     310          13 :         eDT = nBitType == IS_SIGNED ? GDT_Int8 : GDT_Byte;
     311             :     }
     312          14 :     else if (nBitWidth == 16 && nBitType <= IS_SIGNED)
     313             :     {
     314           4 :         eDT = nBitType == IS_SIGNED ? GDT_Int16 : GDT_UInt16;
     315             :     }
     316          10 :     else if (nBitWidth == 32 && nBitType <= IS_FLOATING_POINT)
     317             :     {
     318          13 :         eDT = nBitType == IS_FLOATING_POINT ? GDT_Float32
     319           4 :               : nBitType == IS_SIGNED       ? GDT_Int32
     320             :                                             : GDT_UInt32;
     321             :     }
     322           1 :     else if (nBitWidth == 64 && nBitType == 0)
     323             :     {
     324           1 :         eDT = GDT_Float64;
     325             :     }
     326             :     else
     327             :     {
     328           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     329             :                  "Unhandled nBitWidth=%d, nBitType=%d in %s table", nBitWidth,
     330             :                  nBitType, osBndTableName.c_str());
     331           0 :         return false;
     332             :     }
     333             : 
     334             :     // To avoid potential integer overflows in IReadBlock()
     335          58 :     if (nBlockWidth * nBlockHeight >
     336          29 :         std::numeric_limits<int>::max() / nBitWidth)
     337             :     {
     338           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     339             :                  "Too large block %dx%d in %s table", nBlockWidth, nBlockHeight,
     340             :                  osBndTableName.c_str());
     341           0 :         return false;
     342             :     }
     343             : 
     344             :     // Figure out compression
     345          29 :     const int nCompression = (nBandTypes >> 8) & 0xff;
     346          29 :     switch (nCompression)
     347             :     {
     348           6 :         case 0:
     349           6 :             m_eRasterCompression = Compression::NONE;
     350           6 :             break;
     351          19 :         case 4:
     352          19 :             m_eRasterCompression = Compression::LZ77;
     353          19 :             SetMetadataItem("COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE");
     354          19 :             break;
     355           2 :         case 8:
     356           2 :             m_eRasterCompression = Compression::JPEG;
     357           2 :             SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
     358           2 :             break;
     359           2 :         case 12:
     360           2 :             m_eRasterCompression = Compression::JPEG2000;
     361           2 :             SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
     362           2 :             break;
     363           0 :         default:
     364             :         {
     365           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     366             :                      "Unhandled compression %d in %s table", nCompression,
     367             :                      osBndTableName.c_str());
     368           0 :             return false;
     369             :         }
     370             :     }
     371             : 
     372             :     // Figure out geotransform
     373             : 
     374          29 :     if (!(dfMaxX > dfMinX && dfMaxY > dfMinY))
     375             :     {
     376           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     377             :                  "!(dfMaxX > dfMinX && dfMaxY > dfMinY)");
     378           0 :         return false;
     379             :     }
     380          29 :     else if (nWidth == 1 || nHeight == 1)
     381             :     {
     382           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     383             :                  "nWidth == 1 || nHeight == 1: cannot determine geotransform");
     384             :     }
     385             :     else
     386             :     {
     387             :         // FileGDB uses a center-of-pixel convention for georeferencing
     388             :         // Transform to GDAL's corner-of-pixel convention.
     389          29 :         const double dfResX = (dfMaxX - dfMinX) / (nWidth - 1);
     390          29 :         const double dfResY = (dfMaxY - dfMinY) / (nHeight - 1);
     391          29 :         m_bHasGeoTransform = true;
     392          29 :         const double dfBlockGeorefWidth = dfResX * nBlockWidth;
     393          29 :         if (dfMinX != dfBlockOriginX)
     394             :         {
     395             :             // Take into account MinX by making sure the raster origin is
     396             :             // close to it, while being shifted from an integer number of blocks
     397             :             // from BlockOriginX
     398             :             const double dfTmp =
     399           1 :                 std::floor((dfMinX - dfBlockOriginX) / dfBlockGeorefWidth);
     400           1 :             if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
     401             :             {
     402           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     403             :                          "Inconsistent eminx=%g and block_origin_x=%g", dfMinX,
     404             :                          dfBlockOriginX);
     405           0 :                 return false;
     406             :             }
     407           1 :             m_nShiftBlockX = static_cast<int>(dfTmp);
     408           1 :             CPLDebug("OpenFileGDB", "m_nShiftBlockX = %d", m_nShiftBlockX);
     409           1 :             const double dfMinXAdjusted =
     410           1 :                 dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth;
     411           1 :             nWidth = 1 + static_cast<int>(
     412           1 :                              std::round((dfMaxX - dfMinXAdjusted) / dfResX));
     413             :         }
     414          58 :         m_adfGeoTransform[0] =
     415          29 :             (dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth) - dfResX / 2;
     416          29 :         m_adfGeoTransform[1] = dfResX;
     417          29 :         m_adfGeoTransform[2] = 0.0;
     418          29 :         const double dfBlockGeorefHeight = dfResY * nBlockHeight;
     419          29 :         if (dfMaxY != dfBlockOriginY)
     420             :         {
     421             :             // Take into account MaxY by making sure the raster origin is
     422             :             // close to it, while being shifted from an integer number of blocks
     423             :             // from BlockOriginY
     424             :             const double dfTmp =
     425           3 :                 std::floor((dfBlockOriginY - dfMaxY) / dfBlockGeorefHeight);
     426           3 :             if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
     427             :             {
     428           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     429             :                          "Inconsistent emaxy=%g and block_origin_y=%g", dfMaxY,
     430             :                          dfBlockOriginY);
     431           0 :                 return false;
     432             :             }
     433           3 :             m_nShiftBlockY = static_cast<int>(dfTmp);
     434           3 :             CPLDebug("OpenFileGDB", "m_nShiftBlockY = %d", m_nShiftBlockY);
     435           3 :             const double dfMaxYAdjusted =
     436           3 :                 dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight;
     437           3 :             nHeight = 1 + static_cast<int>(
     438           3 :                               std::round((dfMaxYAdjusted - dfMinY) / dfResY));
     439             :         }
     440          58 :         m_adfGeoTransform[3] =
     441          29 :             (dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight) +
     442          29 :             dfResY / 2;
     443          29 :         m_adfGeoTransform[4] = 0.0;
     444          29 :         m_adfGeoTransform[5] = -dfResY;
     445             :     }
     446             : 
     447             :     // Two cases:
     448             :     // - osDefinition is empty (that is FileGDB v9): find the SRS by looking
     449             :     //   at the SRS attached to the RASTER field definition of the .gdbtable
     450             :     //   file of the main table of the raster (that is the one without fras_XXX
     451             :     //   prefixes)
     452             :     // - or osDefinition is not empty (that is FileGDB v10): get SRID from the
     453             :     //   "srid" field of the _fras_bnd table, and use that has the key to
     454             :     //   lookup the corresponding WKT from the GDBSpatialRefs table.
     455             :     //   In some cases srid might be 0 (invalid), then we try to get it from
     456             :     //   Definition column of the GDB_Items table, stored in osDefinition
     457          29 :     psField = oTable.GetFieldValue(i_srid);
     458          29 :     if (osDefinition.empty())
     459             :     {
     460             :         // osDefinition empty for FileGDB v9
     461           2 :         const auto oIter2 = m_osMapNameToIdx.find(osLayerName);
     462           2 :         if (oIter2 != m_osMapNameToIdx.end())
     463             :         {
     464           2 :             const int nTableIdx = oIter2->second;
     465             : 
     466           4 :             FileGDBTable oTableMain;
     467             : 
     468             :             const CPLString osTableMain(CPLFormFilename(
     469           4 :                 m_osDirName, CPLSPrintf("a%08x.gdbtable", nTableIdx), nullptr));
     470           2 :             if (oTableMain.Open(osTableMain, false))
     471             :             {
     472           2 :                 const int iRasterFieldIdx = oTableMain.GetFieldIdx("RASTER");
     473           2 :                 if (iRasterFieldIdx >= 0)
     474             :                 {
     475           2 :                     const auto poField = oTableMain.GetField(iRasterFieldIdx);
     476           2 :                     if (poField->GetType() == FGFT_RASTER)
     477             :                     {
     478           2 :                         const auto poFieldRaster =
     479             :                             static_cast<FileGDBRasterField *>(poField);
     480           2 :                         const auto &osWKT = poFieldRaster->GetWKT();
     481           2 :                         if (!osWKT.empty() && osWKT[0] != '{')
     482             :                         {
     483           2 :                             auto poSRS = BuildSRS(osWKT.c_str());
     484           2 :                             if (poSRS)
     485             :                             {
     486           2 :                                 m_oRasterSRS = *poSRS;
     487           2 :                                 poSRS->Release();
     488             :                             }
     489             :                         }
     490             :                     }
     491             :                 }
     492             :             }
     493             :         }
     494             :     }
     495          27 :     else if (!psField)
     496             :     {
     497           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     498             :                  "Cannot read field %s in %s table", "srid",
     499             :                  osBndTableName.c_str());
     500             :     }
     501          27 :     else if (m_osGDBSpatialRefsFilename.empty())
     502             :     {
     503           0 :         CPLError(CE_Warning, CPLE_AppDefined, "No GDBSpatialRefs table");
     504             :     }
     505             :     else
     506             :     {
     507             :         // FileGDB v10 case
     508          27 :         const int nSRID = psField->Integer;
     509          54 :         FileGDBTable oTableSRS;
     510          27 :         if (oTableSRS.Open(m_osGDBSpatialRefsFilename.c_str(), false))
     511             :         {
     512          27 :             const int iSRTEXT = oTableSRS.GetFieldIdx("SRTEXT");
     513          54 :             if (iSRTEXT < 0 ||
     514          27 :                 oTableSRS.GetField(iSRTEXT)->GetType() != FGFT_STRING)
     515             :             {
     516           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     517             :                          "Could not find field %s in table %s", "SRTEXT",
     518           0 :                          oTableSRS.GetFilename().c_str());
     519             :             }
     520          27 :             else if (nSRID == 0)
     521             :             {
     522             :                 // BldgHeights.gdb is such. We must fetch the SRS from the
     523             :                 // Definition column of the GDB_Items table
     524             :                 CPLXMLTreeCloser psTree(
     525           0 :                     CPLParseXMLString(osDefinition.c_str()));
     526           0 :                 if (psTree == nullptr)
     527             :                 {
     528           0 :                     CPLError(
     529             :                         CE_Warning, CPLE_AppDefined,
     530             :                         "Cannot parse XML definition. SRS will be missing");
     531             :                 }
     532             :                 else
     533             :                 {
     534           0 :                     CPLStripXMLNamespace(psTree.get(), nullptr, TRUE);
     535             :                     const CPLXMLNode *psInfo =
     536           0 :                         CPLSearchXMLNode(psTree.get(), "=DERasterDataset");
     537           0 :                     if (psInfo)
     538             :                     {
     539           0 :                         auto poSRS = BuildSRS(psInfo);
     540           0 :                         if (poSRS)
     541           0 :                             m_oRasterSRS = *poSRS;
     542             :                     }
     543           0 :                     if (m_oRasterSRS.IsEmpty())
     544             :                     {
     545           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     546             :                                  "Cannot get SRS from XML definition");
     547             :                     }
     548             :                 }
     549             :             }
     550          54 :             else if (nSRID < 0 || !oTableSRS.SelectRow(nSRID - 1) ||
     551          27 :                      oTableSRS.HasGotError())
     552             :             {
     553           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     554             :                          "Cannot find record corresponding to SRID = %d",
     555             :                          nSRID);
     556             :             }
     557             :             else
     558             :             {
     559          27 :                 const auto psSRTEXT = oTableSRS.GetFieldValue(iSRTEXT);
     560          27 :                 if (psSRTEXT && psSRTEXT->String)
     561             :                 {
     562          27 :                     if (psSRTEXT->String[0] != '{')
     563             :                     {
     564          26 :                         auto poSRS = BuildSRS(psSRTEXT->String);
     565          26 :                         if (poSRS)
     566             :                         {
     567          26 :                             m_oRasterSRS = *poSRS;
     568          26 :                             poSRS->Release();
     569             :                         }
     570          27 :                     }
     571             :                 }
     572             :                 else
     573             :                 {
     574           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     575             :                              "Cannot get SRTEXT corresponding to SRID = %d",
     576             :                              nSRID);
     577             :                 }
     578             :             }
     579             :         }
     580             :     }
     581             : 
     582             :     // Open the fras_blk_XXX table, which contains pixel data, as a OGR layer
     583             :     const std::string osBlkTableName(
     584          87 :         std::string("fras_blk_").append(osLayerName).c_str());
     585          29 :     m_poBlkLayer = BuildLayerFromName(osBlkTableName.c_str());
     586          29 :     if (!m_poBlkLayer)
     587             :     {
     588           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
     589             :                  osBlkTableName.c_str());
     590           0 :         return false;
     591             :     }
     592          29 :     auto poFDefn = m_poBlkLayer->GetLayerDefn();
     593          29 :     if (poFDefn->GetFieldIndex("rasterband_id") < 0 ||
     594          29 :         poFDefn->GetFieldIndex("rrd_factor") < 0 ||
     595          29 :         poFDefn->GetFieldIndex("row_nbr") < 0 ||
     596          29 :         poFDefn->GetFieldIndex("col_nbr") < 0 ||
     597          87 :         poFDefn->GetFieldIndex("block_data") < 0 ||
     598          29 :         poFDefn->GetFieldIndex("block_key") < 0)
     599             :     {
     600           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
     601             :                  osBlkTableName.c_str());
     602           0 :         return false;
     603             :     }
     604             : 
     605          29 :     nRasterXSize = nWidth;
     606          29 :     nRasterYSize = nHeight;
     607             : 
     608          29 :     if (m_oMapGDALBandToGDBBandId.size() > 1)
     609             :     {
     610           6 :         SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
     611             :     }
     612             : 
     613             :     // Figure out number of overviews by looking at the biggest block_key
     614             :     // (should only involve looking in the corresponding index).
     615          29 :     int nOverviewCount = 0;
     616          58 :     CPLString osSQL;
     617          29 :     osSQL.Printf("SELECT MAX(block_key) FROM \"%s\"", osBlkTableName.c_str());
     618          29 :     auto poSQLLyr = ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
     619          29 :     if (poSQLLyr)
     620             :     {
     621          58 :         auto poFeat = std::unique_ptr<OGRFeature>(poSQLLyr->GetNextFeature());
     622          29 :         if (poFeat)
     623             :         {
     624          29 :             const char *pszMaxKey = poFeat->GetFieldAsString(0);
     625          29 :             if (strlen(pszMaxKey) == strlen("0000BANDOVYYYYXXXX    ") ||
     626           1 :                 strlen(pszMaxKey) == strlen("0000BANDOV-YYYYXXXX    ") ||
     627           1 :                 strlen(pszMaxKey) == strlen("0000BANDOVYYYY-XXXX    ") ||
     628           1 :                 strlen(pszMaxKey) == strlen("0000BANDOV-YYYY-XXXX    "))
     629             :             {
     630          28 :                 char szHex[3] = {0};
     631          28 :                 memcpy(szHex, pszMaxKey + 8, 2);
     632          28 :                 unsigned nMaxRRD = 0;
     633          28 :                 sscanf(szHex, "%02X", &nMaxRRD);
     634          28 :                 nOverviewCount =
     635          28 :                     static_cast<int>(std::min<unsigned>(31, nMaxRRD));
     636             :             }
     637             :         }
     638          29 :         ReleaseResultSet(poSQLLyr);
     639             :     }
     640             : 
     641          29 :     if (m_eRasterCompression == Compression::JPEG)
     642             :     {
     643           2 :         GuessJPEGQuality(nOverviewCount);
     644             :     }
     645             : 
     646             :     // It seems that the top left corner of overviews is registered against
     647             :     // (eminx, emaxy), contrary to the full resolution layer which is registered
     648             :     // against (block_origin_x, block_origin_y).
     649             :     // At least, that's what was observed on the dataset
     650             :     // ftp://ftp.gisdata.mn.gov/pub/gdrs/data/pub/us_mn_state_dnr/water_lake_bathymetry/fgdb_water_lake_bathymetry.zip
     651          29 :     if ((dfBlockOriginX != dfMinX || dfBlockOriginY != dfMaxY) &&
     652             :         nOverviewCount > 0)
     653             :     {
     654           0 :         CPLDebug("OpenFileGDB",
     655             :                  "Ignoring overviews as block origin != (minx, maxy)");
     656           0 :         nOverviewCount = 0;
     657             :     }
     658             : 
     659             :     // Create raster bands
     660             : 
     661             :     // Create mask band of full resolution, if we don't assign a nodata value
     662          29 :     std::unique_ptr<GDALOpenFileGDBRasterBand> poMaskBand;
     663             : 
     664             :     // Default "nodata" padding in areas whose validity mask is 0 ?
     665             :     // Not reliable on integer data types.
     666             :     // Byte -> 0
     667             :     // Int8 -> -128 ?
     668             :     // Int16 -> 32767
     669             :     // UInt16 -> 0
     670             :     // (u)int10 -> 65535
     671             :     // (u)int12 -> 65535
     672             :     // Int32 -> 2147483647
     673             :     // UInt32 -> 2147483647
     674             :     // Float32 -> 3.4e+38
     675             :     // Float64 -> 1.79e+308
     676             : 
     677          29 :     bool bHasNoData = false;
     678          29 :     double dfNoData = 0.0;
     679          58 :     const char *pszNoDataOrMask = CSLFetchNameValueDef(
     680          29 :         poOpenInfo->papszOpenOptions, "NODATA_OR_MASK", "AUTO");
     681          29 :     if (EQUAL(pszNoDataOrMask, "AUTO"))
     682             :     {
     683             :         // In AUTO mode, we only set nodata for Float32/Float64
     684             :         // For other data types, report a mask band.
     685          29 :         if (eDT == GDT_Float32)
     686             :         {
     687           5 :             bHasNoData = true;
     688           5 :             dfNoData = static_cast<double>(static_cast<float>(3.4e+38));
     689             :         }
     690          24 :         else if (eDT == GDT_Float64)
     691             :         {
     692           1 :             bHasNoData = true;
     693           1 :             dfNoData = 1.79e+308;
     694             :         }
     695             :         else
     696             :         {
     697          23 :             poMaskBand = std::make_unique<GDALOpenFileGDBRasterBand>(
     698          46 :                 this, 1, GDT_Byte, 8, nBlockWidth, nBlockHeight, 0, true);
     699             :         }
     700             :     }
     701           0 :     else if (EQUAL(pszNoDataOrMask, "MASK"))
     702             :     {
     703           0 :         poMaskBand = std::make_unique<GDALOpenFileGDBRasterBand>(
     704           0 :             this, 1, GDT_Byte, 8, nBlockWidth, nBlockHeight, 0, true);
     705             :     }
     706           0 :     else if (!EQUAL(pszNoDataOrMask, "NONE"))
     707             :     {
     708           0 :         dfNoData = CPLAtof(pszNoDataOrMask);
     709           0 :         if (eDT == GDT_Float64)
     710             :         {
     711           0 :             bHasNoData = true;
     712             :         }
     713           0 :         else if (eDT == GDT_Float32)
     714             :         {
     715           0 :             if (std::fabs(dfNoData) > std::numeric_limits<float>::max())
     716             :             {
     717           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     718             :                          "Invalid nodata value %.18g for Float32", dfNoData);
     719           0 :                 return false;
     720             :             }
     721           0 :             bHasNoData = true;
     722             :         }
     723           0 :         else if (GDALDataTypeIsInteger(eDT))
     724             :         {
     725           0 :             double dfMin = 0, dfMax = 0;
     726           0 :             switch (eDT)
     727             :             {
     728           0 :                 case GDT_Int8:
     729           0 :                     dfMin = std::numeric_limits<int8_t>::min();
     730           0 :                     dfMax = std::numeric_limits<int8_t>::max();
     731           0 :                     break;
     732           0 :                 case GDT_Byte:
     733           0 :                     dfMin = std::numeric_limits<uint8_t>::min();
     734           0 :                     dfMax = std::numeric_limits<uint8_t>::max();
     735           0 :                     break;
     736           0 :                 case GDT_Int16:
     737           0 :                     dfMin = std::numeric_limits<int16_t>::min();
     738           0 :                     dfMax = std::numeric_limits<int16_t>::max();
     739           0 :                     break;
     740           0 :                 case GDT_UInt16:
     741           0 :                     dfMin = std::numeric_limits<uint16_t>::min();
     742           0 :                     dfMax = std::numeric_limits<uint16_t>::max();
     743           0 :                     break;
     744           0 :                 case GDT_Int32:
     745           0 :                     dfMin = std::numeric_limits<int32_t>::min();
     746           0 :                     dfMax = std::numeric_limits<int32_t>::max();
     747           0 :                     break;
     748           0 :                 case GDT_UInt32:
     749           0 :                     dfMin = std::numeric_limits<uint32_t>::min();
     750           0 :                     dfMax = std::numeric_limits<uint32_t>::max();
     751           0 :                     break;
     752           0 :                 default:
     753           0 :                     CPLAssert(false);
     754             :                     return false;
     755             :             }
     756           0 :             if (!std::isfinite(dfNoData) || dfNoData < dfMin ||
     757           0 :                 dfNoData > dfMax ||
     758           0 :                 dfNoData != static_cast<double>(static_cast<int64_t>(dfNoData)))
     759             :             {
     760           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     761             :                          "Invalid nodata value %.18g for %s", dfNoData,
     762             :                          GDALGetDataTypeName(eDT));
     763           0 :                 return false;
     764             :             }
     765           0 :             bHasNoData = true;
     766             :         }
     767             :     }
     768             : 
     769          29 :     GDALOpenFileGDBRasterBand *poMaskBandRef = poMaskBand.get();
     770             : 
     771          70 :     for (int iBand = 1; iBand <= l_nBands; ++iBand)
     772             :     {
     773             :         auto poBand = new GDALOpenFileGDBRasterBand(
     774          41 :             this, iBand, eDT, nBitWidth, nBlockWidth, nBlockHeight, 0, false);
     775          41 :         if (poMaskBandRef)
     776             :         {
     777          35 :             if (iBand == 1)
     778             :             {
     779             :                 // Make the mask band owned by the first raster band
     780          23 :                 poBand->m_poMaskBandOwned = std::move(poMaskBand);
     781          23 :                 poMaskBandRef = poBand->m_poMaskBandOwned.get();
     782          23 :                 poMaskBandRef->m_poMainBand = poBand;
     783             :             }
     784          35 :             poBand->m_poMaskBand = poMaskBandRef;
     785             :         }
     786           6 :         else if (bHasNoData)
     787             :         {
     788           6 :             poBand->m_dfNoData = dfNoData;
     789           6 :             poBand->m_bHasNoData = true;
     790             :         }
     791             : 
     792             :         // Create overview bands
     793         116 :         for (int iOvr = 0; iOvr < nOverviewCount; ++iOvr)
     794             :         {
     795             :             auto poOvrBand = std::make_unique<GDALOpenFileGDBRasterBand>(
     796             :                 this, iBand, eDT, nBitWidth, nBlockWidth, nBlockHeight,
     797         150 :                 iOvr + 1, false);
     798          75 :             if (poBand->m_bHasNoData)
     799             :             {
     800           9 :                 poOvrBand->m_dfNoData = dfNoData;
     801           9 :                 poOvrBand->m_bHasNoData = true;
     802             :             }
     803          75 :             poBand->m_apoOverviewBands.emplace_back(std::move(poOvrBand));
     804             :         }
     805             : 
     806          41 :         SetBand(iBand, poBand);
     807             :     }
     808             : 
     809             :     // Create mask band of overview bands
     810          29 :     if (poMaskBandRef)
     811             :     {
     812          59 :         for (int iOvr = 0; iOvr < nOverviewCount; ++iOvr)
     813             :         {
     814         102 :             for (int iBand = 1; iBand <= l_nBands; ++iBand)
     815             :             {
     816          66 :                 auto poOvrBand = cpl::down_cast<GDALOpenFileGDBRasterBand *>(
     817             :                                      GetRasterBand(iBand))
     818          66 :                                      ->m_apoOverviewBands[iOvr]
     819          66 :                                      .get();
     820          66 :                 if (iBand == 1)
     821             :                 {
     822             :                     // Make the mask band owned by the first raster band
     823             :                     poOvrBand->m_poMaskBandOwned =
     824          36 :                         std::make_unique<GDALOpenFileGDBRasterBand>(
     825           0 :                             this, 1, GDT_Byte, 8, nBlockWidth, nBlockHeight,
     826          36 :                             iOvr + 1, true);
     827          36 :                     poMaskBandRef = poOvrBand->m_poMaskBandOwned.get();
     828          36 :                     poMaskBandRef->m_poMainBand = poOvrBand;
     829             :                 }
     830          66 :                 poOvrBand->m_poMaskBand = poMaskBandRef;
     831             :             }
     832             :         }
     833             :     }
     834             : 
     835          29 :     ReadAuxTable(osLayerName);
     836             : 
     837          29 :     SetMetadataItem("RASTER_DATASET", m_osRasterLayerName.c_str());
     838             : 
     839          29 :     if (!osDefinition.empty())
     840             :     {
     841          27 :         const char *const apszMD[] = {osDefinition.c_str(), nullptr};
     842          27 :         SetMetadata(const_cast<char **>(apszMD), "xml:definition");
     843             :     }
     844             : 
     845          29 :     if (!osDocumentation.empty())
     846             :     {
     847          26 :         const char *const apszMD[] = {osDocumentation.c_str(), nullptr};
     848          26 :         SetMetadata(const_cast<char **>(apszMD), "xml:documentation");
     849             :     }
     850             : 
     851             :     // We are all fine after all those preliminary checks and setups !
     852          29 :     return true;
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                       GuessJPEGQuality()                             */
     857             : /************************************************************************/
     858             : 
     859           2 : void OGROpenFileGDBDataSource::GuessJPEGQuality(int nOverviewCount)
     860             : {
     861             :     // For JPEG, fetch JPEG_QUALITY from the data of the smallest overview level
     862           4 :     CPLString osFilter;
     863             :     osFilter.Printf("block_key = '0000%04X%02X%04X%04X'",
     864             :                     1,  // band
     865             :                     nOverviewCount,
     866             :                     0,  // nBlockYOff
     867             :                     0   // nBlockXOff
     868           2 :     );
     869             : 
     870           2 :     CPLAssert(m_poBlkLayer);
     871           2 :     m_poBlkLayer->SetAttributeFilter(osFilter.c_str());
     872             :     auto poFeature =
     873           4 :         std::unique_ptr<OGRFeature>(m_poBlkLayer->GetNextFeature());
     874           2 :     if (poFeature)
     875             :     {
     876           2 :         const int nFieldIdx = poFeature->GetFieldIndex("block_data");
     877           2 :         CPLAssert(nFieldIdx >= 0);
     878           2 :         if (poFeature->IsFieldSetAndNotNull(nFieldIdx))
     879             :         {
     880           2 :             int nInBytes = 0;
     881             :             const GByte *pabyData =
     882           2 :                 poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
     883           2 :             if (nInBytes >= 5)
     884             :             {
     885           2 :                 uint32_t nJPEGSize = nInBytes - 1;
     886           2 :                 uint32_t nJPEGOffset = 1;
     887           2 :                 if (pabyData[0] == 0xFE)
     888             :                 {
     889             :                     // JPEG followed by binary mask
     890           2 :                     memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
     891           2 :                     CPL_LSBPTR32(&nJPEGSize);
     892           2 :                     if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
     893             :                     {
     894           0 :                         nJPEGSize = 0;
     895             :                     }
     896           2 :                     nJPEGOffset = 5;
     897             :                 }
     898           0 :                 else if (pabyData[0] != 1)
     899             :                 {
     900           0 :                     nJPEGSize = 0;
     901             :                 }
     902           2 :                 if (nJPEGSize)
     903             :                 {
     904           4 :                     CPLString osTmpFilename;
     905           2 :                     osTmpFilename.Printf("/vsimem/_openfilegdb/%p.jpg", this);
     906           2 :                     VSIFCloseL(VSIFileFromMemBuffer(
     907             :                         osTmpFilename.c_str(),
     908           2 :                         const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize,
     909             :                         false));
     910           2 :                     const char *const apszDrivers[] = {"JPEG", nullptr};
     911             :                     auto poJPEGDS = std::unique_ptr<GDALDataset>(
     912             :                         GDALDataset::Open(osTmpFilename.c_str(), GDAL_OF_RASTER,
     913           4 :                                           apszDrivers));
     914           2 :                     if (poJPEGDS)
     915             :                     {
     916           2 :                         const char *pszQuality = poJPEGDS->GetMetadataItem(
     917           2 :                             "JPEG_QUALITY", "IMAGE_STRUCTURE");
     918           2 :                         if (pszQuality)
     919             :                         {
     920           2 :                             SetMetadataItem("JPEG_QUALITY", pszQuality,
     921           2 :                                             "IMAGE_STRUCTURE");
     922             :                         }
     923             :                     }
     924           2 :                     VSIUnlink(osTmpFilename);
     925             :                 }
     926             :             }
     927             :         }
     928             :     }
     929           2 : }
     930             : 
     931             : /************************************************************************/
     932             : /*                        ReadAuxTable()                                */
     933             : /************************************************************************/
     934             : 
     935             : // Record type=9 of table fras_ras_XXXX contains a PropertySet object,
     936             : // which may contain statistics
     937             : // For example on
     938             : // https://listdata.thelist.tas.gov.au/opendata/data/NCH_ES_WATER_LOGGING_HAZARD_STATEWIDE.zip
     939          29 : void OGROpenFileGDBDataSource::ReadAuxTable(const std::string &osLayerName)
     940             : {
     941             :     const std::string osAuxTableName(
     942          58 :         std::string("fras_aux_").append(osLayerName).c_str());
     943          29 :     auto poLayer = BuildLayerFromName(osAuxTableName.c_str());
     944          29 :     if (!poLayer)
     945             :     {
     946           0 :         CPLDebug("OpenFileGDB", "Cannot find table %s", osAuxTableName.c_str());
     947           0 :         return;
     948             :     }
     949          29 :     auto poFDefn = poLayer->GetLayerDefn();
     950          29 :     const int iFieldObjectIdx = poFDefn->GetFieldIndex("object");
     951          29 :     if (poFDefn->GetFieldIndex("type") < 0 || iFieldObjectIdx < 0)
     952             :     {
     953           0 :         CPLDebug("OpenFileGDB", "Wrong structure for %s table",
     954             :                  osAuxTableName.c_str());
     955           0 :         return;
     956             :     }
     957          29 :     poLayer->SetAttributeFilter("type = 9");
     958          29 :     auto poFeature = std::unique_ptr<OGRFeature>(poLayer->GetNextFeature());
     959          29 :     if (!poFeature)
     960           3 :         return;
     961          26 :     if (!poFeature->IsFieldSetAndNotNull(iFieldObjectIdx))
     962           0 :         return;
     963          26 :     int nBytes = 0;
     964             :     const GByte *pabyData =
     965          26 :         poFeature->GetFieldAsBinary(iFieldObjectIdx, &nBytes);
     966          26 :     if (!pabyData || nBytes == 0)
     967           0 :         return;
     968          26 :     int iOffset = 0;
     969             : 
     970         545 :     const auto ReadString = [pabyData, &iOffset, nBytes](std::string &osStr)
     971             :     {
     972         110 :         if (iOffset > nBytes - 4)
     973          21 :             return false;
     974             :         int nStrLength;
     975          89 :         memcpy(&nStrLength, pabyData + iOffset, 4);
     976          89 :         CPL_LSBPTR32(&nStrLength);
     977          89 :         iOffset += 4;
     978          89 :         if (nStrLength <= 2 || iOffset > nBytes - nStrLength)
     979           5 :             return false;
     980          84 :         if ((nStrLength % 2) != 0)
     981           0 :             return false;
     982             :         // nStrLength / 2 to get the number of characters
     983             :         // and - 1 to remove the null terminating one
     984          84 :         osStr = ReadUTF16String(pabyData + iOffset, nStrLength / 2 - 1);
     985          84 :         iOffset += nStrLength;
     986          84 :         return true;
     987          26 :     };
     988             : 
     989             :     // pabyData is an ArcObject "PropertySet" object, which is key/value
     990             :     // dictionary. This is hard to parse given there are variable-length value
     991             :     // whose size is not explicit. So let's use a heuristics by looking for
     992             :     // the beginning of a inner PropertySet with band properties that starts
     993             :     // with a KIND=BAND key value pair.
     994          26 :     constexpr GByte abyNeedle[] = {
     995             :         'K', 0, 'I', 0, 'N', 0, 'D', 0, 0, 0, 8, 0,  // 8 = string
     996             :         10,  0, 0,   0,  // number of bytes of following value
     997             :         'B', 0, 'A', 0, 'N', 0, 'D', 0, 0, 0};
     998          26 :     constexpr int nNeedleSize = static_cast<int>(sizeof(abyNeedle));
     999             : 
    1000          26 :     for (int iBand = 1; iBand <= nBands; ++iBand)
    1001             :     {
    1002          26 :         int iNewOffset = -1;
    1003       10218 :         for (int i = iOffset; i < nBytes - nNeedleSize; ++i)
    1004             :         {
    1005       10218 :             if (pabyData[i] == 'K' &&
    1006          78 :                 memcmp(pabyData + i, abyNeedle, nNeedleSize) == 0)
    1007             :             {
    1008          26 :                 iNewOffset = i + nNeedleSize;
    1009          26 :                 break;
    1010             :             }
    1011             :         }
    1012          26 :         if (iNewOffset < 0)
    1013           0 :             return;
    1014          26 :         iOffset = iNewOffset;
    1015             : 
    1016             :         // Try to read as many key/value pairs as possible
    1017             :         while (true)
    1018             :         {
    1019             :             // Read key
    1020          94 :             std::string osKey;
    1021          94 :             if (!ReadString(osKey))
    1022          26 :                 return;
    1023             : 
    1024             :             // Read value type as a short
    1025             :             uint16_t nValueType;
    1026          68 :             if (iOffset > nBytes - 2)
    1027           0 :                 return;
    1028          68 :             memcpy(&nValueType, pabyData + iOffset, 2);
    1029          68 :             CPL_LSBPTR16(&nValueType);
    1030          68 :             iOffset += 2;
    1031             : 
    1032             :             // Skip over non-string values
    1033          68 :             if (nValueType == 0 || nValueType == 1)  // null / empty value
    1034             :             {
    1035           0 :                 continue;
    1036             :             }
    1037          68 :             if (nValueType == 2)  // short value
    1038             :             {
    1039          26 :                 if (iOffset > nBytes - 2)
    1040           0 :                     return;
    1041          26 :                 iOffset += 2;
    1042          26 :                 continue;
    1043             :             }
    1044             : 
    1045          42 :             if (nValueType == 3 || nValueType == 4)  // int or long value
    1046             :             {
    1047          26 :                 if (iOffset > nBytes - 4)
    1048           0 :                     return;
    1049          26 :                 iOffset += 4;
    1050          26 :                 continue;
    1051             :             }
    1052             : 
    1053          16 :             if (nValueType == 5 || nValueType == 7)  // double or date value
    1054             :             {
    1055           0 :                 if (iOffset > nBytes - 8)
    1056           0 :                     return;
    1057           0 :                 iOffset += 8;
    1058           0 :                 continue;
    1059             :             }
    1060             : 
    1061          16 :             if (nValueType != 8)  // 8 = string
    1062             :             {
    1063             :                 // Give up with this band as the value type is not handled,
    1064             :                 // and we can't skip over it.
    1065           0 :                 break;
    1066             :             }
    1067             : 
    1068             :             // Read string value
    1069          16 :             std::string osValue;
    1070          16 :             if (!ReadString(osValue))
    1071           0 :                 return;
    1072             : 
    1073          32 :             GetRasterBand(iBand)->SetMetadataItem(osKey.c_str(),
    1074          16 :                                                   osValue.c_str());
    1075          68 :         }
    1076             :     }
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                         GetGeoTransform()                            */
    1081             : /************************************************************************/
    1082             : 
    1083           4 : CPLErr OGROpenFileGDBDataSource::GetGeoTransform(double *padfGeoTransform)
    1084             : {
    1085           4 :     memcpy(padfGeoTransform, m_adfGeoTransform.data(),
    1086             :            sizeof(m_adfGeoTransform));
    1087           4 :     return m_bHasGeoTransform ? CE_None : CE_Failure;
    1088             : }
    1089             : 
    1090             : /************************************************************************/
    1091             : /*                         GetSpatialRef()                              */
    1092             : /************************************************************************/
    1093             : 
    1094           4 : const OGRSpatialReference *OGROpenFileGDBDataSource::GetSpatialRef() const
    1095             : {
    1096           4 :     return m_oRasterSRS.IsEmpty() ? nullptr : &m_oRasterSRS;
    1097             : }
    1098             : 
    1099             : /************************************************************************/
    1100             : /*                     GDALOpenFileGDBRasterBand()                      */
    1101             : /************************************************************************/
    1102             : 
    1103         175 : GDALOpenFileGDBRasterBand::GDALOpenFileGDBRasterBand(
    1104             :     OGROpenFileGDBDataSource *poDSIn, int nBandIn, GDALDataType eDT,
    1105             :     int nBitWidth, int nBlockWidth, int nBlockHeight, int nOverviewLevel,
    1106         175 :     bool bIsMask)
    1107             :     : m_nBitWidth(nBitWidth), m_nOverviewLevel(nOverviewLevel),
    1108         175 :       m_bIsMask(bIsMask)
    1109             : {
    1110         175 :     poDS = poDSIn;
    1111         175 :     nBand = nBandIn;
    1112         175 :     eDataType = eDT;
    1113         175 :     nRasterXSize = std::max(1, poDSIn->GetRasterXSize() >> nOverviewLevel);
    1114         175 :     nRasterYSize = std::max(1, poDSIn->GetRasterYSize() >> nOverviewLevel);
    1115         175 :     nBlockXSize = nBlockWidth;
    1116         175 :     nBlockYSize = nBlockHeight;
    1117         175 :     if (nBitWidth < 8)
    1118             :     {
    1119           8 :         SetMetadataItem("NBITS", CPLSPrintf("%d", nBitWidth),
    1120             :                         "IMAGE_STRUCTURE");
    1121             :     }
    1122         175 : }
    1123             : 
    1124             : /************************************************************************/
    1125             : /*                         SetNoDataFromMask()                          */
    1126             : /************************************************************************/
    1127             : 
    1128             : template <class T>
    1129           0 : static void SetNoDataFromMask(void *pImage, const GByte *pabyMask,
    1130             :                               size_t nPixels, double dfNoData)
    1131             : {
    1132           0 :     const T noData = static_cast<T>(dfNoData);
    1133           0 :     const T noDataReplacement =
    1134           0 :         noData == std::numeric_limits<T>::max() ? noData - 1 : noData + 1;
    1135           0 :     bool bHasWarned = false;
    1136           0 :     for (size_t i = 0; i < nPixels; ++i)
    1137             :     {
    1138           0 :         if (pabyMask && !(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1139             :         {
    1140           0 :             static_cast<T *>(pImage)[i] = noData;
    1141             :         }
    1142           0 :         else if (static_cast<T *>(pImage)[i] == noData)
    1143             :         {
    1144           0 :             static_cast<T *>(pImage)[i] = noDataReplacement;
    1145           0 :             if (!bHasWarned)
    1146             :             {
    1147           0 :                 bHasWarned = true;
    1148           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1149             :                          "Valid data found with value equal to nodata (%.0f). "
    1150             :                          "Got substituted with %.0f",
    1151             :                          static_cast<double>(noData),
    1152             :                          static_cast<double>(noDataReplacement));
    1153             :             }
    1154             :         }
    1155             :     }
    1156           0 : }
    1157             : 
    1158             : /************************************************************************/
    1159             : /*                         IReadBlock()                                 */
    1160             : /************************************************************************/
    1161             : 
    1162         118 : CPLErr GDALOpenFileGDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1163             :                                              void *pImage)
    1164             : {
    1165         118 :     auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
    1166         118 :     auto &poLyr = poGDS->m_poBlkLayer;
    1167             : 
    1168             :     // Return (pointer to image data, owner block). Works when called from main band
    1169             :     // or mask band. owner block must be DropLock() once done (if not null)
    1170         238 :     const auto GetImageData = [this, nBlockXOff, nBlockYOff, pImage]()
    1171             :     {
    1172         118 :         void *pImageData = nullptr;
    1173         118 :         GDALRasterBlock *poBlock = nullptr;
    1174         118 :         if (m_bIsMask)
    1175             :         {
    1176           1 :             CPLAssert(m_poMainBand);
    1177           1 :             poBlock =
    1178           1 :                 m_poMainBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1179           1 :             if (poBlock)
    1180             :             {
    1181             :                 // The block is already in cache. Return (null, null)
    1182           0 :                 poBlock->DropLock();
    1183           0 :                 poBlock = nullptr;
    1184             :             }
    1185             :             else
    1186             :             {
    1187           1 :                 poBlock = m_poMainBand->GetLockedBlockRef(nBlockXOff,
    1188             :                                                           nBlockYOff, true);
    1189           1 :                 if (poBlock)
    1190           1 :                     pImageData = poBlock->GetDataRef();
    1191             :             }
    1192             :         }
    1193             :         else
    1194             :         {
    1195         117 :             pImageData = pImage;
    1196             :         }
    1197         236 :         return std::make_pair(pImageData, poBlock);
    1198         118 :     };
    1199             : 
    1200             :     // Return (pointer to mask data, owner block). Works when called from main band
    1201             :     // or mask band. owner block must be DropLock() once done (if not null)
    1202         404 :     const auto GetMaskData = [this, nBlockXOff, nBlockYOff, pImage]()
    1203             :     {
    1204         114 :         void *pMaskData = nullptr;
    1205         114 :         GDALRasterBlock *poBlock = nullptr;
    1206         114 :         if (m_bIsMask)
    1207             :         {
    1208           1 :             pMaskData = pImage;
    1209             :         }
    1210             :         else
    1211             :         {
    1212         113 :             CPLAssert(m_poMaskBand);
    1213         113 :             poBlock =
    1214         113 :                 m_poMaskBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1215         113 :             if (poBlock)
    1216             :             {
    1217             :                 // The block is already in cache. Return (null, null)
    1218          50 :                 poBlock->DropLock();
    1219          50 :                 poBlock = nullptr;
    1220             :             }
    1221             :             else
    1222             :             {
    1223          63 :                 poBlock = m_poMaskBand->GetLockedBlockRef(nBlockXOff,
    1224             :                                                           nBlockYOff, true);
    1225          63 :                 if (poBlock)
    1226          63 :                     pMaskData = poBlock->GetDataRef();
    1227             :             }
    1228             :         }
    1229         228 :         return std::make_pair(pMaskData, poBlock);
    1230         118 :     };
    1231             : 
    1232             :     const GDALDataType eImageDT =
    1233         118 :         m_poMainBand ? m_poMainBand->GetRasterDataType() : eDataType;
    1234         118 :     const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1235             : 
    1236             :     const auto FillMissingBlock =
    1237           6 :         [this, eImageDT, nPixels, &GetImageData, &GetMaskData]()
    1238             :     {
    1239             :         // Set image data to nodata / 0
    1240             :         {
    1241           1 :             auto imageDataAndBlock = GetImageData();
    1242           1 :             auto pImageData = imageDataAndBlock.first;
    1243           1 :             auto poBlock = imageDataAndBlock.second;
    1244           1 :             if (pImageData)
    1245             :             {
    1246           1 :                 const int nDTSize = GDALGetDataTypeSizeBytes(eImageDT);
    1247           1 :                 if (m_bHasNoData)
    1248             :                 {
    1249           1 :                     GDALCopyWords64(&m_dfNoData, GDT_Float64, 0, pImageData,
    1250             :                                     eImageDT, nDTSize, nPixels);
    1251             :                 }
    1252             :                 else
    1253             :                 {
    1254           0 :                     memset(pImageData, 0, nPixels * nDTSize);
    1255             :                 }
    1256             :             }
    1257           1 :             if (poBlock)
    1258           0 :                 poBlock->DropLock();
    1259             :         }
    1260             : 
    1261             :         // Set mask band to 0 (when it exists)
    1262           1 :         if (m_poMaskBand || m_bIsMask)
    1263             :         {
    1264           0 :             auto maskDataAndBlock = GetMaskData();
    1265           0 :             auto pMaskData = maskDataAndBlock.first;
    1266           0 :             auto poBlock = maskDataAndBlock.second;
    1267           0 :             if (pMaskData)
    1268             :             {
    1269           0 :                 const size_t nSize =
    1270           0 :                     static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1271           0 :                 memset(pMaskData, 0, nSize);
    1272             :             }
    1273           0 :             if (poBlock)
    1274           0 :                 poBlock->DropLock();
    1275             :         }
    1276           1 :     };
    1277             : 
    1278             :     // Fetch block data from fras_blk_XXX layer
    1279         118 :     const int nGDALBandId = m_bIsMask ? 1 : nBand;
    1280         118 :     auto oIter = poGDS->m_oMapGDALBandToGDBBandId.find(nGDALBandId);
    1281         118 :     if (oIter == poGDS->m_oMapGDALBandToGDBBandId.end())
    1282             :     {
    1283           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1284             :                  "poGDS->m_oMapGDALBandToGDBBandId.find(%d) failed",
    1285             :                  nGDALBandId);
    1286           0 :         return CE_Failure;
    1287             :     }
    1288         118 :     const int nGDBRasterBandId = oIter->second;
    1289             : 
    1290         236 :     CPLString osFilter;
    1291             :     /* osFilter.Printf("rasterband_id = %d AND rrd_factor = %d AND row_nbr = %d "
    1292             :                     "AND col_nbr = %d",
    1293             :                     nGDBRasterBandId,
    1294             :                     m_nOverviewLevel, nBlockYOff, nBlockXOff);
    1295             :     */
    1296         118 :     const int nColNbr = nBlockXOff + poGDS->m_nShiftBlockX;
    1297         118 :     const int nRowNbr = nBlockYOff + poGDS->m_nShiftBlockY;
    1298         118 :     if (nRowNbr >= 0 && nColNbr >= 0)
    1299             :     {
    1300         118 :         osFilter.Printf("block_key = '0000%04X%02X%04X%04X'", nGDBRasterBandId,
    1301         118 :                         m_nOverviewLevel, nRowNbr, nColNbr);
    1302             :     }
    1303           0 :     else if (nRowNbr < 0 && nColNbr >= 0)
    1304             :     {
    1305           0 :         osFilter.Printf("block_key = '0000%04X%02X-%04X%04X'", nGDBRasterBandId,
    1306           0 :                         m_nOverviewLevel, -nRowNbr, nColNbr);
    1307             :     }
    1308           0 :     else if (nRowNbr >= 0 && nColNbr < 0)
    1309             :     {
    1310           0 :         osFilter.Printf("block_key = '0000%04X%02X%04X-%04X'", nGDBRasterBandId,
    1311           0 :                         m_nOverviewLevel, nRowNbr, -nColNbr);
    1312             :     }
    1313             :     else /* if( nRowNbr < 0 && nColNbr < 0 ) */
    1314             :     {
    1315             :         osFilter.Printf("block_key = '0000%04X%02X-%04X-%04X'",
    1316           0 :                         nGDBRasterBandId, m_nOverviewLevel, -nRowNbr, -nColNbr);
    1317             :     }
    1318             :     // CPLDebug("OpenFileGDB", "Request %s", osFilter.c_str());
    1319         118 :     poLyr->SetAttributeFilter(osFilter.c_str());
    1320         236 :     auto poFeature = std::unique_ptr<OGRFeature>(poLyr->GetNextFeature());
    1321         118 :     const int nImageDTSize = GDALGetDataTypeSizeBytes(eImageDT);
    1322         118 :     if (!poFeature)
    1323             :     {
    1324             :         // Missing blocks are legit
    1325           1 :         FillMissingBlock();
    1326           1 :         return CE_None;
    1327             :     }
    1328         117 :     const int nFieldIdx = poFeature->GetFieldIndex("block_data");
    1329         117 :     CPLAssert(nFieldIdx >= 0);
    1330         117 :     int nInBytes = 0;
    1331         117 :     if (!poFeature->IsFieldSetAndNotNull(nFieldIdx))
    1332             :     {
    1333             :         // block_data unset found on ForestFalls.gdb
    1334           0 :         FillMissingBlock();
    1335           0 :         return CE_None;
    1336             :     }
    1337         117 :     const GByte *pabyData = poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
    1338         117 :     if (nInBytes == 0)
    1339             :     {
    1340           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Image block is empty");
    1341           0 :         return CE_Failure;
    1342             :     }
    1343             : 
    1344             :     // The input buffer may be concatenated with a 1-bit binary mask
    1345         117 :     const size_t nImageSize = nPixels * nImageDTSize;
    1346         117 :     const int nImageBitWidth =
    1347         117 :         m_poMainBand ? m_poMainBand->m_nBitWidth : m_nBitWidth;
    1348         117 :     const size_t nImageSizePacked = (nPixels * nImageBitWidth + 7) / 8;
    1349         117 :     const size_t nBinaryMaskSize = (nPixels + 7) / 8;
    1350         117 :     const size_t nImageSizeWithBinaryMask = nImageSizePacked + nBinaryMaskSize;
    1351             : 
    1352             :     // Unpack 1-bit, 2-bit, 4-bit data to full byte
    1353             :     const auto ExpandSubByteData =
    1354     2175500 :         [nPixels, nImageBitWidth](const GByte *pabyInput, void *pDstBuffer)
    1355             :     {
    1356          24 :         CPLAssert(nImageBitWidth < 8);
    1357             : 
    1358          24 :         size_t iBitOffset = 0;
    1359      393240 :         for (size_t i = 0; i < nPixels; ++i)
    1360             :         {
    1361      393216 :             unsigned nOutWord = 0;
    1362             : 
    1363     1523710 :             for (int iBit = 0; iBit < nImageBitWidth; ++iBit)
    1364             :             {
    1365     1130500 :                 if (pabyInput[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
    1366             :                 {
    1367      258524 :                     nOutWord |= (1 << (nImageBitWidth - 1 - iBit));
    1368             :                 }
    1369     1130500 :                 ++iBitOffset;
    1370             :             }
    1371             : 
    1372      393216 :             static_cast<GByte *>(pDstBuffer)[i] = static_cast<GByte>(nOutWord);
    1373             :         }
    1374         141 :     };
    1375             : 
    1376         117 :     const GByte *pabyMask = nullptr;
    1377         116 :     auto &abyTmpBuffer =
    1378         117 :         m_poMainBand ? m_poMainBand->m_abyTmpBuffer : m_abyTmpBuffer;
    1379             : 
    1380         117 :     switch (poGDS->m_eRasterCompression)
    1381             :     {
    1382           6 :         case OGROpenFileGDBDataSource::Compression::NONE:
    1383             :         {
    1384           6 :             if (static_cast<unsigned>(nInBytes) != nImageSizePacked &&
    1385           6 :                 static_cast<unsigned>(nInBytes) != nImageSizeWithBinaryMask)
    1386             :             {
    1387           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1388             :                          "Not expected number of input bytes: %d", nInBytes);
    1389           0 :                 return CE_Failure;
    1390             :             }
    1391             : 
    1392           6 :             auto imageDataAndBlock = GetImageData();
    1393           6 :             auto pImageData = imageDataAndBlock.first;
    1394           6 :             auto poBlock = imageDataAndBlock.second;
    1395             : 
    1396           6 :             if (pImageData)
    1397             :             {
    1398           6 :                 if (nImageSizePacked == nImageSize)
    1399             :                 {
    1400           6 :                     memcpy(pImageData, pabyData, nImageSize);
    1401             : #ifdef CPL_LSB
    1402           6 :                     if (nImageDTSize > 1)
    1403             :                     {
    1404           5 :                         GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
    1405             :                                         nImageDTSize);
    1406             :                     }
    1407             : #endif
    1408             :                 }
    1409             :                 else
    1410             :                 {
    1411           0 :                     ExpandSubByteData(pabyData, pImageData);
    1412             :                 }
    1413             :             }
    1414           6 :             if (poBlock)
    1415           0 :                 poBlock->DropLock();
    1416             : 
    1417           6 :             if (static_cast<unsigned>(nInBytes) == nImageSizeWithBinaryMask)
    1418           6 :                 pabyMask = pabyData + nImageSizePacked;
    1419           6 :             break;
    1420             :         }
    1421             : 
    1422          63 :         case OGROpenFileGDBDataSource::Compression::LZ77:
    1423             :         {
    1424          63 :             if (abyTmpBuffer.empty())
    1425             :             {
    1426             :                 try
    1427             :                 {
    1428          18 :                     abyTmpBuffer.resize(nImageSizeWithBinaryMask);
    1429             :                 }
    1430           0 :                 catch (const std::bad_alloc &e)
    1431             :                 {
    1432           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1433           0 :                     return CE_Failure;
    1434             :                 }
    1435             :             }
    1436             : 
    1437          63 :             size_t nOutBytes = 0;
    1438          63 :             GByte *outPtr = abyTmpBuffer.data();
    1439          63 :             assert(outPtr != nullptr);  // For Coverity Scan
    1440          63 :             if (!CPLZLibInflate(pabyData, nInBytes, outPtr, abyTmpBuffer.size(),
    1441         126 :                                 &nOutBytes) ||
    1442          63 :                 !(nOutBytes == nImageSizePacked ||
    1443          48 :                   nOutBytes == nImageSizeWithBinaryMask))
    1444             :             {
    1445           0 :                 CPLError(
    1446             :                     CE_Failure, CPLE_AppDefined,
    1447             :                     "CPLZLibInflate() failed: nInBytes = %u, nOutBytes = %u, "
    1448             :                     "nImageSizePacked = %u, "
    1449             :                     "nImageSizeWithBinaryMask = %u",
    1450             :                     unsigned(nInBytes), unsigned(nOutBytes),
    1451             :                     unsigned(nImageSizePacked),
    1452             :                     unsigned(nImageSizeWithBinaryMask));
    1453           0 :                 return CE_Failure;
    1454             :             }
    1455             : 
    1456          63 :             auto imageDataAndBlock = GetImageData();
    1457          63 :             auto pImageData = imageDataAndBlock.first;
    1458          63 :             auto poBlock = imageDataAndBlock.second;
    1459             : 
    1460          63 :             if (pImageData)
    1461             :             {
    1462          63 :                 if (nImageSizePacked == nImageSize)
    1463             :                 {
    1464          39 :                     memcpy(pImageData, abyTmpBuffer.data(), nImageSize);
    1465             : #ifdef CPL_LSB
    1466          39 :                     if (nImageDTSize > 1)
    1467             :                     {
    1468           6 :                         GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
    1469             :                                         nImageDTSize);
    1470             :                     }
    1471             : #endif
    1472             :                 }
    1473             :                 else
    1474             :                 {
    1475          24 :                     ExpandSubByteData(abyTmpBuffer.data(), pImageData);
    1476             :                 }
    1477             :             }
    1478          63 :             if (poBlock)
    1479           1 :                 poBlock->DropLock();
    1480             : 
    1481          63 :             if (nOutBytes == nImageSizeWithBinaryMask)
    1482          48 :                 pabyMask = abyTmpBuffer.data() + nImageSizePacked;
    1483          63 :             break;
    1484             :         }
    1485             : 
    1486          24 :         case OGROpenFileGDBDataSource::Compression::JPEG:
    1487             :         {
    1488          24 :             if (GDALGetDriverByName("JPEG") == nullptr)
    1489             :             {
    1490           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "JPEG driver missing");
    1491           0 :                 return CE_Failure;
    1492             :             }
    1493             : 
    1494          24 :             if (static_cast<unsigned>(nInBytes) < 5)
    1495             :             {
    1496           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1497             :                          "Not expected number of input bytes: %d", nInBytes);
    1498           0 :                 return CE_Failure;
    1499             :             }
    1500          24 :             uint32_t nJPEGSize = nInBytes - 1;
    1501          24 :             uint32_t nJPEGOffset = 1;
    1502          24 :             if (pabyData[0] == 0xFE)
    1503             :             {
    1504             :                 // JPEG followed by binary mask
    1505          15 :                 memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
    1506          15 :                 CPL_LSBPTR32(&nJPEGSize);
    1507          15 :                 if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
    1508             :                 {
    1509           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1510             :                              "Invalid nJPEGSize = %u", nJPEGSize);
    1511           0 :                     return CE_Failure;
    1512             :                 }
    1513          15 :                 nJPEGOffset = 5;
    1514             : 
    1515          15 :                 if (abyTmpBuffer.empty())
    1516             :                 {
    1517             :                     try
    1518             :                     {
    1519           3 :                         abyTmpBuffer.resize(nBinaryMaskSize);
    1520             :                     }
    1521           0 :                     catch (const std::bad_alloc &e)
    1522             :                     {
    1523           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1524           0 :                         return CE_Failure;
    1525             :                     }
    1526             :                 }
    1527          15 :                 size_t nOutBytes = 0;
    1528          15 :                 GByte *outPtr = abyTmpBuffer.data();
    1529          15 :                 assert(outPtr != nullptr);  // For Coverity Scan
    1530          45 :                 if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
    1531          15 :                                    nInBytes - 5 - nJPEGSize, outPtr,
    1532          30 :                                    nBinaryMaskSize, &nOutBytes) &&
    1533          15 :                     nOutBytes == nBinaryMaskSize)
    1534             :                 {
    1535          15 :                     pabyMask = abyTmpBuffer.data();
    1536             :                 }
    1537             :                 else
    1538             :                 {
    1539           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1540             :                              "Cannot decompress binary mask");
    1541             :                 }
    1542             :             }
    1543           9 :             else if (pabyData[0] != 1)
    1544             :             {
    1545           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG blob");
    1546           0 :                 return CE_Failure;
    1547             :             }
    1548             : 
    1549          24 :             VSILFILE *fp = VSIFOpenL("tmp.jpg", "wb");
    1550          24 :             VSIFWriteL(pabyData + nJPEGOffset, nJPEGSize, 1, fp);
    1551          24 :             VSIFCloseL(fp);
    1552             : 
    1553          24 :             CPLString osTmpFilename;
    1554          24 :             osTmpFilename.Printf("/vsimem/_openfilegdb/%p.jpg", this);
    1555          24 :             VSIFCloseL(VSIFileFromMemBuffer(
    1556             :                 osTmpFilename.c_str(),
    1557          24 :                 const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
    1558          24 :             const char *const apszDrivers[] = {"JPEG", nullptr};
    1559             :             auto poJPEGDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1560          24 :                 osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
    1561          24 :             if (!poJPEGDS)
    1562             :             {
    1563           0 :                 VSIUnlink(osTmpFilename.c_str());
    1564           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot open JPEG blob");
    1565           0 :                 return CE_Failure;
    1566             :             }
    1567          24 :             if (poJPEGDS->GetRasterCount() != 1 ||
    1568          48 :                 poJPEGDS->GetRasterXSize() != nBlockXSize ||
    1569          24 :                 poJPEGDS->GetRasterYSize() != nBlockYSize)
    1570             :             {
    1571           0 :                 VSIUnlink(osTmpFilename.c_str());
    1572           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1573             :                          "Inconsistent characteristics of JPEG blob");
    1574           0 :                 return CE_Failure;
    1575             :             }
    1576             : 
    1577          24 :             auto imageDataAndBlock = GetImageData();
    1578          24 :             auto pImageData = imageDataAndBlock.first;
    1579          24 :             auto poBlock = imageDataAndBlock.second;
    1580             : 
    1581             :             const CPLErr eErr =
    1582             :                 pImageData
    1583          24 :                     ? poJPEGDS->GetRasterBand(1)->RasterIO(
    1584             :                           GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
    1585             :                           nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
    1586          24 :                     : CE_None;
    1587          24 :             VSIUnlink(osTmpFilename.c_str());
    1588          24 :             if (poBlock)
    1589           0 :                 poBlock->DropLock();
    1590             : 
    1591          24 :             if (eErr != CE_None)
    1592             :             {
    1593           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot read JPEG blob");
    1594           0 :                 return CE_Failure;
    1595             :             }
    1596             : 
    1597          24 :             break;
    1598             :         }
    1599             : 
    1600          24 :         case OGROpenFileGDBDataSource::Compression::JPEG2000:
    1601             :         {
    1602          24 :             const char *const apszDrivers[] = {"JP2KAK",      "JP2ECW",
    1603             :                                                "JP2OpenJPEG", "JP2MrSID",
    1604             :                                                "JP2Lura",     nullptr};
    1605          24 :             bool bFoundJP2Driver = false;
    1606          48 :             for (const char *pszDriver : apszDrivers)
    1607             :             {
    1608          48 :                 if (pszDriver && GDALGetDriverByName(pszDriver))
    1609             :                 {
    1610          24 :                     bFoundJP2Driver = true;
    1611          24 :                     break;
    1612             :                 }
    1613             :             }
    1614          24 :             if (!bFoundJP2Driver)
    1615             :             {
    1616           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1617             :                          "Did not find any JPEG2000 capable driver");
    1618           0 :                 return CE_Failure;
    1619             :             }
    1620             : 
    1621          24 :             if (static_cast<unsigned>(nInBytes) < 5)
    1622             :             {
    1623           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1624             :                          "Not expected number of input bytes: %d", nInBytes);
    1625           0 :                 return CE_Failure;
    1626             :             }
    1627          24 :             uint32_t nJPEGSize = nInBytes - 1;
    1628          24 :             uint32_t nJPEGOffset = 1;
    1629          24 :             if (pabyData[0] == 0xFF)
    1630             :             {
    1631             :                 // JPEG2000 followed by binary mask
    1632          15 :                 memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
    1633          15 :                 CPL_LSBPTR32(&nJPEGSize);
    1634          15 :                 if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
    1635             :                 {
    1636           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1637             :                              "Invalid nJPEGSize = %u", nJPEGSize);
    1638           0 :                     return CE_Failure;
    1639             :                 }
    1640          15 :                 nJPEGOffset = 5;
    1641             : 
    1642          15 :                 if (abyTmpBuffer.empty())
    1643             :                 {
    1644             :                     try
    1645             :                     {
    1646           3 :                         abyTmpBuffer.resize(nBinaryMaskSize);
    1647             :                     }
    1648           0 :                     catch (const std::bad_alloc &e)
    1649             :                     {
    1650           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1651           0 :                         return CE_Failure;
    1652             :                     }
    1653             :                 }
    1654          15 :                 size_t nOutBytes = 0;
    1655          15 :                 GByte *outPtr = abyTmpBuffer.data();
    1656          15 :                 assert(outPtr != nullptr);  // For Coverity Scan
    1657          45 :                 if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
    1658          15 :                                    nInBytes - 5 - nJPEGSize, outPtr,
    1659          30 :                                    nBinaryMaskSize, &nOutBytes) &&
    1660          15 :                     nOutBytes == nBinaryMaskSize)
    1661             :                 {
    1662          15 :                     pabyMask = outPtr;
    1663             :                 }
    1664             :                 else
    1665             :                 {
    1666           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1667             :                              "Cannot decompress binary mask");
    1668             :                 }
    1669             :             }
    1670           9 :             else if (pabyData[0] != 0)
    1671             :             {
    1672           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG2000 blob");
    1673           0 :                 return CE_Failure;
    1674             :             }
    1675             : 
    1676          24 :             CPLString osTmpFilename;
    1677          24 :             osTmpFilename.Printf("/vsimem/_openfilegdb/%p.j2k", this);
    1678          24 :             VSIFCloseL(VSIFileFromMemBuffer(
    1679             :                 osTmpFilename.c_str(),
    1680          24 :                 const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
    1681             :             auto poJP2KDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1682          24 :                 osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
    1683          24 :             if (!poJP2KDS)
    1684             :             {
    1685           0 :                 VSIUnlink(osTmpFilename.c_str());
    1686           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1687             :                          "Cannot open JPEG2000 blob");
    1688           0 :                 return CE_Failure;
    1689             :             }
    1690          24 :             if (poJP2KDS->GetRasterCount() != 1 ||
    1691          48 :                 poJP2KDS->GetRasterXSize() != nBlockXSize ||
    1692          24 :                 poJP2KDS->GetRasterYSize() != nBlockYSize)
    1693             :             {
    1694           0 :                 VSIUnlink(osTmpFilename.c_str());
    1695           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1696             :                          "Inconsistent characteristics of JPEG2000 blob");
    1697           0 :                 return CE_Failure;
    1698             :             }
    1699             : 
    1700          24 :             auto imageDataAndBlock = GetImageData();
    1701          24 :             auto pImageData = imageDataAndBlock.first;
    1702          24 :             auto poBlock = imageDataAndBlock.second;
    1703             : 
    1704             :             const CPLErr eErr =
    1705             :                 pImageData
    1706          24 :                     ? poJP2KDS->GetRasterBand(1)->RasterIO(
    1707             :                           GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
    1708             :                           nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
    1709          24 :                     : CE_None;
    1710          24 :             VSIUnlink(osTmpFilename.c_str());
    1711          24 :             if (poBlock)
    1712           0 :                 poBlock->DropLock();
    1713             : 
    1714          24 :             if (eErr != CE_None)
    1715             :             {
    1716           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1717             :                          "Cannot read JPEG2000 blob");
    1718           0 :                 return CE_Failure;
    1719             :             }
    1720             : 
    1721          24 :             break;
    1722             :         }
    1723             :     }
    1724             : 
    1725         117 :     if (m_bIsMask || m_poMaskBand)
    1726             :     {
    1727         114 :         auto maskDataAndBlock = GetMaskData();
    1728         114 :         auto pMaskData = maskDataAndBlock.first;
    1729         114 :         auto poBlock = maskDataAndBlock.second;
    1730             : 
    1731         114 :         if (pMaskData)
    1732             :         {
    1733          64 :             if (pabyMask)
    1734             :             {
    1735             :                 // Unpack 1-bit array
    1736      802865 :                 for (size_t i = 0; i < nPixels; ++i)
    1737             :                 {
    1738      802816 :                     static_cast<GByte *>(pMaskData)[i] =
    1739      802816 :                         (pabyMask[i / 8] & (0x80 >> (i & 7))) ? 255 : 0;
    1740             :                 }
    1741             :             }
    1742             :             else
    1743             :             {
    1744             :                 // No explicit mask in source block --> all valid
    1745          15 :                 memset(pMaskData, 255, nPixels);
    1746             :             }
    1747             :         }
    1748             : 
    1749         114 :         if (poBlock)
    1750         114 :             poBlock->DropLock();
    1751             :     }
    1752           3 :     else if (m_bHasNoData)
    1753             :     {
    1754           3 :         if (eImageDT == GDT_Byte)
    1755             :         {
    1756           0 :             SetNoDataFromMask<uint8_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1757             :         }
    1758           3 :         else if (eImageDT == GDT_Int8)
    1759             :         {
    1760           0 :             SetNoDataFromMask<int8_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1761             :         }
    1762           3 :         else if (eImageDT == GDT_UInt16)
    1763             :         {
    1764           0 :             SetNoDataFromMask<uint16_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1765             :         }
    1766           3 :         else if (eImageDT == GDT_Int16)
    1767             :         {
    1768           0 :             SetNoDataFromMask<int16_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1769             :         }
    1770           3 :         else if (eImageDT == GDT_UInt32)
    1771             :         {
    1772           0 :             SetNoDataFromMask<uint32_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1773             :         }
    1774           3 :         else if (eImageDT == GDT_Int32)
    1775             :         {
    1776           0 :             SetNoDataFromMask<int32_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1777             :         }
    1778           3 :         else if (eImageDT == GDT_Float32)
    1779             :         {
    1780           2 :             if (pabyMask)
    1781             :             {
    1782       32770 :                 for (size_t i = 0; i < nPixels; ++i)
    1783             :                 {
    1784       32768 :                     if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1785             :                     {
    1786       31968 :                         static_cast<float *>(pImage)[i] =
    1787       31968 :                             static_cast<float>(m_dfNoData);
    1788             :                     }
    1789             :                 }
    1790             :             }
    1791             :         }
    1792           1 :         else if (eImageDT == GDT_Float64)
    1793             :         {
    1794           1 :             if (pabyMask)
    1795             :             {
    1796       16385 :                 for (size_t i = 0; i < nPixels; ++i)
    1797             :                 {
    1798       16384 :                     if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1799             :                     {
    1800       15984 :                         static_cast<double *>(pImage)[i] = m_dfNoData;
    1801             :                     }
    1802             :                 }
    1803             :             }
    1804             :         }
    1805             :         else
    1806             :         {
    1807           0 :             CPLAssert(false);
    1808             :         }
    1809             :     }
    1810             : 
    1811             : #if 0
    1812             :     printf("Data:\n"); // ok
    1813             :     if (eDataType == GDT_Byte)
    1814             :     {
    1815             :         for (int y = 0; y < nBlockYSize; ++y)
    1816             :         {
    1817             :             for (int x = 0; x < nBlockXSize; ++x)
    1818             :             {
    1819             :                 printf("%d ", // ok
    1820             :                        static_cast<GByte *>(pImage)[y * nBlockXSize + x]);
    1821             :             }
    1822             :             printf("\n"); // ok
    1823             :         }
    1824             :     }
    1825             :     else if (eDataType == GDT_Int8)
    1826             :     {
    1827             :         for (int y = 0; y < nBlockYSize; ++y)
    1828             :         {
    1829             :             for (int x = 0; x < nBlockXSize; ++x)
    1830             :             {
    1831             :                 printf("%d ", // ok
    1832             :                        static_cast<int8_t *>(pImage)[y * nBlockXSize + x]);
    1833             :             }
    1834             :             printf("\n"); // ok
    1835             :         }
    1836             :     }
    1837             :     else if (eDataType == GDT_UInt16)
    1838             :     {
    1839             :         for (int y = 0; y < nBlockYSize; ++y)
    1840             :         {
    1841             :             for (int x = 0; x < nBlockXSize; ++x)
    1842             :             {
    1843             :                 printf("%d ", // ok
    1844             :                        static_cast<uint16_t *>(pImage)[y * nBlockXSize + x]);
    1845             :             }
    1846             :             printf("\n"); // ok
    1847             :         }
    1848             :     }
    1849             :     else if (eDataType == GDT_Int16)
    1850             :     {
    1851             :         for (int y = 0; y < nBlockYSize; ++y)
    1852             :         {
    1853             :             for (int x = 0; x < nBlockXSize; ++x)
    1854             :             {
    1855             :                 printf("%d ", // ok
    1856             :                        static_cast<int16_t *>(pImage)[y * nBlockXSize + x]);
    1857             :             }
    1858             :             printf("\n"); // ok
    1859             :         }
    1860             :     }
    1861             :     else if (eDataType == GDT_UInt32)
    1862             :     {
    1863             :         for (int y = 0; y < nBlockYSize; ++y)
    1864             :         {
    1865             :             for (int x = 0; x < nBlockXSize; ++x)
    1866             :             {
    1867             :                 printf("%d ", // ok
    1868             :                        static_cast<uint32_t *>(pImage)[y * nBlockXSize + x]);
    1869             :             }
    1870             :             printf("\n"); // ok
    1871             :         }
    1872             :     }
    1873             :     else if (eDataType == GDT_Int32)
    1874             :     {
    1875             :         for (int y = 0; y < nBlockYSize; ++y)
    1876             :         {
    1877             :             for (int x = 0; x < nBlockXSize; ++x)
    1878             :             {
    1879             :                 printf("%d ", // ok
    1880             :                        static_cast<int32_t *>(pImage)[y * nBlockXSize + x]);
    1881             :             }
    1882             :             printf("\n"); // ok
    1883             :         }
    1884             :     }
    1885             :     else if (eDataType == GDT_Float32)
    1886             :     {
    1887             :         for (int y = 0; y < nBlockYSize; ++y)
    1888             :         {
    1889             :             for (int x = 0; x < nBlockXSize; ++x)
    1890             :             {
    1891             :                 printf("%.8g ", // ok
    1892             :                        static_cast<float *>(pImage)[y * nBlockXSize + x]);
    1893             :             }
    1894             :             printf("\n"); // ok
    1895             :         }
    1896             :     }
    1897             :     else if (eDataType == GDT_Float64)
    1898             :     {
    1899             :         for (int y = 0; y < nBlockYSize; ++y)
    1900             :         {
    1901             :             for (int x = 0; x < nBlockXSize; ++x)
    1902             :             {
    1903             :                 printf("%.18g ", // ok
    1904             :                        static_cast<double *>(pImage)[y * nBlockXSize + x]);
    1905             :             }
    1906             :             printf("\n"); // ok
    1907             :         }
    1908             :     }
    1909             : #endif
    1910             : 
    1911             : #if 0
    1912             :     if (pabyMask)
    1913             :     {
    1914             :         printf("Mask:\n"); // ok
    1915             :         for (int y = 0; y < nBlockYSize; ++y)
    1916             :         {
    1917             :             for (int x = 0; x < nBlockXSize; ++x)
    1918             :             {
    1919             :                 printf("%d ", // ok
    1920             :                        (pabyMask[(y * nBlockXSize + x) / 8] &
    1921             :                         (0x80 >> ((y * nBlockXSize + x) & 7)))
    1922             :                            ? 1
    1923             :                            : 0);
    1924             :             }
    1925             :             printf("\n"); // ok
    1926             :         }
    1927             :     }
    1928             : #endif
    1929             : 
    1930         117 :     return CE_None;
    1931             : }
    1932             : 
    1933             : /************************************************************************/
    1934             : /*                         GetDefaultRAT()                              */
    1935             : /************************************************************************/
    1936             : 
    1937           3 : GDALRasterAttributeTable *GDALOpenFileGDBRasterBand::GetDefaultRAT()
    1938             : {
    1939           3 :     if (m_poRAT)
    1940           1 :         return m_poRAT.get();
    1941           2 :     if (poDS->GetRasterCount() > 1 || m_bIsMask)
    1942           0 :         return nullptr;
    1943           2 :     auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
    1944             :     const std::string osVATTableName(
    1945           6 :         std::string("VAT_").append(poGDS->m_osRasterLayerName));
    1946             :     // Instantiate a new dataset, os that the RAT is standalone
    1947           4 :     auto poDSNew = std::make_unique<OGROpenFileGDBDataSource>();
    1948           4 :     GDALOpenInfo oOpenInfo(poGDS->m_osDirName.c_str(), GA_ReadOnly);
    1949           2 :     bool bRetryFileGDBUnused = false;
    1950           2 :     if (!poDSNew->Open(&oOpenInfo, bRetryFileGDBUnused))
    1951           0 :         return nullptr;
    1952           4 :     auto poVatLayer = poDSNew->BuildLayerFromName(osVATTableName.c_str());
    1953           2 :     if (!poVatLayer)
    1954           1 :         return nullptr;
    1955           2 :     m_poRAT = std::make_unique<GDALOpenFileGDBRasterAttributeTable>(
    1956           2 :         std::move(poDSNew), osVATTableName, std::move(poVatLayer));
    1957           1 :     return m_poRAT.get();
    1958             : }

Generated by: LCOV version 1.14