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(
637 : poGDS->fp,
638 1 : static_cast<vsi_l_offset>(BFH_SIZE + poGDS->sInfoHeader.iSize),
639 : SEEK_SET);
640 2 : if (VSIFWriteL(poGDS->pabyColorTable, 1,
641 1 : static_cast<size_t>(poGDS->nColorElems) *
642 1 : poGDS->sInfoHeader.iClrUsed,
643 1 : poGDS->fp) < static_cast<size_t>(poGDS->nColorElems) *
644 1 : poGDS->sInfoHeader.iClrUsed)
645 : {
646 0 : return CE_Failure;
647 : }
648 : }
649 : else
650 0 : return CE_Failure;
651 :
652 1 : return CE_None;
653 : }
654 :
655 : /************************************************************************/
656 : /* GetColorInterpretation() */
657 : /************************************************************************/
658 :
659 12 : GDALColorInterp BMPRasterBand::GetColorInterpretation()
660 : {
661 12 : BMPDataset *poGDS = cpl::down_cast<BMPDataset *>(poDS);
662 :
663 12 : if (poGDS->sInfoHeader.iBitCount == 24 ||
664 12 : poGDS->sInfoHeader.iBitCount == 32 ||
665 12 : poGDS->sInfoHeader.iBitCount == 16)
666 : {
667 0 : if (nBand == 1)
668 0 : return GCI_RedBand;
669 0 : else if (nBand == 2)
670 0 : return GCI_GreenBand;
671 0 : else if (nBand == 3)
672 0 : return GCI_BlueBand;
673 : else
674 0 : return GCI_Undefined;
675 : }
676 : else
677 : {
678 12 : return GCI_PaletteIndex;
679 : }
680 : }
681 :
682 : /************************************************************************/
683 : /* ==================================================================== */
684 : /* BMPComprRasterBand */
685 : /* ==================================================================== */
686 : /************************************************************************/
687 :
688 : class BMPComprRasterBand final : public BMPRasterBand
689 : {
690 : friend class BMPDataset;
691 :
692 : GByte *pabyComprBuf;
693 : GByte *pabyUncomprBuf;
694 :
695 : public:
696 : BMPComprRasterBand(BMPDataset *, int);
697 : ~BMPComprRasterBand() override;
698 :
699 : CPLErr IReadBlock(int, int, void *) override;
700 : // virtual CPLErr IWriteBlock( int, int, void * );
701 : };
702 :
703 : /************************************************************************/
704 : /* BMPComprRasterBand() */
705 : /************************************************************************/
706 :
707 4 : BMPComprRasterBand::BMPComprRasterBand(BMPDataset *poDSIn, int nBandIn)
708 : : BMPRasterBand(poDSIn, nBandIn), pabyComprBuf(nullptr),
709 4 : pabyUncomprBuf(nullptr)
710 : {
711 : /* TODO: it might be interesting to avoid uncompressing the whole data */
712 : /* in a single pass, especially if nXSize * nYSize is big */
713 : /* We could read incrementally one row at a time */
714 4 : const auto knIntMax = std::numeric_limits<int>::max();
715 4 : if (poDS->GetRasterXSize() > knIntMax / poDS->GetRasterYSize())
716 : {
717 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too big dimensions : %d x %d",
718 0 : poDS->GetRasterXSize(), poDS->GetRasterYSize());
719 0 : return;
720 : }
721 :
722 4 : if (poDSIn->m_nFileSize <= poDSIn->sFileHeader.iOffBits ||
723 4 : poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits > knIntMax)
724 : {
725 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid header");
726 0 : return;
727 : }
728 :
729 4 : if (poDSIn->sInfoHeader.iClrUsed <= 2)
730 0 : SetMetadataItem("NBITS", "1", "IMAGE_STRUCTURE");
731 4 : else if (poDSIn->sInfoHeader.iClrUsed <= 4)
732 0 : SetMetadataItem("NBITS", "2", "IMAGE_STRUCTURE");
733 4 : else if (poDSIn->sInfoHeader.iClrUsed <= 16)
734 2 : SetMetadataItem("NBITS", "4", "IMAGE_STRUCTURE");
735 :
736 4 : const GUInt32 iComprSize = static_cast<GUInt32>(
737 4 : poDSIn->m_nFileSize - poDSIn->sFileHeader.iOffBits);
738 : const GUInt32 iUncomprSize =
739 4 : poDS->GetRasterXSize() * poDS->GetRasterYSize();
740 :
741 : #ifdef DEBUG
742 4 : CPLDebug("BMP", "RLE compression detected.");
743 4 : CPLDebug("BMP",
744 : "Size of compressed buffer %ld bytes,"
745 : " size of uncompressed buffer %ld bytes.",
746 : (long)iComprSize, (long)iUncomprSize);
747 : #endif
748 :
749 4 : pabyComprBuf = (GByte *)VSIMalloc(iComprSize);
750 4 : pabyUncomprBuf = (GByte *)VSIMalloc(iUncomprSize);
751 4 : if (pabyComprBuf == nullptr || pabyUncomprBuf == nullptr)
752 : {
753 0 : CPLFree(pabyComprBuf);
754 0 : pabyComprBuf = nullptr;
755 0 : CPLFree(pabyUncomprBuf);
756 0 : pabyUncomprBuf = nullptr;
757 0 : return;
758 : }
759 :
760 12 : if (VSIFSeekL(poDSIn->fp,
761 4 : static_cast<vsi_l_offset>(poDSIn->sFileHeader.iOffBits),
762 8 : SEEK_SET) != 0 ||
763 4 : VSIFReadL(pabyComprBuf, 1, iComprSize, poDSIn->fp) < iComprSize)
764 : {
765 0 : CPLError(CE_Failure, CPLE_FileIO,
766 : "Can't read from offset %ld in input file.",
767 0 : (long)poDSIn->sFileHeader.iOffBits);
768 0 : CPLFree(pabyComprBuf);
769 0 : pabyComprBuf = nullptr;
770 0 : CPLFree(pabyUncomprBuf);
771 0 : pabyUncomprBuf = nullptr;
772 0 : return;
773 : }
774 :
775 4 : unsigned int i = 0;
776 4 : unsigned int j = 0;
777 4 : if (poDSIn->sInfoHeader.iBitCount == 8) // RLE8
778 : {
779 122 : while (i < iComprSize)
780 : {
781 122 : if (pabyComprBuf[i])
782 : {
783 28 : unsigned int iLength = pabyComprBuf[i++];
784 28 : if (j == iUncomprSize)
785 0 : break;
786 106 : while (iLength > 0 && j < iUncomprSize && i < iComprSize)
787 : {
788 78 : pabyUncomprBuf[j++] = pabyComprBuf[i];
789 78 : iLength--;
790 : }
791 28 : i++;
792 : }
793 : else
794 : {
795 94 : i++;
796 94 : if (i == iComprSize)
797 0 : break;
798 94 : if (pabyComprBuf[i] == 0) // Next scanline
799 : {
800 38 : i++;
801 : }
802 56 : else if (pabyComprBuf[i] == 1) // End of image
803 : {
804 2 : break;
805 : }
806 54 : else if (pabyComprBuf[i] == 2) // Move to...
807 : {
808 0 : if (j == iUncomprSize)
809 0 : break;
810 0 : i++;
811 0 : if (i < iComprSize - 1)
812 : {
813 0 : if (pabyComprBuf[i + 1] >
814 0 : knIntMax / poDS->GetRasterXSize() ||
815 0 : static_cast<int>(pabyComprBuf[i + 1]) *
816 0 : poDS->GetRasterXSize() >
817 0 : knIntMax -
818 0 : static_cast<int>(j + pabyComprBuf[i]))
819 0 : break;
820 0 : j += pabyComprBuf[i] +
821 0 : pabyComprBuf[i + 1] * poDS->GetRasterXSize();
822 0 : i += 2;
823 : }
824 : else
825 0 : break;
826 : }
827 : else // Absolute mode
828 : {
829 54 : CPLAssert(i < iComprSize);
830 54 : unsigned int iLength = pabyComprBuf[i++];
831 54 : if (j == iUncomprSize)
832 0 : break;
833 54 : for (unsigned k = 0;
834 776 : k < iLength && j < iUncomprSize && i < iComprSize; k++)
835 722 : pabyUncomprBuf[j++] = pabyComprBuf[i++];
836 54 : if (i & 0x01)
837 18 : i++;
838 : }
839 : }
840 : }
841 : }
842 : else // RLE4
843 : {
844 142 : while (i < iComprSize)
845 : {
846 142 : if (pabyComprBuf[i])
847 : {
848 100 : unsigned int iLength = pabyComprBuf[i++];
849 100 : if (j == iUncomprSize)
850 0 : break;
851 890 : for (unsigned k = 0;
852 890 : k < iLength && j < iUncomprSize && i < iComprSize; k++)
853 : {
854 790 : if (k & 0x01)
855 378 : pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F;
856 : else
857 412 : pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
858 : }
859 100 : i++;
860 : }
861 : else
862 : {
863 42 : i++;
864 42 : if (i == iComprSize)
865 0 : break;
866 42 : if (pabyComprBuf[i] == 0) // Next scanline
867 : {
868 38 : i++;
869 : }
870 4 : else if (pabyComprBuf[i] == 1) // End of image
871 : {
872 2 : break;
873 : }
874 2 : else if (pabyComprBuf[i] == 2) // Move to...
875 : {
876 0 : if (j == iUncomprSize)
877 0 : break;
878 0 : i++;
879 0 : if (i < iComprSize - 1)
880 : {
881 0 : if (pabyComprBuf[i + 1] >
882 0 : knIntMax / poDS->GetRasterXSize() ||
883 0 : static_cast<int>(pabyComprBuf[i + 1]) *
884 0 : poDS->GetRasterXSize() >
885 0 : knIntMax -
886 0 : static_cast<int>(j + pabyComprBuf[i]))
887 0 : break;
888 0 : j += pabyComprBuf[i] +
889 0 : pabyComprBuf[i + 1] * poDS->GetRasterXSize();
890 0 : i += 2;
891 : }
892 : else
893 0 : break;
894 : }
895 : else // Absolute mode
896 : {
897 2 : CPLAssert(i < iComprSize);
898 2 : unsigned int iLength = pabyComprBuf[i++];
899 2 : if (j == iUncomprSize)
900 0 : break;
901 12 : for (unsigned k = 0;
902 12 : k < iLength && j < iUncomprSize && i < iComprSize; k++)
903 : {
904 10 : if (k & 0x01)
905 4 : pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F;
906 : else
907 6 : pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
908 : }
909 2 : if ((iLength + 1) & 0x02) /* iLength%4 = 1 or 2 */
910 2 : i++;
911 2 : if (iLength & 0x01) /* iLength%4 = 1 (again) or 3 */
912 2 : i++;
913 : }
914 : }
915 : }
916 : }
917 : /* Validate that we have read all compressed data (we tolerate missing */
918 : /* end of image marker) and that we have filled all uncompressed data */
919 : /* i will be odd if and only if we saw the End of Image marker above */
920 4 : if (j < iUncomprSize || (i + 1 != iComprSize && i + 2 != iComprSize))
921 : {
922 0 : CPLFree(pabyUncomprBuf);
923 0 : pabyUncomprBuf = nullptr;
924 : }
925 : // rcg, release compressed buffer here.
926 4 : CPLFree(pabyComprBuf);
927 4 : pabyComprBuf = nullptr;
928 : }
929 :
930 : /************************************************************************/
931 : /* ~BMPComprRasterBand() */
932 : /************************************************************************/
933 :
934 8 : BMPComprRasterBand::~BMPComprRasterBand()
935 : {
936 4 : CPLFree(pabyComprBuf);
937 4 : CPLFree(pabyUncomprBuf);
938 8 : }
939 :
940 : /************************************************************************/
941 : /* IReadBlock() */
942 : /************************************************************************/
943 :
944 40 : CPLErr BMPComprRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
945 : void *pImage)
946 : {
947 80 : memcpy(pImage,
948 120 : pabyUncomprBuf + (poDS->GetRasterYSize() - nBlockYOff - 1) *
949 40 : poDS->GetRasterXSize(),
950 40 : nBlockXSize);
951 :
952 40 : return CE_None;
953 : }
954 :
955 : /************************************************************************/
956 : /* BMPDataset() */
957 : /************************************************************************/
958 :
959 75 : BMPDataset::BMPDataset()
960 : : nColorElems(0), pabyColorTable(nullptr), poColorTable(nullptr),
961 75 : bGeoTransformValid(FALSE), pszFilename(nullptr), fp(nullptr)
962 : {
963 75 : nBands = 0;
964 :
965 75 : memset(&sFileHeader, 0, sizeof(sFileHeader));
966 75 : memset(&sInfoHeader, 0, sizeof(sInfoHeader));
967 75 : }
968 :
969 : /************************************************************************/
970 : /* ~BMPDataset() */
971 : /************************************************************************/
972 :
973 150 : BMPDataset::~BMPDataset()
974 : {
975 75 : FlushCache(true);
976 :
977 75 : if (m_bNewFile && fp)
978 : {
979 : // Extend the file with zeroes if needed
980 23 : VSIFSeekL(fp, 0, SEEK_END);
981 :
982 23 : if (VSIFTellL(fp) < m_nFileSize)
983 : {
984 13 : VSIFTruncateL(fp, m_nFileSize);
985 : }
986 : }
987 :
988 75 : CPLFree(pabyColorTable);
989 75 : if (poColorTable)
990 45 : delete poColorTable;
991 75 : CPLFree(pszFilename);
992 75 : if (fp)
993 72 : VSIFCloseL(fp);
994 150 : }
995 :
996 : /************************************************************************/
997 : /* GetGeoTransform() */
998 : /************************************************************************/
999 :
1000 17 : CPLErr BMPDataset::GetGeoTransform(GDALGeoTransform >) const
1001 : {
1002 17 : if (bGeoTransformValid)
1003 : {
1004 0 : gt = m_gt;
1005 0 : return CE_None;
1006 : }
1007 :
1008 17 : if (GDALPamDataset::GetGeoTransform(gt) == CE_None)
1009 2 : return CE_None;
1010 :
1011 : #ifdef notdef
1012 : // See http://trac.osgeo.org/gdal/ticket/3578
1013 : if (sInfoHeader.iXPelsPerMeter > 0 && sInfoHeader.iYPelsPerMeter > 0)
1014 : {
1015 : gt[1] = sInfoHeader.iXPelsPerMeter;
1016 : gt[5] = -sInfoHeader.iYPelsPerMeter;
1017 : gt[0] = -0.5 * gt[1];
1018 : gt[3] = -0.5 * gt[5];
1019 : return CE_None;
1020 : }
1021 : #endif
1022 :
1023 15 : return CE_Failure;
1024 : }
1025 :
1026 : /************************************************************************/
1027 : /* SetGeoTransform() */
1028 : /************************************************************************/
1029 :
1030 8 : CPLErr BMPDataset::SetGeoTransform(const GDALGeoTransform >)
1031 : {
1032 8 : if (pszFilename && bGeoTransformValid)
1033 : {
1034 0 : m_gt = gt;
1035 :
1036 0 : CPLErr eErr = CE_None;
1037 0 : if (GDALWriteWorldFile(pszFilename, "wld", m_gt.data()) == FALSE)
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_FileIO, "Can't write world file.");
1040 0 : eErr = CE_Failure;
1041 : }
1042 0 : return eErr;
1043 : }
1044 :
1045 8 : return GDALPamDataset::SetGeoTransform(gt);
1046 : }
1047 :
1048 : /************************************************************************/
1049 : /* IRasterIO() */
1050 : /* */
1051 : /* Multi-band raster io handler. We will use block based */
1052 : /* loading is used for multiband BMPs. That is because they */
1053 : /* are effectively pixel interleaved, so processing all bands */
1054 : /* for a given block together avoid extra seeks. */
1055 : /************************************************************************/
1056 :
1057 13 : CPLErr BMPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1058 : int nXSize, int nYSize, void *pData, int nBufXSize,
1059 : int nBufYSize, GDALDataType eBufType,
1060 : int nBandCount, BANDMAP_TYPE panBandMap,
1061 : GSpacing nPixelSpace, GSpacing nLineSpace,
1062 : GSpacing nBandSpace,
1063 : GDALRasterIOExtraArg *psExtraArg)
1064 :
1065 : {
1066 13 : if (nBandCount > 1)
1067 0 : return GDALDataset::BlockBasedRasterIO(
1068 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
1069 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
1070 0 : nBandSpace, psExtraArg);
1071 : else
1072 13 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1073 : pData, nBufXSize, nBufYSize, eBufType,
1074 : nBandCount, panBandMap, nPixelSpace,
1075 13 : nLineSpace, nBandSpace, psExtraArg);
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* Identify() */
1080 : /************************************************************************/
1081 :
1082 65422 : int BMPDataset::Identify(GDALOpenInfo *poOpenInfo)
1083 :
1084 : {
1085 65422 : if (poOpenInfo->nHeaderBytes < BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
1086 7585 : poOpenInfo->pabyHeader[0] != 'B' || poOpenInfo->pabyHeader[1] != 'M' ||
1087 98 : poOpenInfo->pabyHeader[6] != 0 || poOpenInfo->pabyHeader[7] != 0 ||
1088 98 : poOpenInfo->pabyHeader[8] != 0 || poOpenInfo->pabyHeader[9] != 0)
1089 65324 : return FALSE;
1090 :
1091 : uint32_t nInfoHeaderSize;
1092 98 : memcpy(&nInfoHeaderSize, poOpenInfo->pabyHeader + BFH_SIZE,
1093 : sizeof(uint32_t));
1094 98 : CPL_LSBPTR32(&nInfoHeaderSize);
1095 : // Check against the maximum known size
1096 98 : if (nInfoHeaderSize > BIH_BITMAPV5SIZE)
1097 0 : return FALSE;
1098 :
1099 98 : return TRUE;
1100 : }
1101 :
1102 : /************************************************************************/
1103 : /* Open() */
1104 : /************************************************************************/
1105 :
1106 49 : GDALDataset *BMPDataset::Open(GDALOpenInfo *poOpenInfo)
1107 : {
1108 49 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
1109 0 : return nullptr;
1110 :
1111 : /* -------------------------------------------------------------------- */
1112 : /* Create a corresponding GDALDataset. */
1113 : /* -------------------------------------------------------------------- */
1114 49 : BMPDataset *poDS = new BMPDataset();
1115 49 : poDS->eAccess = poOpenInfo->eAccess;
1116 :
1117 : VSIStatBufL sStat;
1118 49 : if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
1119 : {
1120 0 : delete poDS;
1121 0 : return nullptr;
1122 : }
1123 :
1124 : /* -------------------------------------------------------------------- */
1125 : /* Read the BMPFileHeader. We need iOffBits value only */
1126 : /* -------------------------------------------------------------------- */
1127 49 : memcpy(&poDS->sFileHeader.iOffBits, poOpenInfo->pabyHeader + 10, 4);
1128 : #ifdef CPL_MSB
1129 : CPL_SWAP32PTR(&poDS->sFileHeader.iOffBits);
1130 : #endif
1131 49 : poDS->m_nFileSize = sStat.st_size;
1132 :
1133 : #ifdef BMP_DEBUG
1134 : CPLDebug("BMP", "File size " CPL_FRMT_GUIB " bytes.",
1135 : static_cast<GUIntBig>(poDS->m_nFileSize));
1136 : CPLDebug("BMP", "Image offset 0x%x bytes from file start.",
1137 : poDS->sFileHeader.iOffBits);
1138 : #endif
1139 :
1140 : // Validatate iOffBits
1141 49 : if (poDS->sFileHeader.iOffBits <= BFH_SIZE + SIZE_OF_INFOHEADER_SIZE ||
1142 49 : poDS->sFileHeader.iOffBits >= poDS->m_nFileSize)
1143 : {
1144 0 : delete poDS;
1145 0 : return nullptr;
1146 : }
1147 :
1148 : /* -------------------------------------------------------------------- */
1149 : /* Read the BMPInfoHeader. */
1150 : /* -------------------------------------------------------------------- */
1151 49 : poDS->fp = poOpenInfo->fpL;
1152 49 : poOpenInfo->fpL = nullptr;
1153 :
1154 49 : VSIFSeekL(poDS->fp, BFH_SIZE + 0, SEEK_SET);
1155 49 : VSIFReadL(&poDS->sInfoHeader.iSize, 1, 4, poDS->fp);
1156 : #ifdef CPL_MSB
1157 : CPL_SWAP32PTR(&poDS->sInfoHeader.iSize);
1158 : #endif
1159 :
1160 : BMPType eBMPType;
1161 49 : if (poDS->sInfoHeader.iSize == BIH_WIN4SIZE)
1162 47 : eBMPType = BMPT_WIN4;
1163 2 : else if (poDS->sInfoHeader.iSize == BIH_OS21SIZE)
1164 0 : eBMPType = BMPT_OS21;
1165 2 : else if (poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
1166 2 : poDS->sInfoHeader.iSize == 16)
1167 0 : eBMPType = BMPT_OS22;
1168 : else
1169 2 : eBMPType = BMPT_WIN5;
1170 :
1171 49 : if (eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22)
1172 : {
1173 49 : VSIFReadL(&poDS->sInfoHeader.iWidth, 1, 4, poDS->fp);
1174 49 : VSIFReadL(&poDS->sInfoHeader.iHeight, 1, 4, poDS->fp);
1175 49 : VSIFReadL(&poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp);
1176 49 : VSIFReadL(&poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp);
1177 : unsigned int iCompression;
1178 49 : VSIFReadL(&iCompression, 1, 4, poDS->fp);
1179 : #ifdef CPL_MSB
1180 : CPL_SWAP32PTR(&iCompression);
1181 : #endif
1182 49 : if (iCompression > BMPC_PNG)
1183 : {
1184 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported compression");
1185 0 : delete poDS;
1186 0 : return nullptr;
1187 : }
1188 49 : poDS->sInfoHeader.iCompression =
1189 : static_cast<BMPComprMethod>(iCompression);
1190 49 : VSIFReadL(&poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp);
1191 49 : VSIFReadL(&poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp);
1192 49 : VSIFReadL(&poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp);
1193 49 : VSIFReadL(&poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp);
1194 49 : VSIFReadL(&poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp);
1195 :
1196 : // rcg, read win4/5 fields. If we're reading a
1197 : // legacy header that ends at iClrImportant, it turns
1198 : // out that the three DWORD color table entries used
1199 : // by the channel masks start here anyway.
1200 49 : if (poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
1201 : {
1202 0 : VSIFReadL(&poDS->sInfoHeader.iRedMask, 1, 4, poDS->fp);
1203 0 : VSIFReadL(&poDS->sInfoHeader.iGreenMask, 1, 4, poDS->fp);
1204 0 : VSIFReadL(&poDS->sInfoHeader.iBlueMask, 1, 4, poDS->fp);
1205 : }
1206 : #ifdef CPL_MSB
1207 : CPL_SWAP32PTR(&poDS->sInfoHeader.iWidth);
1208 : CPL_SWAP32PTR(&poDS->sInfoHeader.iHeight);
1209 : CPL_SWAP16PTR(&poDS->sInfoHeader.iPlanes);
1210 : CPL_SWAP16PTR(&poDS->sInfoHeader.iBitCount);
1211 : CPL_SWAP32PTR(&poDS->sInfoHeader.iSizeImage);
1212 : CPL_SWAP32PTR(&poDS->sInfoHeader.iXPelsPerMeter);
1213 : CPL_SWAP32PTR(&poDS->sInfoHeader.iYPelsPerMeter);
1214 : CPL_SWAP32PTR(&poDS->sInfoHeader.iClrUsed);
1215 : CPL_SWAP32PTR(&poDS->sInfoHeader.iClrImportant);
1216 : // rcg, swap win4/5 fields.
1217 : CPL_SWAP32PTR(&poDS->sInfoHeader.iRedMask);
1218 : CPL_SWAP32PTR(&poDS->sInfoHeader.iGreenMask);
1219 : CPL_SWAP32PTR(&poDS->sInfoHeader.iBlueMask);
1220 : #endif
1221 49 : poDS->nColorElems = 4;
1222 : }
1223 :
1224 49 : if (eBMPType == BMPT_OS22)
1225 : {
1226 0 : poDS->nColorElems =
1227 : 3; // FIXME: different info in different documents regarding this!
1228 : }
1229 :
1230 49 : if (eBMPType == BMPT_OS21)
1231 : {
1232 : GInt16 iShort;
1233 :
1234 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1235 0 : poDS->sInfoHeader.iWidth = CPL_LSBWORD16(iShort);
1236 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1237 0 : poDS->sInfoHeader.iHeight = CPL_LSBWORD16(iShort);
1238 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1239 0 : poDS->sInfoHeader.iPlanes = CPL_LSBWORD16(iShort);
1240 0 : VSIFReadL(&iShort, 1, 2, poDS->fp);
1241 0 : poDS->sInfoHeader.iBitCount = CPL_LSBWORD16(iShort);
1242 0 : poDS->sInfoHeader.iCompression = BMPC_RGB;
1243 0 : poDS->nColorElems = 3;
1244 : }
1245 :
1246 49 : if (poDS->sInfoHeader.iBitCount != 1 && poDS->sInfoHeader.iBitCount != 4 &&
1247 42 : poDS->sInfoHeader.iBitCount != 8 && poDS->sInfoHeader.iBitCount != 16 &&
1248 4 : poDS->sInfoHeader.iBitCount != 24 && poDS->sInfoHeader.iBitCount != 32)
1249 : {
1250 0 : delete poDS;
1251 0 : return nullptr;
1252 : }
1253 :
1254 : #ifdef BMP_DEBUG
1255 : CPLDebug("BMP",
1256 : "Windows Device Independent Bitmap parameters:\n"
1257 : " info header size: %d bytes\n"
1258 : " width: %d\n height: %d\n planes: %d\n bpp: %d\n"
1259 : " compression: %d\n image size: %d bytes\n X resolution: %d\n"
1260 : " Y resolution: %d\n colours used: %d\n colours important: %d",
1261 : poDS->sInfoHeader.iSize, poDS->sInfoHeader.iWidth,
1262 : poDS->sInfoHeader.iHeight, poDS->sInfoHeader.iPlanes,
1263 : poDS->sInfoHeader.iBitCount, poDS->sInfoHeader.iCompression,
1264 : poDS->sInfoHeader.iSizeImage, poDS->sInfoHeader.iXPelsPerMeter,
1265 : poDS->sInfoHeader.iYPelsPerMeter, poDS->sInfoHeader.iClrUsed,
1266 : poDS->sInfoHeader.iClrImportant);
1267 : #endif
1268 :
1269 49 : if (poDS->sInfoHeader.iHeight == INT_MIN)
1270 : {
1271 0 : delete poDS;
1272 0 : return nullptr;
1273 : }
1274 :
1275 49 : poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
1276 49 : poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)
1277 49 : ? poDS->sInfoHeader.iHeight
1278 : : -poDS->sInfoHeader.iHeight;
1279 :
1280 49 : if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
1281 : {
1282 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dimensions : %d x %d",
1283 : poDS->nRasterXSize, poDS->nRasterYSize);
1284 0 : delete poDS;
1285 0 : return nullptr;
1286 : }
1287 :
1288 49 : switch (poDS->sInfoHeader.iBitCount)
1289 : {
1290 45 : case 1:
1291 : case 4:
1292 : case 8:
1293 : {
1294 45 : poDS->nBands = 1;
1295 : int nColorTableSize;
1296 45 : int nMaxColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
1297 : // Allocate memory for colour table and read it
1298 45 : if (poDS->sInfoHeader.iClrUsed)
1299 : {
1300 28 : if (poDS->sInfoHeader.iClrUsed > (GUInt32)nMaxColorTableSize)
1301 : {
1302 0 : CPLError(CE_Failure, CPLE_NotSupported,
1303 : "Wrong value for iClrUsed: %u",
1304 : poDS->sInfoHeader.iClrUsed);
1305 0 : delete poDS;
1306 0 : return nullptr;
1307 : }
1308 28 : nColorTableSize = poDS->sInfoHeader.iClrUsed;
1309 : }
1310 : else
1311 17 : nColorTableSize = nMaxColorTableSize;
1312 :
1313 45 : poDS->pabyColorTable = (GByte *)VSI_MALLOC2_VERBOSE(
1314 : poDS->nColorElems, nColorTableSize);
1315 45 : if (poDS->pabyColorTable == nullptr)
1316 : {
1317 0 : break;
1318 : }
1319 :
1320 135 : if (VSIFSeekL(poDS->fp,
1321 45 : BFH_SIZE + static_cast<vsi_l_offset>(
1322 45 : poDS->sInfoHeader.iSize),
1323 90 : SEEK_SET) != 0 ||
1324 45 : VSIFReadL(poDS->pabyColorTable, poDS->nColorElems,
1325 45 : nColorTableSize, poDS->fp) != (size_t)nColorTableSize)
1326 : {
1327 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read color table");
1328 0 : delete poDS;
1329 0 : return nullptr;
1330 : }
1331 :
1332 : GDALColorEntry oEntry;
1333 45 : poDS->poColorTable = new GDALColorTable();
1334 9843 : for (int i = 0; i < nColorTableSize; i++)
1335 : {
1336 9798 : oEntry.c1 =
1337 9798 : poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red
1338 9798 : oEntry.c2 =
1339 9798 : poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green
1340 9798 : oEntry.c3 =
1341 9798 : poDS->pabyColorTable[i * poDS->nColorElems]; // Blue
1342 9798 : oEntry.c4 = 255;
1343 :
1344 9798 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1345 : }
1346 : }
1347 45 : break;
1348 4 : case 16:
1349 : case 24:
1350 : case 32:
1351 4 : poDS->nBands = 3;
1352 4 : break;
1353 0 : default:
1354 0 : delete poDS;
1355 0 : return nullptr;
1356 : }
1357 :
1358 : /* -------------------------------------------------------------------- */
1359 : /* Create band information objects. */
1360 : /* -------------------------------------------------------------------- */
1361 49 : if (poDS->sInfoHeader.iCompression == BMPC_RGB ||
1362 4 : poDS->sInfoHeader.iCompression == BMPC_BITFIELDS)
1363 : {
1364 98 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1365 : {
1366 53 : BMPRasterBand *band = new BMPRasterBand(poDS, iBand);
1367 53 : poDS->SetBand(iBand, band);
1368 53 : if (band->pabyScan == nullptr)
1369 : {
1370 0 : CPLError(CE_Failure, CPLE_AppDefined,
1371 : "The BMP file is probably corrupted or too large. "
1372 : "Image width = %d",
1373 : poDS->nRasterXSize);
1374 0 : delete poDS;
1375 0 : return nullptr;
1376 : }
1377 45 : }
1378 : }
1379 4 : else if (poDS->sInfoHeader.iCompression == BMPC_RLE8 ||
1380 2 : poDS->sInfoHeader.iCompression == BMPC_RLE4)
1381 : {
1382 8 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1383 : {
1384 4 : BMPComprRasterBand *band = new BMPComprRasterBand(poDS, iBand);
1385 4 : poDS->SetBand(iBand, band);
1386 4 : if (band->pabyUncomprBuf == nullptr)
1387 : {
1388 0 : CPLError(CE_Failure, CPLE_AppDefined,
1389 : "The BMP file is probably corrupted or too large. "
1390 : "Image width = %d",
1391 : poDS->nRasterXSize);
1392 0 : delete poDS;
1393 0 : return nullptr;
1394 : }
1395 4 : }
1396 : }
1397 : else
1398 : {
1399 0 : delete poDS;
1400 0 : return nullptr;
1401 : }
1402 :
1403 : /* -------------------------------------------------------------------- */
1404 : /* Check for world file. */
1405 : /* -------------------------------------------------------------------- */
1406 49 : poDS->bGeoTransformValid =
1407 49 : GDALReadWorldFile(poOpenInfo->pszFilename, nullptr, poDS->m_gt.data());
1408 :
1409 49 : if (!poDS->bGeoTransformValid)
1410 49 : poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename,
1411 : ".wld", poDS->m_gt.data());
1412 :
1413 : /* -------------------------------------------------------------------- */
1414 : /* Initialize any PAM information. */
1415 : /* -------------------------------------------------------------------- */
1416 49 : poDS->SetDescription(poOpenInfo->pszFilename);
1417 49 : poDS->TryLoadXML();
1418 :
1419 : /* -------------------------------------------------------------------- */
1420 : /* Check for overviews. */
1421 : /* -------------------------------------------------------------------- */
1422 49 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1423 :
1424 49 : return poDS;
1425 : }
1426 :
1427 : /************************************************************************/
1428 : /* Create() */
1429 : /************************************************************************/
1430 :
1431 69 : GDALDataset *BMPDataset::Create(const char *pszFilename, int nXSize, int nYSize,
1432 : int nBandsIn, GDALDataType eType,
1433 : char **papszOptions)
1434 :
1435 : {
1436 69 : if (eType != GDT_UInt8)
1437 : {
1438 36 : CPLError(CE_Failure, CPLE_AppDefined,
1439 : "Attempt to create BMP dataset with an illegal\n"
1440 : "data type (%s), only Byte supported by the format.\n",
1441 : GDALGetDataTypeName(eType));
1442 :
1443 36 : return nullptr;
1444 : }
1445 :
1446 33 : if (nBandsIn != 1 && nBandsIn != 3)
1447 : {
1448 7 : CPLError(CE_Failure, CPLE_NotSupported,
1449 : "BMP driver doesn't support %d bands. Must be 1 or 3.\n",
1450 : nBandsIn);
1451 :
1452 7 : return nullptr;
1453 : }
1454 :
1455 : /* -------------------------------------------------------------------- */
1456 : /* Create the dataset. */
1457 : /* -------------------------------------------------------------------- */
1458 26 : BMPDataset *poDS = new BMPDataset();
1459 26 : poDS->m_bNewFile = true;
1460 :
1461 26 : poDS->fp = VSIFOpenL(pszFilename, "wb+");
1462 26 : if (poDS->fp == nullptr)
1463 : {
1464 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n",
1465 : pszFilename);
1466 3 : delete poDS;
1467 3 : return nullptr;
1468 : }
1469 :
1470 23 : poDS->pszFilename = CPLStrdup(pszFilename);
1471 :
1472 : /* -------------------------------------------------------------------- */
1473 : /* Fill the BMPInfoHeader */
1474 : /* -------------------------------------------------------------------- */
1475 23 : poDS->sInfoHeader.iSize = 40;
1476 23 : poDS->sInfoHeader.iWidth = nXSize;
1477 23 : poDS->sInfoHeader.iHeight = nYSize;
1478 23 : poDS->sInfoHeader.iPlanes = 1;
1479 23 : poDS->sInfoHeader.iBitCount = (nBandsIn == 3) ? 24 : 8;
1480 23 : poDS->sInfoHeader.iCompression = BMPC_RGB;
1481 :
1482 : /* XXX: Avoid integer overflow. We can calculate size in one
1483 : * step using
1484 : *
1485 : * nScanSize = ((poDS->sInfoHeader.iWidth *
1486 : * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8
1487 : *
1488 : * formula, but we should check for overflow conditions
1489 : * during calculation.
1490 : */
1491 23 : GUInt32 nScanSize =
1492 23 : (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31;
1493 23 : if (!poDS->sInfoHeader.iWidth || !poDS->sInfoHeader.iBitCount ||
1494 23 : (nScanSize - 31) / poDS->sInfoHeader.iBitCount !=
1495 23 : (GUInt32)poDS->sInfoHeader.iWidth)
1496 : {
1497 0 : CPLError(CE_Failure, CPLE_FileIO,
1498 : "Wrong image parameters; "
1499 : "can't allocate space for scanline buffer");
1500 0 : delete poDS;
1501 :
1502 0 : return nullptr;
1503 : }
1504 23 : nScanSize = (nScanSize & ~31U) / 8;
1505 :
1506 23 : poDS->sInfoHeader.iXPelsPerMeter = 0;
1507 23 : poDS->sInfoHeader.iYPelsPerMeter = 0;
1508 23 : poDS->nColorElems = 4;
1509 :
1510 : /* -------------------------------------------------------------------- */
1511 : /* Do we need colour table? */
1512 : /* -------------------------------------------------------------------- */
1513 23 : if (nBandsIn == 1)
1514 : {
1515 21 : poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount;
1516 21 : poDS->pabyColorTable =
1517 42 : (GByte *)CPLMalloc(static_cast<size_t>(poDS->nColorElems) *
1518 21 : poDS->sInfoHeader.iClrUsed);
1519 5397 : for (unsigned int i = 0; i < poDS->sInfoHeader.iClrUsed; i++)
1520 : {
1521 5376 : poDS->pabyColorTable[i * poDS->nColorElems] =
1522 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 1] =
1523 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 2] =
1524 5376 : poDS->pabyColorTable[i * poDS->nColorElems + 3] =
1525 : (GByte)i;
1526 : }
1527 : }
1528 : else
1529 : {
1530 2 : poDS->sInfoHeader.iClrUsed = 0;
1531 : }
1532 23 : poDS->sInfoHeader.iClrImportant = 0;
1533 :
1534 : /* -------------------------------------------------------------------- */
1535 : /* Fill the BMPFileHeader */
1536 : /* -------------------------------------------------------------------- */
1537 :
1538 23 : poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize +
1539 23 : poDS->sInfoHeader.iClrUsed * poDS->nColorElems;
1540 :
1541 : // From https://medium.com/@chiaracoetzee/maximum-resolution-of-bmp-image-file-8c729b3f833a
1542 23 : if (nXSize > 30000 || nYSize > 30000)
1543 : {
1544 0 : CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
1545 : "Adobe Photoshop CC 2014.2.2");
1546 : }
1547 23 : if (nXSize > 2147483647 / (nYSize + 1))
1548 : {
1549 0 : CPLDebug("BMP", "Dimensions of BMP file exceed maximum supported by "
1550 : "Windows Photo Viewer");
1551 : }
1552 :
1553 23 : const vsi_l_offset nLargeImageSize =
1554 23 : static_cast<vsi_l_offset>(nScanSize) * poDS->sInfoHeader.iHeight;
1555 23 : poDS->m_nFileSize = poDS->sFileHeader.iOffBits + nLargeImageSize;
1556 23 : if (nLargeImageSize > std::numeric_limits<uint32_t>::max())
1557 : {
1558 0 : CPLError(CE_Warning, CPLE_AppDefined,
1559 : "Image too big for its size to fit in a 32 bit integer! "
1560 : "Writing 0xFFFFFFFF in it, but that could cause compatibility "
1561 : "problems with other readers.");
1562 0 : poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
1563 0 : poDS->sInfoHeader.iSizeImage = std::numeric_limits<uint32_t>::max();
1564 : }
1565 23 : else if (poDS->m_nFileSize > std::numeric_limits<uint32_t>::max())
1566 : {
1567 0 : CPLError(CE_Warning, CPLE_AppDefined,
1568 : "File too big for its size to fit in a 32 bit integer! "
1569 : "Writing 0xFFFFFFFF in it, but that could cause compatibility "
1570 : "problems with other readers.");
1571 0 : poDS->sFileHeader.iSize = std::numeric_limits<uint32_t>::max();
1572 0 : poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
1573 : }
1574 : else
1575 : {
1576 23 : poDS->sFileHeader.iSize = static_cast<uint32_t>(poDS->m_nFileSize);
1577 23 : poDS->sInfoHeader.iSizeImage = static_cast<uint32_t>(nLargeImageSize);
1578 : }
1579 :
1580 23 : poDS->sFileHeader.bType[0] = 'B';
1581 23 : poDS->sFileHeader.bType[1] = 'M';
1582 23 : poDS->sFileHeader.iReserved1 = 0;
1583 23 : poDS->sFileHeader.iReserved2 = 0;
1584 :
1585 : /* -------------------------------------------------------------------- */
1586 : /* Write all structures to the file */
1587 : /* -------------------------------------------------------------------- */
1588 23 : if (VSIFWriteL(&poDS->sFileHeader.bType, 1, 2, poDS->fp) != 2)
1589 : {
1590 1 : CPLError(CE_Failure, CPLE_FileIO,
1591 : "Write of first 2 bytes to BMP file %s failed.\n"
1592 : "Is file system full?",
1593 : pszFilename);
1594 1 : delete poDS;
1595 :
1596 1 : return nullptr;
1597 : }
1598 :
1599 : GInt32 iLong;
1600 : GUInt32 iULong;
1601 : GUInt16 iUShort;
1602 :
1603 22 : iULong = CPL_LSBWORD32(poDS->sFileHeader.iSize);
1604 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1605 22 : iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved1);
1606 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1607 22 : iUShort = CPL_LSBWORD16(poDS->sFileHeader.iReserved2);
1608 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1609 22 : iULong = CPL_LSBWORD32(poDS->sFileHeader.iOffBits);
1610 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1611 :
1612 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSize);
1613 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1614 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iWidth);
1615 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1616 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iHeight);
1617 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1618 22 : iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iPlanes);
1619 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1620 22 : iUShort = CPL_LSBWORD16(poDS->sInfoHeader.iBitCount);
1621 22 : VSIFWriteL(&iUShort, 2, 1, poDS->fp);
1622 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iCompression);
1623 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1624 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iSizeImage);
1625 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1626 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iXPelsPerMeter);
1627 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1628 22 : iLong = CPL_LSBWORD32(poDS->sInfoHeader.iYPelsPerMeter);
1629 22 : VSIFWriteL(&iLong, 4, 1, poDS->fp);
1630 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrUsed);
1631 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1632 22 : iULong = CPL_LSBWORD32(poDS->sInfoHeader.iClrImportant);
1633 22 : VSIFWriteL(&iULong, 4, 1, poDS->fp);
1634 :
1635 22 : if (poDS->sInfoHeader.iClrUsed)
1636 : {
1637 40 : if (VSIFWriteL(poDS->pabyColorTable, 1,
1638 20 : static_cast<size_t>(poDS->nColorElems) *
1639 20 : poDS->sInfoHeader.iClrUsed,
1640 20 : poDS->fp) !=
1641 20 : static_cast<size_t>(poDS->nColorElems) * poDS->sInfoHeader.iClrUsed)
1642 : {
1643 8 : CPLError(CE_Failure, CPLE_FileIO,
1644 : "Error writing color table. Is disk full?");
1645 8 : delete poDS;
1646 :
1647 8 : return nullptr;
1648 : }
1649 : }
1650 :
1651 14 : poDS->nRasterXSize = nXSize;
1652 14 : poDS->nRasterYSize = nYSize;
1653 14 : poDS->eAccess = GA_Update;
1654 14 : poDS->nBands = nBandsIn;
1655 :
1656 : /* -------------------------------------------------------------------- */
1657 : /* Create band information objects. */
1658 : /* -------------------------------------------------------------------- */
1659 32 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1660 : {
1661 18 : auto band = new BMPRasterBand(poDS, iBand);
1662 18 : poDS->SetBand(iBand, band);
1663 18 : if (band->pabyScan == nullptr)
1664 : {
1665 0 : CPLError(CE_Failure, CPLE_AppDefined, "Image with (%d) too large.",
1666 : poDS->nRasterXSize);
1667 0 : delete poDS;
1668 0 : return nullptr;
1669 : }
1670 : }
1671 :
1672 : /* -------------------------------------------------------------------- */
1673 : /* Do we need a world file? */
1674 : /* -------------------------------------------------------------------- */
1675 14 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
1676 0 : poDS->bGeoTransformValid = TRUE;
1677 :
1678 14 : return poDS;
1679 : }
1680 :
1681 : /************************************************************************/
1682 : /* GDALRegister_BMP() */
1683 : /************************************************************************/
1684 :
1685 2058 : void GDALRegister_BMP()
1686 :
1687 : {
1688 2058 : if (GDALGetDriverByName("BMP") != nullptr)
1689 283 : return;
1690 :
1691 1775 : GDALDriver *poDriver = new GDALDriver();
1692 :
1693 1775 : poDriver->SetDescription("BMP");
1694 1775 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1695 1775 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1696 1775 : "MS Windows Device Independent Bitmap");
1697 1775 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/bmp.html");
1698 1775 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bmp");
1699 1775 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte");
1700 1775 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
1701 : "<CreationOptionList>"
1702 : " <Option name='WORLDFILE' type='boolean' "
1703 : "description='Write out world file'/>"
1704 1775 : "</CreationOptionList>");
1705 :
1706 1775 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1707 :
1708 1775 : poDriver->pfnOpen = BMPDataset::Open;
1709 1775 : poDriver->pfnCreate = BMPDataset::Create;
1710 1775 : poDriver->pfnIdentify = BMPDataset::Identify;
1711 :
1712 1775 : GetGDALDriverManager()->RegisterDriver(poDriver);
1713 : }
|