1
/******************************************************************************
2
* $Id: bmpdataset.cpp,v 1.1 2003/08/19 21:32:24 jan Exp $
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
9
******************************************************************************
10
* Copyright (c) 2002, Andrey Kiselev <dron@remotesensing.org>
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:
19
* The above copyright notice and this permission notice shall be included
20
* in all copies or substantial portions of the Software.
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
******************************************************************************
31
* $Log: bmpdataset.cpp,v $
32
* Revision 1.1 2003/08/19 21:32:24 jan
33
* These files have been moved here from thuban/extensions/thuban/
34
* See there in the Attic for the older history.
36
* Revision 1.2 2003/06/17 15:25:06 jonathan
37
* (BMPDataset::Open): Call VSIFClose() not VSIFCloseL() to close the file.
38
* Fixes a crash under Windows.
40
* Revision 1.1 2003/05/20 15:25:45 jonathan
41
* New, but copied from GDAL. Provides a driver to output in .bmp format.
43
* Revision 1.19 2003/05/01 17:40:43 dron
44
* Report colour interpretation GCI_PaletteIndex for 1-bit images.
46
* Revision 1.18 2003/03/27 15:51:06 dron
47
* Improvements in update state handling in IReadBlock().
49
* Revision 1.17 2003/03/27 13:24:53 dron
50
* Fixes for large file support.
52
* Revision 1.16 2003/02/15 14:23:09 dron
53
* Fix in GetGeoTransform().
55
* Revision 1.15 2003/02/03 18:40:15 dron
56
* Type of input data checked in Create() method now.
58
* Revision 1.14 2002/12/15 15:24:41 dron
61
* Revision 1.13 2002/12/15 15:13:33 dron
62
* BMP structures extended.
64
* Revision 1.12 2002/12/13 14:15:50 dron
65
* IWriteBlock() fixed, CreateCopy() removed, added RLE4 decoding and OS/2 BMP
68
* Revision 1.11 2002/12/11 22:09:13 warmerda
69
* Re-enable createcopy.
71
* Revision 1.10 2002/12/11 22:08:20 warmerda
72
* fixed multiband support for writing ... open wit wb+ in create
74
* Revision 1.9 2002/12/11 16:13:04 dron
75
* Support for 16-bit RGB images (untested).
77
* Revision 1.8 2002/12/10 22:12:59 dron
78
* RLE8 decoding added.
80
* Revision 1.7 2002/12/09 19:31:44 dron
81
* Switched to CPL_LSBWORD32 macro.
83
* Revision 1.6 2002/12/07 15:20:23 dron
84
* SetColorTable() added. Create() really works now.
86
* Revision 1.5 2002/12/06 20:50:13 warmerda
89
* Revision 1.4 2002/12/06 18:37:05 dron
90
* Create() method added, 1- and 4-bpp images readed now.
92
* Revision 1.3 2002/12/05 19:25:35 dron
93
* Preliminary CreateCopy() function.
95
* Revision 1.2 2002/12/04 18:37:49 dron
96
* Support 32-bit, 24-bit True Color images and 8-bit pseudocolor ones.
98
* Revision 1.1 2002/12/03 19:04:18 dron
103
#include "gdal_priv.h"
104
#include "cpl_string.h"
105
#include "cpl_mfile.h"
107
CPL_CVSID("$Id: bmpdataset.cpp,v 1.1 2003/08/19 21:32:24 jan Exp $");
110
void GDALRegister_THUBANBMP(void);
115
BMPT_WIN4, // BMP used in Windows 3.0/NT 3.51/95
116
BMPT_WIN5, // BMP used in Windows NT 4.0/98/Me/2000/XP
117
BMPT_OS21, // BMP used in OS/2 PM 1.x
118
BMPT_OS22 // BMP used in OS/2 PM 2.x
121
// Bitmap file consists of a BitmapFileHeader structure followed by
122
// a BitmapInfoHeader structure. An array of BMPColorEntry structures (also
123
// called a colour table) follows the bitmap information header structure. The
124
// colour table is followed by a second array of indexes into the colour table
125
// (the actual bitmap data). Data may be comressed, for 4-bpp and 8-bpp used
128
// +---------------------+
129
// | BitmapFileHeader |
130
// +---------------------+
131
// | BitmapInfoHeader |
132
// +---------------------+
133
// | BMPColorEntry array |
134
// +---------------------+
135
// | Colour-index array |
136
// +---------------------+
138
// All numbers stored in Intel order with least significant byte first.
142
BMPC_RGB = 0L, // Uncompressed
143
BMPC_RLE8 = 1L, // RLE for 8 bpp images
144
BMPC_RLE4 = 2L, // RLE for 4 bpp images
145
BMPC_BITFIELDS = 3L, // Bitmap is not compressed and the colour table
146
// consists of three DWORD color masks that specify
147
// the red, green, and blue components of each pixel.
148
// This is valid when used with 16- and 32-bpp bitmaps.
149
BMPC_JPEG = 4L, // Indicates that the image is a JPEG image.
150
BMPC_PNG = 5L // Indicates that the image is a PNG image.
153
enum BMPLCSType // Type of logical color space.
155
BMPLT_CALIBRATED_RGB = 0, // This value indicates that endpoints and gamma
156
// values are given in the appropriate fields.
157
BMPLT_DEVICE_RGB = 1,
158
BMPLT_DEVICE_CMYK = 2,
168
typedef struct // This structure contains the x, y, and z
169
{ // coordinates of the three colors that correspond
170
BMPCIEXYZ iCIERed; // to the red, green, and blue endpoints for
171
BMPCIEXYZ iCIEGreen; // a specified logical color space.
177
GByte bType[2]; // Signature "BM"
178
GUInt32 iSize; // Size in bytes of the bitmap file. Should always
179
// be ignored while reading because of error
180
// in Windows 3.0 SDK's description of this field
181
GUInt16 iReserved1; // Reserved, set as 0
182
GUInt16 iReserved2; // Reserved, set as 0
183
GUInt32 iOffBits; // Offset of the image from file start in bytes
185
const int BFH_SIZE = 14;
189
GUInt32 iSize; // Size of BitmapInfoHeader structure in bytes.
190
// Should be used to determine start of the
192
GInt32 iWidth; // Image width
193
GInt32 iHeight; // Image height. If positive, image has bottom left
194
// origin, if negative --- top left.
195
GUInt16 iPlanes; // Number of image planes (must be set to 1)
196
GUInt16 iBitCount; // Number of bits per pixel (1, 4, 8, 16, 24 or 32).
197
// If 0 then the number of bits per pixel is
198
// specified or is implied by the JPEG or PNG format.
199
BMPComprMethod iCompression; // Compression method
200
GUInt32 iSizeImage; // Size of uncomressed image in bytes. May be 0
201
// for BMPC_RGB bitmaps. If iCompression is BI_JPEG
202
// or BI_PNG, iSizeImage indicates the size
203
// of the JPEG or PNG image buffer.
204
GInt32 iXPelsPerMeter; // X resolution, pixels per meter (0 if not used)
205
GInt32 iYPelsPerMeter; // Y resolution, pixels per meter (0 if not used)
206
GUInt32 iClrUsed; // Size of colour table. If 0, iBitCount should
207
// be used to calculate this value (1<<iBitCount)
208
GUInt32 iClrImportant; // Number of important colours. If 0, all
209
// colours are required
211
// Fields above should be used for bitmaps, compatible with Windows NT 3.51
212
// and earlier. Windows 98/Me, Windows 2000/XP introduces additional fields:
214
GUInt32 iRedMask; // Colour mask that specifies the red component
215
// of each pixel, valid only if iCompression
216
// is set to BI_BITFIELDS.
217
GUInt32 iGreenMask; // The same for green component
218
GUInt32 iBlueMask; // The same for blue component
219
GUInt32 iAlphaMask; // Colour mask that specifies the alpha
220
// component of each pixel.
221
BMPLCSType iCSType; // Colour space of the DIB.
222
BMPCIEXYZTriple sEndpoints; // This member is ignored unless the iCSType member
223
// specifies BMPLT_CALIBRATED_RGB.
224
GUInt32 iGammaRed; // Toned response curve for red. This member
225
// is ignored unless color values are calibrated
226
// RGB values and iCSType is set to
227
// BMPLT_CALIBRATED_RGB. Specified in 16^16 format.
228
GUInt32 iGammaGreen; // Toned response curve for green.
229
GUInt32 iGammaBlue; // Toned response curve for blue.
231
const unsigned int BIH_WIN4SIZE = 40; // For BMPT_WIN4
232
const unsigned int BIH_WIN5SIZE = 57; // For BMPT_WIN5
233
const unsigned int BIH_OS21SIZE = 12; // For BMPT_OS21
234
const unsigned int BIH_OS22SIZE = 64; // For BMPT_OS22
236
// We will use plain byte array instead of this structure, but declaration
237
// provided for reference
243
GByte bReserved; // Must be 0
246
/************************************************************************/
247
/* ==================================================================== */
249
/* ==================================================================== */
250
/************************************************************************/
252
class BMPDataset : public GDALDataset
254
friend class BMPRasterBand;
255
friend class BMPComprRasterBand;
257
BitmapFileHeader sFileHeader;
258
BitmapInfoHeader sInfoHeader;
259
int nColorTableSize, nColorElems;
260
GByte *pabyColorTable;
261
GDALColorTable *poColorTable;
262
double adfGeoTransform[6];
263
int bGeoTransformValid;
266
const char *pszFilename;
273
static GDALDataset *Open( GDALOpenInfo * );
274
static GDALDataset *Create( const char * pszFilename,
275
int nXSize, int nYSize, int nBands,
276
GDALDataType eType, char ** papszParmList );
277
virtual void FlushCache( void );
279
CPLErr GetGeoTransform( double * padfTransform );
280
virtual CPLErr SetGeoTransform( double * );
281
const char *GetProjectionRef();
284
/************************************************************************/
285
/* ==================================================================== */
287
/* ==================================================================== */
288
/************************************************************************/
290
class BMPRasterBand : public GDALRasterBand
292
friend class BMPDataset;
297
unsigned int iBytesPerPixel;
302
BMPRasterBand( BMPDataset *, int );
305
virtual CPLErr IReadBlock( int, int, void * );
306
virtual CPLErr IWriteBlock( int, int, void * );
307
virtual GDALColorInterp GetColorInterpretation();
308
virtual GDALColorTable *GetColorTable();
309
CPLErr SetColorTable( GDALColorTable * );
313
/************************************************************************/
314
/* BMPRasterBand() */
315
/************************************************************************/
317
BMPRasterBand::BMPRasterBand( BMPDataset *poDS, int nBand )
321
eDataType = GDT_Byte;
322
iBytesPerPixel = poDS->sInfoHeader.iBitCount / 8;
324
// We will read one scanline per time. Scanlines in BMP aligned at 4-byte
326
nBlockXSize = poDS->GetRasterXSize();
328
((poDS->GetRasterXSize() * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8;
331
CPLDebug( "BMP", "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d",
332
nBand, nBlockXSize, nBlockYSize, nScanSize );
334
pabyScan = (GByte *) CPLMalloc( nScanSize * nBlockYSize );
337
/************************************************************************/
338
/* ~BMPRasterBand() */
339
/************************************************************************/
341
BMPRasterBand::~BMPRasterBand()
346
/************************************************************************/
348
/************************************************************************/
350
CPLErr BMPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
353
BMPDataset *poGDS = (BMPDataset *) poDS;
356
int nBlockSize = nBlockXSize * nBlockYSize;
358
if ( poGDS->sInfoHeader.iHeight > 0 )
359
iScanOffset = poGDS->sFileHeader.iSize - (nBlockYOff + 1) * nScanSize;
361
iScanOffset = poGDS->sFileHeader.iOffBits + nBlockYOff * nScanSize;
363
if ( MFILESeek( poGDS->fp, iScanOffset, SEEK_SET ) < 0 )
365
// XXX: We will not report error here, because file just may be
366
// in update state and data for this block will be available later
367
if( poGDS->eAccess == GA_Update )
369
memset( pImage, 0, nBlockSize );
374
CPLError( CE_Failure, CPLE_FileIO,
375
"Can't seek to offset %ld in input file to read data.",
380
if ( MFILERead( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize )
383
if( poGDS->eAccess == GA_Update )
385
memset( pImage, 0, nBlockSize );
390
CPLError( CE_Failure, CPLE_FileIO,
391
"Can't read from offset %ld in input file.", iScanOffset );
396
if ( poGDS->sInfoHeader.iBitCount == 8 ||
397
poGDS->sInfoHeader.iBitCount == 24 ||
398
poGDS->sInfoHeader.iBitCount == 32 )
400
for ( i = 0, j = 0; i < nBlockSize; i++ )
402
// Colour triplets in BMP file organized in reverse order:
404
((GByte *) pImage)[i] = pabyScan[j + iBytesPerPixel - nBand];
408
if ( poGDS->sInfoHeader.iBitCount == 16 )
410
for ( i = 0, j = 0; i < nBlockSize; i++ )
415
((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F;
418
((GByte *) pImage)[i] = ((pabyScan[i] & 0x03) << 3) |
419
((pabyScan[i + 1] & 0xE0) >> 5);
422
((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2;
429
else if ( poGDS->sInfoHeader.iBitCount == 4 )
431
for ( i = 0, j = 0; i < nBlockSize; i++ )
433
// Most significant part of the byte represents leftmost pixel
435
((GByte *) pImage)[i] = pabyScan[j++] & 0x0F;
437
((GByte *) pImage)[i] = (pabyScan[j] & 0xF0) >> 4;
440
else if ( poGDS->sInfoHeader.iBitCount == 1 )
442
for ( i = 0, j = 0; i < nBlockSize; i++ )
447
((GByte *) pImage)[i] = (pabyScan[j] & 0x80) >> 7;
450
((GByte *) pImage)[i] = (pabyScan[j] & 0x40) >> 6;
453
((GByte *) pImage)[i] = (pabyScan[j] & 0x20) >> 5;
456
((GByte *) pImage)[i] = (pabyScan[j] & 0x10) >> 4;
459
((GByte *) pImage)[i] = (pabyScan[j] & 0x08) >> 3;
462
((GByte *) pImage)[i] = (pabyScan[j] & 0x04) >> 2;
465
((GByte *) pImage)[i] = (pabyScan[j] & 0x02) >> 1;
468
((GByte *) pImage)[i] = pabyScan[j++] & 0x01;
479
/************************************************************************/
481
/************************************************************************/
483
CPLErr BMPRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff,
486
BMPDataset *poGDS = (BMPDataset *)poDS;
487
int iInPixel, iOutPixel;
490
CPLAssert( poGDS != NULL
495
iScanOffset = poGDS->sFileHeader.iSize - (nBlockYOff + 1) * nScanSize;
496
if ( MFILESeek( poGDS->fp, iScanOffset, SEEK_SET ) < 0 )
498
CPLError( CE_Failure, CPLE_FileIO,
499
"Can't seek to offset %ld in output file to write data",
504
if( poGDS->nBands != 1 )
506
memset( pabyScan, 0, nScanSize );
507
MFILERead( pabyScan, 1, nScanSize, poGDS->fp );
508
MFILESeek( poGDS->fp, iScanOffset, SEEK_SET );
511
for ( iInPixel = 0, iOutPixel = iBytesPerPixel - nBand;
512
iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands )
514
pabyScan[iOutPixel] = ((GByte *) pImage)[iInPixel];
517
if ( MFILEWrite( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize )
519
CPLError( CE_Failure, CPLE_FileIO,
520
"Can't write block with X offset %d and Y offset %d",
521
nBlockXOff, nBlockYOff );
528
/************************************************************************/
529
/* GetColorTable() */
530
/************************************************************************/
532
GDALColorTable *BMPRasterBand::GetColorTable()
534
BMPDataset *poGDS = (BMPDataset *) poDS;
536
return poGDS->poColorTable;
539
/************************************************************************/
540
/* SetColorTable() */
541
/************************************************************************/
543
CPLErr BMPRasterBand::SetColorTable( GDALColorTable *poColorTable )
545
BMPDataset *poGDS = (BMPDataset *) poDS;
549
GDALColorEntry oEntry;
553
poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount();
554
if ( poGDS->sInfoHeader.iClrUsed < 1 ||
555
poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount) )
558
MFILESeek( poGDS->fp, BFH_SIZE + 32, SEEK_SET );
560
iULong = CPL_LSBWORD32( poGDS->sInfoHeader.iClrUsed );
561
MFILEWrite( &iULong, 4, 1, poGDS->fp );
562
poGDS->pabyColorTable = (GByte *) CPLRealloc( poGDS->pabyColorTable,
563
poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed );
564
if ( !poGDS->pabyColorTable )
567
for( i = 0; i < poGDS->sInfoHeader.iClrUsed; i++ )
569
poColorTable->GetColorEntryAsRGB( i, &oEntry );
570
poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0;
571
poGDS->pabyColorTable[i * poGDS->nColorElems + 2] = oEntry.c1; // Red
572
poGDS->pabyColorTable[i * poGDS->nColorElems + 1] = oEntry.c2; // Green
573
poGDS->pabyColorTable[i * poGDS->nColorElems] = oEntry.c3; // Blue
576
MFILESeek( poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET );
577
if ( MFILEWrite( poGDS->pabyColorTable, 1,
578
poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed, poGDS->fp ) <
579
poGDS->nColorElems * (GUInt32) poGDS->sInfoHeader.iClrUsed )
590
/************************************************************************/
591
/* GetColorInterpretation() */
592
/************************************************************************/
594
GDALColorInterp BMPRasterBand::GetColorInterpretation()
596
BMPDataset *poGDS = (BMPDataset *) poDS;
598
if( poGDS->sInfoHeader.iBitCount == 24 ||
599
poGDS->sInfoHeader.iBitCount == 32 ||
600
poGDS->sInfoHeader.iBitCount == 16 )
604
else if( nBand == 2 )
605
return GCI_GreenBand;
606
else if( nBand == 3 )
609
return GCI_Undefined;
613
return GCI_PaletteIndex;
617
/************************************************************************/
618
/* ==================================================================== */
619
/* BMPComprRasterBand */
620
/* ==================================================================== */
621
/************************************************************************/
623
class BMPComprRasterBand : public BMPRasterBand
625
friend class BMPDataset;
628
GByte *pabyUncomprBuf;
632
BMPComprRasterBand( BMPDataset *, int );
633
~BMPComprRasterBand();
635
virtual CPLErr IReadBlock( int, int, void * );
636
// virtual CPLErr IWriteBlock( int, int, void * );
639
/************************************************************************/
640
/* BMPComprRasterBand() */
641
/************************************************************************/
643
BMPComprRasterBand::BMPComprRasterBand( BMPDataset *poDS, int nBand )
644
: BMPRasterBand( poDS, nBand )
646
unsigned int i, j, k, iLength;
647
GUInt32 iComprSize, iUncomprSize;
649
iComprSize = poDS->sFileHeader.iSize - poDS->sFileHeader.iOffBits;
650
iUncomprSize = poDS->GetRasterXSize() * poDS->GetRasterYSize();
651
pabyComprBuf = (GByte *) CPLMalloc( iComprSize );
652
pabyUncomprBuf = (GByte *) CPLMalloc( iUncomprSize );
654
CPLDebug( "BMP", "RLE8 compression detected." );
655
CPLDebug ( "BMP", "Size of compressed buffer %ld bytes,"
656
" size of uncompressed buffer %ld bytes.",
657
iComprSize, iUncomprSize );
659
MFILESeek( poDS->fp, poDS->sFileHeader.iOffBits, SEEK_SET );
660
MFILERead( pabyComprBuf, 1, iComprSize, poDS->fp );
663
if ( poDS->sInfoHeader.iBitCount == 8 ) // RLE8
665
while( j < iUncomprSize && i < iComprSize )
667
if ( pabyComprBuf[i] )
669
iLength = pabyComprBuf[i++];
670
while( iLength > 0 && j < iUncomprSize && i < iComprSize )
672
pabyUncomprBuf[j++] = pabyComprBuf[i];
680
if ( pabyComprBuf[i] == 0 ) // Next scanline
684
else if ( pabyComprBuf[i] == 1 ) // End of image
688
else if ( pabyComprBuf[i] == 2 ) // Move to...
691
if ( i < iComprSize - 1 )
693
j += pabyComprBuf[i++] +
694
pabyComprBuf[i++] * poDS->GetRasterXSize();
699
else // Absolute mode
701
iLength = pabyComprBuf[i++];
702
for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ )
703
pabyUncomprBuf[j++] = pabyComprBuf[i++];
712
while( j < iUncomprSize && i < iComprSize )
714
if ( pabyComprBuf[i] )
716
iLength = pabyComprBuf[i++];
717
while( iLength > 0 && j < iUncomprSize && i < iComprSize )
719
if ( iLength & 0x01 )
720
pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
722
pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F;
730
if ( pabyComprBuf[i] == 0 ) // Next scanline
734
else if ( pabyComprBuf[i] == 1 ) // End of image
738
else if ( pabyComprBuf[i] == 2 ) // Move to...
741
if ( i < iComprSize - 1 )
743
j += pabyComprBuf[i++] +
744
pabyComprBuf[i++] * poDS->GetRasterXSize();
749
else // Absolute mode
751
iLength = pabyComprBuf[i++];
752
for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ )
755
pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F;
757
pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4;
767
/************************************************************************/
768
/* ~BMPComprRasterBand() */
769
/************************************************************************/
771
BMPComprRasterBand::~BMPComprRasterBand()
774
CPLFree( pabyComprBuf );
775
if ( pabyUncomprBuf )
776
CPLFree( pabyUncomprBuf );
779
/************************************************************************/
781
/************************************************************************/
783
CPLErr BMPComprRasterBand::
784
IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage )
786
memcpy( pImage, pabyUncomprBuf +
787
(poDS->GetRasterYSize() - nBlockYOff - 1) * poDS->GetRasterXSize(),
793
/************************************************************************/
795
/************************************************************************/
797
BMPDataset::BMPDataset()
802
pszProjection = CPLStrdup( "" );
803
bGeoTransformValid = FALSE;
804
adfGeoTransform[0] = 0.0;
805
adfGeoTransform[1] = 1.0;
806
adfGeoTransform[2] = 0.0;
807
adfGeoTransform[3] = 0.0;
808
adfGeoTransform[4] = 0.0;
809
adfGeoTransform[5] = 1.0;
810
pabyColorTable = NULL;
812
memset( &sFileHeader, 0, sizeof(sFileHeader) );
813
memset( &sInfoHeader, 0, sizeof(sInfoHeader) );
816
/************************************************************************/
818
/************************************************************************/
820
BMPDataset::~BMPDataset()
825
CPLFree( pszProjection );
826
if ( pabyColorTable )
827
CPLFree( pabyColorTable );
828
if ( poColorTable != NULL )
834
/************************************************************************/
835
/* GetGeoTransform() */
836
/************************************************************************/
838
CPLErr BMPDataset::GetGeoTransform( double * padfTransform )
840
memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 );
842
if( bGeoTransformValid )
848
/************************************************************************/
849
/* SetGeoTransform() */
850
/************************************************************************/
852
CPLErr BMPDataset::SetGeoTransform( double * padfTransform )
854
CPLErr eErr = CE_None;
856
memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 );
858
if ( pszFilename && bGeoTransformValid )
859
if ( GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform )
862
CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." );
869
/************************************************************************/
870
/* GetProjectionRef() */
871
/************************************************************************/
873
const char *BMPDataset::GetProjectionRef()
876
return pszProjection;
881
/************************************************************************/
883
/************************************************************************/
885
void BMPDataset::FlushCache()
888
GDALDataset::FlushCache();
891
/************************************************************************/
893
/************************************************************************/
895
GDALDataset *BMPDataset::Open( GDALOpenInfo * poOpenInfo )
897
if( poOpenInfo->fp == NULL )
900
if( !EQUALN((const char *) poOpenInfo->pabyHeader, "BM", 2) )
903
VSIFClose( poOpenInfo->fp );
904
poOpenInfo->fp = NULL;
906
/* -------------------------------------------------------------------- */
907
/* Create a corresponding GDALDataset. */
908
/* -------------------------------------------------------------------- */
912
poDS = new BMPDataset();
914
if( poOpenInfo->eAccess == GA_ReadOnly )
915
poDS->fp = MFILEOpen( poOpenInfo->pszFilename );
917
poDS->fp = MFILEOpen( poOpenInfo->pszFilename );
921
CPLStat(poOpenInfo->pszFilename, &sStat);
923
/* -------------------------------------------------------------------- */
924
/* Read the BitmapFileHeader. We need iOffBits value only */
925
/* -------------------------------------------------------------------- */
926
MFILESeek( poDS->fp, 10, SEEK_SET );
927
MFILERead( &poDS->sFileHeader.iOffBits, 1, 4, poDS->fp );
929
CPL_SWAP32PTR( &poDS->sFileHeader.iOffBits );
931
poDS->sFileHeader.iSize = sStat.st_size;
932
CPLDebug( "BMP", "File size %d bytes.", poDS->sFileHeader.iSize );
933
CPLDebug( "BMP", "Image offset 0x%x bytes from file start.",
934
poDS->sFileHeader.iOffBits );
936
/* -------------------------------------------------------------------- */
937
/* Read the BitmapInfoHeader. */
938
/* -------------------------------------------------------------------- */
941
MFILESeek( poDS->fp, BFH_SIZE, SEEK_SET );
942
MFILERead( &poDS->sInfoHeader.iSize, 1, 4, poDS->fp );
944
CPL_SWAP32PTR( &poDS->sInfoHeader.iSize );
947
if ( poDS->sInfoHeader.iSize == BIH_WIN4SIZE )
948
eBMPType = BMPT_WIN4;
949
else if ( poDS->sInfoHeader.iSize == BIH_OS21SIZE )
950
eBMPType = BMPT_OS21;
951
else if ( poDS->sInfoHeader.iSize == BIH_OS22SIZE ||
952
poDS->sInfoHeader.iSize == 16 )
953
eBMPType = BMPT_OS22;
955
eBMPType = BMPT_WIN5;
957
if ( eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22 )
959
MFILERead( &poDS->sInfoHeader.iWidth, 1, 4, poDS->fp );
960
MFILERead( &poDS->sInfoHeader.iHeight, 1, 4, poDS->fp );
961
MFILERead( &poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp );
962
MFILERead( &poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp );
963
MFILERead( &poDS->sInfoHeader.iCompression, 1, 4, poDS->fp );
964
MFILERead( &poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp );
965
MFILERead( &poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp );
966
MFILERead( &poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp );
967
MFILERead( &poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp );
968
MFILERead( &poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp );
970
CPL_SWAP32PTR( &poDS->sInfoHeader.iWidth );
971
CPL_SWAP32PTR( &poDS->sInfoHeader.iHeight );
972
CPL_SWAP16PTR( &poDS->sInfoHeader.iPlanes );
973
CPL_SWAP16PTR( &poDS->sInfoHeader.iBitCount );
974
CPL_SWAP32PTR( &poDS->sInfoHeader.iCompression );
975
CPL_SWAP32PTR( &poDS->sInfoHeader.iSizeImage );
976
CPL_SWAP32PTR( &poDS->sInfoHeader.iXPelsPerMeter );
977
CPL_SWAP32PTR( &poDS->sInfoHeader.iYPelsPerMeter );
978
CPL_SWAP32PTR( &poDS->sInfoHeader.iClrUsed );
979
CPL_SWAP32PTR( &poDS->sInfoHeader.iClrImportant );
981
poDS->nColorElems = 4;
983
if ( eBMPType == BMPT_OS22 )
985
poDS->nColorElems = 3; // FIXME: different info in different documents regarding this!
987
if ( eBMPType == BMPT_OS21 )
991
MFILERead( &iShort, 1, 2, poDS->fp );
992
poDS->sInfoHeader.iWidth = CPL_LSBWORD16( iShort );
993
MFILERead( &iShort, 1, 2, poDS->fp );
994
poDS->sInfoHeader.iHeight = CPL_LSBWORD16( iShort );
995
MFILERead( &iShort, 1, 2, poDS->fp );
996
poDS->sInfoHeader.iPlanes = CPL_LSBWORD16( iShort );
997
MFILERead( &iShort, 1, 2, poDS->fp );
998
poDS->sInfoHeader.iBitCount = CPL_LSBWORD16( iShort );
999
poDS->nColorElems = 3;
1002
if ( poDS->sInfoHeader.iBitCount != 1 &&
1003
poDS->sInfoHeader.iBitCount != 4 &&
1004
poDS->sInfoHeader.iBitCount != 8 &&
1005
poDS->sInfoHeader.iBitCount != 16 &&
1006
poDS->sInfoHeader.iBitCount != 24 &&
1007
poDS->sInfoHeader.iBitCount != 32 )
1013
CPLDebug( "BMP", "Windows Device Independent Bitmap parameters:\n"
1014
" info header size: %d bytes\n"
1015
" width: %d\n height: %d\n planes: %d\n bpp: %d\n"
1016
" compression: %d\n image size: %d bytes\n X resolution: %d\n"
1017
" Y resolution: %d\n colours used: %d\n colours important: %d",
1018
poDS->sInfoHeader.iSize,
1019
poDS->sInfoHeader.iWidth, poDS->sInfoHeader.iHeight,
1020
poDS->sInfoHeader.iPlanes, poDS->sInfoHeader.iBitCount,
1021
poDS->sInfoHeader.iCompression, poDS->sInfoHeader.iSizeImage,
1022
poDS->sInfoHeader.iXPelsPerMeter, poDS->sInfoHeader.iYPelsPerMeter,
1023
poDS->sInfoHeader.iClrUsed, poDS->sInfoHeader.iClrImportant );
1025
poDS->nRasterXSize = poDS->sInfoHeader.iWidth;
1026
poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)?
1027
poDS->sInfoHeader.iHeight:-poDS->sInfoHeader.iHeight;
1028
switch ( poDS->sInfoHeader.iBitCount )
1037
// Allocate memory for colour table and read it
1038
if ( poDS->sInfoHeader.iClrUsed )
1039
poDS->nColorTableSize = poDS->sInfoHeader.iClrUsed;
1041
poDS->nColorTableSize = 1 << poDS->sInfoHeader.iBitCount;
1042
poDS->pabyColorTable =
1043
(GByte *)CPLMalloc( poDS->nColorElems * poDS->nColorTableSize );
1044
MFILESeek( poDS->fp, BFH_SIZE + poDS->sInfoHeader.iSize, SEEK_SET );
1045
MFILERead( poDS->pabyColorTable, poDS->nColorElems,
1046
poDS->nColorTableSize, poDS->fp );
1048
GDALColorEntry oEntry;
1049
poDS->poColorTable = new GDALColorTable();
1050
for( i = 0; i < poDS->nColorTableSize; i++ )
1052
oEntry.c1 = poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red
1053
oEntry.c2 = poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green
1054
oEntry.c3 = poDS->pabyColorTable[i * poDS->nColorElems]; // Blue
1057
poDS->poColorTable->SetColorEntry( i, &oEntry );
1071
/* -------------------------------------------------------------------- */
1072
/* Create band information objects. */
1073
/* -------------------------------------------------------------------- */
1076
if ( poDS->sInfoHeader.iCompression == BMPC_RGB )
1078
for( iBand = 1; iBand <= poDS->nBands; iBand++ )
1079
poDS->SetBand( iBand, new BMPRasterBand( poDS, iBand ) );
1081
else if ( poDS->sInfoHeader.iCompression == BMPC_RLE8 )
1083
for( iBand = 1; iBand <= poDS->nBands; iBand++ )
1084
poDS->SetBand( iBand, new BMPComprRasterBand( poDS, iBand ) );
1092
/* -------------------------------------------------------------------- */
1093
/* Check for world file. */
1094
/* -------------------------------------------------------------------- */
1095
poDS->bGeoTransformValid =
1096
GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
1097
poDS->adfGeoTransform );
1102
/************************************************************************/
1104
/************************************************************************/
1106
GDALDataset *BMPDataset::Create( const char * pszFilename,
1107
int nXSize, int nYSize, int nBands,
1108
GDALDataType eType, char **papszOptions )
1111
if( eType != GDT_Byte )
1113
CPLError( CE_Failure, CPLE_AppDefined,
1114
"Attempt to create BMP dataset with an illegal\n"
1115
"data type (%s), only Byte supported by the format.\n",
1116
GDALGetDataTypeName(eType) );
1121
if( nBands != 1 && nBands != 3 )
1123
CPLError( CE_Failure, CPLE_NotSupported,
1124
"BMP driver doesn't support %d bands. Must be 1 or 3.\n",
1130
/* -------------------------------------------------------------------- */
1131
/* Create the dataset. */
1132
/* -------------------------------------------------------------------- */
1135
poDS = new BMPDataset();
1137
poDS->fp = MFILEOpen( pszFilename );
1138
if( poDS->fp == NULL )
1140
CPLError( CE_Failure, CPLE_OpenFailed,
1141
"Unable to create file %s.\n",
1146
poDS->pszFilename = pszFilename;
1148
/* -------------------------------------------------------------------- */
1149
/* Fill the BitmapInfoHeader */
1150
/* -------------------------------------------------------------------- */
1153
poDS->sInfoHeader.iSize = 40;
1154
poDS->sInfoHeader.iWidth = nXSize;
1155
poDS->sInfoHeader.iHeight = nYSize;
1156
poDS->sInfoHeader.iPlanes = 1;
1157
poDS->sInfoHeader.iBitCount = ( nBands == 3 )?24:8;
1158
poDS->sInfoHeader.iCompression = BMPC_RGB;
1159
nScanSize = ((poDS->sInfoHeader.iWidth *
1160
poDS->sInfoHeader.iBitCount + 31) & ~31) / 8;
1161
poDS->sInfoHeader.iSizeImage = nScanSize * poDS->sInfoHeader.iHeight;
1162
poDS->sInfoHeader.iXPelsPerMeter = 0;
1163
poDS->sInfoHeader.iYPelsPerMeter = 0;
1164
poDS->nColorElems = 4;
1166
/* -------------------------------------------------------------------- */
1167
/* Do we need colour table? */
1168
/* -------------------------------------------------------------------- */
1173
poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount;
1174
poDS->pabyColorTable =
1175
(GByte *) CPLMalloc( poDS->nColorElems * poDS->sInfoHeader.iClrUsed );
1176
for ( i = 0; i < poDS->sInfoHeader.iClrUsed; i++ )
1178
poDS->pabyColorTable[i * poDS->nColorElems] =
1179
poDS->pabyColorTable[i * poDS->nColorElems + 1] =
1180
poDS->pabyColorTable[i * poDS->nColorElems + 2] =
1181
poDS->pabyColorTable[i * poDS->nColorElems + 3] = (GByte) i;
1186
poDS->sInfoHeader.iClrUsed = 0;
1188
poDS->sInfoHeader.iClrImportant = 0;
1190
/* -------------------------------------------------------------------- */
1191
/* Fill the BitmapFileHeader */
1192
/* -------------------------------------------------------------------- */
1193
poDS->sFileHeader.bType[0] = 'B';
1194
poDS->sFileHeader.bType[1] = 'M';
1195
poDS->sFileHeader.iSize = BFH_SIZE + poDS->sInfoHeader.iSize +
1196
poDS->sInfoHeader.iClrUsed * poDS->nColorElems +
1197
poDS->sInfoHeader.iSizeImage;
1198
poDS->sFileHeader.iReserved1 = 0;
1199
poDS->sFileHeader.iReserved2 = 0;
1200
poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize +
1201
poDS->sInfoHeader.iClrUsed * poDS->nColorElems;
1203
/* -------------------------------------------------------------------- */
1204
/* Write all structures to the file */
1205
/* -------------------------------------------------------------------- */
1206
MFILEWrite( &poDS->sFileHeader.bType, 1, 2, poDS->fp );
1212
iULong = CPL_LSBWORD32( poDS->sFileHeader.iSize );
1213
MFILEWrite( &iULong, 4, 1, poDS->fp );
1214
iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved1 );
1215
MFILEWrite( &iUShort, 2, 1, poDS->fp );
1216
iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved2 );
1217
MFILEWrite( &iUShort, 2, 1, poDS->fp );
1218
iULong = CPL_LSBWORD32( poDS->sFileHeader.iOffBits );
1219
MFILEWrite( &iULong, 4, 1, poDS->fp );
1221
iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSize );
1222
MFILEWrite( &iULong, 4, 1, poDS->fp );
1223
iLong = CPL_LSBWORD32( poDS->sInfoHeader.iWidth );
1224
MFILEWrite( &iLong, 4, 1, poDS->fp );
1225
iLong = CPL_LSBWORD32( poDS->sInfoHeader.iHeight );
1226
MFILEWrite( &iLong, 4, 1, poDS->fp );
1227
iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iPlanes );
1228
MFILEWrite( &iUShort, 2, 1, poDS->fp );
1229
iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iBitCount );
1230
MFILEWrite( &iUShort, 2, 1, poDS->fp );
1231
iULong = CPL_LSBWORD32( poDS->sInfoHeader.iCompression );
1232
MFILEWrite( &iULong, 4, 1, poDS->fp );
1233
iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSizeImage );
1234
MFILEWrite( &iULong, 4, 1, poDS->fp );
1235
iLong = CPL_LSBWORD32( poDS->sInfoHeader.iXPelsPerMeter );
1236
MFILEWrite( &iLong, 4, 1, poDS->fp );
1237
iLong = CPL_LSBWORD32( poDS->sInfoHeader.iYPelsPerMeter );
1238
MFILEWrite( &iLong, 4, 1, poDS->fp );
1239
iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrUsed );
1240
MFILEWrite( &iULong, 4, 1, poDS->fp );
1241
iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrImportant );
1242
MFILEWrite( &iULong, 4, 1, poDS->fp );
1244
if ( poDS->sInfoHeader.iClrUsed )
1245
MFILEWrite( poDS->pabyColorTable, 1,
1246
poDS->nColorElems * poDS->sInfoHeader.iClrUsed, poDS->fp );
1248
poDS->nRasterXSize = nXSize;
1249
poDS->nRasterYSize = nYSize;
1250
poDS->eAccess = GA_Update;
1251
poDS->nBands = nBands;
1253
/* -------------------------------------------------------------------- */
1254
/* Create band information objects. */
1255
/* -------------------------------------------------------------------- */
1258
for( iBand = 1; iBand <= poDS->nBands; iBand++ )
1260
poDS->SetBand( iBand, new BMPRasterBand( poDS, iBand ) );
1263
/* -------------------------------------------------------------------- */
1264
/* Do we need a world file? */
1265
/* -------------------------------------------------------------------- */
1266
if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) )
1267
poDS->bGeoTransformValid = TRUE;
1269
return (GDALDataset *) poDS;
1272
/************************************************************************/
1273
/* GDALRegister_BMP() */
1274
/************************************************************************/
1276
void GDALRegister_THUBANBMP()
1279
GDALDriver *poDriver;
1281
if( GDALGetDriverByName( "THUBANBMP" ) == NULL )
1283
poDriver = new GDALDriver();
1285
poDriver->SetDescription( "THUBANBMP" );
1286
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1287
"MS Windows Device Independent Bitmap for Thuban" );
1288
//poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_bmp.html" );
1289
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" );
1290
poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
1291
"<CreationOptionList>"
1292
" <Option name='WORLDFILE' type='boolean' description='Write out world file'/>"
1293
"</CreationOptionList>" );
1295
poDriver->pfnOpen = BMPDataset::Open;
1296
poDriver->pfnCreate = BMPDataset::Create;
1298
GetGDALDriverManager()->RegisterDriver( poDriver );