LCOV - code coverage report
Current view: top level - frmts/bmp - bmpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 447 669 66.8 %
Date: 2025-10-27 00:14:23 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             : #include "gdal_colortable.h"
      19             : #include "gdal_driver.h"
      20             : #include "gdal_drivermanager.h"
      21             : #include "gdal_openinfo.h"
      22             : #include "gdal_cpp_functions.h"
      23             : 
      24             : #include <limits>
      25             : 
      26             : // Enable if you want to see lots of BMP debugging output.
      27             : // #define BMP_DEBUG
      28             : 
      29             : enum BMPType
      30             : {
      31             :     BMPT_WIN4,  // BMP used in Windows 3.0/NT 3.51/95
      32             :     BMPT_WIN5,  // BMP used in Windows NT 4.0/98/Me/2000/XP
      33             :     BMPT_OS21,  // BMP used in OS/2 PM 1.x
      34             :     BMPT_OS22   // BMP used in OS/2 PM 2.x
      35             : };
      36             : 
      37             : // Bitmap file consists of a BMPFileHeader structure followed by a
      38             : // BMPInfoHeader structure. An array of BMPColorEntry structures (also called
      39             : // a colour table) follows the bitmap information header structure. The colour
      40             : // table is followed by a second array of indexes into the colour table (the
      41             : // actual bitmap data). Data may be compressed, for 4-bpp and 8-bpp used RLE
      42             : // compression.
      43             : //
      44             : // +---------------------+
      45             : // | BMPFileHeader       |
      46             : // +---------------------+
      47             : // | BMPInfoHeader       |
      48             : // +---------------------+
      49             : // | BMPColorEntry array |
      50             : // +---------------------+
      51             : // | Colour-index array  |
      52             : // +---------------------+
      53             : //
      54             : // All numbers stored in Intel order with least significant byte first.
      55             : 
      56             : enum BMPComprMethod
      57             : {
      58             :     BMPC_RGB = 0L,        // Uncompressed
      59             :     BMPC_RLE8 = 1L,       // RLE for 8 bpp images
      60             :     BMPC_RLE4 = 2L,       // RLE for 4 bpp images
      61             :     BMPC_BITFIELDS = 3L,  // Bitmap is not compressed and the colour table
      62             :                           // consists of three DWORD color masks that specify
      63             :                           // the red, green, and blue components of each pixel.
      64             :     // This is valid when used with 16- and 32-bpp bitmaps.
      65             :     BMPC_JPEG = 4L,  // Indicates that the image is a JPEG image.
      66             :     BMPC_PNG = 5L    // Indicates that the image is a PNG image.
      67             : };
      68             : 
      69             : enum BMPLCSType  // Type of logical color space.
      70             : {
      71             :     BMPLT_CALIBRATED_RGB = 0,  // This value indicates that endpoints and gamma
      72             :                                // values are given in the appropriate fields.
      73             :     BMPLT_DEVICE_RGB = 1,
      74             :     BMPLT_DEVICE_CMYK = 2
      75             : };
      76             : 
      77             : typedef struct
      78             : {
      79             :     // cppcheck-suppress unusedStructMember
      80             :     GInt32 iCIEX;
      81             :     // cppcheck-suppress unusedStructMember
      82             :     GInt32 iCIEY;
      83             :     // cppcheck-suppress unusedStructMember
      84             :     GInt32 iCIEZ;
      85             : } BMPCIEXYZ;
      86             : 
      87             : typedef struct  // This structure contains the x, y, and z
      88             : {               // coordinates of the three colors that correspond
      89             :     // cppcheck-suppress unusedStructMember
      90             :     BMPCIEXYZ iCIERed;  // to the red, green, and blue endpoints for
      91             :     // cppcheck-suppress unusedStructMember
      92             :     BMPCIEXYZ iCIEGreen;  // a specified logical color space.
      93             :     // cppcheck-suppress unusedStructMember
      94             :     BMPCIEXYZ iCIEBlue;
      95             : } BMPCIEXYZTriple;
      96             : 
      97             : typedef struct
      98             : {
      99             :     GByte bType[2];      // Signature "BM"
     100             :     GUInt32 iSize;       // Size in bytes of the bitmap file. Should always
     101             :                          // be ignored while reading because of error
     102             :                          // in Windows 3.0 SDK's description of this field
     103             :     GUInt16 iReserved1;  // Reserved, set as 0
     104             :     GUInt16 iReserved2;  // Reserved, set as 0
     105             :     GUInt32 iOffBits;    // Offset of the image from file start in bytes
     106             : } BMPFileHeader;
     107             : 
     108             : // File header size in bytes:
     109             : constexpr int BFH_SIZE = 14;
     110             : 
     111             : // Size of sInfoHeader.iSize in bytes
     112             : constexpr int SIZE_OF_INFOHEADER_SIZE = 4;
     113             : 
     114             : typedef struct
     115             : {
     116             :     GUInt32 iSize;      // Size of BMPInfoHeader structure in bytes.
     117             :                         // Should be used to determine start of the
     118             :                         // colour table
     119             :     GInt32 iWidth;      // Image width
     120             :     GInt32 iHeight;     // Image height. If positive, image has bottom left
     121             :                         // origin, if negative --- top left.
     122             :     GUInt16 iPlanes;    // Number of image planes (must be set to 1)
     123             :     GUInt16 iBitCount;  // Number of bits per pixel (1, 4, 8, 16, 24 or 32).
     124             :                         // If 0 then the number of bits per pixel is
     125             :                         // specified or is implied by the JPEG or PNG format.
     126             :     BMPComprMethod iCompression;  // Compression method
     127             :     GUInt32 iSizeImage;     // Size of uncompressed image in bytes. May be 0
     128             :                             // for BMPC_RGB bitmaps. If iCompression is BI_JPEG
     129             :                             // or BI_PNG, iSizeImage indicates the size
     130             :                             // of the JPEG or PNG image buffer.
     131             :     GInt32 iXPelsPerMeter;  // X resolution, pixels per meter (0 if not used)
     132             :     GInt32 iYPelsPerMeter;  // Y resolution, pixels per meter (0 if not used)
     133             :     GUInt32 iClrUsed;       // Size of colour table. If 0, iBitCount should
     134             :                             // be used to calculate this value (1<<iBitCount)
     135             :     GUInt32 iClrImportant;  // Number of important colours. If 0, all
     136             :                             // colours are required
     137             : 
     138             :     // Fields above should be used for bitmaps, compatible with Windows NT 3.51
     139             :     // and earlier. Windows 98/Me, Windows 2000/XP introduces additional fields:
     140             : 
     141             :     GUInt32 iRedMask;    // Colour mask that specifies the red component
     142             :                          // of each pixel, valid only if iCompression
     143             :                          // is set to BI_BITFIELDS.
     144             :     GUInt32 iGreenMask;  // The same for green component
     145             :     GUInt32 iBlueMask;   // The same for blue component
     146             :     // cppcheck-suppress unusedStructMember
     147             :     GUInt32 iAlphaMask;  // Colour mask that specifies the alpha
     148             :                          // component of each pixel.
     149             :     // cppcheck-suppress unusedStructMember
     150             :     BMPLCSType iCSType;  // Colour space of the DIB.
     151             :     // cppcheck-suppress unusedStructMember
     152             :     BMPCIEXYZTriple sEndpoints;  // This member is ignored unless the iCSType
     153             :                                  // member specifies BMPLT_CALIBRATED_RGB.
     154             :     // cppcheck-suppress unusedStructMember
     155             :     GUInt32 iGammaRed;  // Toned response curve for red. This member
     156             :                         // is ignored unless color values are calibrated
     157             :                         // RGB values and iCSType is set to
     158             :                         // BMPLT_CALIBRATED_RGB. Specified in 16^16 format.
     159             :     // cppcheck-suppress unusedStructMember
     160             :     GUInt32 iGammaGreen;  // Toned response curve for green.
     161             :     // cppcheck-suppress unusedStructMember
     162             :     GUInt32 iGammaBlue;  // Toned response curve for blue.
     163             : } BMPInfoHeader;
     164             : 
     165             : // Info header size in bytes:
     166             : constexpr unsigned int BIH_WIN4SIZE = 40;  // for BMPT_WIN4
     167             : #if 0
     168             : /* Unused */
     169             : constexpr unsigned int  BIH_WIN5SIZE = 57; // for BMPT_WIN5
     170             : #endif
     171             : constexpr unsigned int BIH_OS21SIZE = 12;       // for BMPT_OS21
     172             : constexpr unsigned int BIH_OS22SIZE = 64;       // for BMPT_OS22
     173             : constexpr unsigned int BIH_BITMAPV5SIZE = 124;  // for BITMAPV5HEADER
     174             : 
     175             : // We will use plain byte array instead of this structure, but declaration
     176             : // provided for reference
     177             : typedef struct
     178             : {
     179             :     // cppcheck-suppress unusedStructMember
     180             :     GByte bBlue;
     181             :     // cppcheck-suppress unusedStructMember
     182             :     GByte bGreen;
     183             :     // cppcheck-suppress unusedStructMember
     184             :     GByte bRed;
     185             :     // cppcheck-suppress unusedStructMember
     186             :     GByte bReserved;  // Must be 0
     187             : } BMPColorEntry;
     188             : 
     189             : /*****************************************************************/
     190             : 
     191           0 : static int countonbits(GUInt32 dw)
     192             : {
     193           0 :     int r = 0;
     194           0 :     for (int x = 0; x < 32; x++)
     195             :     {
     196           0 :         if ((dw & (1U << x)) != 0)
     197           0 :             r++;
     198             :     }
     199           0 :     return r;
     200             : }
     201             : 
     202           0 : static int findfirstonbit(GUInt32 n)
     203             : {
     204           0 :     for (int x = 0; x < 32; x++)
     205             :     {
     206           0 :         if ((n & (1U << x)) != 0)
     207           0 :             return x;
     208             :     }
     209           0 :     return -1;
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /* ==================================================================== */
     214             : /*                              BMPDataset                              */
     215             : /* ==================================================================== */
     216             : /************************************************************************/
     217             : 
     218             : class BMPDataset final : public GDALPamDataset
     219             : {
     220             :     friend class BMPRasterBand;
     221             :     friend class BMPComprRasterBand;
     222             : 
     223             :     BMPFileHeader sFileHeader;
     224             :     BMPInfoHeader sInfoHeader;
     225             :     int nColorElems;
     226             :     GByte *pabyColorTable;
     227             :     GDALColorTable *poColorTable;
     228             :     GDALGeoTransform m_gt{};
     229             :     int bGeoTransformValid;
     230             :     bool m_bNewFile = false;
     231             :     vsi_l_offset m_nFileSize = 0;
     232             : 
     233             :     char *pszFilename;
     234             :     VSILFILE *fp;
     235             : 
     236             :   protected:
     237             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     238             :                      GDALDataType, int, BANDMAP_TYPE, GSpacing nPixelSpace,
     239             :                      GSpacing nLineSpace, GSpacing nBandSpace,
     240             :                      GDALRasterIOExtraArg *psExtraArg) override;
     241             : 
     242             :   public:
     243             :     BMPDataset();
     244             :     ~BMPDataset() override;
     245             : 
     246             :     static int Identify(GDALOpenInfo *);
     247             :     static GDALDataset *Open(GDALOpenInfo *);
     248             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     249             :                                int nBandsIn, GDALDataType eType,
     250             :                                char **papszParamList);
     251             : 
     252             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     253             :     CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
     254             : };
     255             : 
     256             : /************************************************************************/
     257             : /* ==================================================================== */
     258             : /*                            BMPRasterBand                             */
     259             : /* ==================================================================== */
     260             : /************************************************************************/
     261             : 
     262             : class BMPRasterBand CPL_NON_FINAL : public GDALPamRasterBand
     263             : {
     264             :     friend class BMPDataset;
     265             : 
     266             :   protected:
     267             :     GUInt32 nScanSize;
     268             :     unsigned int iBytesPerPixel;
     269             :     GByte *pabyScan;
     270             : 
     271             :   public:
     272             :     BMPRasterBand(BMPDataset *, int);
     273             :     ~BMPRasterBand() override;
     274             : 
     275             :     CPLErr IReadBlock(int, int, void *) override;
     276             :     CPLErr IWriteBlock(int, int, void *) override;
     277             :     GDALColorInterp GetColorInterpretation() override;
     278             :     GDALColorTable *GetColorTable() override;
     279             :     CPLErr SetColorTable(GDALColorTable *) override;
     280             : };
     281             : 
     282             : /************************************************************************/
     283             : /*                           BMPRasterBand()                            */
     284             : /************************************************************************/
     285             : 
     286          73 : BMPRasterBand::BMPRasterBand(BMPDataset *poDSIn, int nBandIn)
     287          73 :     : nScanSize(0), iBytesPerPixel(poDSIn->sInfoHeader.iBitCount / 8),
     288          73 :       pabyScan(nullptr)
     289             : {
     290          73 :     poDS = poDSIn;
     291          73 :     nBand = nBandIn;
     292          73 :     eDataType = GDT_Byte;
     293             : 
     294             :     // We will read one scanline per time. Scanlines in BMP aligned at 4-byte
     295             :     // boundary
     296          73 :     nBlockXSize = poDS->GetRasterXSize();
     297          73 :     nBlockYSize = 1;
     298             : 
     299          73 :     const auto knIntMax = std::numeric_limits<int>::max();
     300          73 :     if (nBlockXSize < (knIntMax - 31) / poDSIn->sInfoHeader.iBitCount)
     301             :     {
     302          73 :         nScanSize =
     303          73 :             ((poDS->GetRasterXSize() * poDSIn->sInfoHeader.iBitCount + 31) &
     304          73 :              ~31) /
     305             :             8;
     306             :     }
     307             :     else
     308             :     {
     309             :         // pabyScan = NULL;
     310           0 :         return;
     311             :     }
     312             : 
     313             : #ifdef BMP_DEBUG
     314             :     CPLDebug("BMP", "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d",
     315             :              nBand, nBlockXSize, nBlockYSize, nScanSize);
     316             : #endif
     317             : 
     318          73 :     pabyScan = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nScanSize));
     319             : }
     320             : 
     321             : /************************************************************************/
     322             : /*                           ~BMPRasterBand()                           */
     323             : /************************************************************************/
     324             : 
     325         144 : BMPRasterBand::~BMPRasterBand()
     326             : {
     327          73 :     CPLFree(pabyScan);
     328         144 : }
     329             : 
     330             : /************************************************************************/
     331             : /*                             IReadBlock()                             */
     332             : /************************************************************************/
     333             : 
     334        1016 : CPLErr BMPRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     335             :                                  void *pImage)
     336             : {
     337        1016 :     BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
     338        1016 :     vsi_l_offset iScanOffset = 0;
     339             : 
     340        1016 :     if (poGDS->sInfoHeader.iHeight > 0)
     341        2032 :         iScanOffset = poGDS->sFileHeader.iOffBits +
     342        1016 :                       (poGDS->GetRasterYSize() - nBlockYOff - 1) *
     343        1016 :                           static_cast<vsi_l_offset>(nScanSize);
     344             :     else
     345           0 :         iScanOffset = poGDS->sFileHeader.iOffBits +
     346           0 :                       nBlockYOff * static_cast<vsi_l_offset>(nScanSize);
     347             : 
     348        1016 :     if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0)
     349             :     {
     350             :         // XXX: We will not report error here, because file just may be
     351             :         // in update state and data for this block will be available later.
     352           0 :         if (poGDS->eAccess == GA_Update)
     353             :         {
     354           0 :             memset(pImage, 0, nBlockXSize);
     355           0 :             return CE_None;
     356             :         }
     357             :         else
     358             :         {
     359           0 :             CPLError(CE_Failure, CPLE_FileIO,
     360             :                      "Can't seek to offset " CPL_FRMT_GUIB
     361             :                      " in input file to read data.",
     362             :                      iScanOffset);
     363           0 :             return CE_Failure;
     364             :         }
     365             :     }
     366        1016 :     if (VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize)
     367             :     {
     368             :         // XXX
     369           0 :         if (poGDS->eAccess == GA_Update)
     370             :         {
     371           0 :             memset(pImage, 0, nBlockXSize);
     372           0 :             return CE_None;
     373             :         }
     374             :         else
     375             :         {
     376           0 :             CPLError(CE_Failure, CPLE_FileIO,
     377             :                      "Can't read from offset " CPL_FRMT_GUIB " in input file.",
     378             :                      iScanOffset);
     379           0 :             return CE_Failure;
     380             :         }
     381             :     }
     382             : 
     383        1016 :     if (poGDS->sInfoHeader.iBitCount == 24 ||
     384        1014 :         poGDS->sInfoHeader.iBitCount == 32)
     385             :     {
     386           2 :         GByte *pabyTemp = pabyScan + 3 - nBand;
     387             : 
     388       65539 :         for (int i = 0; i < nBlockXSize; i++)
     389             :         {
     390             :             // Colour triplets in BMP file organized in reverse order:
     391             :             // blue, green, red. When we have 32-bit BMP the forth byte
     392             :             // in quadruplet should be discarded as it has no meaning.
     393             :             // That is why we always use 3 byte count in the following
     394             :             // pabyTemp index.
     395       65537 :             static_cast<GByte *>(pImage)[i] = *pabyTemp;
     396       65537 :             pabyTemp += iBytesPerPixel;
     397           2 :         }
     398             :     }
     399        1014 :     else if (poGDS->sInfoHeader.iBitCount == 8)
     400             :     {
     401         930 :         memcpy(pImage, pabyScan, nBlockXSize);
     402             :     }
     403          84 :     else if (poGDS->sInfoHeader.iBitCount == 16)
     404             :     {
     405             :         // rcg, oct 7/06: Byteswap if necessary, use int16
     406             :         // references to file pixels, expand samples to
     407             :         // 8-bit, support BMPC_BITFIELDS channel mask indicators,
     408             :         // and generalize band handling.
     409             : 
     410           0 :         GUInt16 *pScan16 = reinterpret_cast<GUInt16 *>(pabyScan);
     411             : #ifdef CPL_MSB
     412             :         GDALSwapWords(pScan16, sizeof(GUInt16), nBlockXSize, 0);
     413             : #endif
     414             : 
     415             :         // todo: make these band members and precompute.
     416             :         int mask[3], shift[3], size[3];
     417             :         float fTo8bit[3];
     418             : 
     419           0 :         if (poGDS->sInfoHeader.iCompression == BMPC_RGB)
     420             :         {
     421           0 :             mask[0] = 0x7c00;
     422           0 :             mask[1] = 0x03e0;
     423           0 :             mask[2] = 0x001f;
     424             :         }
     425           0 :         else if (poGDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
     426             :         {
     427           0 :             mask[0] = poGDS->sInfoHeader.iRedMask;
     428           0 :             mask[1] = poGDS->sInfoHeader.iGreenMask;
     429           0 :             mask[2] = poGDS->sInfoHeader.iBlueMask;
     430             :         }
     431             :         else
     432             :         {
     433           0 :             CPLError(CE_Failure, CPLE_FileIO, "Unknown 16-bit compression %d.",
     434           0 :                      poGDS->sInfoHeader.iCompression);
     435           0 :             return CE_Failure;
     436             :         }
     437             : 
     438           0 :         for (int i = 0; i < 3; i++)
     439             :         {
     440           0 :             shift[i] = findfirstonbit(mask[i]);
     441           0 :             size[i] = countonbits(mask[i]);
     442           0 :             if (size[i] > 14 || size[i] == 0)
     443             :             {
     444           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     445             :                          "Bad 16-bit channel mask %8x.", mask[i]);
     446           0 :                 return CE_Failure;
     447             :             }
     448           0 :             fTo8bit[i] = 255.0f / ((1 << size[i]) - 1);
     449             :         }
     450             : 
     451           0 :         for (int i = 0; i < nBlockXSize; i++)
     452             :         {
     453           0 :             ((GByte *)pImage)[i] =
     454           0 :                 (GByte)(0.5f +
     455           0 :                         fTo8bit[nBand - 1] * ((pScan16[i] & mask[nBand - 1]) >>
     456           0 :                                               shift[nBand - 1]));
     457             : #if 0
     458             :         // original code
     459             :             switch ( nBand )
     460             :             {
     461             :                 case 1: // Red
     462             :                 ((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F;
     463             :                 break;
     464             : 
     465             :                 case 2: // Green
     466             :                 ((GByte *) pImage)[i] =
     467             :                     ((pabyScan[i] & 0x03) << 3) |
     468             :                     ((pabyScan[i + 1] & 0xE0) >> 5);
     469             :                 break;
     470             : 
     471             :                 case 3: // Blue
     472             :                 ((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2;
     473             :                 break;
     474             :                 default:
     475             :                 break;
     476             :             }
     477             : #endif  // 0
     478             :         }
     479             :     }
     480          84 :     else if (poGDS->sInfoHeader.iBitCount == 4)
     481             :     {
     482          20 :         GByte *pabyTemp = pabyScan;
     483             : 
     484         420 :         for (int i = 0; i < nBlockXSize; i++)
     485             :         {
     486             :             // Most significant part of the byte represents leftmost pixel
     487         400 :             if (i & 0x01)
     488         200 :                 ((GByte *)pImage)[i] = *pabyTemp++ & 0x0F;
     489             :             else
     490         200 :                 ((GByte *)pImage)[i] = (*pabyTemp & 0xF0) >> 4;
     491             :         }
     492             :     }
     493          64 :     else if (poGDS->sInfoHeader.iBitCount == 1)
     494             :     {
     495          64 :         GByte *pabyTemp = pabyScan;
     496             : 
     497        2112 :         for (int i = 0; i < nBlockXSize; i++)
     498             :         {
     499        2048 :             switch (i & 0x7)
     500             :             {
     501         256 :                 case 0:
     502         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x80) >> 7;
     503         256 :                     break;
     504         256 :                 case 1:
     505         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x40) >> 6;
     506         256 :                     break;
     507         256 :                 case 2:
     508         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x20) >> 5;
     509         256 :                     break;
     510         256 :                 case 3:
     511         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x10) >> 4;
     512         256 :                     break;
     513         256 :                 case 4:
     514         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x08) >> 3;
     515         256 :                     break;
     516         256 :                 case 5:
     517         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x04) >> 2;
     518         256 :                     break;
     519         256 :                 case 6:
     520         256 :                     ((GByte *)pImage)[i] = (*pabyTemp & 0x02) >> 1;
     521         256 :                     break;
     522         256 :                 case 7:
     523         256 :                     ((GByte *)pImage)[i] = *pabyTemp++ & 0x01;
     524         256 :                     break;
     525           0 :                 default:
     526           0 :                     break;
     527             :             }
     528             :         }
     529             :     }
     530             : 
     531        1016 :     return CE_None;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                            IWriteBlock()                             */
     536             : /************************************************************************/
     537             : 
     538         441 : CPLErr BMPRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     539             : {
     540         441 :     BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
     541             : 
     542         441 :     CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
     543             :               pImage != nullptr);
     544             : 
     545         441 :     vsi_l_offset iScanOffset = poGDS->sFileHeader.iOffBits +
     546         441 :                                (poGDS->GetRasterYSize() - nBlockYOff - 1) *
     547         441 :                                    static_cast<vsi_l_offset>(nScanSize);
     548         441 :     if (VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET) < 0)
     549             :     {
     550           0 :         CPLError(CE_Failure, CPLE_FileIO,
     551             :                  "Can't seek to offset " CPL_FRMT_GUIB
     552             :                  " in output file to write data.\n%s",
     553           0 :                  iScanOffset, VSIStrerror(errno));
     554           0 :         return CE_Failure;
     555             :     }
     556             : 
     557         441 :     if (poGDS->nBands != 1)
     558             :     {
     559          30 :         memset(pabyScan, 0, nScanSize);
     560          30 :         VSIFReadL(pabyScan, 1, nScanSize, poGDS->fp);
     561          30 :         VSIFSeekL(poGDS->fp, iScanOffset, SEEK_SET);
     562             :     }
     563             : 
     564         441 :     for (int iInPixel = 0, iOutPixel = iBytesPerPixel - nBand;
     565       32851 :          iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands)
     566             :     {
     567       32410 :         pabyScan[iOutPixel] = ((GByte *)pImage)[iInPixel];
     568             :     }
     569             : 
     570         441 :     if (VSIFWriteL(pabyScan, 1, nScanSize, poGDS->fp) < nScanSize)
     571             :     {
     572           1 :         CPLError(CE_Failure, CPLE_FileIO,
     573             :                  "Can't write block with X offset %d and Y offset %d.\n%s",
     574           1 :                  nBlockXOff, nBlockYOff, VSIStrerror(errno));
     575           1 :         return CE_Failure;
     576             :     }
     577             : 
     578         440 :     return CE_None;
     579             : }
     580             : 
     581             : /************************************************************************/
     582             : /*                           GetColorTable()                            */
     583             : /************************************************************************/
     584             : 
     585         228 : GDALColorTable *BMPRasterBand::GetColorTable()
     586             : {
     587         228 :     BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
     588             : 
     589         228 :     return poGDS->poColorTable;
     590             : }
     591             : 
     592             : /************************************************************************/
     593             : /*                           SetColorTable()                            */
     594             : /************************************************************************/
     595             : 
     596           1 : CPLErr BMPRasterBand::SetColorTable(GDALColorTable *poColorTable)
     597             : {
     598           1 :     BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
     599             : 
     600           1 :     if (poColorTable)
     601             :     {
     602           1 :         poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount();
     603           1 :         if (poGDS->sInfoHeader.iClrUsed < 1 ||
     604           1 :             poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount))
     605           0 :             return CE_Failure;
     606             : 
     607           1 :         VSIFSeekL(poGDS->fp, BFH_SIZE + 32, SEEK_SET);
     608             : 
     609           1 :         GUInt32 iULong = CPL_LSBWORD32(poGDS->sInfoHeader.iClrUsed);
     610           1 :         VSIFWriteL(&iULong, 4, 1, poGDS->fp);
     611           2 :         poGDS->pabyColorTable = (GByte *)CPLRealloc(
     612           1 :             poGDS->pabyColorTable, static_cast<size_t>(poGDS->nColorElems) *
     613           1 :                                        poGDS->sInfoHeader.iClrUsed);
     614           1 :         if (!poGDS->pabyColorTable)
     615           0 :             return CE_Failure;
     616             : 
     617         257 :         for (unsigned int i = 0; i < poGDS->sInfoHeader.iClrUsed; i++)
     618             :         {
     619             :             GDALColorEntry oEntry;
     620             : 
     621         256 :             poColorTable->GetColorEntryAsRGB(i, &oEntry);
     622         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0;
     623         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 2] =
     624         256 :                 (GByte)oEntry.c1;  // Red
     625         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems + 1] =
     626         256 :                 (GByte)oEntry.c2;  // Green
     627         256 :             poGDS->pabyColorTable[i * poGDS->nColorElems] =
     628         256 :                 (GByte)oEntry.c3;  // Blue
     629             :         }
     630             : 
     631           1 :         VSIFSeekL(poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET);
     632           2 :         if (VSIFWriteL(poGDS->pabyColorTable, 1,
     633           1 :                        static_cast<size_t>(poGDS->nColorElems) *
     634           1 :                            poGDS->sInfoHeader.iClrUsed,
     635           1 :                        poGDS->fp) < static_cast<size_t>(poGDS->nColorElems) *
     636           1 :                                         poGDS->sInfoHeader.iClrUsed)
     637             :         {
     638           0 :             return CE_Failure;
     639             :         }
     640             :     }
     641             :     else
     642           0 :         return CE_Failure;
     643             : 
     644           1 :     return CE_None;
     645             : }
     646             : 
     647             : /************************************************************************/
     648             : /*                       GetColorInterpretation()                       */
     649             : /************************************************************************/
     650             : 
     651          12 : GDALColorInterp BMPRasterBand::GetColorInterpretation()
     652             : {
     653          12 :     BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
     654             : 
     655          12 :     if (poGDS->sInfoHeader.iBitCount == 24 ||
     656          12 :         poGDS->sInfoHeader.iBitCount == 32 ||
     657          12 :         poGDS->sInfoHeader.iBitCount == 16)
     658             :     {
     659           0 :         if (nBand == 1)
     660           0 :             return GCI_RedBand;
     661           0 :         else if (nBand == 2)
     662           0 :             return GCI_GreenBand;
     663           0 :         else if (nBand == 3)
     664           0 :             return GCI_BlueBand;
     665             :         else
     666           0 :             return GCI_Undefined;
     667             :     }
     668             :     else
     669             :     {
     670          12 :         return GCI_PaletteIndex;
     671             :     }
     672             : }
     673             : 
     674             : /************************************************************************/
     675             : /* ==================================================================== */
     676             : /*                       BMPComprRasterBand                             */
     677             : /* ==================================================================== */
     678             : /************************************************************************/
     679             : 
     680             : class BMPComprRasterBand final : public BMPRasterBand
     681             : {
     682             :     friend class BMPDataset;
     683             : 
     684             :     GByte *pabyComprBuf;
     685             :     GByte *pabyUncomprBuf;
     686             : 
     687             :   public:
     688             :     BMPComprRasterBand(BMPDataset *, int);
     689             :     ~BMPComprRasterBand() override;
     690             : 
     691             :     CPLErr IReadBlock(int, int, void *) override;
     692             :     // virtual CPLErr IWriteBlock( int, int, void * );
     693             : };
     694             : 
     695             : /************************************************************************/
     696             : /*                           BMPComprRasterBand()                       */
     697             : /************************************************************************/
     698             : 
     699           2 : BMPComprRasterBand::BMPComprRasterBand(BMPDataset *poDSIn, int nBandIn)
     700             :     : BMPRasterBand(poDSIn, nBandIn), pabyComprBuf(nullptr),
     701           2 :       pabyUncomprBuf(nullptr)
     702             : {
     703             :     /* TODO: it might be interesting to avoid uncompressing the whole data */
     704             :     /* in a single pass, especially if nXSize * nYSize is big */
     705             :     /* We could read incrementally one row at a time */
     706           2 :     const auto knIntMax = std::numeric_limits<int>::max();
     707           2 :     if (poDS->GetRasterXSize() > knIntMax / poDS->GetRasterYSize())
     708             :     {
     709           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too big dimensions : %d x %d",
     710           0 :                  poDS->GetRasterXSize(), poDS->GetRasterYSize());
     711           0 :         return;
     712             :     }
     713             : 
     714           2 :     if (poDSIn->m_nFileSize <= poDSIn->sFileHeader.iOffBits ||
     715           2 :         poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits > knIntMax)
     716             :     {
     717           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid header");
     718           0 :         return;
     719             :     }
     720             : 
     721           2 :     const GUInt32 iComprSize = static_cast<GUInt32>(
     722           2 :         poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits);
     723             :     const GUInt32 iUncomprSize =
     724           2 :         poDS->GetRasterXSize() * poDS->GetRasterYSize();
     725             : 
     726             : #ifdef DEBUG
     727           2 :     CPLDebug("BMP", "RLE compression detected.");
     728           2 :     CPLDebug("BMP",
     729             :              "Size of compressed buffer %ld bytes,"
     730             :              " size of uncompressed buffer %ld bytes.",
     731             :              (long)iComprSize, (long)iUncomprSize);
     732             : #endif
     733             : 
     734           2 :     pabyComprBuf = (GByte *)VSIMalloc(iComprSize);
     735           2 :     pabyUncomprBuf = (GByte *)VSIMalloc(iUncomprSize);
     736           2 :     if (pabyComprBuf == nullptr || pabyUncomprBuf == nullptr)
     737             :     {
     738           0 :         CPLFree(pabyComprBuf);
     739           0 :         pabyComprBuf = nullptr;
     740           0 :         CPLFree(pabyUncomprBuf);
     741           0 :         pabyUncomprBuf = nullptr;
     742           0 :         return;
     743             :     }
     744             : 
     745           4 :     if (VSIFSeekL(poDSIn->fp, poDSIn->sFileHeader.iOffBits, SEEK_SET) != 0 ||
     746           2 :         VSIFReadL(pabyComprBuf, 1, iComprSize, poDSIn->fp) < iComprSize)
     747             :     {
     748           0 :         CPLError(CE_Failure, CPLE_FileIO,
     749             :                  "Can't read from offset %ld in input file.",
     750           0 :                  (long)poDSIn->sFileHeader.iOffBits);
     751           0 :         CPLFree(pabyComprBuf);
     752           0 :         pabyComprBuf = nullptr;
     753           0 :         CPLFree(pabyUncomprBuf);
     754           0 :         pabyUncomprBuf = nullptr;
     755           0 :         return;
     756             :     }
     757             : 
     758           2 :     unsigned int i = 0;
     759           2 :     unsigned int j = 0;
     760           2 :     if (poDSIn->sInfoHeader.iBitCount == 8)  // RLE8
     761             :     {
     762         122 :         while (i < iComprSize)
     763             :         {
     764         122 :             if (pabyComprBuf[i])
     765             :             {
     766          28 :                 unsigned int iLength = pabyComprBuf[i++];
     767          28 :                 if (j == iUncomprSize)
     768           0 :                     break;
     769         106 :                 while (iLength > 0 && j < iUncomprSize && i < iComprSize)
     770             :                 {
     771          78 :                     pabyUncomprBuf[j++] = pabyComprBuf[i];
     772          78 :                     iLength--;
     773             :                 }
     774          28 :                 i++;
     775             :             }
     776             :             else
     777             :             {
     778          94 :                 i++;
     779          94 :                 if (i == iComprSize)
     780           0 :                     break;
     781          94 :                 if (pabyComprBuf[i] == 0)  // Next scanline
     782             :                 {
     783          38 :                     i++;
     784             :                 }
     785          56 :                 else if (pabyComprBuf[i] == 1)  // End of image
     786             :                 {
     787           2 :                     break;
     788             :                 }
     789          54 :                 else if (pabyComprBuf[i] == 2)  // Move to...
     790             :                 {
     791           0 :                     if (j == iUncomprSize)
     792           0 :                         break;
     793           0 :                     i++;
     794           0 :                     if (i < iComprSize - 1)
     795             :                     {
     796           0 :                         if (pabyComprBuf[i + 1] >
     797           0 :                                 knIntMax / poDS->GetRasterXSize() ||
     798           0 :                             static_cast<int>(pabyComprBuf[i + 1]) *
     799           0 :                                     poDS->GetRasterXSize() >
     800           0 :                                 knIntMax -
     801           0 :                                     static_cast<int>(j + pabyComprBuf[i]))
     802           0 :                             break;
     803           0 :                         j += pabyComprBuf[i] +
     804           0 :                              pabyComprBuf[i + 1] * poDS->GetRasterXSize();
     805           0 :                         i += 2;
     806             :                     }
     807             :                     else
     808           0 :                         break;
     809             :                 }
     810             :                 else  // Absolute mode
     811             :                 {
     812          54 :                     CPLAssert(i < iComprSize);
     813          54 :                     unsigned int iLength = pabyComprBuf[i++];
     814          54 :                     if (j == iUncomprSize)
     815           0 :                         break;
     816          54 :                     for (unsigned k = 0;
     817         776 :                          k < iLength && j < iUncomprSize && i < iComprSize; k++)
     818         722 :                         pabyUncomprBuf[j++] = pabyComprBuf[i++];
     819          54 :                     if (i & 0x01)
     820          18 :                         i++;
     821             :                 }
     822             :             }
     823             :         }
     824             :     }
     825             :     else  // RLE4
     826             :     {
     827           0 :         while (i < iComprSize)
     828             :         {
     829           0 :             if (pabyComprBuf[i])
     830             :             {
     831           0 :                 unsigned int iLength = pabyComprBuf[i++];
     832           0 :                 if (j == iUncomprSize)
     833           0 :                     break;
     834           0 :                 while (iLength > 0 && j < iUncomprSize && i < iComprSize)
     835             :                 {
     836           0 :                     if (iLength & 0x01)
     837           0 :                         pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
     838             :                     else
     839           0 :                         pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F;
     840           0 :                     iLength--;
     841             :                 }
     842           0 :                 i++;
     843             :             }
     844             :             else
     845             :             {
     846           0 :                 i++;
     847           0 :                 if (i == iComprSize)
     848           0 :                     break;
     849           0 :                 if (pabyComprBuf[i] == 0)  // Next scanline
     850             :                 {
     851           0 :                     i++;
     852             :                 }
     853           0 :                 else if (pabyComprBuf[i] == 1)  // End of image
     854             :                 {
     855           0 :                     break;
     856             :                 }
     857           0 :                 else if (pabyComprBuf[i] == 2)  // Move to...
     858             :                 {
     859           0 :                     if (j == iUncomprSize)
     860           0 :                         break;
     861           0 :                     i++;
     862           0 :                     if (i < iComprSize - 1)
     863             :                     {
     864           0 :                         if (pabyComprBuf[i + 1] >
     865           0 :                                 knIntMax / poDS->GetRasterXSize() ||
     866           0 :                             static_cast<int>(pabyComprBuf[i + 1]) *
     867           0 :                                     poDS->GetRasterXSize() >
     868           0 :                                 knIntMax -
     869           0 :                                     static_cast<int>(j + pabyComprBuf[i]))
     870           0 :                             break;
     871           0 :                         j += pabyComprBuf[i] +
     872           0 :                              pabyComprBuf[i + 1] * poDS->GetRasterXSize();
     873           0 :                         i += 2;
     874             :                     }
     875             :                     else
     876           0 :                         break;
     877             :                 }
     878             :                 else  // Absolute mode
     879             :                 {
     880           0 :                     CPLAssert(i < iComprSize);
     881           0 :                     unsigned int iLength = pabyComprBuf[i++];
     882           0 :                     if (j == iUncomprSize)
     883           0 :                         break;
     884           0 :                     for (unsigned k = 0;
     885           0 :                          k < iLength && j < iUncomprSize && i < iComprSize; k++)
     886             :                     {
     887           0 :                         if (k & 0x01)
     888           0 :                             pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F;
     889             :                         else
     890           0 :                             pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
     891             :                     }
     892           0 :                     if (i & 0x01)
     893           0 :                         i++;
     894             :                 }
     895             :             }
     896             :         }
     897             :     }
     898             :     /* Validate that we have read all compressed data (we tolerate missing */
     899             :     /* end of image marker) and that we have filled all uncompressed data */
     900           2 :     if (j < iUncomprSize || (i + 1 != iComprSize && i + 2 != iComprSize))
     901             :     {
     902           0 :         CPLFree(pabyUncomprBuf);
     903           0 :         pabyUncomprBuf = nullptr;
     904             :     }
     905             :     // rcg, release compressed buffer here.
     906           2 :     CPLFree(pabyComprBuf);
     907           2 :     pabyComprBuf = nullptr;
     908             : }
     909             : 
     910             : /************************************************************************/
     911             : /*                           ~BMPComprRasterBand()                      */
     912             : /************************************************************************/
     913             : 
     914           4 : BMPComprRasterBand::~BMPComprRasterBand()
     915             : {
     916           2 :     CPLFree(pabyComprBuf);
     917           2 :     CPLFree(pabyUncomprBuf);
     918           4 : }
     919             : 
     920             : /************************************************************************/
     921             : /*                             IReadBlock()                             */
     922             : /************************************************************************/
     923             : 
     924          20 : CPLErr BMPComprRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
     925             :                                       void *pImage)
     926             : {
     927          40 :     memcpy(pImage,
     928          60 :            pabyUncomprBuf + (poDS->GetRasterYSize() - nBlockYOff - 1) *
     929          20 :                                 poDS->GetRasterXSize(),
     930          20 :            nBlockXSize);
     931             : 
     932          20 :     return CE_None;
     933             : }
     934             : 
     935             : /************************************************************************/
     936             : /*                           BMPDataset()                               */
     937             : /************************************************************************/
     938             : 
     939          73 : BMPDataset::BMPDataset()
     940             :     : nColorElems(0), pabyColorTable(nullptr), poColorTable(nullptr),
     941          73 :       bGeoTransformValid(FALSE), pszFilename(nullptr), fp(nullptr)
     942             : {
     943          73 :     nBands = 0;
     944             : 
     945          73 :     memset(&sFileHeader, 0, sizeof(sFileHeader));
     946          73 :     memset(&sInfoHeader, 0, sizeof(sInfoHeader));
     947          73 : }
     948             : 
     949             : /************************************************************************/
     950             : /*                            ~BMPDataset()                             */
     951             : /************************************************************************/
     952             : 
     953         146 : BMPDataset::~BMPDataset()
     954             : {
     955          73 :     FlushCache(true);
     956             : 
     957          73 :     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          73 :     CPLFree(pabyColorTable);
     969          73 :     if (poColorTable)
     970          43 :         delete poColorTable;
     971          73 :     CPLFree(pszFilename);
     972          73 :     if (fp)
     973          70 :         VSIFCloseL(fp);
     974         146 : }
     975             : 
     976             : /************************************************************************/
     977             : /*                          GetGeoTransform()                           */
     978             : /************************************************************************/
     979             : 
     980          17 : CPLErr BMPDataset::GetGeoTransform(GDALGeoTransform &gt) const
     981             : {
     982          17 :     if (bGeoTransformValid)
     983             :     {
     984           0 :         gt = m_gt;
     985           0 :         return CE_None;
     986             :     }
     987             : 
     988          17 :     if (GDALPamDataset::GetGeoTransform(gt) == 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             :         gt[1] = sInfoHeader.iXPelsPerMeter;
     996             :         gt[5] = -sInfoHeader.iYPelsPerMeter;
     997             :         gt[0] = -0.5 * gt[1];
     998             :         gt[3] = -0.5 * gt[5];
     999             :         return CE_None;
    1000             :     }
    1001             : #endif
    1002             : 
    1003          15 :     return CE_Failure;
    1004             : }
    1005             : 
    1006             : /************************************************************************/
    1007             : /*                          SetGeoTransform()                           */
    1008             : /************************************************************************/
    1009             : 
    1010           8 : CPLErr BMPDataset::SetGeoTransform(const GDALGeoTransform &gt)
    1011             : {
    1012           8 :     if (pszFilename && bGeoTransformValid)
    1013             :     {
    1014           0 :         m_gt = gt;
    1015             : 
    1016           0 :         CPLErr eErr = CE_None;
    1017           0 :         if (GDALWriteWorldFile(pszFilename, "wld", m_gt.data()) == 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(gt);
    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       63002 : int BMPDataset::Identify(GDALOpenInfo *poOpenInfo)
    1063             : 
    1064             : {
    1065       63002 :     if (poOpenInfo->nHeaderBytes < BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
    1066        6476 :         poOpenInfo->pabyHeader[0] != 'B' || poOpenInfo->pabyHeader[1] != 'M' ||
    1067          94 :         poOpenInfo->pabyHeader[6] != 0 || poOpenInfo->pabyHeader[7] != 0 ||
    1068          94 :         poOpenInfo->pabyHeader[8] != 0 || poOpenInfo->pabyHeader[9] != 0)
    1069       62908 :         return FALSE;
    1070             : 
    1071             :     uint32_t nInfoHeaderSize;
    1072          94 :     memcpy(&nInfoHeaderSize, poOpenInfo->pabyHeader + BFH_SIZE,
    1073             :            sizeof(uint32_t));
    1074          94 :     CPL_LSBPTR32(&nInfoHeaderSize);
    1075             :     // Check against the maximum known size
    1076          94 :     if (nInfoHeaderSize > BIH_BITMAPV5SIZE)
    1077           0 :         return FALSE;
    1078             : 
    1079          94 :     return TRUE;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                                Open()                                */
    1084             : /************************************************************************/
    1085             : 
    1086          47 : GDALDataset *BMPDataset::Open(GDALOpenInfo *poOpenInfo)
    1087             : {
    1088          47 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
    1089           0 :         return nullptr;
    1090             : 
    1091             :     /* -------------------------------------------------------------------- */
    1092             :     /*      Create a corresponding GDALDataset.                             */
    1093             :     /* -------------------------------------------------------------------- */
    1094          47 :     BMPDataset *poDS = new BMPDataset();
    1095          47 :     poDS->eAccess = poOpenInfo->eAccess;
    1096             : 
    1097             :     VSIStatBufL sStat;
    1098          47 :     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          47 :     memcpy(&poDS->sFileHeader.iOffBits, poOpenInfo->pabyHeader + 10, 4);
    1108             : #ifdef CPL_MSB
    1109             :     CPL_SWAP32PTR(&poDS->sFileHeader.iOffBits);
    1110             : #endif
    1111          47 :     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          47 :     if (poDS->sFileHeader.iOffBits <= BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
    1122          47 :         poDS->sFileHeader.iOffBits >= poDS->m_nFileSize)
    1123             :     {
    1124           0 :         delete poDS;
    1125           0 :         return nullptr;
    1126             :     }
    1127             : 
    1128             :     /* -------------------------------------------------------------------- */
    1129             :     /*      Read the BMPInfoHeader.                                         */
    1130             :     /* -------------------------------------------------------------------- */
    1131          47 :     poDS->fp = poOpenInfo->fpL;
    1132          47 :     poOpenInfo->fpL = nullptr;
    1133             : 
    1134          47 :     VSIFSeekL(poDS->fp, BFH_SIZE, SEEK_SET);
    1135          47 :     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          47 :     if (poDS->sInfoHeader.iSize == BIH_WIN4SIZE)
    1142          45 :         eBMPType = BMPT_WIN4;
    1143           2 :     else if (poDS->sInfoHeader.iSize == BIH_OS21SIZE)
    1144           0 :         eBMPType = BMPT_OS21;
    1145           2 :     else if (poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
    1146           2 :              poDS->sInfoHeader.iSize == 16)
    1147           0 :         eBMPType = BMPT_OS22;
    1148             :     else
    1149           2 :         eBMPType = BMPT_WIN5;
    1150             : 
    1151          47 :     if (eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22)
    1152             :     {
    1153          47 :         VSIFReadL(&poDS->sInfoHeader.iWidth, 1, 4, poDS->fp);
    1154          47 :         VSIFReadL(&poDS->sInfoHeader.iHeight, 1, 4, poDS->fp);
    1155          47 :         VSIFReadL(&poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp);
    1156          47 :         VSIFReadL(&poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp);
    1157             :         unsigned int iCompression;
    1158          47 :         VSIFReadL(&iCompression, 1, 4, poDS->fp);
    1159             : #ifdef CPL_MSB
    1160             :         CPL_SWAP32PTR(&iCompression);
    1161             : #endif
    1162          47 :         if (iCompression > BMPC_PNG)
    1163             :         {
    1164           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported compression");
    1165           0 :             delete poDS;
    1166           0 :             return nullptr;
    1167             :         }
    1168          47 :         poDS->sInfoHeader.iCompression =
    1169             :             static_cast<BMPComprMethod>(iCompression);
    1170          47 :         VSIFReadL(&poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp);
    1171          47 :         VSIFReadL(&poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp);
    1172          47 :         VSIFReadL(&poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp);
    1173          47 :         VSIFReadL(&poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp);
    1174          47 :         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          47 :         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          47 :         poDS->nColorElems = 4;
    1202             :     }
    1203             : 
    1204          47 :     if (eBMPType == BMPT_OS22)
    1205             :     {
    1206           0 :         poDS->nColorElems =
    1207             :             3;  // FIXME: different info in different documents regarding this!
    1208             :     }
    1209             : 
    1210          47 :     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          47 :     if (poDS->sInfoHeader.iBitCount != 1 && poDS->sInfoHeader.iBitCount != 4 &&
    1227          42 :         poDS->sInfoHeader.iBitCount != 8 && poDS->sInfoHeader.iBitCount != 16 &&
    1228           4 :         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          47 :     if (poDS->sInfoHeader.iHeight == INT_MIN)
    1250             :     {
    1251           0 :         delete poDS;
    1252           0 :         return nullptr;
    1253             :     }
    1254             : 
    1255          47 :     poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
    1256          47 :     poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)
    1257          47 :                              ? poDS->sInfoHeader.iHeight
    1258             :                              : -poDS->sInfoHeader.iHeight;
    1259             : 
    1260          47 :     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          47 :     switch (poDS->sInfoHeader.iBitCount)
    1269             :     {
    1270          43 :         case 1:
    1271             :         case 4:
    1272             :         case 8:
    1273             :         {
    1274          43 :             poDS->nBands = 1;
    1275             :             int nColorTableSize;
    1276          43 :             int nMaxColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
    1277             :             // Allocate memory for colour table and read it
    1278          43 :             if (poDS->sInfoHeader.iClrUsed)
    1279             :             {
    1280          26 :                 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          26 :                 nColorTableSize = poDS->sInfoHeader.iClrUsed;
    1289             :             }
    1290             :             else
    1291          17 :                 nColorTableSize = nMaxColorTableSize;
    1292             : 
    1293          43 :             poDS->pabyColorTable = (GByte *)VSI_MALLOC2_VERBOSE(
    1294             :                 poDS->nColorElems, nColorTableSize);
    1295          43 :             if (poDS->pabyColorTable == nullptr)
    1296             :             {
    1297           0 :                 break;
    1298             :             }
    1299             : 
    1300         129 :             if (VSIFSeekL(poDS->fp,
    1301          43 :                           BFH_SIZE + static_cast<vsi_l_offset>(
    1302          43 :                                          poDS->sInfoHeader.iSize),
    1303          86 :                           SEEK_SET) != 0 ||
    1304          43 :                 VSIFReadL(poDS->pabyColorTable, poDS->nColorElems,
    1305          43 :                           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          43 :             poDS->poColorTable = new GDALColorTable();
    1314        9809 :             for (int i = 0; i < nColorTableSize; i++)
    1315             :             {
    1316        9766 :                 oEntry.c1 =
    1317        9766 :                     poDS->pabyColorTable[i * poDS->nColorElems + 2];  // Red
    1318        9766 :                 oEntry.c2 =
    1319        9766 :                     poDS->pabyColorTable[i * poDS->nColorElems + 1];  // Green
    1320        9766 :                 oEntry.c3 =
    1321        9766 :                     poDS->pabyColorTable[i * poDS->nColorElems];  // Blue
    1322        9766 :                 oEntry.c4 = 255;
    1323             : 
    1324        9766 :                 poDS->poColorTable->SetColorEntry(i, &oEntry);
    1325             :             }
    1326             :         }
    1327          43 :         break;
    1328           4 :         case 16:
    1329             :         case 24:
    1330             :         case 32:
    1331           4 :             poDS->nBands = 3;
    1332           4 :             break;
    1333           0 :         default:
    1334           0 :             delete poDS;
    1335           0 :             return nullptr;
    1336             :     }
    1337             : 
    1338             :     /* -------------------------------------------------------------------- */
    1339             :     /*      Create band information objects.                                */
    1340             :     /* -------------------------------------------------------------------- */
    1341          47 :     if (poDS->sInfoHeader.iCompression == BMPC_RGB ||
    1342           2 :         poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
    1343             :     {
    1344          98 :         for (int iBand = 1; iBand <= poDS->nBands; iBand++)
    1345             :         {
    1346          53 :             BMPRasterBand *band = new BMPRasterBand(poDS, iBand);
    1347          53 :             poDS->SetBand(iBand, band);
    1348          53 :             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          45 :         }
    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          47 :     poDS->bGeoTransformValid =
    1387          47 :         GDALReadWorldFile(poOpenInfo->pszFilename, nullptr, poDS->m_gt.data());
    1388             : 
    1389          47 :     if (!poDS->bGeoTransformValid)
    1390          47 :         poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename,
    1391             :                                                      ".wld", poDS->m_gt.data());
    1392             : 
    1393             :     /* -------------------------------------------------------------------- */
    1394             :     /*      Initialize any PAM information.                                 */
    1395             :     /* -------------------------------------------------------------------- */
    1396          47 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1397          47 :     poDS->TryLoadXML();
    1398             : 
    1399             :     /* -------------------------------------------------------------------- */
    1400             :     /*      Check for overviews.                                            */
    1401             :     /* -------------------------------------------------------------------- */
    1402          47 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1403             : 
    1404          47 :     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 :         auto band = new BMPRasterBand(poDS, iBand);
    1642          18 :         poDS->SetBand(iBand, band);
    1643          18 :         if (band->pabyScan == nullptr)
    1644             :         {
    1645           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Image with (%d) too large.",
    1646             :                      poDS->nRasterXSize);
    1647           0 :             delete poDS;
    1648           0 :             return nullptr;
    1649             :         }
    1650             :     }
    1651             : 
    1652             :     /* -------------------------------------------------------------------- */
    1653             :     /*      Do we need a world file?                                        */
    1654             :     /* -------------------------------------------------------------------- */
    1655          14 :     if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    1656           0 :         poDS->bGeoTransformValid = TRUE;
    1657             : 
    1658          14 :     return poDS;
    1659             : }
    1660             : 
    1661             : /************************************************************************/
    1662             : /*                        GDALRegister_BMP()                            */
    1663             : /************************************************************************/
    1664             : 
    1665        2038 : void GDALRegister_BMP()
    1666             : 
    1667             : {
    1668        2038 :     if (GDALGetDriverByName("BMP") != nullptr)
    1669         283 :         return;
    1670             : 
    1671        1755 :     GDALDriver *poDriver = new GDALDriver();
    1672             : 
    1673        1755 :     poDriver->SetDescription("BMP");
    1674        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1675        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1676        1755 :                               "MS Windows Device Independent Bitmap");
    1677        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bmp.html");
    1678        1755 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bmp");
    1679        1755 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
    1680        1755 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
    1681             :                               "<CreationOptionList>"
    1682             :                               "   <Option name='WORLDFILE' type='boolean' "
    1683             :                               "description='Write out world file'/>"
    1684        1755 :                               "</CreationOptionList>");
    1685             : 
    1686        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1687             : 
    1688        1755 :     poDriver->pfnOpen = BMPDataset::Open;
    1689        1755 :     poDriver->pfnCreate = BMPDataset::Create;
    1690        1755 :     poDriver->pfnIdentify = BMPDataset::Identify;
    1691             : 
    1692        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1693             : }

Generated by: LCOV version 1.14