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

Generated by: LCOV version 1.14