LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - gdalopenfilegdbrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 669 918 72.9 %
Date: 2024-11-21 22:18:42 Functions: 13 19 68.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB raster driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * 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 CPLString osBndFilename(CPLFormFilename(
      56          58 :         m_osDirName, CPLSPrintf("a%08x.gdbtable", nBndIdx), nullptr));
      57          29 :     if (!oTable.Open(osBndFilename, false))
      58             :     {
      59           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open table %s",
      60             :                  osBndTableName.c_str());
      61           0 :         return false;
      62             :     }
      63             : 
      64          29 :     const int i_rasterband_id = oTable.GetFieldIdx("rasterband_id");
      65          29 :     const int i_sequence_nbr = oTable.GetFieldIdx("sequence_nbr");
      66          29 :     const int i_raster_id = oTable.GetFieldIdx("raster_id");
      67          29 :     const int i_band_width = oTable.GetFieldIdx("band_width");
      68          29 :     const int i_band_height = oTable.GetFieldIdx("band_height");
      69          29 :     const int i_band_types = oTable.GetFieldIdx("band_types");
      70          29 :     const int i_block_width = oTable.GetFieldIdx("block_width");
      71          29 :     const int i_block_height = oTable.GetFieldIdx("block_height");
      72          29 :     const int i_block_origin_x = oTable.GetFieldIdx("block_origin_x");
      73          29 :     const int i_block_origin_y = oTable.GetFieldIdx("block_origin_y");
      74          29 :     const int i_eminx = oTable.GetFieldIdx("eminx");
      75          29 :     const int i_eminy = oTable.GetFieldIdx("eminy");
      76          29 :     const int i_emaxx = oTable.GetFieldIdx("emaxx");
      77          29 :     const int i_emaxy = oTable.GetFieldIdx("emaxy");
      78          29 :     const int i_srid = oTable.GetFieldIdx("srid");
      79          29 :     if (i_rasterband_id < 0 || i_sequence_nbr < 0 || i_raster_id < 0 ||
      80          29 :         i_band_width < 0 || i_band_height < 0 || i_band_types < 0 ||
      81          29 :         i_block_width < 0 || i_block_height < 0 || i_block_origin_x < 0 ||
      82          29 :         i_block_origin_y < 0 || i_eminx < 0 || i_eminy < 0 || i_emaxx < 0 ||
      83          58 :         i_emaxy < 0 || i_srid < 0 ||
      84          58 :         oTable.GetField(i_rasterband_id)->GetType() != FGFT_OBJECTID ||
      85          58 :         oTable.GetField(i_sequence_nbr)->GetType() != FGFT_INT32 ||
      86          58 :         oTable.GetField(i_raster_id)->GetType() != FGFT_INT32 ||
      87          58 :         oTable.GetField(i_band_width)->GetType() != FGFT_INT32 ||
      88          58 :         oTable.GetField(i_band_height)->GetType() != FGFT_INT32 ||
      89          58 :         oTable.GetField(i_band_types)->GetType() != FGFT_INT32 ||
      90          58 :         oTable.GetField(i_block_width)->GetType() != FGFT_INT32 ||
      91          58 :         oTable.GetField(i_block_height)->GetType() != FGFT_INT32 ||
      92          58 :         oTable.GetField(i_block_origin_x)->GetType() != FGFT_FLOAT64 ||
      93          58 :         oTable.GetField(i_block_origin_y)->GetType() != FGFT_FLOAT64 ||
      94          58 :         oTable.GetField(i_eminx)->GetType() != FGFT_FLOAT64 ||
      95          58 :         oTable.GetField(i_eminy)->GetType() != FGFT_FLOAT64 ||
      96          58 :         oTable.GetField(i_emaxx)->GetType() != FGFT_FLOAT64 ||
      97          87 :         oTable.GetField(i_emaxy)->GetType() != FGFT_FLOAT64 ||
      98          29 :         oTable.GetField(i_srid)->GetType() != FGFT_INT32)
      99             :     {
     100           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong structure for %s table",
     101             :                  osBndTableName.c_str());
     102           0 :         return false;
     103             :     }
     104             : 
     105          29 :     int64_t iRow = 0;
     106         111 :     while (iRow < oTable.GetTotalRecordCount() &&
     107          41 :            (iRow = oTable.GetAndSelectNextNonEmptyRow(iRow)) >= 0)
     108             :     {
     109          41 :         if (iRow >= INT32_MAX)
     110             :         {
     111           0 :             return false;
     112             :         }
     113          41 :         auto psField = oTable.GetFieldValue(i_raster_id);
     114          41 :         if (!psField)
     115             :         {
     116           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     117             :                      "Cannot read field %s in %s table", "raster_id",
     118             :                      osBndTableName.c_str());
     119           0 :             return false;
     120             :         }
     121          41 :         if (psField->Integer != 1)
     122             :         {
     123           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     124             :                      "Raster with raster_id = %d (!= 1) ignored",
     125           0 :                      psField->Integer);
     126           0 :             continue;
     127             :         }
     128             : 
     129          41 :         const int nGDBRasterBandId = static_cast<int>(iRow) + 1;
     130             : 
     131          41 :         psField = oTable.GetFieldValue(i_sequence_nbr);
     132          41 :         if (!psField)
     133             :         {
     134           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     135             :                      "Cannot read field %s in %s table", "sequence_nbr",
     136             :                      osBndTableName.c_str());
     137           0 :             return false;
     138             :         }
     139          41 :         const int nSequenceNr = psField->Integer;
     140             : 
     141          41 :         m_oMapGDALBandToGDBBandId[nSequenceNr] = nGDBRasterBandId;
     142             : 
     143          41 :         ++iRow;
     144             :     }
     145             : 
     146          29 :     if (m_oMapGDALBandToGDBBandId.empty())
     147             :     {
     148           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record in %s table",
     149             :                  osBndTableName.c_str());
     150           0 :         return false;
     151             :     }
     152             : 
     153          29 :     auto psField = oTable.GetFieldValue(i_band_width);
     154          29 :     if (!psField)
     155             :     {
     156           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     157             :                  "Cannot read field %s in %s table", "band_width",
     158             :                  osBndTableName.c_str());
     159           0 :         return false;
     160             :     }
     161          29 :     int nWidth = psField->Integer;
     162             : 
     163          29 :     psField = oTable.GetFieldValue(i_band_height);
     164          29 :     if (!psField)
     165             :     {
     166           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     167             :                  "Cannot read field %s in %s table", "band_height",
     168             :                  osBndTableName.c_str());
     169           0 :         return false;
     170             :     }
     171          29 :     int nHeight = psField->Integer;
     172             : 
     173          29 :     const int l_nBands = static_cast<int>(m_oMapGDALBandToGDBBandId.size());
     174          58 :     if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
     175          29 :         !GDALCheckBandCount(l_nBands, /*bIsZeroAllowed=*/false))
     176             :     {
     177           0 :         return false;
     178             :     }
     179             : 
     180          29 :     psField = oTable.GetFieldValue(i_block_width);
     181          29 :     if (!psField)
     182             :     {
     183           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     184             :                  "Cannot read field %s in %s table", "block_width",
     185             :                  osBndTableName.c_str());
     186           0 :         return false;
     187             :     }
     188          29 :     const int nBlockWidth = psField->Integer;
     189             : 
     190             :     // 32768 somewhat arbitrary
     191          29 :     if (nBlockWidth <= 0 || nBlockWidth > 32768)
     192             :     {
     193           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
     194             :                  "block_width", osBndTableName.c_str());
     195           0 :         return false;
     196             :     }
     197             : 
     198          29 :     psField = oTable.GetFieldValue(i_block_height);
     199          29 :     if (!psField)
     200             :     {
     201           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     202             :                  "Cannot read field %s in %s table", "block_height",
     203             :                  osBndTableName.c_str());
     204           0 :         return false;
     205             :     }
     206          29 :     const int nBlockHeight = psField->Integer;
     207             : 
     208             :     // 32768 somewhat arbitrary
     209          29 :     if (nBlockHeight <= 0 || nBlockHeight > 32768)
     210             :     {
     211           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s in %s table",
     212             :                  "block_height", osBndTableName.c_str());
     213           0 :         return false;
     214             :     }
     215             : 
     216          29 :     psField = oTable.GetFieldValue(i_band_types);
     217          29 :     if (!psField)
     218             :     {
     219           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     220             :                  "Cannot read field %s in %s table", "band_types",
     221             :                  osBndTableName.c_str());
     222           0 :         return false;
     223             :     }
     224          29 :     const int nBandTypes = psField->Integer;
     225             : 
     226          29 :     psField = oTable.GetFieldValue(i_eminx);
     227          29 :     if (!psField)
     228             :     {
     229           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     230             :                  "Cannot read field %s in %s table", "eminx",
     231             :                  osBndTableName.c_str());
     232           0 :         return false;
     233             :     }
     234          29 :     const double dfMinX = psField->Real;
     235             : 
     236          29 :     psField = oTable.GetFieldValue(i_eminy);
     237          29 :     if (!psField)
     238             :     {
     239           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     240             :                  "Cannot read field %s in %s table", "eminy",
     241             :                  osBndTableName.c_str());
     242           0 :         return false;
     243             :     }
     244          29 :     const double dfMinY = psField->Real;
     245             : 
     246          29 :     psField = oTable.GetFieldValue(i_emaxx);
     247          29 :     if (!psField)
     248             :     {
     249           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     250             :                  "Cannot read field %s in %s table", "emaxx",
     251             :                  osBndTableName.c_str());
     252           0 :         return false;
     253             :     }
     254          29 :     const double dfMaxX = psField->Real;
     255             : 
     256          29 :     psField = oTable.GetFieldValue(i_emaxy);
     257          29 :     if (!psField)
     258             :     {
     259           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     260             :                  "Cannot read field %s in %s table", "emaxy",
     261             :                  osBndTableName.c_str());
     262           0 :         return false;
     263             :     }
     264          29 :     const double dfMaxY = psField->Real;
     265             : 
     266          29 :     psField = oTable.GetFieldValue(i_block_origin_x);
     267          29 :     if (!psField)
     268             :     {
     269           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     270             :                  "Cannot read field %s in %s table", "block_origin_x",
     271             :                  osBndTableName.c_str());
     272           0 :         return false;
     273             :     }
     274          29 :     const double dfBlockOriginX = psField->Real;
     275             : 
     276          29 :     psField = oTable.GetFieldValue(i_block_origin_y);
     277          29 :     if (!psField)
     278             :     {
     279           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     280             :                  "Cannot read field %s in %s table", "block_origin_y",
     281             :                  osBndTableName.c_str());
     282           0 :         return false;
     283             :     }
     284          29 :     const double dfBlockOriginY = psField->Real;
     285             : 
     286             :     // Figure out data type
     287          29 :     GDALDataType eDT = GDT_Byte;
     288          29 :     const int nBitWidth = (nBandTypes >> 19) & ((1 << 7) - 1);
     289          29 :     const int nBitType = (nBandTypes >> 16) & ((1 << 2) - 1);
     290          29 :     constexpr int IS_UNSIGNED = 0;
     291          29 :     constexpr int IS_SIGNED = 1;
     292          29 :     constexpr int IS_FLOATING_POINT = 2;
     293          29 :     if ((nBitWidth >= 1 && nBitWidth < 8) && nBitType == IS_UNSIGNED)
     294             :     {
     295           2 :         eDT = GDT_Byte;
     296             :     }
     297          27 :     else if (nBitWidth == 8 && nBitType <= IS_SIGNED)
     298             :     {
     299          13 :         eDT = nBitType == IS_SIGNED ? GDT_Int8 : GDT_Byte;
     300             :     }
     301          14 :     else if (nBitWidth == 16 && nBitType <= IS_SIGNED)
     302             :     {
     303           4 :         eDT = nBitType == IS_SIGNED ? GDT_Int16 : GDT_UInt16;
     304             :     }
     305          10 :     else if (nBitWidth == 32 && nBitType <= IS_FLOATING_POINT)
     306             :     {
     307          13 :         eDT = nBitType == IS_FLOATING_POINT ? GDT_Float32
     308           4 :               : nBitType == IS_SIGNED       ? GDT_Int32
     309             :                                             : GDT_UInt32;
     310             :     }
     311           1 :     else if (nBitWidth == 64 && nBitType == 0)
     312             :     {
     313           1 :         eDT = GDT_Float64;
     314             :     }
     315             :     else
     316             :     {
     317           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     318             :                  "Unhandled nBitWidth=%d, nBitType=%d in %s table", nBitWidth,
     319             :                  nBitType, osBndTableName.c_str());
     320           0 :         return false;
     321             :     }
     322             : 
     323             :     // To avoid potential integer overflows in IReadBlock()
     324          58 :     if (nBlockWidth * nBlockHeight >
     325          29 :         std::numeric_limits<int>::max() / nBitWidth)
     326             :     {
     327           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     328             :                  "Too large block %dx%d in %s table", nBlockWidth, nBlockHeight,
     329             :                  osBndTableName.c_str());
     330           0 :         return false;
     331             :     }
     332             : 
     333             :     // Figure out compression
     334          29 :     const int nCompression = (nBandTypes >> 8) & 0xff;
     335          29 :     switch (nCompression)
     336             :     {
     337           6 :         case 0:
     338           6 :             m_eRasterCompression = Compression::NONE;
     339           6 :             break;
     340          19 :         case 4:
     341          19 :             m_eRasterCompression = Compression::LZ77;
     342          19 :             SetMetadataItem("COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE");
     343          19 :             break;
     344           2 :         case 8:
     345           2 :             m_eRasterCompression = Compression::JPEG;
     346           2 :             SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
     347           2 :             break;
     348           2 :         case 12:
     349           2 :             m_eRasterCompression = Compression::JPEG2000;
     350           2 :             SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
     351           2 :             break;
     352           0 :         default:
     353             :         {
     354           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     355             :                      "Unhandled compression %d in %s table", nCompression,
     356             :                      osBndTableName.c_str());
     357           0 :             return false;
     358             :         }
     359             :     }
     360             : 
     361             :     // Figure out geotransform
     362             : 
     363          29 :     if (!(dfMaxX > dfMinX && dfMaxY > dfMinY))
     364             :     {
     365           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     366             :                  "!(dfMaxX > dfMinX && dfMaxY > dfMinY)");
     367           0 :         return false;
     368             :     }
     369          29 :     else if (nWidth == 1 || nHeight == 1)
     370             :     {
     371           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     372             :                  "nWidth == 1 || nHeight == 1: cannot determine geotransform");
     373             :     }
     374             :     else
     375             :     {
     376             :         // FileGDB uses a center-of-pixel convention for georeferencing
     377             :         // Transform to GDAL's corner-of-pixel convention.
     378          29 :         const double dfResX = (dfMaxX - dfMinX) / (nWidth - 1);
     379          29 :         const double dfResY = (dfMaxY - dfMinY) / (nHeight - 1);
     380          29 :         m_bHasGeoTransform = true;
     381          29 :         const double dfBlockGeorefWidth = dfResX * nBlockWidth;
     382          29 :         if (dfMinX != dfBlockOriginX)
     383             :         {
     384             :             // Take into account MinX by making sure the raster origin is
     385             :             // close to it, while being shifted from an integer number of blocks
     386             :             // from BlockOriginX
     387             :             const double dfTmp =
     388           1 :                 std::floor((dfMinX - dfBlockOriginX) / dfBlockGeorefWidth);
     389           1 :             if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
     390             :             {
     391           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     392             :                          "Inconsistent eminx=%g and block_origin_x=%g", dfMinX,
     393             :                          dfBlockOriginX);
     394           0 :                 return false;
     395             :             }
     396           1 :             m_nShiftBlockX = static_cast<int>(dfTmp);
     397           1 :             CPLDebug("OpenFileGDB", "m_nShiftBlockX = %d", m_nShiftBlockX);
     398           1 :             const double dfMinXAdjusted =
     399           1 :                 dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth;
     400           1 :             nWidth = 1 + static_cast<int>(
     401           1 :                              std::round((dfMaxX - dfMinXAdjusted) / dfResX));
     402             :         }
     403          58 :         m_adfGeoTransform[0] =
     404          29 :             (dfBlockOriginX + m_nShiftBlockX * dfBlockGeorefWidth) - dfResX / 2;
     405          29 :         m_adfGeoTransform[1] = dfResX;
     406          29 :         m_adfGeoTransform[2] = 0.0;
     407          29 :         const double dfBlockGeorefHeight = dfResY * nBlockHeight;
     408          29 :         if (dfMaxY != dfBlockOriginY)
     409             :         {
     410             :             // Take into account MaxY by making sure the raster origin is
     411             :             // close to it, while being shifted from an integer number of blocks
     412             :             // from BlockOriginY
     413             :             const double dfTmp =
     414           3 :                 std::floor((dfBlockOriginY - dfMaxY) / dfBlockGeorefHeight);
     415           3 :             if (std::fabs(dfTmp) > std::numeric_limits<int>::max())
     416             :             {
     417           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     418             :                          "Inconsistent emaxy=%g and block_origin_y=%g", dfMaxY,
     419             :                          dfBlockOriginY);
     420           0 :                 return false;
     421             :             }
     422           3 :             m_nShiftBlockY = static_cast<int>(dfTmp);
     423           3 :             CPLDebug("OpenFileGDB", "m_nShiftBlockY = %d", m_nShiftBlockY);
     424           3 :             const double dfMaxYAdjusted =
     425           3 :                 dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight;
     426           3 :             nHeight = 1 + static_cast<int>(
     427           3 :                               std::round((dfMaxYAdjusted - dfMinY) / dfResY));
     428             :         }
     429          58 :         m_adfGeoTransform[3] =
     430          29 :             (dfBlockOriginY - m_nShiftBlockY * dfBlockGeorefHeight) +
     431          29 :             dfResY / 2;
     432          29 :         m_adfGeoTransform[4] = 0.0;
     433          29 :         m_adfGeoTransform[5] = -dfResY;
     434             :     }
     435             : 
     436             :     // Two cases:
     437             :     // - osDefinition is empty (that is FileGDB v9): find the SRS by looking
     438             :     //   at the SRS attached to the RASTER field definition of the .gdbtable
     439             :     //   file of the main table of the raster (that is the one without fras_XXX
     440             :     //   prefixes)
     441             :     // - or osDefinition is not empty (that is FileGDB v10): get SRID from the
     442             :     //   "srid" field of the _fras_bnd table, and use that has the key to
     443             :     //   lookup the corresponding WKT from the GDBSpatialRefs table.
     444             :     //   In some cases srid might be 0 (invalid), then we try to get it from
     445             :     //   Definition column of the GDB_Items table, stored in osDefinition
     446          29 :     psField = oTable.GetFieldValue(i_srid);
     447          29 :     if (osDefinition.empty())
     448             :     {
     449             :         // osDefinition empty for FileGDB v9
     450           2 :         const auto oIter2 = m_osMapNameToIdx.find(osLayerName);
     451           2 :         if (oIter2 != m_osMapNameToIdx.end())
     452             :         {
     453           2 :             const int nTableIdx = oIter2->second;
     454             : 
     455           4 :             FileGDBTable oTableMain;
     456             : 
     457             :             const CPLString osTableMain(CPLFormFilename(
     458           4 :                 m_osDirName, CPLSPrintf("a%08x.gdbtable", nTableIdx), nullptr));
     459           2 :             if (oTableMain.Open(osTableMain, false))
     460             :             {
     461           2 :                 const int iRasterFieldIdx = oTableMain.GetFieldIdx("RASTER");
     462           2 :                 if (iRasterFieldIdx >= 0)
     463             :                 {
     464           2 :                     const auto poField = oTableMain.GetField(iRasterFieldIdx);
     465           2 :                     if (poField->GetType() == FGFT_RASTER)
     466             :                     {
     467           2 :                         const auto poFieldRaster =
     468             :                             static_cast<FileGDBRasterField *>(poField);
     469           2 :                         const auto &osWKT = poFieldRaster->GetWKT();
     470           2 :                         if (!osWKT.empty() && osWKT[0] != '{')
     471             :                         {
     472           2 :                             auto poSRS = BuildSRS(osWKT.c_str());
     473           2 :                             if (poSRS)
     474             :                             {
     475           2 :                                 m_oRasterSRS = *poSRS;
     476           2 :                                 poSRS->Release();
     477             :                             }
     478             :                         }
     479             :                     }
     480             :                 }
     481             :             }
     482             :         }
     483             :     }
     484          27 :     else if (!psField)
     485             :     {
     486           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     487             :                  "Cannot read field %s in %s table", "srid",
     488             :                  osBndTableName.c_str());
     489             :     }
     490          27 :     else if (m_osGDBSpatialRefsFilename.empty())
     491             :     {
     492           0 :         CPLError(CE_Warning, CPLE_AppDefined, "No GDBSpatialRefs table");
     493             :     }
     494             :     else
     495             :     {
     496             :         // FileGDB v10 case
     497          27 :         const int nSRID = psField->Integer;
     498          54 :         FileGDBTable oTableSRS;
     499          27 :         if (oTableSRS.Open(m_osGDBSpatialRefsFilename.c_str(), false))
     500             :         {
     501          27 :             const int iSRTEXT = oTableSRS.GetFieldIdx("SRTEXT");
     502          54 :             if (iSRTEXT < 0 ||
     503          27 :                 oTableSRS.GetField(iSRTEXT)->GetType() != FGFT_STRING)
     504             :             {
     505           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     506             :                          "Could not find field %s in table %s", "SRTEXT",
     507           0 :                          oTableSRS.GetFilename().c_str());
     508             :             }
     509          27 :             else if (nSRID == 0)
     510             :             {
     511             :                 // BldgHeights.gdb is such. We must fetch the SRS from the
     512             :                 // Definition column of the GDB_Items table
     513             :                 CPLXMLTreeCloser psTree(
     514           0 :                     CPLParseXMLString(osDefinition.c_str()));
     515           0 :                 if (psTree == nullptr)
     516             :                 {
     517           0 :                     CPLError(
     518             :                         CE_Warning, CPLE_AppDefined,
     519             :                         "Cannot parse XML definition. SRS will be missing");
     520             :                 }
     521             :                 else
     522             :                 {
     523           0 :                     CPLStripXMLNamespace(psTree.get(), nullptr, TRUE);
     524             :                     const CPLXMLNode *psInfo =
     525           0 :                         CPLSearchXMLNode(psTree.get(), "=DERasterDataset");
     526           0 :                     if (psInfo)
     527             :                     {
     528           0 :                         auto poSRS = BuildSRS(psInfo);
     529           0 :                         if (poSRS)
     530           0 :                             m_oRasterSRS = *poSRS;
     531             :                     }
     532           0 :                     if (m_oRasterSRS.IsEmpty())
     533             :                     {
     534           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     535             :                                  "Cannot get SRS from XML definition");
     536             :                     }
     537             :                 }
     538             :             }
     539          54 :             else if (nSRID < 0 || !oTableSRS.SelectRow(nSRID - 1) ||
     540          27 :                      oTableSRS.HasGotError())
     541             :             {
     542           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     543             :                          "Cannot find record corresponding to SRID = %d",
     544             :                          nSRID);
     545             :             }
     546             :             else
     547             :             {
     548          27 :                 const auto psSRTEXT = oTableSRS.GetFieldValue(iSRTEXT);
     549          27 :                 if (psSRTEXT && psSRTEXT->String)
     550             :                 {
     551          27 :                     if (psSRTEXT->String[0] != '{')
     552             :                     {
     553          26 :                         auto poSRS = BuildSRS(psSRTEXT->String);
     554          26 :                         if (poSRS)
     555             :                         {
     556          26 :                             m_oRasterSRS = *poSRS;
     557          26 :                             poSRS->Release();
     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("INTERLEAVE", "BAND", "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_Byte, 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_Byte, 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_Byte:
     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_Byte, 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", "IMAGE_STRUCTURE");
     907           2 :                         if (pszQuality)
     908             :                         {
     909           2 :                             SetMetadataItem("JPEG_QUALITY", pszQuality,
     910           2 :                                             "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(double *padfGeoTransform)
    1073             : {
    1074           4 :     memcpy(padfGeoTransform, m_adfGeoTransform.data(),
    1075             :            sizeof(m_adfGeoTransform));
    1076           4 :     return m_bHasGeoTransform ? CE_None : CE_Failure;
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                         GetSpatialRef()                              */
    1081             : /************************************************************************/
    1082             : 
    1083           4 : const OGRSpatialReference *OGROpenFileGDBDataSource::GetSpatialRef() const
    1084             : {
    1085           4 :     return m_oRasterSRS.IsEmpty() ? nullptr : &m_oRasterSRS;
    1086             : }
    1087             : 
    1088             : /************************************************************************/
    1089             : /*                     GDALOpenFileGDBRasterBand()                      */
    1090             : /************************************************************************/
    1091             : 
    1092         175 : GDALOpenFileGDBRasterBand::GDALOpenFileGDBRasterBand(
    1093             :     OGROpenFileGDBDataSource *poDSIn, int nBandIn, GDALDataType eDT,
    1094             :     int nBitWidth, int nBlockWidth, int nBlockHeight, int nOverviewLevel,
    1095         175 :     bool bIsMask)
    1096             :     : m_nBitWidth(nBitWidth), m_nOverviewLevel(nOverviewLevel),
    1097         175 :       m_bIsMask(bIsMask)
    1098             : {
    1099         175 :     poDS = poDSIn;
    1100         175 :     nBand = nBandIn;
    1101         175 :     eDataType = eDT;
    1102         175 :     nRasterXSize = std::max(1, poDSIn->GetRasterXSize() >> nOverviewLevel);
    1103         175 :     nRasterYSize = std::max(1, poDSIn->GetRasterYSize() >> nOverviewLevel);
    1104         175 :     nBlockXSize = nBlockWidth;
    1105         175 :     nBlockYSize = nBlockHeight;
    1106         175 :     if (nBitWidth < 8)
    1107             :     {
    1108           8 :         SetMetadataItem("NBITS", CPLSPrintf("%d", nBitWidth),
    1109             :                         "IMAGE_STRUCTURE");
    1110             :     }
    1111         175 : }
    1112             : 
    1113             : /************************************************************************/
    1114             : /*                         SetNoDataFromMask()                          */
    1115             : /************************************************************************/
    1116             : 
    1117             : template <class T>
    1118           0 : static void SetNoDataFromMask(void *pImage, const GByte *pabyMask,
    1119             :                               size_t nPixels, double dfNoData)
    1120             : {
    1121           0 :     const T noData = static_cast<T>(dfNoData);
    1122           0 :     const T noDataReplacement =
    1123           0 :         noData == std::numeric_limits<T>::max() ? noData - 1 : noData + 1;
    1124           0 :     bool bHasWarned = false;
    1125           0 :     for (size_t i = 0; i < nPixels; ++i)
    1126             :     {
    1127           0 :         if (pabyMask && !(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1128             :         {
    1129           0 :             static_cast<T *>(pImage)[i] = noData;
    1130             :         }
    1131           0 :         else if (static_cast<T *>(pImage)[i] == noData)
    1132             :         {
    1133           0 :             static_cast<T *>(pImage)[i] = noDataReplacement;
    1134           0 :             if (!bHasWarned)
    1135             :             {
    1136           0 :                 bHasWarned = true;
    1137           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1138             :                          "Valid data found with value equal to nodata (%.0f). "
    1139             :                          "Got substituted with %.0f",
    1140             :                          static_cast<double>(noData),
    1141             :                          static_cast<double>(noDataReplacement));
    1142             :             }
    1143             :         }
    1144             :     }
    1145           0 : }
    1146             : 
    1147             : /************************************************************************/
    1148             : /*                         IReadBlock()                                 */
    1149             : /************************************************************************/
    1150             : 
    1151         118 : CPLErr GDALOpenFileGDBRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1152             :                                              void *pImage)
    1153             : {
    1154         118 :     auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
    1155         118 :     auto &poLyr = poGDS->m_poBlkLayer;
    1156             : 
    1157             :     // Return (pointer to image data, owner block). Works when called from main band
    1158             :     // or mask band. owner block must be DropLock() once done (if not null)
    1159         238 :     const auto GetImageData = [this, nBlockXOff, nBlockYOff, pImage]()
    1160             :     {
    1161         118 :         void *pImageData = nullptr;
    1162         118 :         GDALRasterBlock *poBlock = nullptr;
    1163         118 :         if (m_bIsMask)
    1164             :         {
    1165           1 :             CPLAssert(m_poMainBand);
    1166           1 :             poBlock =
    1167           1 :                 m_poMainBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1168           1 :             if (poBlock)
    1169             :             {
    1170             :                 // The block is already in cache. Return (null, null)
    1171           0 :                 poBlock->DropLock();
    1172           0 :                 poBlock = nullptr;
    1173             :             }
    1174             :             else
    1175             :             {
    1176           2 :                 poBlock = m_poMainBand->GetLockedBlockRef(nBlockXOff,
    1177           1 :                                                           nBlockYOff, true);
    1178           1 :                 if (poBlock)
    1179           1 :                     pImageData = poBlock->GetDataRef();
    1180             :             }
    1181             :         }
    1182             :         else
    1183             :         {
    1184         117 :             pImageData = pImage;
    1185             :         }
    1186         236 :         return std::make_pair(pImageData, poBlock);
    1187         118 :     };
    1188             : 
    1189             :     // Return (pointer to mask data, owner block). Works when called from main band
    1190             :     // or mask band. owner block must be DropLock() once done (if not null)
    1191         404 :     const auto GetMaskData = [this, nBlockXOff, nBlockYOff, pImage]()
    1192             :     {
    1193         114 :         void *pMaskData = nullptr;
    1194         114 :         GDALRasterBlock *poBlock = nullptr;
    1195         114 :         if (m_bIsMask)
    1196             :         {
    1197           1 :             pMaskData = pImage;
    1198             :         }
    1199             :         else
    1200             :         {
    1201         113 :             CPLAssert(m_poMaskBand);
    1202         113 :             poBlock =
    1203         113 :                 m_poMaskBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
    1204         113 :             if (poBlock)
    1205             :             {
    1206             :                 // The block is already in cache. Return (null, null)
    1207          50 :                 poBlock->DropLock();
    1208          50 :                 poBlock = nullptr;
    1209             :             }
    1210             :             else
    1211             :             {
    1212         126 :                 poBlock = m_poMaskBand->GetLockedBlockRef(nBlockXOff,
    1213          63 :                                                           nBlockYOff, true);
    1214          63 :                 if (poBlock)
    1215          63 :                     pMaskData = poBlock->GetDataRef();
    1216             :             }
    1217             :         }
    1218         228 :         return std::make_pair(pMaskData, poBlock);
    1219         118 :     };
    1220             : 
    1221             :     const GDALDataType eImageDT =
    1222         118 :         m_poMainBand ? m_poMainBand->GetRasterDataType() : eDataType;
    1223         118 :     const size_t nPixels = static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1224             : 
    1225             :     const auto FillMissingBlock =
    1226           6 :         [this, eImageDT, nPixels, &GetImageData, &GetMaskData]()
    1227             :     {
    1228             :         // Set image data to nodata / 0
    1229             :         {
    1230           1 :             auto imageDataAndBlock = GetImageData();
    1231           1 :             auto pImageData = imageDataAndBlock.first;
    1232           1 :             auto poBlock = imageDataAndBlock.second;
    1233           1 :             if (pImageData)
    1234             :             {
    1235           1 :                 const int nDTSize = GDALGetDataTypeSizeBytes(eImageDT);
    1236           1 :                 if (m_bHasNoData)
    1237             :                 {
    1238           1 :                     GDALCopyWords64(&m_dfNoData, GDT_Float64, 0, pImageData,
    1239             :                                     eImageDT, nDTSize, nPixels);
    1240             :                 }
    1241             :                 else
    1242             :                 {
    1243           0 :                     memset(pImageData, 0, nPixels * nDTSize);
    1244             :                 }
    1245             :             }
    1246           1 :             if (poBlock)
    1247           0 :                 poBlock->DropLock();
    1248             :         }
    1249             : 
    1250             :         // Set mask band to 0 (when it exists)
    1251           1 :         if (m_poMaskBand || m_bIsMask)
    1252             :         {
    1253           0 :             auto maskDataAndBlock = GetMaskData();
    1254           0 :             auto pMaskData = maskDataAndBlock.first;
    1255           0 :             auto poBlock = maskDataAndBlock.second;
    1256           0 :             if (pMaskData)
    1257             :             {
    1258           0 :                 const size_t nSize =
    1259           0 :                     static_cast<size_t>(nBlockXSize) * nBlockYSize;
    1260           0 :                 memset(pMaskData, 0, nSize);
    1261             :             }
    1262           0 :             if (poBlock)
    1263           0 :                 poBlock->DropLock();
    1264             :         }
    1265           1 :     };
    1266             : 
    1267             :     // Fetch block data from fras_blk_XXX layer
    1268         118 :     const int nGDALBandId = m_bIsMask ? 1 : nBand;
    1269         118 :     auto oIter = poGDS->m_oMapGDALBandToGDBBandId.find(nGDALBandId);
    1270         118 :     if (oIter == poGDS->m_oMapGDALBandToGDBBandId.end())
    1271             :     {
    1272           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1273             :                  "poGDS->m_oMapGDALBandToGDBBandId.find(%d) failed",
    1274             :                  nGDALBandId);
    1275           0 :         return CE_Failure;
    1276             :     }
    1277         118 :     const int nGDBRasterBandId = oIter->second;
    1278             : 
    1279         236 :     CPLString osFilter;
    1280             :     /* osFilter.Printf("rasterband_id = %d AND rrd_factor = %d AND row_nbr = %d "
    1281             :                     "AND col_nbr = %d",
    1282             :                     nGDBRasterBandId,
    1283             :                     m_nOverviewLevel, nBlockYOff, nBlockXOff);
    1284             :     */
    1285         118 :     const int nColNbr = nBlockXOff + poGDS->m_nShiftBlockX;
    1286         118 :     const int nRowNbr = nBlockYOff + poGDS->m_nShiftBlockY;
    1287         118 :     if (nRowNbr >= 0 && nColNbr >= 0)
    1288             :     {
    1289         118 :         osFilter.Printf("block_key = '0000%04X%02X%04X%04X'", nGDBRasterBandId,
    1290         118 :                         m_nOverviewLevel, nRowNbr, nColNbr);
    1291             :     }
    1292           0 :     else if (nRowNbr < 0 && nColNbr >= 0)
    1293             :     {
    1294           0 :         osFilter.Printf("block_key = '0000%04X%02X-%04X%04X'", nGDBRasterBandId,
    1295           0 :                         m_nOverviewLevel, -nRowNbr, nColNbr);
    1296             :     }
    1297           0 :     else if (nRowNbr >= 0 && nColNbr < 0)
    1298             :     {
    1299           0 :         osFilter.Printf("block_key = '0000%04X%02X%04X-%04X'", nGDBRasterBandId,
    1300           0 :                         m_nOverviewLevel, nRowNbr, -nColNbr);
    1301             :     }
    1302             :     else /* if( nRowNbr < 0 && nColNbr < 0 ) */
    1303             :     {
    1304             :         osFilter.Printf("block_key = '0000%04X%02X-%04X-%04X'",
    1305           0 :                         nGDBRasterBandId, m_nOverviewLevel, -nRowNbr, -nColNbr);
    1306             :     }
    1307             :     // CPLDebug("OpenFileGDB", "Request %s", osFilter.c_str());
    1308         118 :     poLyr->SetAttributeFilter(osFilter.c_str());
    1309         236 :     auto poFeature = std::unique_ptr<OGRFeature>(poLyr->GetNextFeature());
    1310         118 :     const int nImageDTSize = GDALGetDataTypeSizeBytes(eImageDT);
    1311         118 :     if (!poFeature)
    1312             :     {
    1313             :         // Missing blocks are legit
    1314           1 :         FillMissingBlock();
    1315           1 :         return CE_None;
    1316             :     }
    1317         117 :     const int nFieldIdx = poFeature->GetFieldIndex("block_data");
    1318         117 :     CPLAssert(nFieldIdx >= 0);
    1319         117 :     int nInBytes = 0;
    1320         117 :     if (!poFeature->IsFieldSetAndNotNull(nFieldIdx))
    1321             :     {
    1322             :         // block_data unset found on ForestFalls.gdb
    1323           0 :         FillMissingBlock();
    1324           0 :         return CE_None;
    1325             :     }
    1326         117 :     const GByte *pabyData = poFeature->GetFieldAsBinary(nFieldIdx, &nInBytes);
    1327         117 :     if (nInBytes == 0)
    1328             :     {
    1329           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Image block is empty");
    1330           0 :         return CE_Failure;
    1331             :     }
    1332             : 
    1333             :     // The input buffer may be concatenated with a 1-bit binary mask
    1334         117 :     const size_t nImageSize = nPixels * nImageDTSize;
    1335         117 :     const int nImageBitWidth =
    1336         117 :         m_poMainBand ? m_poMainBand->m_nBitWidth : m_nBitWidth;
    1337         117 :     const size_t nImageSizePacked = (nPixels * nImageBitWidth + 7) / 8;
    1338         117 :     const size_t nBinaryMaskSize = (nPixels + 7) / 8;
    1339         117 :     const size_t nImageSizeWithBinaryMask = nImageSizePacked + nBinaryMaskSize;
    1340             : 
    1341             :     // Unpack 1-bit, 2-bit, 4-bit data to full byte
    1342             :     const auto ExpandSubByteData =
    1343     2175500 :         [nPixels, nImageBitWidth](const GByte *pabyInput, void *pDstBuffer)
    1344             :     {
    1345          24 :         CPLAssert(nImageBitWidth < 8);
    1346             : 
    1347          24 :         size_t iBitOffset = 0;
    1348      393240 :         for (size_t i = 0; i < nPixels; ++i)
    1349             :         {
    1350      393216 :             unsigned nOutWord = 0;
    1351             : 
    1352     1523710 :             for (int iBit = 0; iBit < nImageBitWidth; ++iBit)
    1353             :             {
    1354     1130500 :                 if (pabyInput[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7)))
    1355             :                 {
    1356      258524 :                     nOutWord |= (1 << (nImageBitWidth - 1 - iBit));
    1357             :                 }
    1358     1130500 :                 ++iBitOffset;
    1359             :             }
    1360             : 
    1361      393216 :             static_cast<GByte *>(pDstBuffer)[i] = static_cast<GByte>(nOutWord);
    1362             :         }
    1363         141 :     };
    1364             : 
    1365         117 :     const GByte *pabyMask = nullptr;
    1366         116 :     auto &abyTmpBuffer =
    1367         117 :         m_poMainBand ? m_poMainBand->m_abyTmpBuffer : m_abyTmpBuffer;
    1368             : 
    1369         117 :     switch (poGDS->m_eRasterCompression)
    1370             :     {
    1371           6 :         case OGROpenFileGDBDataSource::Compression::NONE:
    1372             :         {
    1373           6 :             if (static_cast<unsigned>(nInBytes) != nImageSizePacked &&
    1374           6 :                 static_cast<unsigned>(nInBytes) != nImageSizeWithBinaryMask)
    1375             :             {
    1376           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1377             :                          "Not expected number of input bytes: %d", nInBytes);
    1378           0 :                 return CE_Failure;
    1379             :             }
    1380             : 
    1381           6 :             auto imageDataAndBlock = GetImageData();
    1382           6 :             auto pImageData = imageDataAndBlock.first;
    1383           6 :             auto poBlock = imageDataAndBlock.second;
    1384             : 
    1385           6 :             if (pImageData)
    1386             :             {
    1387           6 :                 if (nImageSizePacked == nImageSize)
    1388             :                 {
    1389           6 :                     memcpy(pImageData, pabyData, nImageSize);
    1390             : #ifdef CPL_LSB
    1391           6 :                     if (nImageDTSize > 1)
    1392             :                     {
    1393           5 :                         GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
    1394             :                                         nImageDTSize);
    1395             :                     }
    1396             : #endif
    1397             :                 }
    1398             :                 else
    1399             :                 {
    1400           0 :                     ExpandSubByteData(pabyData, pImageData);
    1401             :                 }
    1402             :             }
    1403           6 :             if (poBlock)
    1404           0 :                 poBlock->DropLock();
    1405             : 
    1406           6 :             if (static_cast<unsigned>(nInBytes) == nImageSizeWithBinaryMask)
    1407           6 :                 pabyMask = pabyData + nImageSizePacked;
    1408           6 :             break;
    1409             :         }
    1410             : 
    1411          63 :         case OGROpenFileGDBDataSource::Compression::LZ77:
    1412             :         {
    1413          63 :             if (abyTmpBuffer.empty())
    1414             :             {
    1415             :                 try
    1416             :                 {
    1417          18 :                     abyTmpBuffer.resize(nImageSizeWithBinaryMask);
    1418             :                 }
    1419           0 :                 catch (const std::bad_alloc &e)
    1420             :                 {
    1421           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1422           0 :                     return CE_Failure;
    1423             :                 }
    1424             :             }
    1425             : 
    1426          63 :             size_t nOutBytes = 0;
    1427          63 :             GByte *outPtr = abyTmpBuffer.data();
    1428          63 :             assert(outPtr != nullptr);  // For Coverity Scan
    1429          63 :             if (!CPLZLibInflate(pabyData, nInBytes, outPtr, abyTmpBuffer.size(),
    1430         126 :                                 &nOutBytes) ||
    1431          63 :                 !(nOutBytes == nImageSizePacked ||
    1432          48 :                   nOutBytes == nImageSizeWithBinaryMask))
    1433             :             {
    1434           0 :                 CPLError(
    1435             :                     CE_Failure, CPLE_AppDefined,
    1436             :                     "CPLZLibInflate() failed: nInBytes = %u, nOutBytes = %u, "
    1437             :                     "nImageSizePacked = %u, "
    1438             :                     "nImageSizeWithBinaryMask = %u",
    1439             :                     unsigned(nInBytes), unsigned(nOutBytes),
    1440             :                     unsigned(nImageSizePacked),
    1441             :                     unsigned(nImageSizeWithBinaryMask));
    1442           0 :                 return CE_Failure;
    1443             :             }
    1444             : 
    1445          63 :             auto imageDataAndBlock = GetImageData();
    1446          63 :             auto pImageData = imageDataAndBlock.first;
    1447          63 :             auto poBlock = imageDataAndBlock.second;
    1448             : 
    1449          63 :             if (pImageData)
    1450             :             {
    1451          63 :                 if (nImageSizePacked == nImageSize)
    1452             :                 {
    1453          39 :                     memcpy(pImageData, abyTmpBuffer.data(), nImageSize);
    1454             : #ifdef CPL_LSB
    1455          39 :                     if (nImageDTSize > 1)
    1456             :                     {
    1457           6 :                         GDALSwapWordsEx(pImageData, nImageDTSize, nPixels,
    1458             :                                         nImageDTSize);
    1459             :                     }
    1460             : #endif
    1461             :                 }
    1462             :                 else
    1463             :                 {
    1464          24 :                     ExpandSubByteData(abyTmpBuffer.data(), pImageData);
    1465             :                 }
    1466             :             }
    1467          63 :             if (poBlock)
    1468           1 :                 poBlock->DropLock();
    1469             : 
    1470          63 :             if (nOutBytes == nImageSizeWithBinaryMask)
    1471          48 :                 pabyMask = abyTmpBuffer.data() + nImageSizePacked;
    1472          63 :             break;
    1473             :         }
    1474             : 
    1475          24 :         case OGROpenFileGDBDataSource::Compression::JPEG:
    1476             :         {
    1477          24 :             if (GDALGetDriverByName("JPEG") == nullptr)
    1478             :             {
    1479           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "JPEG driver missing");
    1480           0 :                 return CE_Failure;
    1481             :             }
    1482             : 
    1483          24 :             if (static_cast<unsigned>(nInBytes) < 5)
    1484             :             {
    1485           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1486             :                          "Not expected number of input bytes: %d", nInBytes);
    1487           0 :                 return CE_Failure;
    1488             :             }
    1489          24 :             uint32_t nJPEGSize = nInBytes - 1;
    1490          24 :             uint32_t nJPEGOffset = 1;
    1491          24 :             if (pabyData[0] == 0xFE)
    1492             :             {
    1493             :                 // JPEG followed by binary mask
    1494          15 :                 memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
    1495          15 :                 CPL_LSBPTR32(&nJPEGSize);
    1496          15 :                 if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
    1497             :                 {
    1498           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1499             :                              "Invalid nJPEGSize = %u", nJPEGSize);
    1500           0 :                     return CE_Failure;
    1501             :                 }
    1502          15 :                 nJPEGOffset = 5;
    1503             : 
    1504          15 :                 if (abyTmpBuffer.empty())
    1505             :                 {
    1506             :                     try
    1507             :                     {
    1508           3 :                         abyTmpBuffer.resize(nBinaryMaskSize);
    1509             :                     }
    1510           0 :                     catch (const std::bad_alloc &e)
    1511             :                     {
    1512           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1513           0 :                         return CE_Failure;
    1514             :                     }
    1515             :                 }
    1516          15 :                 size_t nOutBytes = 0;
    1517          15 :                 GByte *outPtr = abyTmpBuffer.data();
    1518          15 :                 assert(outPtr != nullptr);  // For Coverity Scan
    1519          45 :                 if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
    1520          15 :                                    nInBytes - 5 - nJPEGSize, outPtr,
    1521          30 :                                    nBinaryMaskSize, &nOutBytes) &&
    1522          15 :                     nOutBytes == nBinaryMaskSize)
    1523             :                 {
    1524          15 :                     pabyMask = abyTmpBuffer.data();
    1525             :                 }
    1526             :                 else
    1527             :                 {
    1528           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1529             :                              "Cannot decompress binary mask");
    1530             :                 }
    1531             :             }
    1532           9 :             else if (pabyData[0] != 1)
    1533             :             {
    1534           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG blob");
    1535           0 :                 return CE_Failure;
    1536             :             }
    1537             : 
    1538             :             const CPLString osTmpFilename(
    1539          24 :                 VSIMemGenerateHiddenFilename("openfilegdb.jpg"));
    1540          24 :             VSIFCloseL(VSIFileFromMemBuffer(
    1541             :                 osTmpFilename.c_str(),
    1542          24 :                 const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
    1543          24 :             const char *const apszDrivers[] = {"JPEG", nullptr};
    1544             :             auto poJPEGDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1545          24 :                 osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
    1546          24 :             if (!poJPEGDS)
    1547             :             {
    1548           0 :                 VSIUnlink(osTmpFilename.c_str());
    1549           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot open JPEG blob");
    1550           0 :                 return CE_Failure;
    1551             :             }
    1552          24 :             if (poJPEGDS->GetRasterCount() != 1 ||
    1553          48 :                 poJPEGDS->GetRasterXSize() != nBlockXSize ||
    1554          24 :                 poJPEGDS->GetRasterYSize() != nBlockYSize)
    1555             :             {
    1556           0 :                 VSIUnlink(osTmpFilename.c_str());
    1557           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1558             :                          "Inconsistent characteristics of JPEG blob");
    1559           0 :                 return CE_Failure;
    1560             :             }
    1561             : 
    1562          24 :             auto imageDataAndBlock = GetImageData();
    1563          24 :             auto pImageData = imageDataAndBlock.first;
    1564          24 :             auto poBlock = imageDataAndBlock.second;
    1565             : 
    1566             :             const CPLErr eErr =
    1567             :                 pImageData
    1568          24 :                     ? poJPEGDS->GetRasterBand(1)->RasterIO(
    1569             :                           GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
    1570             :                           nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
    1571          24 :                     : CE_None;
    1572          24 :             VSIUnlink(osTmpFilename.c_str());
    1573          24 :             if (poBlock)
    1574           0 :                 poBlock->DropLock();
    1575             : 
    1576          24 :             if (eErr != CE_None)
    1577             :             {
    1578           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot read JPEG blob");
    1579           0 :                 return CE_Failure;
    1580             :             }
    1581             : 
    1582          24 :             break;
    1583             :         }
    1584             : 
    1585          24 :         case OGROpenFileGDBDataSource::Compression::JPEG2000:
    1586             :         {
    1587          24 :             const char *const apszDrivers[] = {"JP2KAK",      "JP2ECW",
    1588             :                                                "JP2OpenJPEG", "JP2MrSID",
    1589             :                                                "JP2Lura",     nullptr};
    1590          24 :             bool bFoundJP2Driver = false;
    1591          48 :             for (const char *pszDriver : apszDrivers)
    1592             :             {
    1593          48 :                 if (pszDriver && GDALGetDriverByName(pszDriver))
    1594             :                 {
    1595          24 :                     bFoundJP2Driver = true;
    1596          24 :                     break;
    1597             :                 }
    1598             :             }
    1599          24 :             if (!bFoundJP2Driver)
    1600             :             {
    1601           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1602             :                          "Did not find any JPEG2000 capable driver");
    1603           0 :                 return CE_Failure;
    1604             :             }
    1605             : 
    1606          24 :             if (static_cast<unsigned>(nInBytes) < 5)
    1607             :             {
    1608           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1609             :                          "Not expected number of input bytes: %d", nInBytes);
    1610           0 :                 return CE_Failure;
    1611             :             }
    1612          24 :             uint32_t nJPEGSize = nInBytes - 1;
    1613          24 :             uint32_t nJPEGOffset = 1;
    1614          24 :             if (pabyData[0] == 0xFF)
    1615             :             {
    1616             :                 // JPEG2000 followed by binary mask
    1617          15 :                 memcpy(&nJPEGSize, pabyData + 1, sizeof(uint32_t));
    1618          15 :                 CPL_LSBPTR32(&nJPEGSize);
    1619          15 :                 if (nJPEGSize > static_cast<unsigned>(nInBytes - 5))
    1620             :                 {
    1621           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1622             :                              "Invalid nJPEGSize = %u", nJPEGSize);
    1623           0 :                     return CE_Failure;
    1624             :                 }
    1625          15 :                 nJPEGOffset = 5;
    1626             : 
    1627          15 :                 if (abyTmpBuffer.empty())
    1628             :                 {
    1629             :                     try
    1630             :                     {
    1631           3 :                         abyTmpBuffer.resize(nBinaryMaskSize);
    1632             :                     }
    1633           0 :                     catch (const std::bad_alloc &e)
    1634             :                     {
    1635           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1636           0 :                         return CE_Failure;
    1637             :                     }
    1638             :                 }
    1639          15 :                 size_t nOutBytes = 0;
    1640          15 :                 GByte *outPtr = abyTmpBuffer.data();
    1641          15 :                 assert(outPtr != nullptr);  // For Coverity Scan
    1642          45 :                 if (CPLZLibInflate(pabyData + 5 + nJPEGSize,
    1643          15 :                                    nInBytes - 5 - nJPEGSize, outPtr,
    1644          30 :                                    nBinaryMaskSize, &nOutBytes) &&
    1645          15 :                     nOutBytes == nBinaryMaskSize)
    1646             :                 {
    1647          15 :                     pabyMask = outPtr;
    1648             :                 }
    1649             :                 else
    1650             :                 {
    1651           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1652             :                              "Cannot decompress binary mask");
    1653             :                 }
    1654             :             }
    1655           9 :             else if (pabyData[0] != 0)
    1656             :             {
    1657           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid JPEG2000 blob");
    1658           0 :                 return CE_Failure;
    1659             :             }
    1660             : 
    1661             :             const CPLString osTmpFilename(
    1662          24 :                 VSIMemGenerateHiddenFilename("openfilegdb.j2k"));
    1663          24 :             VSIFCloseL(VSIFileFromMemBuffer(
    1664             :                 osTmpFilename.c_str(),
    1665          24 :                 const_cast<GByte *>(pabyData + nJPEGOffset), nJPEGSize, false));
    1666             :             auto poJP2KDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1667          24 :                 osTmpFilename.c_str(), GDAL_OF_RASTER, apszDrivers));
    1668          24 :             if (!poJP2KDS)
    1669             :             {
    1670           0 :                 VSIUnlink(osTmpFilename.c_str());
    1671           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1672             :                          "Cannot open JPEG2000 blob");
    1673           0 :                 return CE_Failure;
    1674             :             }
    1675          24 :             if (poJP2KDS->GetRasterCount() != 1 ||
    1676          48 :                 poJP2KDS->GetRasterXSize() != nBlockXSize ||
    1677          24 :                 poJP2KDS->GetRasterYSize() != nBlockYSize)
    1678             :             {
    1679           0 :                 VSIUnlink(osTmpFilename.c_str());
    1680           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1681             :                          "Inconsistent characteristics of JPEG2000 blob");
    1682           0 :                 return CE_Failure;
    1683             :             }
    1684             : 
    1685          24 :             auto imageDataAndBlock = GetImageData();
    1686          24 :             auto pImageData = imageDataAndBlock.first;
    1687          24 :             auto poBlock = imageDataAndBlock.second;
    1688             : 
    1689             :             const CPLErr eErr =
    1690             :                 pImageData
    1691          24 :                     ? poJP2KDS->GetRasterBand(1)->RasterIO(
    1692             :                           GF_Read, 0, 0, nBlockXSize, nBlockYSize, pImageData,
    1693             :                           nBlockXSize, nBlockYSize, eImageDT, 0, 0, nullptr)
    1694          24 :                     : CE_None;
    1695          24 :             VSIUnlink(osTmpFilename.c_str());
    1696          24 :             if (poBlock)
    1697           0 :                 poBlock->DropLock();
    1698             : 
    1699          24 :             if (eErr != CE_None)
    1700             :             {
    1701           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1702             :                          "Cannot read JPEG2000 blob");
    1703           0 :                 return CE_Failure;
    1704             :             }
    1705             : 
    1706          24 :             break;
    1707             :         }
    1708             :     }
    1709             : 
    1710         117 :     if (m_bIsMask || m_poMaskBand)
    1711             :     {
    1712         114 :         auto maskDataAndBlock = GetMaskData();
    1713         114 :         auto pMaskData = maskDataAndBlock.first;
    1714         114 :         auto poBlock = maskDataAndBlock.second;
    1715             : 
    1716         114 :         if (pMaskData)
    1717             :         {
    1718          64 :             if (pabyMask)
    1719             :             {
    1720             :                 // Unpack 1-bit array
    1721      802865 :                 for (size_t i = 0; i < nPixels; ++i)
    1722             :                 {
    1723      802816 :                     static_cast<GByte *>(pMaskData)[i] =
    1724      802816 :                         (pabyMask[i / 8] & (0x80 >> (i & 7))) ? 255 : 0;
    1725             :                 }
    1726             :             }
    1727             :             else
    1728             :             {
    1729             :                 // No explicit mask in source block --> all valid
    1730          15 :                 memset(pMaskData, 255, nPixels);
    1731             :             }
    1732             :         }
    1733             : 
    1734         114 :         if (poBlock)
    1735         114 :             poBlock->DropLock();
    1736             :     }
    1737           3 :     else if (m_bHasNoData)
    1738             :     {
    1739           3 :         if (eImageDT == GDT_Byte)
    1740             :         {
    1741           0 :             SetNoDataFromMask<uint8_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1742             :         }
    1743           3 :         else if (eImageDT == GDT_Int8)
    1744             :         {
    1745           0 :             SetNoDataFromMask<int8_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1746             :         }
    1747           3 :         else if (eImageDT == GDT_UInt16)
    1748             :         {
    1749           0 :             SetNoDataFromMask<uint16_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1750             :         }
    1751           3 :         else if (eImageDT == GDT_Int16)
    1752             :         {
    1753           0 :             SetNoDataFromMask<int16_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1754             :         }
    1755           3 :         else if (eImageDT == GDT_UInt32)
    1756             :         {
    1757           0 :             SetNoDataFromMask<uint32_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1758             :         }
    1759           3 :         else if (eImageDT == GDT_Int32)
    1760             :         {
    1761           0 :             SetNoDataFromMask<int32_t>(pImage, pabyMask, nPixels, m_dfNoData);
    1762             :         }
    1763           3 :         else if (eImageDT == GDT_Float32)
    1764             :         {
    1765           2 :             if (pabyMask)
    1766             :             {
    1767       32770 :                 for (size_t i = 0; i < nPixels; ++i)
    1768             :                 {
    1769       32768 :                     if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1770             :                     {
    1771       31968 :                         static_cast<float *>(pImage)[i] =
    1772       31968 :                             static_cast<float>(m_dfNoData);
    1773             :                     }
    1774             :                 }
    1775             :             }
    1776             :         }
    1777           1 :         else if (eImageDT == GDT_Float64)
    1778             :         {
    1779           1 :             if (pabyMask)
    1780             :             {
    1781       16385 :                 for (size_t i = 0; i < nPixels; ++i)
    1782             :                 {
    1783       16384 :                     if (!(pabyMask[i / 8] & (0x80 >> (i & 7))))
    1784             :                     {
    1785       15984 :                         static_cast<double *>(pImage)[i] = m_dfNoData;
    1786             :                     }
    1787             :                 }
    1788             :             }
    1789             :         }
    1790             :         else
    1791             :         {
    1792           0 :             CPLAssert(false);
    1793             :         }
    1794             :     }
    1795             : 
    1796             : #if 0
    1797             :     printf("Data:\n"); // ok
    1798             :     if (eDataType == GDT_Byte)
    1799             :     {
    1800             :         for (int y = 0; y < nBlockYSize; ++y)
    1801             :         {
    1802             :             for (int x = 0; x < nBlockXSize; ++x)
    1803             :             {
    1804             :                 printf("%d ", // ok
    1805             :                        static_cast<GByte *>(pImage)[y * nBlockXSize + x]);
    1806             :             }
    1807             :             printf("\n"); // ok
    1808             :         }
    1809             :     }
    1810             :     else if (eDataType == GDT_Int8)
    1811             :     {
    1812             :         for (int y = 0; y < nBlockYSize; ++y)
    1813             :         {
    1814             :             for (int x = 0; x < nBlockXSize; ++x)
    1815             :             {
    1816             :                 printf("%d ", // ok
    1817             :                        static_cast<int8_t *>(pImage)[y * nBlockXSize + x]);
    1818             :             }
    1819             :             printf("\n"); // ok
    1820             :         }
    1821             :     }
    1822             :     else if (eDataType == GDT_UInt16)
    1823             :     {
    1824             :         for (int y = 0; y < nBlockYSize; ++y)
    1825             :         {
    1826             :             for (int x = 0; x < nBlockXSize; ++x)
    1827             :             {
    1828             :                 printf("%d ", // ok
    1829             :                        static_cast<uint16_t *>(pImage)[y * nBlockXSize + x]);
    1830             :             }
    1831             :             printf("\n"); // ok
    1832             :         }
    1833             :     }
    1834             :     else if (eDataType == GDT_Int16)
    1835             :     {
    1836             :         for (int y = 0; y < nBlockYSize; ++y)
    1837             :         {
    1838             :             for (int x = 0; x < nBlockXSize; ++x)
    1839             :             {
    1840             :                 printf("%d ", // ok
    1841             :                        static_cast<int16_t *>(pImage)[y * nBlockXSize + x]);
    1842             :             }
    1843             :             printf("\n"); // ok
    1844             :         }
    1845             :     }
    1846             :     else if (eDataType == GDT_UInt32)
    1847             :     {
    1848             :         for (int y = 0; y < nBlockYSize; ++y)
    1849             :         {
    1850             :             for (int x = 0; x < nBlockXSize; ++x)
    1851             :             {
    1852             :                 printf("%d ", // ok
    1853             :                        static_cast<uint32_t *>(pImage)[y * nBlockXSize + x]);
    1854             :             }
    1855             :             printf("\n"); // ok
    1856             :         }
    1857             :     }
    1858             :     else if (eDataType == GDT_Int32)
    1859             :     {
    1860             :         for (int y = 0; y < nBlockYSize; ++y)
    1861             :         {
    1862             :             for (int x = 0; x < nBlockXSize; ++x)
    1863             :             {
    1864             :                 printf("%d ", // ok
    1865             :                        static_cast<int32_t *>(pImage)[y * nBlockXSize + x]);
    1866             :             }
    1867             :             printf("\n"); // ok
    1868             :         }
    1869             :     }
    1870             :     else if (eDataType == GDT_Float32)
    1871             :     {
    1872             :         for (int y = 0; y < nBlockYSize; ++y)
    1873             :         {
    1874             :             for (int x = 0; x < nBlockXSize; ++x)
    1875             :             {
    1876             :                 printf("%.8g ", // ok
    1877             :                        static_cast<float *>(pImage)[y * nBlockXSize + x]);
    1878             :             }
    1879             :             printf("\n"); // ok
    1880             :         }
    1881             :     }
    1882             :     else if (eDataType == GDT_Float64)
    1883             :     {
    1884             :         for (int y = 0; y < nBlockYSize; ++y)
    1885             :         {
    1886             :             for (int x = 0; x < nBlockXSize; ++x)
    1887             :             {
    1888             :                 printf("%.17g ", // ok
    1889             :                        static_cast<double *>(pImage)[y * nBlockXSize + x]);
    1890             :             }
    1891             :             printf("\n"); // ok
    1892             :         }
    1893             :     }
    1894             : #endif
    1895             : 
    1896             : #if 0
    1897             :     if (pabyMask)
    1898             :     {
    1899             :         printf("Mask:\n"); // ok
    1900             :         for (int y = 0; y < nBlockYSize; ++y)
    1901             :         {
    1902             :             for (int x = 0; x < nBlockXSize; ++x)
    1903             :             {
    1904             :                 printf("%d ", // ok
    1905             :                        (pabyMask[(y * nBlockXSize + x) / 8] &
    1906             :                         (0x80 >> ((y * nBlockXSize + x) & 7)))
    1907             :                            ? 1
    1908             :                            : 0);
    1909             :             }
    1910             :             printf("\n"); // ok
    1911             :         }
    1912             :     }
    1913             : #endif
    1914             : 
    1915         117 :     return CE_None;
    1916             : }
    1917             : 
    1918             : /************************************************************************/
    1919             : /*                         GetDefaultRAT()                              */
    1920             : /************************************************************************/
    1921             : 
    1922           3 : GDALRasterAttributeTable *GDALOpenFileGDBRasterBand::GetDefaultRAT()
    1923             : {
    1924           3 :     if (m_poRAT)
    1925           1 :         return m_poRAT.get();
    1926           2 :     if (poDS->GetRasterCount() > 1 || m_bIsMask)
    1927           0 :         return nullptr;
    1928           2 :     auto poGDS = cpl::down_cast<OGROpenFileGDBDataSource *>(poDS);
    1929             :     const std::string osVATTableName(
    1930           6 :         std::string("VAT_").append(poGDS->m_osRasterLayerName));
    1931             :     // Instantiate a new dataset, os that the RAT is standalone
    1932           4 :     auto poDSNew = std::make_unique<OGROpenFileGDBDataSource>();
    1933           4 :     GDALOpenInfo oOpenInfo(poGDS->m_osDirName.c_str(), GA_ReadOnly);
    1934           2 :     bool bRetryFileGDBUnused = false;
    1935           2 :     if (!poDSNew->Open(&oOpenInfo, bRetryFileGDBUnused))
    1936           0 :         return nullptr;
    1937           4 :     auto poVatLayer = poDSNew->BuildLayerFromName(osVATTableName.c_str());
    1938           2 :     if (!poVatLayer)
    1939           1 :         return nullptr;
    1940           2 :     m_poRAT = std::make_unique<GDALOpenFileGDBRasterAttributeTable>(
    1941           2 :         std::move(poDSNew), osVATTableName, std::move(poVatLayer));
    1942           1 :     return m_poRAT.get();
    1943             : }

Generated by: LCOV version 1.14