LCOV - code coverage report
Current view: top level - frmts/bmp - bmpdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 481 678 70.9 %
Date: 2025-12-17 01:24:48 Functions: 22 24 91.7 %

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

Generated by: LCOV version 1.14