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