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