Coverage Report

Created: 2025-06-09 07:43

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