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