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