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