LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - gdalopenfilegdbrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 676 927 72.9 %
Date: 2025-07-13 21:00:45 Functions: 14 20 70.0 %

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

Generated by: LCOV version 1.14