LCOV - code coverage report
Current view: top level - frmts/bmp - bmpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 448 671 66.8 %
Date: 2024-11-21 22:18:42 Functions: 22 24 91.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Microsoft Windows Bitmap
       4             :  * Purpose:  Read/write MS Windows Device Independent Bitmap (DIB) files
       5             :  *           and OS/2 Presentation Manager bitmaps v. 1.x and v. 2.x
       6             :  * Author:   Andrey Kiselev, dron@remotesensing.org
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org>
      10             :  * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_string.h"
      16             : #include "gdal_frmts.h"
      17             : #include "gdal_pam.h"
      18             : 
      19             : #include <limits>
      20             : 
      21             : // Enable if you want to see lots of BMP debugging output.
      22             : // #define BMP_DEBUG
      23             : 
      24             : enum BMPType
      25             : {
      26             :     BMPT_WIN4,  // BMP used in Windows 3.0/NT 3.51/95
      27             :     BMPT_WIN5,  // BMP used in Windows NT 4.0/98/Me/2000/XP
      28             :     BMPT_OS21,  // BMP used in OS/2 PM 1.x
      29             :     BMPT_OS22   // BMP used in OS/2 PM 2.x
      30             : };
      31             : 
      32             : // Bitmap file consists of a BMPFileHeader structure followed by a
      33             : // BMPInfoHeader structure. An array of BMPColorEntry structures (also called
      34             : // a colour table) follows the bitmap information header structure. The colour
      35             : // table is followed by a second array of indexes into the colour table (the
      36             : // actual bitmap data). Data may be compressed, for 4-bpp and 8-bpp used RLE
      37             : // compression.
      38             : //
      39             : // +---------------------+
      40             : // | BMPFileHeader       |
      41             : // +---------------------+
      42             : // | BMPInfoHeader       |
      43             : // +---------------------+
      44             : // | BMPColorEntry array |
      45             : // +---------------------+
      46             : // | Colour-index array  |
      47             : // +---------------------+
      48             : //
      49             : // All numbers stored in Intel order with least significant byte first.
      50             : 
      51             : enum BMPComprMethod
      52             : {
      53             :     BMPC_RGB = 0L,        // Uncompressed
      54             :     BMPC_RLE8 = 1L,       // RLE for 8 bpp images
      55             :     BMPC_RLE4 = 2L,       // RLE for 4 bpp images
      56             :     BMPC_BITFIELDS = 3L,  // Bitmap is not compressed and the colour table
      57             :                           // consists of three DWORD color masks that specify
      58             :                           // the red, green, and blue components of each pixel.
      59             :     // This is valid when used with 16- and 32-bpp bitmaps.
      60             :     BMPC_JPEG = 4L,  // Indicates that the image is a JPEG image.
      61             :     BMPC_PNG = 5L    // Indicates that the image is a PNG image.
      62             : };
      63             : 
      64             : enum BMPLCSType  // Type of logical color space.
      65             : {
      66             :     BMPLT_CALIBRATED_RGB = 0,  // This value indicates that endpoints and gamma
      67             :                                // values are given in the appropriate fields.
      68             :     BMPLT_DEVICE_RGB = 1,
      69             :     BMPLT_DEVICE_CMYK = 2
      70             : };
      71             : 
      72             : typedef struct
      73             : {
      74             :     // cppcheck-suppress unusedStructMember
      75             :     GInt32 iCIEX;
      76             :     // cppcheck-suppress unusedStructMember
      77             :     GInt32 iCIEY;
      78             :     // cppcheck-suppress unusedStructMember
      79             :     GInt32 iCIEZ;
      80             : } BMPCIEXYZ;
      81             : 
      82             : typedef struct  // This structure contains the x, y, and z
      83             : {               // coordinates of the three colors that correspond
      84             :     // cppcheck-suppress unusedStructMember
      85             :     BMPCIEXYZ iCIERed;  // to the red, green, and blue endpoints for
      86             :     // cppcheck-suppress unusedStructMember
      87             :     BMPCIEXYZ iCIEGreen;  // a specified logical color space.
      88             :     // cppcheck-suppress unusedStructMember
      89             :     BMPCIEXYZ iCIEBlue;
      90             : } BMPCIEXYZTriple;
      91             : 
      92             : typedef struct
      93             : {
      94             :     GByte bType[2];      // Signature "BM"
      95             :     GUInt32 iSize;       // Size in bytes of the bitmap file. Should always
      96             :                          // be ignored while reading because of error
      97             :                          // in Windows 3.0 SDK's description of this field
      98             :     GUInt16 iReserved1;  // Reserved, set as 0
      99             :     GUInt16 iReserved2;  // Reserved, set as 0
     100             :     GUInt32 iOffBits;    // Offset of the image from file start in bytes
     101             : } BMPFileHeader;
     102             : 
     103             : // File header size in bytes:
     104             : constexpr int BFH_SIZE = 14;
     105             : 
     106             : // Size of sInfoHeader.iSize in bytes
     107             : constexpr int SIZE_OF_INFOHEADER_SIZE = 4;
     108             : 
     109             : typedef struct
     110             : {
     111             :     GUInt32 iSize;      // Size of BMPInfoHeader structure in bytes.
     112             :                         // Should be used to determine start of the
     113             :                         // colour table
     114             :     GInt32 iWidth;      // Image width
     115             :     GInt32 iHeight;     // Image height. If positive, image has bottom left
     116             :                         // origin, if negative --- top left.
     117             :     GUInt16 iPlanes;    // Number of image planes (must be set to 1)
     118             :     GUInt16 iBitCount;  // Number of bits per pixel (1, 4, 8, 16, 24 or 32).
     119             :                         // If 0 then the number of bits per pixel is
     120             :                         // specified or is implied by the JPEG or PNG format.
     121             :     BMPComprMethod iCompression;  // Compression method
     122             :     GUInt32 iSizeImage;     // Size of uncompressed image in bytes. May be 0
     123             :                             // for BMPC_RGB bitmaps. If iCompression is BI_JPEG
     124             :                             // or BI_PNG, iSizeImage indicates the size
     125             :                             // of the JPEG or PNG image buffer.
     126             :     GInt32 iXPelsPerMeter;  // X resolution, pixels per meter (0 if not used)
     127             :     GInt32 iYPelsPerMeter;  // Y resolution, pixels per meter (0 if not used)
     128             :     GUInt32 iClrUsed;       // Size of colour table. If 0, iBitCount should
     129             :                             // be used to calculate this value (1<<iBitCount)
     130             :     GUInt32 iClrImportant;  // Number of important colours. If 0, all
     131             :                             // colours are required
     132             : 
     133             :     // Fields above should be used for bitmaps, compatible with Windows NT 3.51
     134             :     // and earlier. Windows 98/Me, Windows 2000/XP introduces additional fields:
     135             : 
     136             :     GUInt32 iRedMask;    // Colour mask that specifies the red component
     137             :                          // of each pixel, valid only if iCompression
     138             :                          // is set to BI_BITFIELDS.
     139             :     GUInt32 iGreenMask;  // The same for green component
     140             :     GUInt32 iBlueMask;   // The same for blue component
     141             :     // cppcheck-suppress unusedStructMember
     142             :     GUInt32 iAlphaMask;  // Colour mask that specifies the alpha
     143             :                          // component of each pixel.
     144             :     // cppcheck-suppress unusedStructMember
     145             :     BMPLCSType iCSType;  // Colour space of the DIB.
     146             :     // cppcheck-suppress unusedStructMember
     147             :     BMPCIEXYZTriple sEndpoints;  // This member is ignored unless the iCSType
     148             :                                  // member specifies BMPLT_CALIBRATED_RGB.
     149             :     // cppcheck-suppress unusedStructMember
     150             :     GUInt32 iGammaRed;  // Toned response curve for red. This member
     151             :                         // is ignored unless color values are calibrated
     152             :                         // RGB values and iCSType is set to
     153             :                         // BMPLT_CALIBRATED_RGB. Specified in 16^16 format.
     154             :     // cppcheck-suppress unusedStructMember
     155             :     GUInt32 iGammaGreen;  // Toned response curve for green.
     156             :     // cppcheck-suppress unusedStructMember
     157             :     GUInt32 iGammaBlue;  // Toned response curve for blue.
     158             : } BMPInfoHeader;
     159             : 
     160             : // Info header size in bytes:
     161             : const unsigned int BIH_WIN4SIZE = 40;  // for BMPT_WIN4
     162             : #if 0                                  /* Unused */
     163             : const unsigned int  BIH_WIN5SIZE = 57; // for BMPT_WIN5
     164             : #endif
     165             : const unsigned int BIH_OS21SIZE = 12;  // for BMPT_OS21
     166             : const unsigned int BIH_OS22SIZE = 64;  // for BMPT_OS22
     167             : 
     168             : // We will use plain byte array instead of this structure, but declaration
     169             : // provided for reference
     170             : typedef struct
     171             : {
     172             :     // cppcheck-suppress unusedStructMember
     173             :     GByte bBlue;
     174             :     // cppcheck-suppress unusedStructMember
     175             :     GByte bGreen;
     176             :     // cppcheck-suppress unusedStructMember
     177             :     GByte bRed;
     178             :     // cppcheck-suppress unusedStructMember
     179             :     GByte bReserved;  // Must be 0
     180             : } BMPColorEntry;
     181             : 
     182             : /*****************************************************************/
     183             : 
     184           0 : static int countonbits(GUInt32 dw)
     185             : {
     186           0 :     int r = 0;
     187           0 :     for (int x = 0; x < 32; x++)
     188             :     {
     189           0 :         if ((dw & (1U << x)) != 0)
     190           0 :             r++;
     191             :     }
     192           0 :     return r;
     193             : }
     194             : 
     195           0 : static int findfirstonbit(GUInt32 n)
     196             : {
     197           0 :     for (int x = 0; x < 32; x++)
     198             :     {
     199           0 :         if ((n & (1U << x)) != 0)
     200           0 :             return x;
     201             :     }
     202           0 :     return -1;
     203             : }
     204             : 
     205             : /************************************************************************/
     206             : /* ==================================================================== */
     207             : /*                              BMPDataset                              */
     208             : /* ==================================================================== */
     209             : /************************************************************************/
     210             : 
     211             : class BMPDataset final : public GDALPamDataset
     212             : {
     213             :     friend class BMPRasterBand;
     214             :     friend class BMPComprRasterBand;
     215             : 
     216             :     BMPFileHeader sFileHeader;
     217             :     BMPInfoHeader sInfoHeader;
     218             :     int nColorElems;
     219             :     GByte *pabyColorTable;
     220             :     GDALColorTable *poColorTable;
     221             :     double adfGeoTransform[6];
     222             :     int bGeoTransformValid;
     223             :     bool m_bNewFile = false;
     224             :     vsi_l_offset m_nFileSize = 0;
     225             : 
     226             :     char *pszFilename;
     227             :     VSILFILE *fp;
     228             : 
     229             :   protected:
     230             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     231             :                      GDALDataType, int, BANDMAP_TYPE, GSpacing nPixelSpace,
     232             :                      GSpacing nLineSpace, GSpacing nBandSpace,
     233             :                      GDALRasterIOExtraArg *psExtraArg) override;
     234             : 
     235             :   public:
     236             :     BMPDataset();
     237             :     ~BMPDataset() override;
     238             : 
     239             :     static int Identify(GDALOpenInfo *);
     240             :     static GDALDataset *Open(GDALOpenInfo *);
     241             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     242             :                                int nBandsIn, GDALDataType eType,
     243             :                                char **papszParamList);
     244             : 
     245             :     CPLErr GetGeoTransform(double *padfTransform) override;
     246             :     CPLErr SetGeoTransform(double *) override;
     247             : };
     248             : 
     249             : /************************************************************************/
     250             : /* ==================================================================== */
     251             : /*                            BMPRasterBand                             */
     252             : /* ==================================================================== */
     253             : /************************************************************************/
     254             : 
     255             : class BMPRasterBand CPL_NON_FINAL : public GDALPamRasterBand
     256             : {
     257             :     friend class BMPDataset;
     258             : 
     259             :   protected:
     260             :     GUInt32 nScanSize;
     261             :     unsigned int iBytesPerPixel;
     262             :     GByte *pabyScan;
     263             : 
     264             :   public:
     265             :     BMPRasterBand(BMPDataset *, int);
     266             :     ~BMPRasterBand() override;
     267             : 
     268             :     CPLErr IReadBlock(int, int, void *) override;
     269             :     CPLErr IWriteBlock(int, int, void *) override;
     270             :     GDALColorInterp GetColorInterpretation() override;
     271             :     GDALColorTable *GetColorTable() override;
     272             :     CPLErr SetColorTable(GDALColorTable *) override;
     273             : };
     274             : 
     275             : /************************************************************************/
     276             : /*                           BMPRasterBand()                            */
     277             : /************************************************************************/
     278             : 
     279          62 : BMPRasterBand::BMPRasterBand(BMPDataset *poDSIn, int nBandIn)
     280          62 :     : nScanSize(0), iBytesPerPixel(poDSIn->sInfoHeader.iBitCount / 8),
     281          62 :       pabyScan(nullptr)
     282             : {
     283          62 :     poDS = poDSIn;
     284          62 :     nBand = nBandIn;
     285          62 :     eDataType = GDT_Byte;
     286             : 
     287             :     // We will read one scanline per time. Scanlines in BMP aligned at 4-byte
     288             :     // boundary
     289          62 :     nBlockXSize = poDS->GetRasterXSize();
     290          62 :     nBlockYSize = 1;
     291             : 
     292          62 :     const auto knIntMax = std::numeric_limits<int>::max();
     293          62 :     if (nBlockXSize < (knIntMax - 31) / poDSIn->sInfoHeader.iBitCount)
     294             :     {
     295          62 :         nScanSize =
     296          62 :             ((poDS->GetRasterXSize() * poDSIn->sInfoHeader.iBitCount + 31) &
     297          62 :              ~31) /
     298             :             8;
     299             :     }
     300             :     else
     301             :     {
     302             :         // pabyScan = NULL;
     303           0 :         return;
     304             :     }
     305             : 
     306             : #ifdef BMP_DEBUG
     307             :     CPLDebug("BMP", "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d",
     308             :              nBand, nBlockXSize, nBlockYSize, nScanSize);
     309             : #endif
     310             : 
     311          62 :     pabyScan = static_cast<GByte *>(VSIMalloc(nScanSize));
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                           ~BMPRasterBand()                           */
     316             : /************************************************************************/
     317             : 
     318         122 : BMPRasterBand::~BMPRasterBand()
     319             : {
     320          62 :     CPLFree(pabyScan);
     321         122 : }
     322             : 
     323             : /************************************************************************/
     324             : /*                             IReadBlock()                             */
     325             : /************************************************************************/
     326             : 
     327         895 : CPLErr BMPRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     328             :                                  void *pImage)
     329             : {
     330         895 :     BMPDataset *poGDS = (BMPDataset *)poDS;
     331         895 :     vsi_l_offset iScanOffset = 0;
     332             : 
     333         895 :     if (poGDS->sInfoHeader.iHeight > 0)
     334        1790 :         iScanOffset = poGDS->sFileHeader.iOffBits +
     335         895 :                       (poGDS->GetRasterYSize() - nBlockYOff - 1) *
     336         895 :                           static_cast<vsi_l_offset>(nScanSize);
     337             :     else
     338           0 :         iScanOffset = poGDS->sFileHeader.iOffBits +
     339           0 :                       nBlockYOff * static_cast<vsi_l_offset>(nScanSize);
     340             : 
     341         895 :     if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0)
     342             :     {
     343             :         // XXX: We will not report error here, because file just may be
     344             :         // in update state and data for this block will be available later.
     345           0 :         if (poGDS->eAccess == GA_Update)
     346             :         {
     347           0 :             memset(pImage, 0, nBlockXSize);
     348           0 :             return CE_None;
     349             :         }
     350             :         else
     351             :         {
     352           0 :             CPLError(CE_Failure, CPLE_FileIO,
     353             :                      "Can't seek to offset " CPL_FRMT_GUIB
     354             :                      " in input file to read data.",
     355             :                      iScanOffset);
     356           0 :             return CE_Failure;
     357             :         }
     358             :     }
     359         895 :     if (VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize)
     360             :     {
     361             :         // XXX
     362           0 :         if (poGDS->eAccess == GA_Update)
     363             :         {
     364           0 :             memset(pImage, 0, nBlockXSize);
     365           0 :             return CE_None;
     366             :         }
     367             :         else
     368             :         {
     369           0 :             CPLError(CE_Failure, CPLE_FileIO,
     370             :                      "Can't read from offset " CPL_FRMT_GUIB " in input file.",
     371             :                      iScanOffset);
     372           0 :             return CE_Failure;
     373             :         }
     374             :     }
     375             : 
     376         895 :     if (poGDS->sInfoHeader.iBitCount == 24 ||
     377         894 :         poGDS->sInfoHeader.iBitCount == 32)
     378             :     {
     379           1 :         GByte *pabyTemp = pabyScan + 3 - nBand;
     380             : 
     381       65537 :         for (int i = 0; i < nBlockXSize; i++)
     382             :         {
     383             :             // Colour triplets in BMP file organized in reverse order:
     384             :             // blue, green, red. When we have 32-bit BMP the forth byte
     385             :             // in quadruplet should be discarded as it has no meaning.
     386             :             // That is why we always use 3 byte count in the following
     387             :             // pabyTemp index.
     388       65536 :             ((GByte *)pImage)[i] = *pabyTemp;
     389       65536 :             pabyTemp += iBytesPerPixel;
     390           1 :         }
     391             :     }
     392         894 :     else if (poGDS->sInfoHeader.iBitCount == 8)
     393             :     {
     394         810 :         memcpy(pImage, pabyScan, nBlockXSize);
     395             :     }
     396          84 :     else if (poGDS->sInfoHeader.iBitCount == 16)
     397             :     {
     398             :         // rcg, oct 7/06: Byteswap if necessary, use int16
     399             :         // references to file pixels, expand samples to
     400             :         // 8-bit, support BMPC_BITFIELDS channel mask indicators,
     401             :         // and generalize band handling.
     402             : 
     403           0 :         GUInt16 *pScan16 = (GUInt16 *)pabyScan;
     404             : #ifdef CPL_MSB
     405             :         GDALSwapWords(pScan16, sizeof(GUInt16), nBlockXSize, 0);
     406             : #endif
     407             : 
     408             :         // todo: make these band members and precompute.
     409             :         int mask[3], shift[3], size[3];
     410             :         float fTo8bit[3];
     411             : 
     412           0 :         if (poGDS->sInfoHeader.iCompression == BMPC_RGB)
     413             :         {
     414           0 :             mask[0] = 0x7c00;
     415           0 :             mask[1] = 0x03e0;
     416           0 :             mask[2] = 0x001f;
     417             :         }
     418           0 :         else if (poGDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
     419             :         {
     420           0 :             mask[0] = poGDS->sInfoHeader.iRedMask;
     421           0 :             mask[1] = poGDS->sInfoHeader.iGreenMask;
     422           0 :             mask[2] = poGDS->sInfoHeader.iBlueMask;
     423             :         }
     424             :         else
     425             :         {
     426           0 :             CPLError(CE_Failure, CPLE_FileIO, "Unknown 16-bit compression %d.",
     427           0 :                      poGDS->sInfoHeader.iCompression);
     428           0 :             return CE_Failure;
     429             :         }
     430             : 
     431           0 :         for (int i = 0; i < 3; i++)
     432             :         {
     433           0 :             shift[i] = findfirstonbit(mask[i]);
     434           0 :             size[i] = countonbits(mask[i]);
     435           0 :             if (size[i] > 14 || size[i] == 0)
     436             :             {
     437           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     438             :                          "Bad 16-bit channel mask %8x.", mask[i]);
     439           0 :                 return CE_Failure;
     440             :             }
     441           0 :             fTo8bit[i] = 255.0f / ((1 << size[i]) - 1);
     442             :         }
     443             : 
     444           0 :         for (int i = 0; i < nBlockXSize; i++)
     445             :         {
     446           0 :             ((GByte *)pImage)[i] =
     447           0 :                 (GByte)(0.5f +
     448           0 :                         fTo8bit[nBand - 1] * ((pScan16[i] & mask[nBand - 1]) >>
     449           0 :                                               shift[nBand - 1]));
     450             : #if 0
     451             :         // original code
     452             :             switch ( nBand )
     453             :             {
     454             :                 case 1: // Red
     455             :                 ((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F;
     456             :                 break;
     457             : 
     458             :                 case 2: // Green
     459             :                 ((GByte *) pImage)[i] =
     460             :                     ((pabyScan[i] & 0x03) << 3) |
     461             :                     ((pabyScan[i + 1] & 0xE0) >> 5);
     462             :                 break;
     463             : 
     464             :                 case 3: // Blue
     465             :                 ((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2;
     466             :                 break;
     467             :                 default:
     468             :                 break;
     469             :             }
     470             : #endif  // 0
     471             :         }
     472             :     }
     473          84 :     else if (poGDS->sInfoHeader.iBitCount == 4)
     474             :     {
     475          20 :         GByte *pabyTemp = pabyScan;
     476             : 
     477         420 :         for (int i = 0; i < nBlockXSize; i++)
     478             :         {
     479             :             // Most significant part of the byte represents leftmost pixel
     480         400 :             if (i & 0x01)
     481         200 :                 ((GByte *)pImage)[i] = *pabyTemp++ & 0x0F;
     482             :             else
     483         200 :                 ((GByte *)pImage)[i] = (*pabyTemp & 0xF0) >> 4;
     484             :         }
     485             :     }
     486          64 :     else if (poGDS->sInfoHeader.iBitCount == 1)
     487             :     {
     488          64 :         GByte *pabyTemp = pabyScan;
     489             : 
     490        2112 :         for (int i = 0; i < nBlockXSize; i++)
     491             :         {
     492        2048 :             switch (i & 0x7)
     493             :             {
     494         256 :                 case 0:
     495         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x80) >> 7;
     496         256 :                     break;
     497         256 :                 case 1:
     498         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x40) >> 6;
     499         256 :                     break;
     500         256 :                 case 2:
     501         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x20) >> 5;
     502         256 :                     break;
     503         256 :                 case 3:
     504         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x10) >> 4;
     505         256 :                     break;
     506         256 :                 case 4:
     507         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x08) >> 3;
     508         256 :                     break;
     509         256 :                 case 5:
     510         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x04) >> 2;
     511         256 :                     break;
     512         256 :                 case 6:
     513         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x02) >> 1;
     514         256 :                     break;
     515         256 :                 case 7:
     516         256 :                     ((GByte *)pImage)[i] = *pabyTemp++ & 0x01;
     517         256 :                     break;
     518           0 :                 default:
     519           0 :                     break;
     520             :             }
     521             :         }
     522             :     }
     523             : 
     524         895 :     return CE_None;
     525             : }
     526             : 
     527             : /************************************************************************/
     528             : /*                            IWriteBlock()                             */
     529             : /************************************************************************/
     530             : 
     531         441 : CPLErr BMPRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     532             : {
     533         441 :     BMPDataset *poGDS = (BMPDataset *)poDS;
     534             : 
     535         441 :     CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     536             :               pImage != nullptr);
     537             : 
     538         441 :     vsi_l_offset iScanOffset = poGDS->sFileHeader.iOffBits +
     539         441 :                                (poGDS->GetRasterYSize() - nBlockYOff - 1) *
     540         441 :                                    static_cast<vsi_l_offset>(nScanSize);
     541         441 :     if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0)
     542             :     {
     543           0 :         CPLError(CE_Failure, CPLE_FileIO,
     544             :                  "Can't seek to offset " CPL_FRMT_GUIB
     545             :                  " in output file to write data.\n%s",
     546           0 :                  iScanOffset, VSIStrerror(errno));
     547           0 :         return CE_Failure;
     548             :     }
     549             : 
     550         441 :     if (poGDS->nBands != 1)
     551             :     {
     552          30 :         memset(pabyScan, 0, nScanSize);
     553          30 :         VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp);
     554          30 :         VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET);
     555             :     }
     556             : 
     557         441 :     for (int iInPixel = 0, iOutPixel = iBytesPerPixel - nBand;
     558       32851 :          iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands)
     559             :     {
     560       32410 :         pabyScan[iOutPixel] = ((GByte *)pImage)[iInPixel];
     561             :     }
     562             : 
     563         441 :     if (VSIFWriteL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize)
     564             :     {
     565           1 :         CPLError(CE_Failure, CPLE_FileIO,
     566             :                  "Can't write block with X offset %d and Y offset %d.\n%s",
     567           1 :                  nBlockXOff, nBlockYOff, VSIStrerror(errno));
     568           1 :         return CE_Failure;
     569             :     }
     570             : 
     571         440 :     return CE_None;
     572             : }
     573             : 
     574             : /************************************************************************/
     575             : /*                           GetColorTable()                            */
     576             : /************************************************************************/
     577             : 
     578           6 : GDALColorTable *BMPRasterBand::GetColorTable()
     579             : {
     580           6 :     BMPDataset *poGDS = (BMPDataset *)poDS;
     581             : 
     582           6 :     return poGDS->poColorTable;
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                           SetColorTable()                            */
     587             : /************************************************************************/
     588             : 
     589           1 : CPLErr BMPRasterBand::SetColorTable(GDALColorTable *poColorTable)
     590             : {
     591           1 :     BMPDataset *poGDS = (BMPDataset *)poDS;
     592             : 
     593           1 :     if (poColorTable)
     594             :     {
     595           1 :         poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount();
     596           1 :         if (poGDS->sInfoHeader.iClrUsed < 1 ||
     597           1 :             poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount))
     598           0 :             return CE_Failure;
     599             : 
     600           1 :         VSIFSeekL(poGDS->fp, BFH_SIZE + 32, SEEK_SET);
     601             : 
     602           1 :         GUInt32 iULong = CPL_LSBWORD32(poGDS->sInfoHeader.iClrUsed);
     603           1 :         VSIFWriteL(&iULong, 4, 1, poGDS->fp);
     604           2 :         poGDS->pabyColorTable = (GByte *)CPLRealloc(
     605           1 :             poGDS->pabyColorTable, static_cast<size_t>(poGDS->nColorElems) *
     606           1 :                                        poGDS->sInfoHeader.iClrUsed);
     607           1 :         if (!poGDS->pabyColorTable)
     608           0 :             return CE_Failure;
     609             : 
     610         257 :         for (unsigned int i = 0; i < poGDS->sInfoHeader.iClrUsed; i++)
     611             :         {
     612             :             GDALColorEntry oEntry;
     613             : 
     614         256 :             poColorTable->GetColorEntryAsRGB(i, &oEntry);
     615         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0;
     616         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 2] =
     617         256 :                 (GByte)oEntry.c1;  // Red
     618         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 1] =
     619         256 :                 (GByte)oEntry.c2;  // Green
     620         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems] =
     621         256 :                 (GByte)oEntry.c3;  // Blue
     622             :         }
     623             : 
     624           1 :         VSIFSeekL(poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET);
     625           2 :         if (VSIFWriteL(poGDS->pabyColorTable, 1,
     626           1 :                        static_cast<size_t>(poGDS->nColorElems) *
     627           1 :                            poGDS->sInfoHeader.iClrUsed,
     628           1 :                        poGDS->fp) < static_cast<size_t>(poGDS->nColorElems) *
     629           1 :                                         poGDS->sInfoHeader.iClrUsed)
     630             :         {
     631           0 :             return CE_Failure;
     632             :         }
     633             :     }
     634             :     else
     635           0 :         return CE_Failure;
     636             : 
     637           1 :     return CE_None;
     638             : }
     639             : 
     640             : /************************************************************************/
     641             : /*                       GetColorInterpretation()                       */
     642             : /************************************************************************/
     643             : 
     644          12 : GDALColorInterp BMPRasterBand::GetColorInterpretation()
     645             : {
     646          12 :     BMPDataset *poGDS = (BMPDataset *)poDS;
     647             : 
     648          12 :     if (poGDS->sInfoHeader.iBitCount == 24 ||
     649          12 :         poGDS->sInfoHeader.iBitCount == 32 ||
     650          12 :         poGDS->sInfoHeader.iBitCount == 16)
     651             :     {
     652           0 :         if (nBand == 1)
     653           0 :             return GCI_RedBand;
     654           0 :         else if (nBand == 2)
     655           0 :             return GCI_GreenBand;
     656           0 :         else if (nBand == 3)
     657           0 :             return GCI_BlueBand;
     658             :         else
     659           0 :             return GCI_Undefined;
     660             :     }
     661             :     else
     662             :     {
     663          12 :         return GCI_PaletteIndex;
     664             :     }
     665             : }
     666             : 
     667             : /************************************************************************/
     668             : /* ==================================================================== */
     669             : /*                       BMPComprRasterBand                             */
     670             : /* ==================================================================== */
     671             : /************************************************************************/
     672             : 
     673             : class BMPComprRasterBand final : public BMPRasterBand
     674             : {
     675             :     friend class BMPDataset;
     676             : 
     677             :     GByte *pabyComprBuf;
     678             :     GByte *pabyUncomprBuf;
     679             : 
     680             :   public:
     681             :     BMPComprRasterBand(BMPDataset *, int);
     682             :     ~BMPComprRasterBand() override;
     683             : 
     684             :     CPLErr IReadBlock(int, int, void *) override;
     685             :     // virtual CPLErr IWriteBlock( int, int, void * );
     686             : };
     687             : 
     688             : /************************************************************************/
     689             : /*                           BMPComprRasterBand()                       */
     690             : /************************************************************************/
     691             : 
     692           2 : BMPComprRasterBand::BMPComprRasterBand(BMPDataset *poDSIn, int nBandIn)
     693             :     : BMPRasterBand(poDSIn, nBandIn), pabyComprBuf(nullptr),
     694           2 :       pabyUncomprBuf(nullptr)
     695             : {
     696             :     /* TODO: it might be interesting to avoid uncompressing the whole data */
     697             :     /* in a single pass, especially if nXSize * nYSize is big */
     698             :     /* We could read incrementally one row at a time */
     699           2 :     const auto knIntMax = std::numeric_limits<int>::max();
     700           2 :     if (poDS->GetRasterXSize() > knIntMax / poDS->GetRasterYSize())
     701             :     {
     702           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too big dimensions : %d x %d",
     703           0 :                  poDS->GetRasterXSize(), poDS->GetRasterYSize());
     704           0 :         return;
     705             :     }
     706             : 
     707           2 :     if (poDSIn->m_nFileSize <= poDSIn->sFileHeader.iOffBits ||
     708           2 :         poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits > knIntMax)
     709             :     {
     710           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid header");
     711           0 :         return;
     712             :     }
     713             : 
     714           2 :     const GUInt32 iComprSize = static_cast<GUInt32>(
     715           2 :         poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits);
     716             :     const GUInt32 iUncomprSize =
     717           2 :         poDS->GetRasterXSize() * poDS->GetRasterYSize();
     718             : 
     719             : #ifdef DEBUG
     720           2 :     CPLDebug("BMP", "RLE compression detected.");
     721           2 :     CPLDebug("BMP",
     722             :              "Size of compressed buffer %ld bytes,"
     723             :              " size of uncompressed buffer %ld bytes.",
     724             :              (long)iComprSize, (long)iUncomprSize);
     725             : #endif
     726             : 
     727           2 :     pabyComprBuf = (GByte *)VSIMalloc(iComprSize);
     728           2 :     pabyUncomprBuf = (GByte *)VSIMalloc(iUncomprSize);
     729           2 :     if (pabyComprBuf == nullptr || pabyUncomprBuf == nullptr)
     730             :     {
     731           0 :         CPLFree(pabyComprBuf);
     732           0 :         pabyComprBuf = nullptr;
     733           0 :         CPLFree(pabyUncomprBuf);
     734           0 :         pabyUncomprBuf = nullptr;
     735           0 :         return;
     736             :     }
     737             : 
     738           4 :     if (VSIFSeekL(poDSIn->fp, poDSIn->sFileHeader.iOffBits, SEEK_SET) != 0 ||
     739           2 :         VSIFReadL(pabyComprBuf, 1, iComprSize, poDSIn->fp) < iComprSize)
     740             :     {
     741           0 :         CPLError(CE_Failure, CPLE_FileIO,
     742             :                  "Can't read from offset %ld in input file.",
     743           0 :                  (long)poDSIn->sFileHeader.iOffBits);
     744           0 :         CPLFree(pabyComprBuf);
     745           0 :         pabyComprBuf = nullptr;
     746           0 :         CPLFree(pabyUncomprBuf);
     747           0 :         pabyUncomprBuf = nullptr;
     748           0 :         return;
     749             :     }
     750             : 
     751           2 :     unsigned int i = 0;
     752           2 :     unsigned int j = 0;
     753           2 :     if (poDSIn->sInfoHeader.iBitCount == 8)  // RLE8
     754             :     {
     755         122 :         while (i < iComprSize)
     756             :         {
     757         122 :             if (pabyComprBuf[i])
     758             :             {
     759          28 :                 unsigned int iLength = pabyComprBuf[i++];
     760          28 :                 if (j == iUncomprSize)
     761           0 :                     break;
     762         106 :                 while (iLength > 0 && j < iUncomprSize && i < iComprSize)
     763             :                 {
     764          78 :                     pabyUncomprBuf[j++] = pabyComprBuf[i];
     765          78 :                     iLength--;
     766             :                 }
     767          28 :                 i++;
     768             :             }
     769             :             else
     770             :             {
     771          94 :                 i++;
     772          94 :                 if (i == iComprSize)
     773           0 :                     break;
     774          94 :                 if (pabyComprBuf[i] == 0)  // Next scanline
     775             :                 {
     776          38 :                     i++;
     777             :                 }
     778          56 :                 else if (pabyComprBuf[i] == 1)  // End of image
     779             :                 {
     780           2 :                     break;
     781             :                 }
     782          54 :                 else if (pabyComprBuf[i] == 2)  // Move to...
     783             :                 {
     784           0 :                     if (j == iUncomprSize)
     785           0 :                         break;
     786           0 :                     i++;
     787           0 :                     if (i < iComprSize - 1)
     788             :                     {
     789           0 :                         if (pabyComprBuf[i + 1] >
     790           0 :                                 knIntMax / poDS->GetRasterXSize() ||
     791           0 :                             static_cast<int>(pabyComprBuf[i + 1]) *
     792           0 :                                     poDS->GetRasterXSize() >
     793           0 :                                 knIntMax -
     794           0 :                                     static_cast<int>(j + pabyComprBuf[i]))
     795           0 :                             break;
     796           0 :                         j += pabyComprBuf[i] +
     797           0 :                              pabyComprBuf[i + 1] * poDS->GetRasterXSize();
     798           0 :                         i += 2;
     799             :                     }
     800             :                     else
     801           0 :                         break;
     802             :                 }
     803             :                 else  // Absolute mode
     804             :                 {
     805          54 :                     CPLAssert(i < iComprSize);
     806          54 :                     unsigned int iLength = pabyComprBuf[i++];
     807          54 :                     if (j == iUncomprSize)
     808           0 :                         break;
     809          54 :                     for (unsigned k = 0;
     810         776 :                          k < iLength && j < iUncomprSize && i < iComprSize; k++)
     811         722 :                         pabyUncomprBuf[j++] = pabyComprBuf[i++];
     812          54 :                     if (i & 0x01)
     813          18 :                         i++;
     814             :                 }
     815             :             }
     816             :         }
     817             :     }
     818             :     else  // RLE4
     819             :     {
     820           0 :         while (i < iComprSize)
     821             :         {
     822           0 :             if (pabyComprBuf[i])
     823             :             {
     824           0 :                 unsigned int iLength = pabyComprBuf[i++];
     825           0 :                 if (j == iUncomprSize)
     826           0 :                     break;
     827           0 :                 while (iLength > 0 && j < iUncomprSize && i < iComprSize)
     828             :                 {
     829           0 :                     if (iLength & 0x01)
     830           0 :                         pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
     831             :                     else
     832           0 :                         pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F;
     833           0 :                     iLength--;
     834             :                 }
     835           0 :                 i++;
     836             :             }
     837             :             else
     838             :             {
     839           0 :                 i++;
     840           0 :                 if (i == iComprSize)
     841           0 :                     break;
     842           0 :                 if (pabyComprBuf[i] == 0)  // Next scanline
     843             :                 {
     844           0 :                     i++;
     845             :                 }
     846           0 :                 else if (pabyComprBuf[i] == 1)  // End of image
     847             :                 {
     848           0 :                     break;
     849             :                 }
     850           0 :                 else if (pabyComprBuf[i] == 2)  // Move to...
     851             :                 {
     852           0 :                     if (j == iUncomprSize)
     853           0 :                         break;
     854           0 :                     i++;
     855           0 :                     if (i < iComprSize - 1)
     856             :                     {
     857           0 :                         if (pabyComprBuf[i + 1] >
     858           0 :                                 knIntMax / poDS->GetRasterXSize() ||
     859           0 :                             static_cast<int>(pabyComprBuf[i + 1]) *
     860           0 :                                     poDS->GetRasterXSize() >
     861           0 :                                 knIntMax -
     862           0 :                                     static_cast<int>(j + pabyComprBuf[i]))
     863           0 :                             break;
     864           0 :                         j += pabyComprBuf[i] +
     865           0 :                              pabyComprBuf[i + 1] * poDS->GetRasterXSize();
     866           0 :                         i += 2;
     867             :                     }
     868             :                     else
     869           0 :                         break;
     870             :                 }
     871             :                 else  // Absolute mode
     872             :                 {
     873           0 :                     CPLAssert(i < iComprSize);
     874           0 :                     unsigned int iLength = pabyComprBuf[i++];
     875           0 :                     if (j == iUncomprSize)
     876           0 :                         break;
     877           0 :                     for (unsigned k = 0;
     878           0 :                          k < iLength && j < iUncomprSize && i < iComprSize; k++)
     879             :                     {
     880           0 :                         if (k & 0x01)
     881           0 :                             pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F;
     882             :                         else
     883           0 :                             pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
     884             :                     }
     885           0 :                     if (i & 0x01)
     886           0 :                         i++;
     887             :                 }
     888             :             }
     889             :         }
     890             :     }
     891             :     /* Validate that we have read all compressed data (we tolerate missing */
     892             :     /* end of image marker) and that we have filled all uncompressed data */
     893           2 :     if (j < iUncomprSize || (i + 1 != iComprSize && i + 2 != iComprSize))
     894             :     {
     895           0 :         CPLFree(pabyUncomprBuf);
     896           0 :         pabyUncomprBuf = nullptr;
     897             :     }
     898             :     // rcg, release compressed buffer here.
     899           2 :     CPLFree(pabyComprBuf);
     900           2 :     pabyComprBuf = nullptr;
     901             : }
     902             : 
     903             : /************************************************************************/
     904             : /*                           ~BMPComprRasterBand()                      */
     905             : /************************************************************************/
     906             : 
     907           4 : BMPComprRasterBand::~BMPComprRasterBand()
     908             : {
     909           2 :     CPLFree(pabyComprBuf);
     910           2 :     CPLFree(pabyUncomprBuf);
     911           4 : }
     912             : 
     913             : /************************************************************************/
     914             : /*                             IReadBlock()                             */
     915             : /************************************************************************/
     916             : 
     917          20 : CPLErr BMPComprRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     918             :                                       void *pImage)
     919             : {
     920          40 :     memcpy(pImage,
     921          60 :            pabyUncomprBuf + (poDS->GetRasterYSize() - nBlockYOff - 1) *
     922          20 :                                 poDS->GetRasterXSize(),
     923          20 :            nBlockXSize);
     924             : 
     925          20 :     return CE_None;
     926             : }
     927             : 
     928             : /************************************************************************/
     929             : /*                           BMPDataset()                               */
     930             : /************************************************************************/
     931             : 
     932          66 : BMPDataset::BMPDataset()
     933             :     : nColorElems(0), pabyColorTable(nullptr), poColorTable(nullptr),
     934          66 :       bGeoTransformValid(FALSE), pszFilename(nullptr), fp(nullptr)
     935             : {
     936          66 :     nBands = 0;
     937             : 
     938          66 :     memset(&sFileHeader, 0, sizeof(sFileHeader));
     939          66 :     memset(&sInfoHeader, 0, sizeof(sInfoHeader));
     940             : 
     941          66 :     adfGeoTransform[0] = 0.0;
     942          66 :     adfGeoTransform[1] = 1.0;
     943          66 :     adfGeoTransform[2] = 0.0;
     944          66 :     adfGeoTransform[3] = 0.0;
     945          66 :     adfGeoTransform[4] = 0.0;
     946          66 :     adfGeoTransform[5] = 1.0;
     947          66 : }
     948             : 
     949             : /************************************************************************/
     950             : /*                            ~BMPDataset()                             */
     951             : /************************************************************************/
     952             : 
     953         132 : BMPDataset::~BMPDataset()
     954             : {
     955          66 :     FlushCache(true);
     956             : 
     957          66 :     if (m_bNewFile && fp)
     958             :     {
     959             :         // Extend the file with zeroes if needed
     960          23 :         VSIFSeekL(fp, 0, SEEK_END);
     961             : 
     962          23 :         if (VSIFTellL(fp) < m_nFileSize)
     963             :         {
     964          13 :             VSIFTruncateL(fp, m_nFileSize);
     965             :         }
     966             :     }
     967             : 
     968          66 :     CPLFree(pabyColorTable);
     969          66 :     if (poColorTable)
     970          38 :         delete poColorTable;
     971          66 :     CPLFree(pszFilename);
     972          66 :     if (fp)
     973          63 :         VSIFCloseL(fp);
     974         132 : }
     975             : 
     976             : /************************************************************************/
     977             : /*                          GetGeoTransform()                           */
     978             : /************************************************************************/
     979             : 
     980          11 : CPLErr BMPDataset::GetGeoTransform(double *padfTransform)
     981             : {
     982          11 :     if (bGeoTransformValid)
     983             :     {
     984           0 :         memcpy(padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6);
     985           0 :         return CE_None;
     986             :     }
     987             : 
     988          11 :     if (GDALPamDataset::GetGeoTransform(padfTransform) == CE_None)
     989           2 :         return CE_None;
     990             : 
     991             : #ifdef notdef
     992             :     // See http://trac.osgeo.org/gdal/ticket/3578
     993             :     if (sInfoHeader.iXPelsPerMeter > 0 && sInfoHeader.iYPelsPerMeter > 0)
     994             :     {
     995             :         padfTransform[1] = sInfoHeader.iXPelsPerMeter;
     996             :         padfTransform[5] = -sInfoHeader.iYPelsPerMeter;
     997             :         padfTransform[0] = -0.5 * padfTransform[1];
     998             :         padfTransform[3] = -0.5 * padfTransform[5];
     999             :         return CE_None;
    1000             :     }
    1001             : #endif
    1002             : 
    1003           9 :     return CE_Failure;
    1004             : }
    1005             : 
    1006             : /************************************************************************/
    1007             : /*                          SetGeoTransform()                           */
    1008             : /************************************************************************/
    1009             : 
    1010           8 : CPLErr BMPDataset::SetGeoTransform(double *padfTransform)
    1011             : {
    1012           8 :     if (pszFilename && bGeoTransformValid)
    1013             :     {
    1014           0 :         memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
    1015             : 
    1016           0 :         CPLErr eErr = CE_None;
    1017           0 :         if (GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform) == FALSE)
    1018             :         {
    1019           0 :             CPLError(CE_Failure, CPLE_FileIO, "Can't write world file.");
    1020           0 :             eErr = CE_Failure;
    1021             :         }
    1022           0 :         return eErr;
    1023             :     }
    1024             : 
    1025           8 :     return GDALPamDataset::SetGeoTransform(padfTransform);
    1026             : }
    1027             : 
    1028             : /************************************************************************/
    1029             : /*                             IRasterIO()                              */
    1030             : /*                                                                      */
    1031             : /*      Multi-band raster io handler.  We will use  block based         */
    1032             : /*      loading is used for multiband BMPs.  That is because they       */
    1033             : /*      are effectively pixel interleaved, so processing all bands      */
    1034             : /*      for a given block together avoid extra seeks.                   */
    1035             : /************************************************************************/
    1036             : 
    1037          13 : CPLErr BMPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    1038             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
    1039             :                              int nBufYSize, GDALDataType eBufType,
    1040             :                              int nBandCount, BANDMAP_TYPE panBandMap,
    1041             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
    1042             :                              GSpacing nBandSpace,
    1043             :                              GDALRasterIOExtraArg *psExtraArg)
    1044             : 
    1045             : {
    1046          13 :     if (nBandCount > 1)
    1047           0 :         return GDALDataset::BlockBasedRasterIO(
    1048             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
    1049             :             eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
    1050           0 :             nBandSpace, psExtraArg);
    1051             :     else
    1052          13 :         return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1053             :                                       pData, nBufXSize, nBufYSize, eBufType,
    1054             :                                       nBandCount, panBandMap, nPixelSpace,
    1055          13 :                                       nLineSpace, nBandSpace, psExtraArg);
    1056             : }
    1057             : 
    1058             : /************************************************************************/
    1059             : /*                              Identify()                              */
    1060             : /************************************************************************/
    1061             : 
    1062       55711 : int BMPDataset::Identify(GDALOpenInfo *poOpenInfo)
    1063             : 
    1064             : {
    1065       55711 :     if (poOpenInfo->nHeaderBytes < BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
    1066        6408 :         poOpenInfo->pabyHeader[0] != 'B' || poOpenInfo->pabyHeader[1] != 'M' ||
    1067          80 :         poOpenInfo->pabyHeader[6] != 0 || poOpenInfo->pabyHeader[7] != 0 ||
    1068          80 :         poOpenInfo->pabyHeader[8] != 0 || poOpenInfo->pabyHeader[9] != 0)
    1069       55631 :         return FALSE;
    1070             : 
    1071             :     uint32_t nInfoHeaderSize;
    1072          80 :     memcpy(&nInfoHeaderSize, poOpenInfo->pabyHeader + BFH_SIZE,
    1073             :            sizeof(uint32_t));
    1074          80 :     CPL_LSBPTR32(&nInfoHeaderSize);
    1075             :     // Check against the maximum known size
    1076          80 :     if (nInfoHeaderSize > BIH_OS22SIZE)
    1077           0 :         return FALSE;
    1078             : 
    1079          80 :     return TRUE;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                                Open()                                */
    1084             : /************************************************************************/
    1085             : 
    1086          40 : GDALDataset *BMPDataset::Open(GDALOpenInfo *poOpenInfo)
    1087             : {
    1088          40 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
    1089           0 :         return nullptr;
    1090             : 
    1091             :     /* -------------------------------------------------------------------- */
    1092             :     /*      Create a corresponding GDALDataset.                             */
    1093             :     /* -------------------------------------------------------------------- */
    1094          40 :     BMPDataset *poDS = new BMPDataset();
    1095          40 :     poDS->eAccess = poOpenInfo->eAccess;
    1096             : 
    1097             :     VSIStatBufL sStat;
    1098          40 :     if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
    1099             :     {
    1100           0 :         delete poDS;
    1101           0 :         return nullptr;
    1102             :     }
    1103             : 
    1104             :     /* -------------------------------------------------------------------- */
    1105             :     /*      Read the BMPFileHeader. We need iOffBits value only             */
    1106             :     /* -------------------------------------------------------------------- */
    1107          40 :     memcpy(&poDS->sFileHeader.iOffBits, poOpenInfo->pabyHeader + 10, 4);
    1108             : #ifdef CPL_MSB
    1109             :     CPL_SWAP32PTR(&poDS->sFileHeader.iOffBits);
    1110             : #endif
    1111          40 :     poDS->m_nFileSize = sStat.st_size;
    1112             : 
    1113             : #ifdef BMP_DEBUG
    1114             :     CPLDebug("BMP", "File size " CPL_FRMT_GUIB " bytes.",
    1115             :              static_cast<GUIntBig>(poDS->m_nFileSize));
    1116             :     CPLDebug("BMP", "Image offset 0x%x bytes from file start.",
    1117             :              poDS->sFileHeader.iOffBits);
    1118             : #endif
    1119             : 
    1120             :     // Validatate iOffBits
    1121          40 :     if (poDS->sFileHeader.iOffBits <= BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
    1122          40 :         poDS->sFileHeader.iOffBits >= poDS->m_nFileSize)
    1123             :     {
    1124           0 :         delete poDS;
    1125           0 :         return nullptr;
    1126             :     }
    1127             : 
    1128             :     /* -------------------------------------------------------------------- */
    1129             :     /*      Read the BMPInfoHeader.                                         */
    1130             :     /* -------------------------------------------------------------------- */
    1131          40 :     poDS->fp = poOpenInfo->fpL;
    1132          40 :     poOpenInfo->fpL = nullptr;
    1133             : 
    1134          40 :     VSIFSeekL(poDS->fp, BFH_SIZE, SEEK_SET);
    1135          40 :     VSIFReadL(&poDS->sInfoHeader.iSize, 1, 4, poDS->fp);
    1136             : #ifdef CPL_MSB
    1137             :     CPL_SWAP32PTR(&poDS->sInfoHeader.iSize);
    1138             : #endif
    1139             : 
    1140             :     BMPType eBMPType;
    1141          40 :     if (poDS->sInfoHeader.iSize == BIH_WIN4SIZE)
    1142          40 :         eBMPType = BMPT_WIN4;
    1143           0 :     else if (poDS->sInfoHeader.iSize == BIH_OS21SIZE)
    1144           0 :         eBMPType = BMPT_OS21;
    1145           0 :     else if (poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
    1146           0 :              poDS->sInfoHeader.iSize == 16)
    1147           0 :         eBMPType = BMPT_OS22;
    1148             :     else
    1149           0 :         eBMPType = BMPT_WIN5;
    1150             : 
    1151          40 :     if (eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22)
    1152             :     {
    1153          40 :         VSIFReadL(&poDS->sInfoHeader.iWidth, 1, 4, poDS->fp);
    1154          40 :         VSIFReadL(&poDS->sInfoHeader.iHeight, 1, 4, poDS->fp);
    1155          40 :         VSIFReadL(&poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp);
    1156          40 :         VSIFReadL(&poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp);
    1157             :         unsigned int iCompression;
    1158          40 :         VSIFReadL(&iCompression, 1, 4, poDS->fp);
    1159             : #ifdef CPL_MSB
    1160             :         CPL_SWAP32PTR(&iCompression);
    1161             : #endif
    1162          40 :         if (iCompression > BMPC_PNG)
    1163             :         {
    1164           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported compression");
    1165           0 :             delete poDS;
    1166           0 :             return nullptr;
    1167             :         }
    1168          40 :         poDS->sInfoHeader.iCompression =
    1169             :             static_cast<BMPComprMethod>(iCompression);
    1170          40 :         VSIFReadL(&poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp);
    1171          40 :         VSIFReadL(&poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp);
    1172          40 :         VSIFReadL(&poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp);
    1173          40 :         VSIFReadL(&poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp);
    1174          40 :         VSIFReadL(&poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp);
    1175             : 
    1176             :         // rcg, read win4/5 fields. If we're reading a
    1177             :         // legacy header that ends at iClrImportant, it turns
    1178             :         // out that the three DWORD color table entries used
    1179             :         // by the channel masks start here anyway.
    1180          40 :         if (poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
    1181             :         {
    1182           0 :             VSIFReadL(&poDS->sInfoHeader.iRedMask, 1, 4, poDS->fp);
    1183           0 :             VSIFReadL(&poDS->sInfoHeader.iGreenMask, 1, 4, poDS->fp);
    1184           0 :             VSIFReadL(&poDS->sInfoHeader.iBlueMask, 1, 4, poDS->fp);
    1185             :         }
    1186             : #ifdef CPL_MSB
    1187             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iWidth);
    1188             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iHeight);
    1189             :         CPL_SWAP16PTR(&poDS->sInfoHeader.iPlanes);
    1190             :         CPL_SWAP16PTR(&poDS->sInfoHeader.iBitCount);
    1191             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iSizeImage);
    1192             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iXPelsPerMeter);
    1193             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iYPelsPerMeter);
    1194             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iClrUsed);
    1195             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iClrImportant);
    1196             :         // rcg, swap win4/5 fields.
    1197             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iRedMask);
    1198             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iGreenMask);
    1199             :         CPL_SWAP32PTR(&poDS->sInfoHeader.iBlueMask);
    1200             : #endif
    1201          40 :         poDS->nColorElems = 4;
    1202             :     }
    1203             : 
    1204          40 :     if (eBMPType == BMPT_OS22)
    1205             :     {
    1206           0 :         poDS->nColorElems =
    1207             :             3;  // FIXME: different info in different documents regarding this!
    1208             :     }
    1209             : 
    1210          40 :     if (eBMPType == BMPT_OS21)
    1211             :     {
    1212             :         GInt16 iShort;
    1213             : 
    1214           0 :         VSIFReadL(&iShort, 1, 2, poDS->fp);
    1215           0 :         poDS->sInfoHeader.iWidth = CPL_LSBWORD16(iShort);
    1216           0 :         VSIFReadL(&iShort, 1, 2, poDS->fp);
    1217           0 :         poDS->sInfoHeader.iHeight = CPL_LSBWORD16(iShort);
    1218           0 :         VSIFReadL(&iShort, 1, 2, poDS->fp);
    1219           0 :         poDS->sInfoHeader.iPlanes = CPL_LSBWORD16(iShort);
    1220           0 :         VSIFReadL(&iShort, 1, 2, poDS->fp);
    1221           0 :         poDS->sInfoHeader.iBitCount = CPL_LSBWORD16(iShort);
    1222           0 :         poDS->sInfoHeader.iCompression = BMPC_RGB;
    1223           0 :         poDS->nColorElems = 3;
    1224             :     }
    1225             : 
    1226          40 :     if (poDS->sInfoHeader.iBitCount != 1 && poDS->sInfoHeader.iBitCount != 4 &&
    1227          35 :         poDS->sInfoHeader.iBitCount != 8 && poDS->sInfoHeader.iBitCount != 16 &&
    1228           2 :         poDS->sInfoHeader.iBitCount != 24 && poDS->sInfoHeader.iBitCount != 32)
    1229             :     {
    1230           0 :         delete poDS;
    1231           0 :         return nullptr;
    1232             :     }
    1233             : 
    1234             : #ifdef BMP_DEBUG
    1235             :     CPLDebug("BMP",
    1236             :              "Windows Device Independent Bitmap parameters:\n"
    1237             :              " info header size: %d bytes\n"
    1238             :              " width: %d\n height: %d\n planes: %d\n bpp: %d\n"
    1239             :              " compression: %d\n image size: %d bytes\n X resolution: %d\n"
    1240             :              " Y resolution: %d\n colours used: %d\n colours important: %d",
    1241             :              poDS->sInfoHeader.iSize, poDS->sInfoHeader.iWidth,
    1242             :              poDS->sInfoHeader.iHeight, poDS->sInfoHeader.iPlanes,
    1243             :              poDS->sInfoHeader.iBitCount, poDS->sInfoHeader.iCompression,
    1244             :              poDS->sInfoHeader.iSizeImage, poDS->sInfoHeader.iXPelsPerMeter,
    1245             :              poDS->sInfoHeader.iYPelsPerMeter, poDS->sInfoHeader.iClrUsed,
    1246             :              poDS->sInfoHeader.iClrImportant);
    1247             : #endif
    1248             : 
    1249          40 :     if (poDS->sInfoHeader.iHeight == INT_MIN)
    1250             :     {
    1251           0 :         delete poDS;
    1252           0 :         return nullptr;
    1253             :     }
    1254             : 
    1255          40 :     poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
    1256          40 :     poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)
    1257          40 :                              ? poDS->sInfoHeader.iHeight
    1258             :                              : -poDS->sInfoHeader.iHeight;
    1259             : 
    1260          40 :     if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
    1261             :     {
    1262           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
    1263             :                  poDS->nRasterXSize, poDS->nRasterYSize);
    1264           0 :         delete poDS;
    1265           0 :         return nullptr;
    1266             :     }
    1267             : 
    1268          40 :     switch (poDS->sInfoHeader.iBitCount)
    1269             :     {
    1270          38 :         case 1:
    1271             :         case 4:
    1272             :         case 8:
    1273             :         {
    1274          38 :             poDS->nBands = 1;
    1275             :             int nColorTableSize;
    1276          38 :             int nMaxColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
    1277             :             // Allocate memory for colour table and read it
    1278          38 :             if (poDS->sInfoHeader.iClrUsed)
    1279             :             {
    1280          27 :                 if (poDS->sInfoHeader.iClrUsed > (GUInt32)nMaxColorTableSize)
    1281             :                 {
    1282           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1283             :                              "Wrong value for iClrUsed: %u",
    1284             :                              poDS->sInfoHeader.iClrUsed);
    1285           0 :                     delete poDS;
    1286           0 :                     return nullptr;
    1287             :                 }
    1288          27 :                 nColorTableSize = poDS->sInfoHeader.iClrUsed;
    1289             :             }
    1290             :             else
    1291          11 :                 nColorTableSize = nMaxColorTableSize;
    1292             : 
    1293          38 :             poDS->pabyColorTable = (GByte *)VSI_MALLOC2_VERBOSE(
    1294             :                 poDS->nColorElems, nColorTableSize);
    1295          38 :             if (poDS->pabyColorTable == nullptr)
    1296             :             {
    1297           0 :                 break;
    1298             :             }
    1299             : 
    1300         114 :             if (VSIFSeekL(poDS->fp,
    1301          38 :                           BFH_SIZE + static_cast<vsi_l_offset>(
    1302          38 :                                          poDS->sInfoHeader.iSize),
    1303          76 :                           SEEK_SET) != 0 ||
    1304          38 :                 VSIFReadL(poDS->pabyColorTable, poDS->nColorElems,
    1305          38 :                           nColorTableSize, poDS->fp) != (size_t)nColorTableSize)
    1306             :             {
    1307           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot read color table");
    1308           0 :                 delete poDS;
    1309           0 :                 return nullptr;
    1310             :             }
    1311             : 
    1312             :             GDALColorEntry oEntry;
    1313          38 :             poDS->poColorTable = new GDALColorTable();
    1314        8524 :             for (int i = 0; i < nColorTableSize; i++)
    1315             :             {
    1316        8486 :                 oEntry.c1 =
    1317        8486 :                     poDS->pabyColorTable[i * poDS->nColorElems + 2];  // Red
    1318        8486 :                 oEntry.c2 =
    1319        8486 :                     poDS->pabyColorTable[i * poDS->nColorElems + 1];  // Green
    1320        8486 :                 oEntry.c3 =
    1321        8486 :                     poDS->pabyColorTable[i * poDS->nColorElems];  // Blue
    1322        8486 :                 oEntry.c4 = 255;
    1323             : 
    1324        8486 :                 poDS->poColorTable->SetColorEntry(i, &oEntry);
    1325             :             }
    1326             :         }
    1327          38 :         break;
    1328           2 :         case 16:
    1329             :         case 24:
    1330             :         case 32:
    1331           2 :             poDS->nBands = 3;
    1332           2 :             break;
    1333           0 :         default:
    1334           0 :             delete poDS;
    1335           0 :             return nullptr;
    1336             :     }
    1337             : 
    1338             :     /* -------------------------------------------------------------------- */
    1339             :     /*      Create band information objects.                                */
    1340             :     /* -------------------------------------------------------------------- */
    1341          40 :     if (poDS->sInfoHeader.iCompression == BMPC_RGB ||
    1342           2 :         poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
    1343             :     {
    1344          80 :         for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    1345             :         {
    1346          42 :             BMPRasterBand *band = new BMPRasterBand(poDS, iBand);
    1347          42 :             poDS->SetBand(iBand, band);
    1348          42 :             if (band->pabyScan == nullptr)
    1349             :             {
    1350           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1351             :                          "The BMP file is probably corrupted or too large. "
    1352             :                          "Image width = %d",
    1353             :                          poDS->nRasterXSize);
    1354           0 :                 delete poDS;
    1355           0 :                 return nullptr;
    1356             :             }
    1357          38 :         }
    1358             :     }
    1359           2 :     else if (poDS->sInfoHeader.iCompression == BMPC_RLE8 ||
    1360           0 :              poDS->sInfoHeader.iCompression == BMPC_RLE4)
    1361             :     {
    1362           4 :         for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    1363             :         {
    1364           2 :             BMPComprRasterBand *band = new BMPComprRasterBand(poDS, iBand);
    1365           2 :             poDS->SetBand(iBand, band);
    1366           2 :             if (band->pabyUncomprBuf == nullptr)
    1367             :             {
    1368           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1369             :                          "The BMP file is probably corrupted or too large. "
    1370             :                          "Image width = %d",
    1371             :                          poDS->nRasterXSize);
    1372           0 :                 delete poDS;
    1373           0 :                 return nullptr;
    1374             :             }
    1375           2 :         }
    1376             :     }
    1377             :     else
    1378             :     {
    1379           0 :         delete poDS;
    1380           0 :         return nullptr;
    1381             :     }
    1382             : 
    1383             :     /* -------------------------------------------------------------------- */
    1384             :     /*      Check for world file.                                           */
    1385             :     /* -------------------------------------------------------------------- */
    1386          80 :     poDS->bGeoTransformValid = GDALReadWorldFile(
    1387          40 :         poOpenInfo->pszFilename, nullptr, poDS->adfGeoTransform);
    1388             : 
    1389          40 :     if (!poDS->bGeoTransformValid)
    1390          40 :         poDS->bGeoTransformValid = GDALReadWorldFile(
    1391          40 :             poOpenInfo->pszFilename, ".wld", poDS->adfGeoTransform);
    1392             : 
    1393             :     /* -------------------------------------------------------------------- */
    1394             :     /*      Initialize any PAM information.                                 */
    1395             :     /* -------------------------------------------------------------------- */
    1396          40 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1397          40 :     poDS->TryLoadXML();
    1398             : 
    1399             :     /* -------------------------------------------------------------------- */
    1400             :     /*      Check for overviews.                                            */
    1401             :     /* -------------------------------------------------------------------- */
    1402          40 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1403             : 
    1404          40 :     return poDS;
    1405             : }
    1406             : 
    1407             : /************************************************************************/
    1408             : /*                               Create()                               */
    1409             : /************************************************************************/
    1410             : 
    1411          69 : GDALDataset *BMPDataset::Create(const char *pszFilename, int nXSize, int nYSize,
    1412             :                                 int nBandsIn, GDALDataType eType,
    1413             :                                 char **papszOptions)
    1414             : 
    1415             : {
    1416          69 :     if (eType != GDT_Byte)
    1417             :     {
    1418          36 :         CPLError(CE_Failure, CPLE_AppDefined,
    1419             :                  "Attempt to create BMP dataset with an illegal\n"
    1420             :                  "data type (%s), only Byte supported by the format.\n",
    1421             :                  GDALGetDataTypeName(eType));
    1422             : 
    1423          36 :         return nullptr;
    1424             :     }
    1425             : 
    1426          33 :     if (nBandsIn != 1 && nBandsIn != 3)
    1427             :     {
    1428           7 :         CPLError(CE_Failure, CPLE_NotSupported,
    1429             :                  "BMP driver doesn't support %d bands. Must be 1 or 3.\n",
    1430             :                  nBandsIn);
    1431             : 
    1432           7 :         return nullptr;
    1433             :     }
    1434             : 
    1435             :     /* -------------------------------------------------------------------- */
    1436             :     /*      Create the dataset.                                             */
    1437             :     /* -------------------------------------------------------------------- */
    1438          26 :     BMPDataset *poDS = new BMPDataset();
    1439          26 :     poDS->m_bNewFile = true;
    1440             : 
    1441          26 :     poDS->fp = VSIFOpenL(pszFilename, "wb+");
    1442          26 :     if (poDS->fp == nullptr)
    1443             :     {
    1444           3 :         CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n",
    1445             :                  pszFilename);
    1446           3 :         delete poDS;
    1447           3 :         return nullptr;
    1448             :     }
    1449             : 
    1450          23 :     poDS->pszFilename = CPLStrdup(pszFilename);
    1451             : 
    1452             :     /* -------------------------------------------------------------------- */
    1453             :     /*      Fill the BMPInfoHeader                                          */
    1454             :     /* -------------------------------------------------------------------- */
    1455          23 :     poDS->sInfoHeader.iSize = 40;
    1456          23 :     poDS->sInfoHeader.iWidth = nXSize;
    1457          23 :     poDS->sInfoHeader.iHeight = nYSize;
    1458          23 :     poDS->sInfoHeader.iPlanes = 1;
    1459          23 :     poDS->sInfoHeader.iBitCount = (nBandsIn == 3) ? 24 : 8;
    1460          23 :     poDS->sInfoHeader.iCompression = BMPC_RGB;
    1461             : 
    1462             :     /* XXX: Avoid integer overflow. We can calculate size in one
    1463             :      * step using
    1464             :      *
    1465             :      *   nScanSize = ((poDS->sInfoHeader.iWidth *
    1466             :      *            poDS->sInfoHeader.iBitCount + 31) & ~31) / 8
    1467             :      *
    1468             :      * formula, but we should check for overflow conditions
    1469             :      * during calculation.
    1470             :      */
    1471          23 :     GUInt32 nScanSize =
    1472          23 :         (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31;
    1473          23 :     if (!poDS->sInfoHeader.iWidth || !poDS->sInfoHeader.iBitCount ||
    1474          23 :         (nScanSize - 31) / poDS->sInfoHeader.iBitCount !=
    1475          23 :             (GUInt32)poDS->sInfoHeader.iWidth)
    1476             :     {
    1477           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1478             :                  "Wrong image parameters; "
    1479             :                  "can't allocate space for scanline buffer");
    1480           0 :         delete poDS;
    1481             : 
    1482           0 :         return nullptr;
    1483             :     }
    1484          23 :     nScanSize = (nScanSize & ~31U) / 8;
    1485             : 
    1486          23 :     poDS->sInfoHeader.iXPelsPerMeter = 0;
    1487          23 :     poDS->sInfoHeader.iYPelsPerMeter = 0;
    1488          23 :     poDS->nColorElems = 4;
    1489             : 
    1490             :     /* -------------------------------------------------------------------- */
    1491             :     /*      Do we need colour table?                                        */
    1492             :     /* -------------------------------------------------------------------- */
    1493          23 :     if (nBandsIn == 1)
    1494             :     {
    1495          21 :         poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount;
    1496          21 :         poDS->pabyColorTable =
    1497          42 :             (GByte *)CPLMalloc(static_cast<size_t>(poDS->nColorElems) *
    1498          21 :                                poDS->sInfoHeader.iClrUsed);
    1499        5397 :         for (unsigned int i = 0; i < poDS->sInfoHeader.iClrUsed; i++)
    1500             :         {
    1501        5376 :             poDS->pabyColorTable[i * poDS->nColorElems] =
    1502        5376 :                 poDS->pabyColorTable[i * poDS->nColorElems + 1] =
    1503        5376 :                     poDS->pabyColorTable[i * poDS->nColorElems + 2] =
    1504        5376 :                         poDS->pabyColorTable[i * poDS->nColorElems + 3] =
    1505             :                             (GByte)i;
    1506             :         }
    1507             :     }
    1508             :     else
    1509             :     {
    1510           2 :         poDS->sInfoHeader.iClrUsed = 0;
    1511             :     }
    1512          23 :     poDS->sInfoHeader.iClrImportant = 0;
    1513             : 
    1514             :     /* -------------------------------------------------------------------- */
    1515             :     /*      Fill the BMPFileHeader                                          */
    1516             :     /* -------------------------------------------------------------------- */
    1517             : 
    1518          23 :     poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize +
    1519          23 :                                  poDS->sInfoHeader.iClrUsed * poDS->nColorElems;
    1520             : 
    1521             :     // From https://medium.com/@chiaracoetzee/maximum-resolution-of-bmp-image-file-8c729b3f833a
    1522          23 :     if (nXSize > 30000 || nYSize > 30000)
    1523             :     {
    1524           0 :         CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
    1525             :                         "Adobe Photoshop CC 2014.2.2");
    1526             :     }
    1527          23 :     if (nXSize > 2147483647 / (nYSize + 1))
    1528             :     {
    1529           0 :         CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
    1530             :                         "Windows Photo Viewer");
    1531             :     }
    1532             : 
    1533          23 :     const vsi_l_offset nLargeImageSize =
    1534          23 :         static_cast<vsi_l_offset>(nScanSize) * poDS->sInfoHeader.iHeight;
    1535          23 :     poDS->m_nFileSize = poDS->sFileHeader.iOffBits + nLargeImageSize;
    1536          23 :     if (nLargeImageSize > std::numeric_limits<uint32_t>::max())
    1537             :     {
    1538           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1539             :                  "Image too big for its size to fit in a 32 bit integer! "
    1540             :                  "Writing 0xFFFFFFFF in it, but that could cause compatibility "
    1541             :                  "problems with other readers.");
    1542           0 :         poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
    1543           0 :         poDS->sInfoHeader.iSizeImage = std::numeric_limits<uint32_t>::max();
    1544             :     }
    1545          23 :     else if (poDS->m_nFileSize > std::numeric_limits<uint32_t>::max())
    1546             :     {
    1547           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1548             :                  "File too big for its size to fit in a 32 bit integer! "
    1549             :                  "Writing 0xFFFFFFFF in it, but that could cause compatibility "
    1550             :                  "problems with other readers.");
    1551           0 :         poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
    1552           0 :         poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
    1553             :     }
    1554             :     else
    1555             :     {
    1556          23 :         poDS->sFileHeader.iSize = static_cast<uint32_t>(poDS->m_nFileSize);
    1557          23 :         poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
    1558             :     }
    1559             : 
    1560          23 :     poDS->sFileHeader.bType[0] = 'B';
    1561          23 :     poDS->sFileHeader.bType[1] = 'M';
    1562          23 :     poDS->sFileHeader.iReserved1 = 0;
    1563          23 :     poDS->sFileHeader.iReserved2 = 0;
    1564             : 
    1565             :     /* -------------------------------------------------------------------- */
    1566             :     /*      Write all structures to the file                                */
    1567             :     /* -------------------------------------------------------------------- */
    1568          23 :     if (VSIFWriteL(&poDS->sFileHeader.bType, 1, 2, poDS->fp) != 2)
    1569             :     {
    1570           1 :         CPLError(CE_Failure, CPLE_FileIO,
    1571             :                  "Write of first 2 bytes to BMP file %s failed.\n"
    1572             :                  "Is file system full?",
    1573             :                  pszFilename);
    1574           1 :         delete poDS;
    1575             : 
    1576           1 :         return nullptr;
    1577             :     }
    1578             : 
    1579             :     GInt32 iLong;
    1580             :     GUInt32 iULong;
    1581             :     GUInt16 iUShort;
    1582             : 
    1583          22 :     iULong = CPL_LSBWORD32(poDS->sFileHeader.iSize);
    1584          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1585          22 :     iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved1);
    1586          22 :     VSIFWriteL(&iUShort, 2, 1, poDS->fp);
    1587          22 :     iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved2);
    1588          22 :     VSIFWriteL(&iUShort, 2, 1, poDS->fp);
    1589          22 :     iULong = CPL_LSBWORD32(poDS->sFileHeader.iOffBits);
    1590          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1591             : 
    1592          22 :     iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSize);
    1593          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1594          22 :     iLong = CPL_LSBWORD32(poDS->sInfoHeader.iWidth);
    1595          22 :     VSIFWriteL(&iLong, 4, 1, poDS->fp);
    1596          22 :     iLong = CPL_LSBWORD32(poDS->sInfoHeader.iHeight);
    1597          22 :     VSIFWriteL(&iLong, 4, 1, poDS->fp);
    1598          22 :     iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iPlanes);
    1599          22 :     VSIFWriteL(&iUShort, 2, 1, poDS->fp);
    1600          22 :     iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iBitCount);
    1601          22 :     VSIFWriteL(&iUShort, 2, 1, poDS->fp);
    1602          22 :     iULong = CPL_LSBWORD32(poDS->sInfoHeader.iCompression);
    1603          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1604          22 :     iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSizeImage);
    1605          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1606          22 :     iLong = CPL_LSBWORD32(poDS->sInfoHeader.iXPelsPerMeter);
    1607          22 :     VSIFWriteL(&iLong, 4, 1, poDS->fp);
    1608          22 :     iLong = CPL_LSBWORD32(poDS->sInfoHeader.iYPelsPerMeter);
    1609          22 :     VSIFWriteL(&iLong, 4, 1, poDS->fp);
    1610          22 :     iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrUsed);
    1611          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1612          22 :     iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrImportant);
    1613          22 :     VSIFWriteL(&iULong, 4, 1, poDS->fp);
    1614             : 
    1615          22 :     if (poDS->sInfoHeader.iClrUsed)
    1616             :     {
    1617          40 :         if (VSIFWriteL(poDS->pabyColorTable, 1,
    1618          20 :                        static_cast<size_t>(poDS->nColorElems) *
    1619          20 :                            poDS->sInfoHeader.iClrUsed,
    1620          20 :                        poDS->fp) !=
    1621          20 :             static_cast<size_t>(poDS->nColorElems) * poDS->sInfoHeader.iClrUsed)
    1622             :         {
    1623           8 :             CPLError(CE_Failure, CPLE_FileIO,
    1624             :                      "Error writing color table.  Is disk full?");
    1625           8 :             delete poDS;
    1626             : 
    1627           8 :             return nullptr;
    1628             :         }
    1629             :     }
    1630             : 
    1631          14 :     poDS->nRasterXSize = nXSize;
    1632          14 :     poDS->nRasterYSize = nYSize;
    1633          14 :     poDS->eAccess = GA_Update;
    1634          14 :     poDS->nBands = nBandsIn;
    1635             : 
    1636             :     /* -------------------------------------------------------------------- */
    1637             :     /*      Create band information objects.                                */
    1638             :     /* -------------------------------------------------------------------- */
    1639          32 :     for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    1640             :     {
    1641          18 :         poDS->SetBand(iBand, new BMPRasterBand(poDS, iBand));
    1642             :     }
    1643             : 
    1644             :     /* -------------------------------------------------------------------- */
    1645             :     /*      Do we need a world file?                                        */
    1646             :     /* -------------------------------------------------------------------- */
    1647          14 :     if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    1648           0 :         poDS->bGeoTransformValid = TRUE;
    1649             : 
    1650          14 :     return (GDALDataset *)poDS;
    1651             : }
    1652             : 
    1653             : /************************************************************************/
    1654             : /*                        GDALRegister_BMP()                            */
    1655             : /************************************************************************/
    1656             : 
    1657        1595 : void GDALRegister_BMP()
    1658             : 
    1659             : {
    1660        1595 :     if (GDALGetDriverByName("BMP") != nullptr)
    1661         302 :         return;
    1662             : 
    1663        1293 :     GDALDriver *poDriver = new GDALDriver();
    1664             : 
    1665        1293 :     poDriver->SetDescription("BMP");
    1666        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1667        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1668        1293 :                               "MS Windows Device Independent Bitmap");
    1669        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bmp.html");
    1670        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bmp");
    1671        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
    1672        1293 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
    1673             :                               "<CreationOptionList>"
    1674             :                               "   <Option name='WORLDFILE' type='boolean' "
    1675             :                               "description='Write out world file'/>"
    1676        1293 :                               "</CreationOptionList>");
    1677             : 
    1678        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1679             : 
    1680        1293 :     poDriver->pfnOpen = BMPDataset::Open;
    1681        1293 :     poDriver->pfnCreate = BMPDataset::Create;
    1682        1293 :     poDriver->pfnIdentify = BMPDataset::Identify;
    1683             : 
    1684        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1685             : }

Generated by: LCOV version 1.14