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

Generated by: LCOV version 1.14