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

Generated by: LCOV version 1.14