LCOV - code coverage report
Current view: top level - frmts/bmp - bmpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 452 671 67.4 %
Date: 2025-05-31 00:00:17 Functions: 22 24 91.7 %

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

Generated by: LCOV version 1.14