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 : GDALGeoTransform m_gt{};
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(GDALGeoTransform >) const override;
248 : CPLErr SetGeoTransform(const GDALGeoTransform >) 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 73 : }
943 :
944 : /************************************************************************/
945 : /* ~BMPDataset() */
946 : /************************************************************************/
947 :
948 146 : BMPDataset::~BMPDataset()
949 : {
950 73 : FlushCache(true);
951 :
952 73 : if (m_bNewFile && fp)
953 : {
954 : // Extend the file with zeroes if needed
955 23 : VSIFSeekL(fp, 0, SEEK_END);
956 :
957 23 : if (VSIFTellL(fp) < m_nFileSize)
958 : {
959 13 : VSIFTruncateL(fp, m_nFileSize);
960 : }
961 : }
962 :
963 73 : CPLFree(pabyColorTable);
964 73 : if (poColorTable)
965 43 : delete poColorTable;
966 73 : CPLFree(pszFilename);
967 73 : if (fp)
968 70 : VSIFCloseL(fp);
969 146 : }
970 :
971 : /************************************************************************/
972 : /* GetGeoTransform() */
973 : /************************************************************************/
974 :
975 17 : CPLErr BMPDataset::GetGeoTransform(GDALGeoTransform >) const
976 : {
977 17 : if (bGeoTransformValid)
978 : {
979 0 : gt = m_gt;
980 0 : return CE_None;
981 : }
982 :
983 17 : if (GDALPamDataset::GetGeoTransform(gt) == CE_None)
984 2 : return CE_None;
985 :
986 : #ifdef notdef
987 : // See http://trac.osgeo.org/gdal/ticket/3578
988 : if (sInfoHeader.iXPelsPerMeter > 0 && sInfoHeader.iYPelsPerMeter > 0)
989 : {
990 : gt[1] = sInfoHeader.iXPelsPerMeter;
991 : gt[5] = -sInfoHeader.iYPelsPerMeter;
992 : gt[0] = -0.5 * gt[1];
993 : gt[3] = -0.5 * gt[5];
994 : return CE_None;
995 : }
996 : #endif
997 :
998 15 : return CE_Failure;
999 : }
1000 :
1001 : /************************************************************************/
1002 : /* SetGeoTransform() */
1003 : /************************************************************************/
1004 :
1005 8 : CPLErr BMPDataset::SetGeoTransform(const GDALGeoTransform >)
1006 : {
1007 8 : if (pszFilename && bGeoTransformValid)
1008 : {
1009 0 : m_gt = gt;
1010 :
1011 0 : CPLErr eErr = CE_None;
1012 0 : if (GDALWriteWorldFile(pszFilename, "wld", m_gt.data()) == FALSE)
1013 : {
1014 0 : CPLError(CE_Failure, CPLE_FileIO, "Can't write world file.");
1015 0 : eErr = CE_Failure;
1016 : }
1017 0 : return eErr;
1018 : }
1019 :
1020 8 : return GDALPamDataset::SetGeoTransform(gt);
1021 : }
1022 :
1023 : /************************************************************************/
1024 : /* IRasterIO() */
1025 : /* */
1026 : /* Multi-band raster io handler. We will use block based */
1027 : /* loading is used for multiband BMPs. That is because they */
1028 : /* are effectively pixel interleaved, so processing all bands */
1029 : /* for a given block together avoid extra seeks. */
1030 : /************************************************************************/
1031 :
1032 13 : CPLErr BMPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1033 : int nXSize, int nYSize, void *pData, int nBufXSize,
1034 : int nBufYSize, GDALDataType eBufType,
1035 : int nBandCount, BANDMAP_TYPE panBandMap,
1036 : GSpacing nPixelSpace, GSpacing nLineSpace,
1037 : GSpacing nBandSpace,
1038 : GDALRasterIOExtraArg *psExtraArg)
1039 :
1040 : {
1041 13 : if (nBandCount > 1)
1042 0 : return GDALDataset::BlockBasedRasterIO(
1043 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1044 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1045 0 : nBandSpace, psExtraArg);
1046 : else
1047 13 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1048 : pData, nBufXSize, nBufYSize, eBufType,
1049 : nBandCount, panBandMap, nPixelSpace,
1050 13 : nLineSpace, nBandSpace, psExtraArg);
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* Identify() */
1055 : /************************************************************************/
1056 :
1057 61152 : int BMPDataset::Identify(GDALOpenInfo *poOpenInfo)
1058 :
1059 : {
1060 61152 : if (poOpenInfo->nHeaderBytes < BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
1061 6367 : poOpenInfo->pabyHeader[0] != 'B' || poOpenInfo->pabyHeader[1] != 'M' ||
1062 94 : poOpenInfo->pabyHeader[6] != 0 || poOpenInfo->pabyHeader[7] != 0 ||
1063 94 : poOpenInfo->pabyHeader[8] != 0 || poOpenInfo->pabyHeader[9] != 0)
1064 61058 : return FALSE;
1065 :
1066 : uint32_t nInfoHeaderSize;
1067 94 : memcpy(&nInfoHeaderSize, poOpenInfo->pabyHeader + BFH_SIZE,
1068 : sizeof(uint32_t));
1069 94 : CPL_LSBPTR32(&nInfoHeaderSize);
1070 : // Check against the maximum known size
1071 94 : if (nInfoHeaderSize > BIH_BITMAPV5SIZE)
1072 0 : return FALSE;
1073 :
1074 94 : return TRUE;
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* Open() */
1079 : /************************************************************************/
1080 :
1081 47 : GDALDataset *BMPDataset::Open(GDALOpenInfo *poOpenInfo)
1082 : {
1083 47 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
1084 0 : return nullptr;
1085 :
1086 : /* -------------------------------------------------------------------- */
1087 : /* Create a corresponding GDALDataset. */
1088 : /* -------------------------------------------------------------------- */
1089 47 : BMPDataset *poDS = new BMPDataset();
1090 47 : poDS->eAccess = poOpenInfo->eAccess;
1091 :
1092 : VSIStatBufL sStat;
1093 47 : if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
1094 : {
1095 0 : delete poDS;
1096 0 : return nullptr;
1097 : }
1098 :
1099 : /* -------------------------------------------------------------------- */
1100 : /* Read the BMPFileHeader. We need iOffBits value only */
1101 : /* -------------------------------------------------------------------- */
1102 47 : memcpy(&poDS->sFileHeader.iOffBits, poOpenInfo->pabyHeader + 10, 4);
1103 : #ifdef CPL_MSB
1104 : CPL_SWAP32PTR(&poDS->sFileHeader.iOffBits);
1105 : #endif
1106 47 : poDS->m_nFileSize = sStat.st_size;
1107 :
1108 : #ifdef BMP_DEBUG
1109 : CPLDebug("BMP", "File size " CPL_FRMT_GUIB " bytes.",
1110 : static_cast<GUIntBig>(poDS->m_nFileSize));
1111 : CPLDebug("BMP", "Image offset 0x%x bytes from file start.",
1112 : poDS->sFileHeader.iOffBits);
1113 : #endif
1114 :
1115 : // Validatate iOffBits
1116 47 : if (poDS->sFileHeader.iOffBits <= BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
1117 47 : poDS->sFileHeader.iOffBits >= poDS->m_nFileSize)
1118 : {
1119 0 : delete poDS;
1120 0 : return nullptr;
1121 : }
1122 :
1123 : /* -------------------------------------------------------------------- */
1124 : /* Read the BMPInfoHeader. */
1125 : /* -------------------------------------------------------------------- */
1126 47 : poDS->fp = poOpenInfo->fpL;
1127 47 : poOpenInfo->fpL = nullptr;
1128 :
1129 47 : VSIFSeekL(poDS->fp, BFH_SIZE, SEEK_SET);
1130 47 : VSIFReadL(&poDS->sInfoHeader.iSize, 1, 4, poDS->fp);
1131 : #ifdef CPL_MSB
1132 : CPL_SWAP32PTR(&poDS->sInfoHeader.iSize);
1133 : #endif
1134 :
1135 : BMPType eBMPType;
1136 47 : if (poDS->sInfoHeader.iSize == BIH_WIN4SIZE)
1137 45 : eBMPType = BMPT_WIN4;
1138 2 : else if (poDS->sInfoHeader.iSize == BIH_OS21SIZE)
1139 0 : eBMPType = BMPT_OS21;
1140 2 : else if (poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
1141 2 : poDS->sInfoHeader.iSize == 16)
1142 0 : eBMPType = BMPT_OS22;
1143 : else
1144 2 : eBMPType = BMPT_WIN5;
1145 :
1146 47 : if (eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22)
1147 : {
1148 47 : VSIFReadL(&poDS->sInfoHeader.iWidth, 1, 4, poDS->fp);
1149 47 : VSIFReadL(&poDS->sInfoHeader.iHeight, 1, 4, poDS->fp);
1150 47 : VSIFReadL(&poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp);
1151 47 : VSIFReadL(&poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp);
1152 : unsigned int iCompression;
1153 47 : VSIFReadL(&iCompression, 1, 4, poDS->fp);
1154 : #ifdef CPL_MSB
1155 : CPL_SWAP32PTR(&iCompression);
1156 : #endif
1157 47 : if (iCompression > BMPC_PNG)
1158 : {
1159 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported compression");
1160 0 : delete poDS;
1161 0 : return nullptr;
1162 : }
1163 47 : poDS->sInfoHeader.iCompression =
1164 : static_cast<BMPComprMethod>(iCompression);
1165 47 : VSIFReadL(&poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp);
1166 47 : VSIFReadL(&poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp);
1167 47 : VSIFReadL(&poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp);
1168 47 : VSIFReadL(&poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp);
1169 47 : VSIFReadL(&poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp);
1170 :
1171 : // rcg, read win4/5 fields. If we're reading a
1172 : // legacy header that ends at iClrImportant, it turns
1173 : // out that the three DWORD color table entries used
1174 : // by the channel masks start here anyway.
1175 47 : if (poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
1176 : {
1177 0 : VSIFReadL(&poDS->sInfoHeader.iRedMask, 1, 4, poDS->fp);
1178 0 : VSIFReadL(&poDS->sInfoHeader.iGreenMask, 1, 4, poDS->fp);
1179 0 : VSIFReadL(&poDS->sInfoHeader.iBlueMask, 1, 4, poDS->fp);
1180 : }
1181 : #ifdef CPL_MSB
1182 : CPL_SWAP32PTR(&poDS->sInfoHeader.iWidth);
1183 : CPL_SWAP32PTR(&poDS->sInfoHeader.iHeight);
1184 : CPL_SWAP16PTR(&poDS->sInfoHeader.iPlanes);
1185 : CPL_SWAP16PTR(&poDS->sInfoHeader.iBitCount);
1186 : CPL_SWAP32PTR(&poDS->sInfoHeader.iSizeImage);
1187 : CPL_SWAP32PTR(&poDS->sInfoHeader.iXPelsPerMeter);
1188 : CPL_SWAP32PTR(&poDS->sInfoHeader.iYPelsPerMeter);
1189 : CPL_SWAP32PTR(&poDS->sInfoHeader.iClrUsed);
1190 : CPL_SWAP32PTR(&poDS->sInfoHeader.iClrImportant);
1191 : // rcg, swap win4/5 fields.
1192 : CPL_SWAP32PTR(&poDS->sInfoHeader.iRedMask);
1193 : CPL_SWAP32PTR(&poDS->sInfoHeader.iGreenMask);
1194 : CPL_SWAP32PTR(&poDS->sInfoHeader.iBlueMask);
1195 : #endif
1196 47 : poDS->nColorElems = 4;
1197 : }
1198 :
1199 47 : if (eBMPType == BMPT_OS22)
1200 : {
1201 0 : poDS->nColorElems =
1202 : 3; // FIXME: different info in different documents regarding this!
1203 : }
1204 :
1205 47 : if (eBMPType == BMPT_OS21)
1206 : {
1207 : GInt16 iShort;
1208 :
1209 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1210 0 : poDS->sInfoHeader.iWidth = CPL_LSBWORD16(iShort);
1211 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1212 0 : poDS->sInfoHeader.iHeight = CPL_LSBWORD16(iShort);
1213 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1214 0 : poDS->sInfoHeader.iPlanes = CPL_LSBWORD16(iShort);
1215 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1216 0 : poDS->sInfoHeader.iBitCount = CPL_LSBWORD16(iShort);
1217 0 : poDS->sInfoHeader.iCompression = BMPC_RGB;
1218 0 : poDS->nColorElems = 3;
1219 : }
1220 :
1221 47 : if (poDS->sInfoHeader.iBitCount != 1 && poDS->sInfoHeader.iBitCount != 4 &&
1222 42 : poDS->sInfoHeader.iBitCount != 8 && poDS->sInfoHeader.iBitCount != 16 &&
1223 4 : poDS->sInfoHeader.iBitCount != 24 && poDS->sInfoHeader.iBitCount != 32)
1224 : {
1225 0 : delete poDS;
1226 0 : return nullptr;
1227 : }
1228 :
1229 : #ifdef BMP_DEBUG
1230 : CPLDebug("BMP",
1231 : "Windows Device Independent Bitmap parameters:\n"
1232 : " info header size: %d bytes\n"
1233 : " width: %d\n height: %d\n planes: %d\n bpp: %d\n"
1234 : " compression: %d\n image size: %d bytes\n X resolution: %d\n"
1235 : " Y resolution: %d\n colours used: %d\n colours important: %d",
1236 : poDS->sInfoHeader.iSize, poDS->sInfoHeader.iWidth,
1237 : poDS->sInfoHeader.iHeight, poDS->sInfoHeader.iPlanes,
1238 : poDS->sInfoHeader.iBitCount, poDS->sInfoHeader.iCompression,
1239 : poDS->sInfoHeader.iSizeImage, poDS->sInfoHeader.iXPelsPerMeter,
1240 : poDS->sInfoHeader.iYPelsPerMeter, poDS->sInfoHeader.iClrUsed,
1241 : poDS->sInfoHeader.iClrImportant);
1242 : #endif
1243 :
1244 47 : if (poDS->sInfoHeader.iHeight == INT_MIN)
1245 : {
1246 0 : delete poDS;
1247 0 : return nullptr;
1248 : }
1249 :
1250 47 : poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
1251 47 : poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)
1252 47 : ? poDS->sInfoHeader.iHeight
1253 : : -poDS->sInfoHeader.iHeight;
1254 :
1255 47 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
1256 : {
1257 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
1258 : poDS->nRasterXSize, poDS->nRasterYSize);
1259 0 : delete poDS;
1260 0 : return nullptr;
1261 : }
1262 :
1263 47 : switch (poDS->sInfoHeader.iBitCount)
1264 : {
1265 43 : case 1:
1266 : case 4:
1267 : case 8:
1268 : {
1269 43 : poDS->nBands = 1;
1270 : int nColorTableSize;
1271 43 : int nMaxColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
1272 : // Allocate memory for colour table and read it
1273 43 : if (poDS->sInfoHeader.iClrUsed)
1274 : {
1275 26 : if (poDS->sInfoHeader.iClrUsed > (GUInt32)nMaxColorTableSize)
1276 : {
1277 0 : CPLError(CE_Failure, CPLE_NotSupported,
1278 : "Wrong value for iClrUsed: %u",
1279 : poDS->sInfoHeader.iClrUsed);
1280 0 : delete poDS;
1281 0 : return nullptr;
1282 : }
1283 26 : nColorTableSize = poDS->sInfoHeader.iClrUsed;
1284 : }
1285 : else
1286 17 : nColorTableSize = nMaxColorTableSize;
1287 :
1288 43 : poDS->pabyColorTable = (GByte *)VSI_MALLOC2_VERBOSE(
1289 : poDS->nColorElems, nColorTableSize);
1290 43 : if (poDS->pabyColorTable == nullptr)
1291 : {
1292 0 : break;
1293 : }
1294 :
1295 129 : if (VSIFSeekL(poDS->fp,
1296 43 : BFH_SIZE + static_cast<vsi_l_offset>(
1297 43 : poDS->sInfoHeader.iSize),
1298 86 : SEEK_SET) != 0 ||
1299 43 : VSIFReadL(poDS->pabyColorTable, poDS->nColorElems,
1300 43 : nColorTableSize, poDS->fp) != (size_t)nColorTableSize)
1301 : {
1302 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read color table");
1303 0 : delete poDS;
1304 0 : return nullptr;
1305 : }
1306 :
1307 : GDALColorEntry oEntry;
1308 43 : poDS->poColorTable = new GDALColorTable();
1309 9809 : for (int i = 0; i < nColorTableSize; i++)
1310 : {
1311 9766 : oEntry.c1 =
1312 9766 : poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red
1313 9766 : oEntry.c2 =
1314 9766 : poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green
1315 9766 : oEntry.c3 =
1316 9766 : poDS->pabyColorTable[i * poDS->nColorElems]; // Blue
1317 9766 : oEntry.c4 = 255;
1318 :
1319 9766 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1320 : }
1321 : }
1322 43 : break;
1323 4 : case 16:
1324 : case 24:
1325 : case 32:
1326 4 : poDS->nBands = 3;
1327 4 : break;
1328 0 : default:
1329 0 : delete poDS;
1330 0 : return nullptr;
1331 : }
1332 :
1333 : /* -------------------------------------------------------------------- */
1334 : /* Create band information objects. */
1335 : /* -------------------------------------------------------------------- */
1336 47 : if (poDS->sInfoHeader.iCompression == BMPC_RGB ||
1337 2 : poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
1338 : {
1339 98 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1340 : {
1341 53 : BMPRasterBand *band = new BMPRasterBand(poDS, iBand);
1342 53 : poDS->SetBand(iBand, band);
1343 53 : if (band->pabyScan == nullptr)
1344 : {
1345 0 : CPLError(CE_Failure, CPLE_AppDefined,
1346 : "The BMP file is probably corrupted or too large. "
1347 : "Image width = %d",
1348 : poDS->nRasterXSize);
1349 0 : delete poDS;
1350 0 : return nullptr;
1351 : }
1352 45 : }
1353 : }
1354 2 : else if (poDS->sInfoHeader.iCompression == BMPC_RLE8 ||
1355 0 : poDS->sInfoHeader.iCompression == BMPC_RLE4)
1356 : {
1357 4 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1358 : {
1359 2 : BMPComprRasterBand *band = new BMPComprRasterBand(poDS, iBand);
1360 2 : poDS->SetBand(iBand, band);
1361 2 : if (band->pabyUncomprBuf == nullptr)
1362 : {
1363 0 : CPLError(CE_Failure, CPLE_AppDefined,
1364 : "The BMP file is probably corrupted or too large. "
1365 : "Image width = %d",
1366 : poDS->nRasterXSize);
1367 0 : delete poDS;
1368 0 : return nullptr;
1369 : }
1370 2 : }
1371 : }
1372 : else
1373 : {
1374 0 : delete poDS;
1375 0 : return nullptr;
1376 : }
1377 :
1378 : /* -------------------------------------------------------------------- */
1379 : /* Check for world file. */
1380 : /* -------------------------------------------------------------------- */
1381 47 : poDS->bGeoTransformValid =
1382 47 : GDALReadWorldFile(poOpenInfo->pszFilename, nullptr, poDS->m_gt.data());
1383 :
1384 47 : if (!poDS->bGeoTransformValid)
1385 47 : poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename,
1386 : ".wld", poDS->m_gt.data());
1387 :
1388 : /* -------------------------------------------------------------------- */
1389 : /* Initialize any PAM information. */
1390 : /* -------------------------------------------------------------------- */
1391 47 : poDS->SetDescription(poOpenInfo->pszFilename);
1392 47 : poDS->TryLoadXML();
1393 :
1394 : /* -------------------------------------------------------------------- */
1395 : /* Check for overviews. */
1396 : /* -------------------------------------------------------------------- */
1397 47 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1398 :
1399 47 : return poDS;
1400 : }
1401 :
1402 : /************************************************************************/
1403 : /* Create() */
1404 : /************************************************************************/
1405 :
1406 69 : GDALDataset *BMPDataset::Create(const char *pszFilename, int nXSize, int nYSize,
1407 : int nBandsIn, GDALDataType eType,
1408 : char **papszOptions)
1409 :
1410 : {
1411 69 : if (eType != GDT_Byte)
1412 : {
1413 36 : CPLError(CE_Failure, CPLE_AppDefined,
1414 : "Attempt to create BMP dataset with an illegal\n"
1415 : "data type (%s), only Byte supported by the format.\n",
1416 : GDALGetDataTypeName(eType));
1417 :
1418 36 : return nullptr;
1419 : }
1420 :
1421 33 : if (nBandsIn != 1 && nBandsIn != 3)
1422 : {
1423 7 : CPLError(CE_Failure, CPLE_NotSupported,
1424 : "BMP driver doesn't support %d bands. Must be 1 or 3.\n",
1425 : nBandsIn);
1426 :
1427 7 : return nullptr;
1428 : }
1429 :
1430 : /* -------------------------------------------------------------------- */
1431 : /* Create the dataset. */
1432 : /* -------------------------------------------------------------------- */
1433 26 : BMPDataset *poDS = new BMPDataset();
1434 26 : poDS->m_bNewFile = true;
1435 :
1436 26 : poDS->fp = VSIFOpenL(pszFilename, "wb+");
1437 26 : if (poDS->fp == nullptr)
1438 : {
1439 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n",
1440 : pszFilename);
1441 3 : delete poDS;
1442 3 : return nullptr;
1443 : }
1444 :
1445 23 : poDS->pszFilename = CPLStrdup(pszFilename);
1446 :
1447 : /* -------------------------------------------------------------------- */
1448 : /* Fill the BMPInfoHeader */
1449 : /* -------------------------------------------------------------------- */
1450 23 : poDS->sInfoHeader.iSize = 40;
1451 23 : poDS->sInfoHeader.iWidth = nXSize;
1452 23 : poDS->sInfoHeader.iHeight = nYSize;
1453 23 : poDS->sInfoHeader.iPlanes = 1;
1454 23 : poDS->sInfoHeader.iBitCount = (nBandsIn == 3) ? 24 : 8;
1455 23 : poDS->sInfoHeader.iCompression = BMPC_RGB;
1456 :
1457 : /* XXX: Avoid integer overflow. We can calculate size in one
1458 : * step using
1459 : *
1460 : * nScanSize = ((poDS->sInfoHeader.iWidth *
1461 : * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8
1462 : *
1463 : * formula, but we should check for overflow conditions
1464 : * during calculation.
1465 : */
1466 23 : GUInt32 nScanSize =
1467 23 : (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31;
1468 23 : if (!poDS->sInfoHeader.iWidth || !poDS->sInfoHeader.iBitCount ||
1469 23 : (nScanSize - 31) / poDS->sInfoHeader.iBitCount !=
1470 23 : (GUInt32)poDS->sInfoHeader.iWidth)
1471 : {
1472 0 : CPLError(CE_Failure, CPLE_FileIO,
1473 : "Wrong image parameters; "
1474 : "can't allocate space for scanline buffer");
1475 0 : delete poDS;
1476 :
1477 0 : return nullptr;
1478 : }
1479 23 : nScanSize = (nScanSize & ~31U) / 8;
1480 :
1481 23 : poDS->sInfoHeader.iXPelsPerMeter = 0;
1482 23 : poDS->sInfoHeader.iYPelsPerMeter = 0;
1483 23 : poDS->nColorElems = 4;
1484 :
1485 : /* -------------------------------------------------------------------- */
1486 : /* Do we need colour table? */
1487 : /* -------------------------------------------------------------------- */
1488 23 : if (nBandsIn == 1)
1489 : {
1490 21 : poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount;
1491 21 : poDS->pabyColorTable =
1492 42 : (GByte *)CPLMalloc(static_cast<size_t>(poDS->nColorElems) *
1493 21 : poDS->sInfoHeader.iClrUsed);
1494 5397 : for (unsigned int i = 0; i < poDS->sInfoHeader.iClrUsed; i++)
1495 : {
1496 5376 : poDS->pabyColorTable[i * poDS->nColorElems] =
1497 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 1] =
1498 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 2] =
1499 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 3] =
1500 : (GByte)i;
1501 : }
1502 : }
1503 : else
1504 : {
1505 2 : poDS->sInfoHeader.iClrUsed = 0;
1506 : }
1507 23 : poDS->sInfoHeader.iClrImportant = 0;
1508 :
1509 : /* -------------------------------------------------------------------- */
1510 : /* Fill the BMPFileHeader */
1511 : /* -------------------------------------------------------------------- */
1512 :
1513 23 : poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize +
1514 23 : poDS->sInfoHeader.iClrUsed * poDS->nColorElems;
1515 :
1516 : // From https://medium.com/@chiaracoetzee/maximum-resolution-of-bmp-image-file-8c729b3f833a
1517 23 : if (nXSize > 30000 || nYSize > 30000)
1518 : {
1519 0 : CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
1520 : "Adobe Photoshop CC 2014.2.2");
1521 : }
1522 23 : if (nXSize > 2147483647 / (nYSize + 1))
1523 : {
1524 0 : CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
1525 : "Windows Photo Viewer");
1526 : }
1527 :
1528 23 : const vsi_l_offset nLargeImageSize =
1529 23 : static_cast<vsi_l_offset>(nScanSize) * poDS->sInfoHeader.iHeight;
1530 23 : poDS->m_nFileSize = poDS->sFileHeader.iOffBits + nLargeImageSize;
1531 23 : if (nLargeImageSize > std::numeric_limits<uint32_t>::max())
1532 : {
1533 0 : CPLError(CE_Warning, CPLE_AppDefined,
1534 : "Image too big for its size to fit in a 32 bit integer! "
1535 : "Writing 0xFFFFFFFF in it, but that could cause compatibility "
1536 : "problems with other readers.");
1537 0 : poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
1538 0 : poDS->sInfoHeader.iSizeImage = std::numeric_limits<uint32_t>::max();
1539 : }
1540 23 : else if (poDS->m_nFileSize > std::numeric_limits<uint32_t>::max())
1541 : {
1542 0 : CPLError(CE_Warning, CPLE_AppDefined,
1543 : "File too big for its size to fit in a 32 bit integer! "
1544 : "Writing 0xFFFFFFFF in it, but that could cause compatibility "
1545 : "problems with other readers.");
1546 0 : poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
1547 0 : poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
1548 : }
1549 : else
1550 : {
1551 23 : poDS->sFileHeader.iSize = static_cast<uint32_t>(poDS->m_nFileSize);
1552 23 : poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
1553 : }
1554 :
1555 23 : poDS->sFileHeader.bType[0] = 'B';
1556 23 : poDS->sFileHeader.bType[1] = 'M';
1557 23 : poDS->sFileHeader.iReserved1 = 0;
1558 23 : poDS->sFileHeader.iReserved2 = 0;
1559 :
1560 : /* -------------------------------------------------------------------- */
1561 : /* Write all structures to the file */
1562 : /* -------------------------------------------------------------------- */
1563 23 : if (VSIFWriteL(&poDS->sFileHeader.bType, 1, 2, poDS->fp) != 2)
1564 : {
1565 1 : CPLError(CE_Failure, CPLE_FileIO,
1566 : "Write of first 2 bytes to BMP file %s failed.\n"
1567 : "Is file system full?",
1568 : pszFilename);
1569 1 : delete poDS;
1570 :
1571 1 : return nullptr;
1572 : }
1573 :
1574 : GInt32 iLong;
1575 : GUInt32 iULong;
1576 : GUInt16 iUShort;
1577 :
1578 22 : iULong = CPL_LSBWORD32(poDS->sFileHeader.iSize);
1579 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1580 22 : iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved1);
1581 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1582 22 : iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved2);
1583 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1584 22 : iULong = CPL_LSBWORD32(poDS->sFileHeader.iOffBits);
1585 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1586 :
1587 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSize);
1588 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1589 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iWidth);
1590 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1591 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iHeight);
1592 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1593 22 : iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iPlanes);
1594 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1595 22 : iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iBitCount);
1596 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1597 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iCompression);
1598 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1599 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSizeImage);
1600 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1601 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iXPelsPerMeter);
1602 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1603 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iYPelsPerMeter);
1604 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1605 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrUsed);
1606 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1607 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrImportant);
1608 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1609 :
1610 22 : if (poDS->sInfoHeader.iClrUsed)
1611 : {
1612 40 : if (VSIFWriteL(poDS->pabyColorTable, 1,
1613 20 : static_cast<size_t>(poDS->nColorElems) *
1614 20 : poDS->sInfoHeader.iClrUsed,
1615 20 : poDS->fp) !=
1616 20 : static_cast<size_t>(poDS->nColorElems) * poDS->sInfoHeader.iClrUsed)
1617 : {
1618 8 : CPLError(CE_Failure, CPLE_FileIO,
1619 : "Error writing color table. Is disk full?");
1620 8 : delete poDS;
1621 :
1622 8 : return nullptr;
1623 : }
1624 : }
1625 :
1626 14 : poDS->nRasterXSize = nXSize;
1627 14 : poDS->nRasterYSize = nYSize;
1628 14 : poDS->eAccess = GA_Update;
1629 14 : poDS->nBands = nBandsIn;
1630 :
1631 : /* -------------------------------------------------------------------- */
1632 : /* Create band information objects. */
1633 : /* -------------------------------------------------------------------- */
1634 32 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1635 : {
1636 18 : poDS->SetBand(iBand, new BMPRasterBand(poDS, iBand));
1637 : }
1638 :
1639 : /* -------------------------------------------------------------------- */
1640 : /* Do we need a world file? */
1641 : /* -------------------------------------------------------------------- */
1642 14 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
1643 0 : poDS->bGeoTransformValid = TRUE;
1644 :
1645 14 : return (GDALDataset *)poDS;
1646 : }
1647 :
1648 : /************************************************************************/
1649 : /* GDALRegister_BMP() */
1650 : /************************************************************************/
1651 :
1652 1911 : void GDALRegister_BMP()
1653 :
1654 : {
1655 1911 : if (GDALGetDriverByName("BMP") != nullptr)
1656 282 : return;
1657 :
1658 1629 : GDALDriver *poDriver = new GDALDriver();
1659 :
1660 1629 : poDriver->SetDescription("BMP");
1661 1629 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1662 1629 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1663 1629 : "MS Windows Device Independent Bitmap");
1664 1629 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bmp.html");
1665 1629 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bmp");
1666 1629 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
1667 1629 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
1668 : "<CreationOptionList>"
1669 : " <Option name='WORLDFILE' type='boolean' "
1670 : "description='Write out world file'/>"
1671 1629 : "</CreationOptionList>");
1672 :
1673 1629 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1674 :
1675 1629 : poDriver->pfnOpen = BMPDataset::Open;
1676 1629 : poDriver->pfnCreate = BMPDataset::Create;
1677 1629 : poDriver->pfnIdentify = BMPDataset::Identify;
1678 :
1679 1629 : GetGDALDriverManager()->RegisterDriver(poDriver);
1680 : }
|