Logo Search packages:      
Sourcecode: passage version File versions  Download package

BMPImageConverter.h

/*
 * 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
 */
00041 class BMPImageConverter : public LittleEndianImageConverter {
      
      public:
            
            // implement the ImageConverter interface
            virtual void formatImage( Image *inImage, 
                  OutputStream *inStream );
                  
            virtual Image *deformatImage( InputStream *inStream );            


      };



00056 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;
      }



00160 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

Generated by  Doxygen 1.6.0   Back to index