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