251 lines
6 KiB
C++
251 lines
6 KiB
C++
/*
|
|
* Modification History
|
|
*
|
|
* 2001-February-19 Jason Rohrer
|
|
* Created.
|
|
*
|
|
* 2001-February-19 Jason Rohrer
|
|
* Fixed some bugs in the raster formatting code.
|
|
*
|
|
* 2001-May-19 Jason Rohrer
|
|
* Fixed a bug in zero padding for each line.
|
|
*
|
|
* 2001-May-21 Jason Rohrer
|
|
* Fixed another bug in zero padding for each line.
|
|
*
|
|
* 2001-September-22 Jason Rohrer
|
|
* Changed to subclass LittleEndianImageConverter.
|
|
*/
|
|
|
|
|
|
#ifndef BMP_IMAGE_CONVERTER_INCLUDED
|
|
#define BMP_IMAGE_CONVERTER_INCLUDED
|
|
|
|
|
|
#include "LittleEndianImageConverter.h"
|
|
|
|
|
|
/**
|
|
* Windows BMP implementation of the image conversion interface.
|
|
*
|
|
* Note that it only supports 24-bit BMP files (and thus only 3-channel
|
|
* Images).
|
|
*
|
|
* BMP format information taken from:
|
|
* http://www.daubnet.com/formats/BMP.html
|
|
* Some information from this site is incorrect:
|
|
* 24-bit pixels are _not_ stored in 32-bit blocks.
|
|
*
|
|
* @author Jason Rohrer
|
|
*/
|
|
class BMPImageConverter : public LittleEndianImageConverter {
|
|
|
|
public:
|
|
|
|
// implement the ImageConverter interface
|
|
virtual void formatImage( Image *inImage,
|
|
OutputStream *inStream );
|
|
|
|
virtual Image *deformatImage( InputStream *inStream );
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
inline void BMPImageConverter::formatImage( Image *inImage,
|
|
OutputStream *inStream ) {
|
|
|
|
// make sure the image is in the right format
|
|
if( inImage->getNumChannels() != 3 ) {
|
|
printf(
|
|
"Only 3-channel images can be converted to the BMP format.\n" );
|
|
return;
|
|
}
|
|
|
|
long width = inImage->getWidth();
|
|
long height = inImage->getHeight();
|
|
|
|
long numPixels = width * height;
|
|
|
|
// each line should be padded with zeros to
|
|
// end on a 4-byte boundary
|
|
int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4;
|
|
|
|
short bitCount = 24;
|
|
long rasterSize = numPixels * 3;
|
|
// zero padding bytes for each row
|
|
rasterSize += numZeroPaddingBytes * height;
|
|
|
|
// offset past two headers
|
|
long offsetToRaster = 14 + 40;
|
|
|
|
long fileSize = offsetToRaster + rasterSize;
|
|
|
|
long compressionType = 0;
|
|
|
|
|
|
long pixelsPerMeter = 2834;
|
|
|
|
// both are 0 since we have no color map
|
|
long colorsUsed = 0;
|
|
long colorsImportant = 0;
|
|
|
|
// write the header
|
|
unsigned char *signature = new unsigned char[2];
|
|
signature[0] = 'B';
|
|
signature[1] = 'M';
|
|
inStream->write( signature, 2 );
|
|
delete [] signature;
|
|
writeLittleEndianLong( fileSize, inStream );
|
|
writeLittleEndianLong( 0, inStream );
|
|
writeLittleEndianLong( offsetToRaster, inStream );
|
|
|
|
// write the info header
|
|
// header size
|
|
writeLittleEndianLong( 40, inStream );
|
|
writeLittleEndianLong( width, inStream );
|
|
writeLittleEndianLong( height, inStream );
|
|
// numPlanes
|
|
writeLittleEndianShort( 1, inStream );
|
|
writeLittleEndianShort( bitCount, inStream );
|
|
writeLittleEndianLong( compressionType, inStream );
|
|
writeLittleEndianLong( rasterSize, inStream );
|
|
writeLittleEndianLong( pixelsPerMeter, inStream );
|
|
writeLittleEndianLong( pixelsPerMeter, inStream );
|
|
writeLittleEndianLong( colorsUsed, inStream );
|
|
writeLittleEndianLong( colorsImportant, inStream );
|
|
|
|
// no color table...
|
|
|
|
// now write the raster
|
|
|
|
unsigned char *raster = new unsigned char[ rasterSize ];
|
|
double *red = inImage->getChannel( 0 );
|
|
double *green = inImage->getChannel( 1 );
|
|
double *blue = inImage->getChannel( 2 );
|
|
|
|
// pixels are stored bottom-up, left to right
|
|
// (row major order)
|
|
|
|
long rasterIndex = 0;
|
|
for( int y=height-1; y>=0; y-- ) {
|
|
for( int x=0; x<width; x++ ) {
|
|
int imageIndex = y * width + x;
|
|
|
|
raster[rasterIndex] =
|
|
(unsigned char)( 255 * blue[imageIndex] );
|
|
raster[rasterIndex + 1] =
|
|
(unsigned char)( 255 * green[imageIndex] );
|
|
raster[rasterIndex + 2] =
|
|
(unsigned char)( 255 * red[imageIndex] );
|
|
|
|
rasterIndex += 3;
|
|
}
|
|
|
|
for( int p=0; p<numZeroPaddingBytes; p++ ) {
|
|
// insert at the end of this line
|
|
raster[ rasterIndex ] = 0;
|
|
rasterIndex++;
|
|
}
|
|
}
|
|
|
|
inStream->write( raster, rasterSize );
|
|
|
|
delete [] raster;
|
|
}
|
|
|
|
|
|
|
|
inline Image *BMPImageConverter::deformatImage( InputStream *inStream ) {
|
|
// temp buffer used to skip data in the stream
|
|
unsigned char *temp = new unsigned char[ 100 ];
|
|
|
|
// skip signature
|
|
inStream->read( temp, 2 );
|
|
|
|
long fileSize = readLittleEndianLong( inStream );
|
|
|
|
// skip unused
|
|
inStream->read( temp, 4 );
|
|
|
|
long rasterOffset = readLittleEndianLong( inStream );
|
|
long rasterSize = fileSize - rasterOffset;
|
|
|
|
// skip size of header
|
|
inStream->read( temp, 4 );
|
|
|
|
long width = readLittleEndianLong( inStream );
|
|
long height = readLittleEndianLong( inStream );
|
|
|
|
// skip planes
|
|
inStream->read( temp, 2 );
|
|
|
|
short bitCount = readLittleEndianShort( inStream );
|
|
|
|
char failing = false;
|
|
if( bitCount != 24 ) {
|
|
printf( "Only 24-bit BMP file formats supported.\n" );
|
|
failing = true;
|
|
}
|
|
long compression = readLittleEndianLong( inStream );
|
|
if( compression != 0 ) {
|
|
printf( "Only uncompressed BMP file formats supported.\n" );
|
|
failing = true;
|
|
}
|
|
|
|
// skip imageSize, resolution, and color usage information
|
|
inStream->read( temp, 20 );
|
|
|
|
// now we're at the raster.
|
|
|
|
// each line should be padded with zeros to
|
|
// end on a 4-byte boundary
|
|
int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4;
|
|
|
|
unsigned char *raster = new unsigned char[ rasterSize ];
|
|
inStream->read( raster, rasterSize );
|
|
Image *returnImage;
|
|
if( failing ) {
|
|
return NULL;
|
|
}
|
|
else {
|
|
|
|
returnImage = new Image( width, height, 3 );
|
|
|
|
double *red = returnImage->getChannel( 0 );
|
|
double *green = returnImage->getChannel( 1 );
|
|
double *blue = returnImage->getChannel( 2 );
|
|
|
|
// pixels are stored bottom-up, left to right
|
|
// (row major order)
|
|
|
|
long rasterIndex = 0;
|
|
for( int y=height-1; y>=0; y-- ) {
|
|
for( int x=0; x<width; x++ ) {
|
|
int imageIndex = y * width + x;
|
|
|
|
blue[imageIndex] =
|
|
(double)( raster[rasterIndex] ) / 255.0;
|
|
green[imageIndex] =
|
|
(double)( raster[rasterIndex + 1] ) / 255.0;
|
|
red[imageIndex] =
|
|
(double)( raster[rasterIndex + 2] ) / 255.0;
|
|
|
|
rasterIndex += 3;
|
|
}
|
|
|
|
// skip the zero padding bytes at the end of this line
|
|
rasterIndex += numZeroPaddingBytes;
|
|
}
|
|
}
|
|
|
|
delete [] raster;
|
|
delete [] temp;
|
|
|
|
return returnImage;
|
|
}
|
|
|
|
|
|
|
|
#endif
|