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