LCOV - code coverage report
Current view: top level - frmts/miramon - miramon_band.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 414 553 74.9 %
Date: 2025-09-10 17:48:50 Functions: 25 26 96.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  MiraMonRaster driver
       4             :  * Purpose:  Implements MMRBand class: This class manages the metadata of each
       5             :  *           band to be processed. It is useful for maintaining a list of bands
       6             :  *           and for determining the number of subdatasets that need to be
       7             :  *           generated.
       8             :  * Author:   Abel Pau
       9             :  *
      10             :  ******************************************************************************
      11             :  * Copyright (c) 2025, Xavier Pons
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : #include <algorithm>
      16             : 
      17             : #include "miramon_rel.h"
      18             : #include "miramon_band.h"
      19             : 
      20             : #include "../miramon_common/mm_gdal_driver_structs.h"  // For SECTION_ATTRIBUTE_DATA
      21             : 
      22             : /************************************************************************/
      23             : /*                              MMRBand()                               */
      24             : /************************************************************************/
      25          87 : MMRBand::MMRBand(MMRRel &fRel, const CPLString &osBandSectionIn)
      26             :     : m_pfRel(&fRel), m_nWidth(0), m_nHeight(0),
      27          87 :       m_osBandSection(osBandSectionIn)
      28             : 
      29             : {
      30             :     // Getting band and band file name from metadata
      31          87 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osBandSectionIn,
      32         118 :                                    KEY_NomFitxer, m_osRawBandFileName) ||
      33          31 :         m_osRawBandFileName.empty())
      34             :     {
      35             :         m_osBandFileName =
      36          58 :             m_pfRel->MMRGetFileNameFromRelName(m_pfRel->GetRELName());
      37          58 :         if (m_osBandFileName.empty())
      38             :         {
      39           0 :             m_nWidth = 0;
      40           0 :             m_nHeight = 0;
      41           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
      42             :                      "The REL file '%s' contains a documented \
      43             :                 band with no explicit name. Section [%s] or [%s:%s].",
      44           0 :                      m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
      45             :                      SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
      46           0 :             return;
      47             :         }
      48          58 :         m_osBandName = CPLGetBasenameSafe(m_osBandFileName);
      49          58 :         m_osRawBandFileName = m_osBandName;
      50             :     }
      51             :     else
      52             :     {
      53          29 :         m_osBandName = CPLGetBasenameSafe(m_osRawBandFileName);
      54          29 :         CPLString osAux = CPLGetPathSafe(m_pfRel->GetRELNameChar());
      55             :         m_osBandFileName =
      56          29 :             CPLFormFilenameSafe(osAux.c_str(), m_osRawBandFileName.c_str(), "");
      57             :     }
      58             : 
      59             :     // There is a band file documented?
      60          87 :     if (m_osBandName.empty())
      61             :     {
      62           0 :         m_nWidth = 0;
      63           0 :         m_nHeight = 0;
      64           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
      65             :                  "The REL file '%s' contains a documented \
      66             :             band with no explicit name. Section [%s] or [%s:%s].",
      67           0 :                  m_pfRel->GetRELNameChar(), SECTION_ATTRIBUTE_DATA,
      68             :                  SECTION_ATTRIBUTE_DATA, m_osBandSection.c_str());
      69           0 :         return;
      70             :     }
      71             : 
      72             :     // Getting essential metadata documented at
      73             :     // https://www.miramon.cat/new_note/eng/notes/MiraMon_raster_file_format.pdf
      74             : 
      75             :     // Getting number of columns and rows
      76          87 :     if (!UpdateColumnsNumberFromREL(m_osBandSection))
      77             :     {
      78           1 :         m_nWidth = 0;
      79           1 :         m_nHeight = 0;
      80           1 :         return;
      81             :     }
      82             : 
      83          86 :     if (!UpdateRowsNumberFromREL(m_osBandSection))
      84             :     {
      85           1 :         m_nWidth = 0;
      86           1 :         m_nHeight = 0;
      87           1 :         return;
      88             :     }
      89             : 
      90          85 :     if (m_nWidth <= 0 || m_nHeight <= 0)
      91             :     {
      92           1 :         m_nWidth = 0;
      93           1 :         m_nHeight = 0;
      94           1 :         CPLError(CE_Failure, CPLE_AppDefined,
      95             :                  "MMRBand::MMRBand : (nWidth <= 0 || nHeight <= 0)");
      96           1 :         return;
      97             :     }
      98             : 
      99             :     // Getting data type and compression.
     100             :     // If error, message given inside.
     101          84 :     if (!UpdateDataTypeFromREL(m_osBandSection))
     102           2 :         return;
     103             : 
     104             :     // Let's see if there is RLE compression
     105          82 :     m_bIsCompressed =
     106         104 :         (((m_eMMDataType >= MMDataType::DATATYPE_AND_COMPR_BYTE_RLE) &&
     107         142 :           (m_eMMDataType <= MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE)) ||
     108          60 :          m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT);
     109             : 
     110             :     // Getting min and max values
     111          82 :     UpdateMinMaxValuesFromREL(m_osBandSection);
     112             : 
     113             :     // Getting min and max values for simbolization
     114          82 :     UpdateMinMaxVisuValuesFromREL(m_osBandSection);
     115          82 :     if (!m_bMinVisuSet)
     116             :     {
     117          77 :         if (m_bMinSet)
     118             :         {
     119          74 :             m_dfVisuMin = m_dfMin;
     120          74 :             m_bMinVisuSet = true;
     121             :         }
     122             :     }
     123          82 :     if (!m_bMaxVisuSet)
     124             :     {
     125          77 :         if (m_bMaxSet)
     126             :         {
     127          74 :             m_dfVisuMax = m_dfMax;
     128          74 :             m_bMaxVisuSet = true;
     129             :         }
     130             :     }
     131             : 
     132             :     // Getting the friendly description of the band
     133          82 :     UpdateFriendlyDescriptionFromREL(m_osBandSection);
     134             : 
     135             :     // Getting NoData value and definition
     136          82 :     UpdateNoDataValue(m_osBandSection);
     137             : 
     138             :     // Getting reference system and coordinates of the geographic bounding box
     139          82 :     UpdateReferenceSystemFromREL();
     140             : 
     141             :     // Getting the bounding box: coordinates in the terrain
     142          82 :     UpdateBoundingBoxFromREL(m_osBandSection);
     143             : 
     144             :     // MiraMon IMG files are efficient in going to an specified row.
     145             :     // So le'ts configurate the blocks as line blocks.
     146          82 :     m_nBlockXSize = m_nWidth;
     147          82 :     m_nBlockYSize = 1;
     148          82 :     m_nNRowsPerBlock = 1;
     149             : 
     150             :     // Can the binary file that contains all data for this band be opened?
     151          82 :     m_pfIMG = VSIFOpenL(m_osBandFileName, "rb");
     152          82 :     if (!m_pfIMG)
     153             :     {
     154           1 :         m_nWidth = 0;
     155           1 :         m_nHeight = 0;
     156           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     157             :                  "Failed to open MiraMon band file `%s' with access 'rb'.",
     158             :                  m_osBandFileName.c_str());
     159           1 :         return;
     160             :     }
     161             : 
     162             :     // We have a valid MMRBand.
     163          81 :     m_bIsValid = true;
     164             : }
     165             : 
     166             : /************************************************************************/
     167             : /*                              ~MMRBand()                              */
     168             : /************************************************************************/
     169          87 : MMRBand::~MMRBand()
     170             : 
     171             : {
     172          87 :     if (m_pfIMG != nullptr)
     173          81 :         CPL_IGNORE_RET_VAL(VSIFCloseL(m_pfIMG));
     174          87 : }
     175             : 
     176          18 : const CPLString MMRBand::GetRELFileName() const
     177             : {
     178          18 :     if (!m_pfRel)
     179           0 :         return "";
     180          18 :     return m_pfRel->GetRELName();
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                           GetRasterBlock()                           */
     185             : /************************************************************************/
     186         115 : CPLErr MMRBand::GetRasterBlock(int /*nXBlock*/, int nYBlock, void *pData,
     187             :                                int nDataSize)
     188             : 
     189             : {
     190         115 :     if (nYBlock > INT_MAX / (std::max(1, m_nNRowsPerBlock)))
     191             :     {
     192           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     193           0 :         return CE_Failure;
     194             :     }
     195         115 :     const int iBlock = nYBlock * m_nNRowsPerBlock;
     196             : 
     197         115 :     if (m_nBlockXSize > INT_MAX / (std::max(1, m_nDataTypeSizeBytes)))
     198             :     {
     199           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     200           0 :         return CE_Failure;
     201             :     }
     202             : 
     203         230 :     if (m_nBlockYSize >
     204         115 :         INT_MAX / (std::max(1, m_nDataTypeSizeBytes * m_nBlockXSize)))
     205             :     {
     206           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "Error in GetRasterBlock");
     207           0 :         return CE_Failure;
     208             :     }
     209             : 
     210         115 :     const int nGDALBlockSize =
     211         115 :         m_nDataTypeSizeBytes * m_nBlockXSize * m_nBlockYSize;
     212             : 
     213             :     // Calculate block offset in case we have spill file. Use predefined
     214             :     // block map otherwise.
     215             : 
     216         115 :     if (nDataSize != -1 && nGDALBlockSize > nDataSize)
     217             :     {
     218           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d",
     219             :                  nGDALBlockSize);
     220           0 :         return CE_Failure;
     221             :     }
     222             : 
     223             :     // Getting the row offsets to optimize access.
     224         115 :     if (FillRowOffsets() == false || m_aFileOffsets.empty())
     225             :     {
     226           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     227             :                  "Some error in offsets calculation");
     228           0 :         return CE_Failure;
     229             :     }
     230             : 
     231             :     // Read the block in the documented or deduced offset
     232         115 :     if (VSIFSeekL(m_pfIMG, m_aFileOffsets[iBlock], SEEK_SET))
     233             :     {
     234           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     235             :                  "Read from invalid offset for grid block.");
     236           0 :         return CE_Failure;
     237             :     }
     238             : 
     239             :     size_t nCompressedRawSize;
     240         115 :     if (iBlock == m_nHeight - 1)
     241          35 :         nCompressedRawSize = SIZE_MAX;  // We don't know it
     242             :     else
     243         160 :         nCompressedRawSize = static_cast<size_t>(m_aFileOffsets[iBlock + 1] -
     244          80 :                                                  m_aFileOffsets[iBlock]);
     245             : 
     246         115 :     return GetBlockData(pData, nCompressedRawSize);
     247             : }
     248             : 
     249          63 : void MMRBand::UpdateGeoTransform()
     250             : {
     251          63 :     m_gt[0] = GetBoundingBoxMinX();
     252          63 :     m_gt[1] = (GetBoundingBoxMaxX() - m_gt[0]) / GetWidth();
     253          63 :     m_gt[2] = 0.0;  // No rotation in MiraMon rasters
     254          63 :     m_gt[3] = GetBoundingBoxMaxY();
     255          63 :     m_gt[4] = 0.0;
     256          63 :     m_gt[5] = (GetBoundingBoxMinY() - m_gt[3]) / GetHeight();
     257          63 : }
     258             : 
     259             : /************************************************************************/
     260             : /*                      Other functions                                 */
     261             : /************************************************************************/
     262             : 
     263             : // [ATTRIBUTE_DATA:xxxx] or [OVERVIEW:ASPECTES_TECNICS]
     264         173 : bool MMRBand::Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     265             :     const CPLString &osSection, const char *pszKey, int *nValue,
     266             :     const char *pszErrorMessage)
     267             : {
     268         173 :     if (osSection.empty() || !pszKey || !nValue)
     269           0 :         return false;
     270             : 
     271         346 :     CPLString osValue;
     272         173 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, pszKey,
     273         173 :                                    osValue) ||
     274           0 :         osValue.empty())
     275             :     {
     276         173 :         if (m_pfRel->GetMetadataValue(SECTION_OVERVIEW,
     277             :                                       SECTION_ASPECTES_TECNICS, pszKey,
     278         344 :                                       osValue) == false ||
     279         171 :             osValue.empty())
     280             :         {
     281           2 :             if (pszErrorMessage)
     282           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     283           2 :             return false;
     284             :         }
     285             :     }
     286             : 
     287         171 :     if (1 != sscanf(osValue, "%d", nValue))
     288             :     {
     289           0 :         if (pszErrorMessage)
     290           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
     291           0 :         return false;
     292             :     }
     293         171 :     return true;
     294             : }
     295             : 
     296          83 : bool MMRBand::GetDataTypeAndBytesPerPixel(const char *pszCompType,
     297             :                                           MMDataType *nCompressionType,
     298             :                                           MMBytesPerPixel *nBytesPerPixel)
     299             : {
     300          83 :     if (!nCompressionType || !nBytesPerPixel || !pszCompType)
     301           0 :         return false;
     302             : 
     303          83 :     if (EQUAL(pszCompType, "bit"))
     304             :     {
     305           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BIT;
     306           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     307           2 :         return true;
     308             :     }
     309          81 :     if (EQUAL(pszCompType, "byte"))
     310             :     {
     311          43 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE;
     312          43 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     313          43 :         return true;
     314             :     }
     315          38 :     if (EQUAL(pszCompType, "byte-RLE"))
     316             :     {
     317          10 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_BYTE_RLE;
     318          10 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_BYTE_I_RLE;
     319          10 :         return true;
     320             :     }
     321          28 :     if (EQUAL(pszCompType, "integer"))
     322             :     {
     323           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER;
     324           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     325           2 :         return true;
     326             :     }
     327          26 :     if (EQUAL(pszCompType, "integer-RLE"))
     328             :     {
     329           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE;
     330           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     331           2 :         return true;
     332             :     }
     333          24 :     if (EQUAL(pszCompType, "uinteger"))
     334             :     {
     335           4 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER;
     336           4 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     337           4 :         return true;
     338             :     }
     339          20 :     if (EQUAL(pszCompType, "uinteger-RLE"))
     340             :     {
     341           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE;
     342           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_INTEGER_I_RLE;
     343           2 :         return true;
     344             :     }
     345          18 :     if (EQUAL(pszCompType, "long"))
     346             :     {
     347           3 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG;
     348           3 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     349           3 :         return true;
     350             :     }
     351          15 :     if (EQUAL(pszCompType, "long-RLE"))
     352             :     {
     353           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_LONG_RLE;
     354           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     355           2 :         return true;
     356             :     }
     357          13 :     if (EQUAL(pszCompType, "real"))
     358             :     {
     359           4 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL;
     360           4 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     361           4 :         return true;
     362             :     }
     363           9 :     if (EQUAL(pszCompType, "real-RLE"))
     364             :     {
     365           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_REAL_RLE;
     366           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_LONG_REAL_I_RLE;
     367           2 :         return true;
     368             :     }
     369           7 :     if (EQUAL(pszCompType, "double"))
     370             :     {
     371           2 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE;
     372           2 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     373           2 :         return true;
     374             :     }
     375           5 :     if (EQUAL(pszCompType, "double-RLE"))
     376             :     {
     377           4 :         *nCompressionType = MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE;
     378           4 :         *nBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_DOUBLE_I_RLE;
     379           4 :         return true;
     380             :     }
     381             : 
     382           1 :     return false;
     383             : }
     384             : 
     385             : // Getting data type from metadata
     386          84 : bool MMRBand::UpdateDataTypeFromREL(const CPLString osSection)
     387             : {
     388          84 :     m_eMMDataType = MMDataType::DATATYPE_AND_COMPR_UNDEFINED;
     389          84 :     m_eMMBytesPerPixel = MMBytesPerPixel::TYPE_BYTES_PER_PIXEL_UNDEFINED;
     390             : 
     391         168 :     CPLString osValue;
     392          84 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     393         168 :                                    "TipusCompressio", osValue) ||
     394          84 :         osValue.empty())
     395             :     {
     396           1 :         m_nWidth = 0;
     397           1 :         m_nHeight = 0;
     398           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     399             :                  "MiraMonRaster: no nDataType documented");
     400           1 :         return false;
     401             :     }
     402             : 
     403          83 :     if (!GetDataTypeAndBytesPerPixel(osValue.c_str(), &m_eMMDataType,
     404             :                                      &m_eMMBytesPerPixel))
     405             :     {
     406           1 :         m_nWidth = 0;
     407           1 :         m_nHeight = 0;
     408           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     409             :                  "MiraMonRaster: data type unhandled");
     410           1 :         return false;
     411             :     }
     412             : 
     413          82 :     m_nDataTypeSizeBytes = std::max(1, static_cast<int>(m_eMMBytesPerPixel));
     414          82 :     return true;
     415             : }
     416             : 
     417             : // Getting number of columns from metadata
     418          87 : bool MMRBand::UpdateColumnsNumberFromREL(const CPLString &osSection)
     419             : {
     420          87 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     421             :         osSection, "columns", &m_nWidth,
     422          87 :         "MMRBand::MMRBand : No number of columns documented");
     423             : }
     424             : 
     425          86 : bool MMRBand::UpdateRowsNumberFromREL(const CPLString &osSection)
     426             : {
     427          86 :     return Get_ATTRIBUTE_DATA_or_OVERVIEW_ASPECTES_TECNICS_int(
     428             :         osSection, "rows", &m_nHeight,
     429          86 :         "MMRBand::MMRBand : No number of rows documented");
     430             : }
     431             : 
     432             : // Getting nodata value from metadata
     433          82 : void MMRBand::UpdateNoDataValue(const CPLString &osSection)
     434             : {
     435         164 :     CPLString osValue;
     436          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection, "NODATA",
     437         121 :                                    osValue) ||
     438          39 :         osValue.empty())
     439             :     {
     440          51 :         m_dfNoData = 0;  // No a valid value.
     441          51 :         m_bNoDataSet = false;
     442             :     }
     443             :     else
     444             :     {
     445          31 :         m_dfNoData = CPLAtof(osValue);
     446          31 :         m_bNoDataSet = true;
     447             :     }
     448          82 : }
     449             : 
     450          82 : void MMRBand::UpdateMinMaxValuesFromREL(const CPLString &osSection)
     451             : {
     452          82 :     m_bMinSet = false;
     453             : 
     454         164 :     CPLString osValue;
     455             : 
     456         164 :     CPLString osAuxSection = SECTION_ATTRIBUTE_DATA;
     457          82 :     osAuxSection.append(":");
     458          82 :     osAuxSection.append(osSection);
     459         164 :     if (m_pfRel->GetMetadataValue(osAuxSection, "min", osValue) &&
     460          82 :         !osValue.empty())
     461             :     {
     462          79 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMin))
     463          79 :             m_bMinSet = true;
     464             :     }
     465             : 
     466          82 :     m_bMaxSet = false;
     467         164 :     if (m_pfRel->GetMetadataValue(osAuxSection, "max", osValue) &&
     468          82 :         !osValue.empty())
     469             :     {
     470          79 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfMax))
     471          79 :             m_bMaxSet = true;
     472             :     }
     473             : 
     474             :     // Special case: dfMin > dfMax
     475          82 :     if (m_bMinSet && m_bMaxSet && m_dfMin > m_dfMax)
     476             :     {
     477           0 :         m_bMinSet = false;
     478           0 :         m_bMaxSet = false;
     479             :     }
     480          82 : }
     481             : 
     482          82 : void MMRBand::UpdateMinMaxVisuValuesFromREL(const CPLString &osSection)
     483             : {
     484          82 :     m_bMinVisuSet = false;
     485          82 :     m_dfVisuMin = 1;
     486             : 
     487         164 :     CPLString osValue;
     488          82 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     489          87 :                                   "Color_ValorColor_0", osValue) &&
     490           5 :         !osValue.empty())
     491             :     {
     492           5 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMin))
     493           5 :             m_bMinVisuSet = true;
     494             :     }
     495             : 
     496          82 :     m_bMaxVisuSet = false;
     497          82 :     m_dfVisuMax = 1;
     498             : 
     499          82 :     if (m_pfRel->GetMetadataValue(SECTION_COLOR_TEXT, osSection,
     500          87 :                                   "Color_ValorColor_n_1", osValue) &&
     501           5 :         !osValue.empty())
     502             :     {
     503           5 :         if (1 == CPLsscanf(osValue, "%lf", &m_dfVisuMax))
     504           5 :             m_bMaxVisuSet = true;
     505             :     }
     506          82 : }
     507             : 
     508          82 : void MMRBand::UpdateFriendlyDescriptionFromREL(const CPLString &osSection)
     509             : {
     510             :     // This "if" is due to CID 1620830 in Coverity Scan
     511          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     512          82 :                                    "descriptor", m_osFriendlyDescription))
     513          10 :         m_osFriendlyDescription = "";
     514          82 : }
     515             : 
     516          82 : void MMRBand::UpdateReferenceSystemFromREL()
     517             : {
     518             :     // This "if" is due to CID 1620842 in Coverity Scan
     519          82 :     if (!m_pfRel->GetMetadataValue("SPATIAL_REFERENCE_SYSTEM:HORIZONTAL",
     520          82 :                                    "HorizontalSystemIdentifier", m_osRefSystem))
     521           0 :         m_osRefSystem = "";
     522          82 : }
     523             : 
     524          82 : void MMRBand::UpdateBoundingBoxFromREL(const CPLString &osSection)
     525             : {
     526             :     // Bounding box of the band
     527             :     // [ATTRIBUTE_DATA:xxxx:EXTENT] or [EXTENT]
     528         164 :     CPLString osValue;
     529          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     530         154 :                                    SECTION_EXTENT, "MinX", osValue) ||
     531          72 :         osValue.empty())
     532             :     {
     533          10 :         m_dfBBMinX = 0;
     534             :     }
     535             :     else
     536             :     {
     537          72 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinX))
     538           0 :             m_dfBBMinX = 0;
     539             :     }
     540             : 
     541          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     542         154 :                                    SECTION_EXTENT, "MaxX", osValue) ||
     543          72 :         osValue.empty())
     544             :     {
     545          10 :         m_dfBBMaxX = m_nWidth;
     546             :     }
     547             :     else
     548             :     {
     549          72 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxX))
     550             :         {
     551             :             // If the value is something that cannot be scanned,
     552             :             // we silently continue as it was undefined.
     553           0 :             m_dfBBMaxX = m_nWidth;
     554             :         }
     555             :     }
     556             : 
     557          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     558         154 :                                    SECTION_EXTENT, "MinY", osValue) ||
     559          72 :         osValue.empty())
     560             :     {
     561          10 :         m_dfBBMinY = 0;
     562             :     }
     563             :     else
     564             :     {
     565          72 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMinY))
     566           0 :             m_dfBBMinY = 0;
     567             :     }
     568             : 
     569          82 :     if (!m_pfRel->GetMetadataValue(SECTION_ATTRIBUTE_DATA, osSection,
     570         154 :                                    SECTION_EXTENT, "MaxY", osValue) ||
     571          72 :         osValue.empty())
     572             :     {
     573          10 :         m_dfBBMaxY = m_nHeight;
     574             :     }
     575             :     else
     576             :     {
     577          72 :         if (1 != CPLsscanf(osValue, "%lf", &m_dfBBMaxY))
     578             :         {
     579             :             // If the value is something that cannot be scanned,
     580             :             // we silently continue as it was undefined.
     581           0 :             m_dfBBMaxY = m_nHeight;
     582             :         }
     583             :     }
     584          82 : }
     585             : 
     586             : /************************************************************************/
     587             : /*          Functions that read bytes from IMG file band                */
     588             : /************************************************************************/
     589             : template <typename TYPE>
     590          48 : CPLErr MMRBand::UncompressRow(void *rowBuffer, size_t nCompressedRawSize)
     591             : {
     592          48 :     int nAccumulated = 0L, nIAccumulated = 0L;
     593             :     unsigned char cCounter;
     594          48 :     size_t nCompressedIndex = 0;
     595             : 
     596             :     TYPE RLEValue;
     597             :     TYPE *pDst;
     598          48 :     size_t sizeof_TYPE = sizeof(TYPE);
     599             : 
     600          96 :     std::vector<unsigned char> aCompressedRow;
     601             : 
     602          48 :     if (nCompressedRawSize != SIZE_MAX)
     603             :     {
     604          28 :         if (nCompressedRawSize > 1000 * 1000 &&
     605           0 :             GetFileSize() < nCompressedRawSize)
     606             :         {
     607           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
     608           0 :             return CE_Failure;
     609             :         }
     610             :         try
     611             :         {
     612          28 :             aCompressedRow.resize(nCompressedRawSize);
     613             :         }
     614           0 :         catch (const std::exception &)
     615             :         {
     616           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
     617             :                      "Out of memory allocating working buffer");
     618           0 :             return CE_Failure;
     619             :         }
     620          28 :         if (VSIFReadL(aCompressedRow.data(), nCompressedRawSize, 1, m_pfIMG) !=
     621             :             1)
     622           0 :             return CE_Failure;
     623             :     }
     624             : 
     625         144 :     while (nAccumulated < m_nWidth)
     626             :     {
     627          96 :         if (nCompressedRawSize == SIZE_MAX)
     628             :         {
     629          40 :             if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     630           0 :                 return CE_Failure;
     631             :         }
     632             :         else
     633             :         {
     634          56 :             if (nCompressedIndex >= aCompressedRow.size())
     635             :             {
     636           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     637             :                          "Invalid nCompressedIndex");
     638           0 :                 return CE_Failure;
     639             :             }
     640          56 :             cCounter = aCompressedRow[nCompressedIndex];
     641          56 :             nCompressedIndex++;
     642             :         }
     643             : 
     644          96 :         if (cCounter == 0) /* Not compressed part */
     645             :         {
     646             :             /* The following counter read does not indicate
     647             :             "how many repeated values follow" but rather
     648             :             "how many are decompressed in standard raster format" */
     649           0 :             if (nCompressedRawSize == SIZE_MAX)
     650             :             {
     651           0 :                 if (VSIFReadL(&cCounter, 1, 1, m_pfIMG) != 1)
     652           0 :                     return CE_Failure;
     653             :             }
     654             :             else
     655             :             {
     656           0 :                 if (nCompressedIndex >= aCompressedRow.size())
     657             :                 {
     658           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     659             :                              "Invalid nCompressedIndex");
     660           0 :                     return CE_Failure;
     661             :                 }
     662           0 :                 cCounter = aCompressedRow[nCompressedIndex];
     663           0 :                 nCompressedIndex++;
     664             :             }
     665             : 
     666           0 :             nAccumulated += cCounter;
     667             : 
     668           0 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     669             :                                   is RLE and does not share counters across rows */
     670           0 :                 return CE_Failure;
     671             : 
     672           0 :             for (; nIAccumulated < nAccumulated; nIAccumulated++)
     673             :             {
     674           0 :                 if (nCompressedRawSize == SIZE_MAX)
     675             :                 {
     676           0 :                     VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG);
     677           0 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     678             :                            &RLEValue, sizeof_TYPE);
     679             :                 }
     680             :                 else
     681             :                 {
     682           0 :                     if (nCompressedIndex + sizeof_TYPE > aCompressedRow.size())
     683             :                     {
     684           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     685             :                                  "Invalid nCompressedIndex");
     686           0 :                         return CE_Failure;
     687             :                     }
     688           0 :                     memcpy((static_cast<TYPE *>(rowBuffer)) + nIAccumulated,
     689           0 :                            &aCompressedRow[nCompressedIndex], sizeof_TYPE);
     690           0 :                     nCompressedIndex += sizeof_TYPE;
     691             :                 }
     692             :             }
     693             :         }
     694             :         else
     695             :         {
     696          96 :             nAccumulated += cCounter;
     697          96 :             if (nAccumulated > m_nWidth) /* This should not happen if the file
     698             :                                   is RLE and does not share counters across rows */
     699           0 :                 return CE_Failure;
     700             : 
     701          96 :             if (nCompressedRawSize == SIZE_MAX)
     702             :             {
     703          40 :                 if (VSIFReadL(&RLEValue, sizeof_TYPE, 1, m_pfIMG) != 1)
     704           0 :                     return CE_Failure;
     705             :             }
     706             :             else
     707             :             {
     708          56 :                 if (nCompressedIndex + sizeof(TYPE) > aCompressedRow.size())
     709             :                 {
     710           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     711             :                              "Invalid nCompressedIndex");
     712           0 :                     return CE_Failure;
     713             :                 }
     714          56 :                 memcpy(&RLEValue, &aCompressedRow[nCompressedIndex],
     715             :                        sizeof(TYPE));
     716          56 :                 nCompressedIndex += sizeof(TYPE);
     717             :             }
     718             : 
     719          96 :             const int nCount = nAccumulated - nIAccumulated;
     720          96 :             pDst = static_cast<TYPE *>(rowBuffer) + nIAccumulated;
     721             : 
     722          96 :             std::fill(pDst, pDst + nCount, RLEValue);
     723             : 
     724          96 :             nIAccumulated = nAccumulated;
     725             :         }
     726             :     }
     727             : 
     728          48 :     return CE_None;
     729             : }
     730             : 
     731         121 : CPLErr MMRBand::GetBlockData(void *rowBuffer, size_t nCompressedRawSize)
     732             : {
     733         121 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BIT)
     734             :     {
     735          16 :         const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
     736             : 
     737          16 :         if (VSIFReadL(rowBuffer, nGDALBlockSize, 1, m_pfIMG) != 1)
     738             :         {
     739           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     740           0 :             return CE_Failure;
     741             :         }
     742          16 :         return CE_None;
     743             :     }
     744             : 
     745         105 :     if (m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_BYTE ||
     746          78 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_INTEGER ||
     747          72 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_UINTEGER ||
     748          66 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_LONG ||
     749          60 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_REAL ||
     750          54 :         m_eMMDataType == MMDataType::DATATYPE_AND_COMPR_DOUBLE)
     751             :     {
     752          57 :         if (VSIFReadL(rowBuffer, m_nDataTypeSizeBytes, m_nWidth, m_pfIMG) !=
     753          57 :             static_cast<size_t>(m_nWidth))
     754             :         {
     755           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error while reading band");
     756           0 :             return CE_Failure;
     757             :         }
     758          57 :         return CE_None;
     759             :     }
     760             : 
     761             :     CPLErr eErr;
     762          48 :     switch (m_eMMDataType)
     763             :     {
     764          18 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
     765          18 :             eErr = UncompressRow<GByte>(rowBuffer, nCompressedRawSize);
     766          18 :             break;
     767           6 :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
     768           6 :             eErr = UncompressRow<GInt16>(rowBuffer, nCompressedRawSize);
     769           6 :             break;
     770           6 :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
     771           6 :             eErr = UncompressRow<GUInt16>(rowBuffer, nCompressedRawSize);
     772           6 :             break;
     773           6 :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
     774           6 :             eErr = UncompressRow<GInt32>(rowBuffer, nCompressedRawSize);
     775           6 :             break;
     776           6 :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
     777           6 :             eErr = UncompressRow<float>(rowBuffer, nCompressedRawSize);
     778           6 :             break;
     779           6 :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
     780           6 :             eErr = UncompressRow<double>(rowBuffer, nCompressedRawSize);
     781           6 :             break;
     782             : 
     783           0 :         default:
     784           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Error in datatype");
     785           0 :             eErr = CE_Failure;
     786             :     }
     787             : 
     788          48 :     return eErr;
     789             : }  // End of GetBlockData()
     790             : 
     791          14 : int MMRBand::PositionAtStartOfRowOffsetsInFile()
     792             : {
     793             :     vsi_l_offset nFileSize, nHeaderOffset;
     794             :     char szChain[16];
     795             :     short int nVersion, nSubVersion;
     796             :     int nOffsetSize, nOffsetsSectionType;
     797             : 
     798          14 :     if (VSIFSeekL(m_pfIMG, 0, SEEK_END))
     799           0 :         return 0;
     800             : 
     801          14 :     nFileSize = VSIFTellL(m_pfIMG);
     802             : 
     803          14 :     if (nFileSize < 32)  // Minimum required size
     804           2 :         return 0;
     805             : 
     806          12 :     if (m_nHeight)
     807             :     {
     808          12 :         if (nFileSize < static_cast<vsi_l_offset>(32) + m_nHeight + 32)
     809           0 :             return 0;
     810             :     }
     811             : 
     812          12 :     vsi_l_offset nHeadOffset = nFileSize - 32;
     813             : 
     814          12 :     if (VSIFSeekL(m_pfIMG, nHeadOffset, SEEK_SET))  // Reading final header.
     815           0 :         return 0;
     816          12 :     if (VSIFReadL(szChain, 16, 1, m_pfIMG) != 1)
     817           0 :         return 0;
     818         204 :     for (int nIndex = 0; nIndex < 16; nIndex++)
     819             :     {
     820         192 :         if (szChain[nIndex] != '\0')
     821           0 :             return 0;  // Supposed 0's are not 0.
     822             :     }
     823             : 
     824          12 :     if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
     825           0 :         return 0;
     826             : 
     827          12 :     if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
     828           0 :         return 0;
     829             : 
     830             :     // Some version checks
     831          12 :     szChain[7] = 0;
     832          12 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
     833           0 :         return 0;
     834             : 
     835          12 :     szChain[5] = 0;
     836          12 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
     837           0 :         return 0;
     838             : 
     839             :     // Next header to be read
     840          12 :     if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) != 1)
     841           0 :         return 0;
     842             : 
     843          24 :     std::set<vsi_l_offset> alreadyVisitedOffsets;
     844             :     bool bRepeat;
     845          12 :     do
     846             :     {
     847          12 :         bRepeat = FALSE;
     848             : 
     849          12 :         if (VSIFSeekL(m_pfIMG, nHeaderOffset, SEEK_SET))
     850           0 :             return 0;
     851             : 
     852          12 :         if (VSIFReadL(szChain, 8, 1, m_pfIMG) != 1)
     853           0 :             return 0;
     854             : 
     855          12 :         if (strncmp(szChain, "IMG ", 4) || szChain[5] != '.')
     856           0 :             return 0;
     857             : 
     858          12 :         if (VSIFReadL(&nOffsetsSectionType, 4, 1, m_pfIMG) != 1)
     859           0 :             return 0;
     860             : 
     861          12 :         if (nOffsetsSectionType != 2)  // 2 = row offsets section
     862             :         {
     863             :             // This is not the section I am looking for
     864           0 :             if (VSIFSeekL(m_pfIMG, 8 + 4, SEEK_CUR))
     865           0 :                 return 0;
     866             : 
     867           0 :             if (VSIFReadL(&nHeaderOffset, sizeof(vsi_l_offset), 1, m_pfIMG) !=
     868             :                 1)
     869           0 :                 return 0;
     870             : 
     871           0 :             if (nHeaderOffset == 0)
     872           0 :                 return 0;
     873             : 
     874           0 :             if (cpl::contains(alreadyVisitedOffsets, nHeaderOffset))
     875             :             {
     876           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     877             :                          "Error reading offsets. They will be ignored.");
     878           0 :                 return 0;
     879             :             }
     880             : 
     881           0 :             alreadyVisitedOffsets.insert(nHeaderOffset);
     882             : 
     883           0 :             bRepeat = TRUE;
     884             :         }
     885             : 
     886             :     } while (bRepeat);
     887             : 
     888          12 :     szChain[7] = 0;
     889          12 :     if (sscanf(szChain + 6, "%hd", &nSubVersion) != 1 || nSubVersion < 0)
     890           0 :         return 0;
     891          12 :     szChain[5] = 0;
     892          12 :     if (sscanf(szChain + 4, "%hd", &nVersion) != 1 || nVersion != 1)
     893           0 :         return 0;
     894             : 
     895             :     /*
     896             :         Now I'm in the correct section
     897             :         -------------------------------
     898             :         Info about this section:
     899             :         RasterRLE: minimum size: nHeight*2
     900             :         Offsets:   minimum size: 32+nHeight*4
     901             :         Final:     size: 32
     902             :     */
     903             : 
     904          12 :     if (m_nHeight)
     905             :     {
     906          12 :         if (nHeaderOffset < static_cast<vsi_l_offset>(m_nHeight) *
     907          12 :                                 2 ||  // Minimum size of an RLE
     908          12 :             nFileSize - nHeaderOffset <
     909          12 :                 static_cast<vsi_l_offset>(32) + m_nHeight +
     910             :                     32)  // Minimum size of the section in version 1.0
     911           0 :             return 0;
     912             :     }
     913             : 
     914          24 :     if (VSIFReadL(&nOffsetSize, 4, 1, m_pfIMG) != 1 ||
     915          12 :         (nOffsetSize != 8 && nOffsetSize != 4 && nOffsetSize != 2 &&
     916          12 :          nOffsetSize != 1))
     917           0 :         return 0;
     918             : 
     919          12 :     if (m_nHeight)
     920             :     {
     921          12 :         if (nFileSize - nHeaderOffset <
     922          12 :             32 + static_cast<vsi_l_offset>(nOffsetSize) * m_nHeight +
     923             :                 32)  // No space for this section in this file
     924           0 :             return 0;
     925             : 
     926             :         // I leave the file prepared to read offsets
     927          12 :         if (VSIFSeekL(m_pfIMG, 16, SEEK_CUR))
     928           0 :             return 0;
     929             :     }
     930             :     else
     931             :     {
     932           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
     933           0 :             return 0;
     934             : 
     935           0 :         if (VSIFSeekL(m_pfIMG, 4, SEEK_CUR))
     936           0 :             return 0;
     937             : 
     938             :         // I leave the file prepared to read offsets
     939           0 :         if (VSIFSeekL(m_pfIMG, 8, SEEK_CUR))
     940           0 :             return 0;
     941             :     }
     942             : 
     943             :     // There are offsets!
     944          12 :     return nOffsetSize;
     945             : }  // Fi de PositionAtStartOfRowOffsetsInFile()
     946             : 
     947             : /************************************************************************/
     948             : /*                              GetFileSize()                           */
     949             : /************************************************************************/
     950             : 
     951           0 : vsi_l_offset MMRBand::GetFileSize()
     952             : {
     953           0 :     if (m_nFileSize == 0)
     954             :     {
     955           0 :         const auto nCurPos = VSIFTellL(m_pfIMG);
     956           0 :         VSIFSeekL(m_pfIMG, 0, SEEK_END);
     957           0 :         m_nFileSize = VSIFTellL(m_pfIMG);
     958           0 :         VSIFSeekL(m_pfIMG, nCurPos, SEEK_SET);
     959             :     }
     960           0 :     return m_nFileSize;
     961             : }
     962             : 
     963             : /************************************************************************/
     964             : /*                              FillRowOffsets()                        */
     965             : /************************************************************************/
     966             : 
     967         115 : bool MMRBand::FillRowOffsets()
     968             : {
     969             :     vsi_l_offset nStartOffset;
     970             :     int nIRow;
     971             :     vsi_l_offset nBytesPerPixelPerNCol;
     972             :     int nSizeToRead;  // nSizeToRead is not an offset, but the size of the offsets being read
     973             :                       // directly from the IMG file (can be 1, 2, 4, or 8).
     974             :     vsi_l_offset nFileByte;
     975             :     size_t nMaxBytesPerCompressedRow;
     976         115 :     const int nGDALBlockSize = DIV_ROUND_UP(m_nBlockXSize, 8);
     977             : 
     978             :     // If it's filled, there is no need to fill it again
     979         115 :     if (!m_aFileOffsets.empty())
     980          80 :         return true;
     981             : 
     982             :     // Sanity check to avoid attempting huge memory allocation
     983          35 :     if (m_nHeight > 1000 * 1000)
     984             :     {
     985           0 :         if (GetFileSize() < static_cast<vsi_l_offset>(m_nHeight))
     986             :         {
     987           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too small file");
     988           0 :             return false;
     989             :         }
     990             :     }
     991             : 
     992             :     try
     993             :     {
     994          35 :         m_aFileOffsets.resize(static_cast<size_t>(m_nHeight) + 1);
     995             :     }
     996           0 :     catch (const std::bad_alloc &e)
     997             :     {
     998           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     999           0 :         return false;
    1000             :     }
    1001             : 
    1002          35 :     switch (m_eMMDataType)
    1003             :     {
    1004           2 :         case MMDataType::DATATYPE_AND_COMPR_BIT:
    1005             : 
    1006             :             // "<=" it's ok. There is space and it's to make easier the programming
    1007          20 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1008          18 :                 m_aFileOffsets[nIRow] =
    1009          18 :                     static_cast<vsi_l_offset>(nIRow) * nGDALBlockSize;
    1010           2 :             break;
    1011             : 
    1012          19 :         case MMDataType::DATATYPE_AND_COMPR_BYTE:
    1013             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER:
    1014             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER:
    1015             :         case MMDataType::DATATYPE_AND_COMPR_LONG:
    1016             :         case MMDataType::DATATYPE_AND_COMPR_REAL:
    1017             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE:
    1018          19 :             nBytesPerPixelPerNCol =
    1019          19 :                 m_nDataTypeSizeBytes * static_cast<vsi_l_offset>(m_nWidth);
    1020             :             // "<=" it's ok. There is space and it's to make easier the programming
    1021          95 :             for (nIRow = 0; nIRow <= m_nHeight; nIRow++)
    1022          76 :                 m_aFileOffsets[nIRow] =
    1023          76 :                     static_cast<vsi_l_offset>(nIRow) * nBytesPerPixelPerNCol;
    1024          19 :             break;
    1025             : 
    1026          14 :         case MMDataType::DATATYPE_AND_COMPR_BYTE_RLE:
    1027             :         case MMDataType::DATATYPE_AND_COMPR_INTEGER_RLE:
    1028             :         case MMDataType::DATATYPE_AND_COMPR_UINTEGER_RLE:
    1029             :         case MMDataType::DATATYPE_AND_COMPR_LONG_RLE:
    1030             :         case MMDataType::DATATYPE_AND_COMPR_REAL_RLE:
    1031             :         case MMDataType::DATATYPE_AND_COMPR_DOUBLE_RLE:
    1032             : 
    1033          14 :             nStartOffset = VSIFTellL(m_pfIMG);
    1034             : 
    1035             :             // Let's determine if are there offsets in the file
    1036          14 :             if (0 < (nSizeToRead = PositionAtStartOfRowOffsetsInFile()))
    1037             :             {
    1038             :                 // I have offsets!!
    1039          12 :                 nFileByte = 0L;  // all bits to 0
    1040          48 :                 for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1041             :                 {
    1042          36 :                     if (VSIFReadL(&nFileByte, nSizeToRead, 1, m_pfIMG) != 1)
    1043           0 :                         return false;
    1044             : 
    1045          36 :                     m_aFileOffsets[nIRow] = nFileByte;
    1046             : 
    1047             :                     // Let's check that the difference between two offsets is in a int range
    1048          36 :                     if (nIRow > 0)
    1049             :                     {
    1050          48 :                         if (m_aFileOffsets[nIRow] <=
    1051          24 :                             m_aFileOffsets[static_cast<size_t>(nIRow) - 1])
    1052           0 :                             return false;
    1053             : 
    1054          24 :                         if (m_aFileOffsets[nIRow] -
    1055          24 :                                 m_aFileOffsets[static_cast<size_t>(nIRow) -
    1056          24 :                                                1] >=
    1057             :                             static_cast<vsi_l_offset>(SIZE_MAX))
    1058           0 :                             return false;
    1059             :                     }
    1060             :                 }
    1061          12 :                 m_aFileOffsets[nIRow] = 0;  // Not reliable
    1062          12 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1063          12 :                 break;
    1064             :             }
    1065             : 
    1066             :             // Not indexed RLE. We create a dynamic indexation
    1067           4 :             if (m_nWidth >
    1068           2 :                 INT_MAX /
    1069           2 :                     (std::max(1, static_cast<int>(m_eMMBytesPerPixel)) + 1))
    1070             :             {
    1071           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large row: %d",
    1072             :                          m_nWidth);
    1073           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1074           0 :                 return false;
    1075             :             }
    1076             : 
    1077           2 :             nMaxBytesPerCompressedRow =
    1078           2 :                 static_cast<int>(m_eMMBytesPerPixel)
    1079           2 :                     ? (m_nWidth * (static_cast<int>(m_eMMBytesPerPixel) + 1))
    1080           0 :                     : (m_nWidth * (1 + 1));
    1081             :             unsigned char *pBuffer;
    1082             : 
    1083           2 :             if (nullptr == (pBuffer = static_cast<unsigned char *>(
    1084           2 :                                 VSI_MALLOC_VERBOSE(nMaxBytesPerCompressedRow))))
    1085             :             {
    1086           0 :                 VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1087           0 :                 return false;
    1088             :             }
    1089             : 
    1090           2 :             VSIFSeekL(m_pfIMG, 0, SEEK_SET);
    1091           2 :             m_aFileOffsets[0] = 0;
    1092           8 :             for (nIRow = 0; nIRow < m_nHeight; nIRow++)
    1093             :             {
    1094           6 :                 GetBlockData(pBuffer, SIZE_MAX);
    1095           6 :                 m_aFileOffsets[static_cast<size_t>(nIRow) + 1] =
    1096           6 :                     VSIFTellL(m_pfIMG);
    1097             :             }
    1098           2 :             VSIFree(pBuffer);
    1099           2 :             VSIFSeekL(m_pfIMG, nStartOffset, SEEK_SET);
    1100           2 :             break;
    1101             : 
    1102           0 :         default:
    1103           0 :             return false;
    1104             :     }  // End of switch (eMMDataType)
    1105          35 :     return true;
    1106             : 
    1107             : }  // End of FillRowOffsets()

Generated by: LCOV version 1.14