sábado, 25 de julho de 2015

C++ BMP Util

#pragma hdrstop

#include
#include
#include
#include


/*
 * Classes of BMP files
 */

#define C_WIN   1
#define C_OS2   2

#define BI_RGB        0L
#define BI_RLE8       1L
#define BI_RLE4       2L
#define BI_BITFIELDS  3L

#define MAXCOLORS 256

#ifdef __GNUC__
#define PACKED_SUFF __attribute__ ((packed))
#else
#define PACKED_SUFF
#endif

#ifdef _MSC_VER
#pragma pack( 1 )
#endif

#if defined( __arm ) && !defined( __GNUC__ )
#define PACKED_PREF __packed
#else
#define PACKED_PREF
#endif

#if !defined( _MSC_VER ) && !defined( __GNUC__ ) && !defined( __arm )
// Error: Unknown compiler. Unable to pack structure.
char compileTimeErrorArray[-1];
#endif

PACKED_PREF struct bmp_header_16 {
  char            magic1 PACKED_SUFF; // 'B'
  char            magic2 PACKED_SUFF; // 'M'
  unsigned long   cbSize PACKED_SUFF;   // cbSize,    file size
  unsigned short  res1 PACKED_SUFF;  //
  unsigned short  res2 PACKED_SUFF;  //
  unsigned long   offbits PACKED_SUFF;   // offbits   76 ???
  // 14 bytes

  unsigned long   cbFix PACKED_SUFF;   // cbFix  12 = C_OS2   40 = C_WIN
  unsigned long   xsize PACKED_SUFF;   // xsize;
  unsigned long   ysize PACKED_SUFF;   // ysize;
  unsigned short  cPlanes PACKED_SUFF; // 1
  unsigned short  cBitCount PACKED_SUFF; // 4 = 16 colors, 8=256 colors
  unsigned long  compression PACKED_SUFF;
  unsigned long  sizeimage PACKED_SUFF;
  // 24 bytes

  unsigned long   xpermeter PACKED_SUFF;
  unsigned long   ypermeter PACKED_SUFF;

  unsigned long  clrused PACKED_SUFF;
  unsigned long  clrimportant PACKED_SUFF;
  // 16 bytes

  unsigned long   palette[16] PACKED_SUFF;
};

PACKED_PREF struct bmp_header_256 {
  char            magic1 PACKED_SUFF; // 'B'
  char            magic2 PACKED_SUFF; // 'M'
  unsigned long   cbSize PACKED_SUFF;   // cbSize,    file size
  unsigned short  res1 PACKED_SUFF;  //
  unsigned short  res2 PACKED_SUFF;  //
  unsigned long   offbits PACKED_SUFF;   // offbits   76 ???

  unsigned long   cbFix PACKED_SUFF;   // cbFix  12 = C_OS2   40 = C_WIN
  unsigned long   xsize PACKED_SUFF;   // xsize;
  unsigned long   ysize PACKED_SUFF;   // ysize;
  unsigned short  cPlanes PACKED_SUFF; // 1
  unsigned short  cBitCount PACKED_SUFF; // 4 = 16 colors, 8=256 colors
  unsigned long  compression PACKED_SUFF;
  unsigned long  sizeimage PACKED_SUFF;

  unsigned long   xpermeter PACKED_SUFF;
  unsigned long   ypermeter PACKED_SUFF;

  unsigned long  clrused PACKED_SUFF;
  unsigned long  clrimportant PACKED_SUFF;
 
  unsigned long   palette[256] PACKED_SUFF;
};

PACKED_PREF struct bmp_header {
  char            magic1 PACKED_SUFF; // 'B'
  char            magic2 PACKED_SUFF; // 'M'
  unsigned long   cbSize PACKED_SUFF;   // cbSize,    file size
  unsigned short  res1 PACKED_SUFF;  //
  unsigned short  res2 PACKED_SUFF;  //
  unsigned long   offbits PACKED_SUFF;   // offbits   76 ???

  unsigned long   cbFix PACKED_SUFF;   // cbFix  12 = C_OS2   40 = C_WIN
  unsigned long   xsize PACKED_SUFF;   // xsize;
  unsigned long   ysize PACKED_SUFF;   // ysize;
  unsigned short  cPlanes PACKED_SUFF; // 1
  unsigned short  cBitCount PACKED_SUFF; // 4 = 16 colors, 8=256 colors
  unsigned long  compression PACKED_SUFF;
  unsigned long  sizeimage PACKED_SUFF;

  unsigned long   xpermeter PACKED_SUFF;
  unsigned long   ypermeter PACKED_SUFF;

  unsigned long  clrused PACKED_SUFF;
  unsigned long  clrimportant PACKED_SUFF;
 
  unsigned long   palette[16] PACKED_SUFF;
};

#ifdef _MSC_VER
#pragma pack()
#endif

//--------------------------------------------------------------------

static unsigned char* processBW( unsigned char* nibbles, struct bmp_header_256* head, unsigned char* pixdata, int imageByteWidth )
{
       // need to copy lines in reverse order
       if( head->palette[0] == 0 ) {
             for( int i = 0; i < head->ysize; i++ ) {
                    memcpy( &nibbles[i * imageByteWidth],
                           &pixdata[( head->ysize - i - 1 ) * imageByteWidth], imageByteWidth );
             }
       } else {
             // need to invert
             for( int i = 0; i < head->ysize; i++ ) {
                    unsigned char* from = &pixdata[(head->ysize - i - 1) * imageByteWidth];
                    unsigned char* to = &nibbles[i * imageByteWidth];
                    for( int j = 0; j < imageByteWidth; j++ ) {
                           to[j] = ~(from[j]);
                    }
             }
       }
       return nibbles;
}

static unsigned char* processBI_RLE4( unsigned char* nibbles, struct bmp_header_256* head, unsigned char* pixdata, int imageByteWidth )
{
       int EOB = 0;
       int len;
       int x = 0;
       unsigned char *cur = nibbles + ( ( head->xsize / 2 ) * ( head->ysize - 1 ) );
      
       memset( nibbles, 0xff, ( ( head->xsize*head->ysize ) >> 1 ) + 1 );

       while( !EOB ) {
             if( *pixdata >= 3 ) { // RLE packed
                    unsigned char color;
                    len = *pixdata++;   // Get number of pixels
                    color = *pixdata++; // Get color to paint with
                    x += len;

                    if( len & 1 ) {
                           printf( "Bad packing RLE packing of BMP( len = %d\n", len );
                           free( nibbles );
                           return NULL;
                    }
                    len = len/2;
                    while( len-- ) {
                           *cur++ = color;
                    }
             } else { // ESC sequence
                    pixdata++; // skip zero

                    if( *pixdata >= 3 ) { // unpacked data
                           len = *pixdata++;   // Must be even!!!
                           x += len;
                           if( len & 1 ) {
                                  printf( "Bad packing upacked of BMP(len =%d)\n", len );
                                  free( nibbles );
                                  return NULL;
                           }
                           memcpy( cur, pixdata, len/2);
                           cur += len/2;
                           pixdata += ( len/2 );
                           if( (unsigned long)pixdata & 1 ) {
                                  pixdata++;   // word aligned
                           }
                    } else if(*pixdata == 0) { // EOL
                           pixdata++; // skip EOL
                           cur += ( head->xsize - x )/2 - head->xsize;
                           x = 0;
                    } else if( *pixdata == 1 ) { // EOB
                           pixdata++; // skip EOB
                           EOB = 1;
                    } else {
                           printf( "Bad packing RLE packing of BMP. ESCcode < 4.\n" );
                           free( nibbles );
                           return NULL;
                    }
             }
       }
       return nibbles;
}

static unsigned char* processGrayAndColor( unsigned char* nibbles, struct bmp_header_256* head,
       int bmpsize, unsigned char* pixdata, int imageByteWidth, bool hasGreyPalette )
{
       int niboff = 0;
       int nbyte = 0;

       int isDec = head->ysize > 0;
       int ysize = abs( static_cast<int>( head->ysize ) );

       // for corrupted bitmaps
       if( head->cBitCount == 4 ) {
             int byteWidth = ( head->xsize + 1 ) / 2;
             byteWidth = ( ( byteWidth + 3 ) / 4 ) * 4;
             const int delta = head->offbits + byteWidth * ysize - bmpsize;
             if( delta > 0 ) {
                    ysize -= ( delta + byteWidth - 1 ) / byteWidth;
             }
       }

       memset( nibbles, 0x00, imageByteWidth * ysize );
       for( int y = isDec ? ysize - 1 : 0; y >= 0 && y < ysize; isDec ? y-- : y++ ) {
             niboff = ( imageByteWidth * y ) << ( head->cBitCount == 4 ? 1 : 0 );
      
             for ( int x = 0; x < head->xsize; x++ ) {
                    unsigned char b = 0;
                    switch( head->cBitCount ) {
                           case 4:
                           {
                                  if( ( x%2 ) == 0 ) {
                                        b = pixdata[nbyte] >> 4 ; // Left nibble                                
                                  } else {
                                        b =    pixdata[nbyte] & 0xf ; // Right nibble
                                        nbyte++;
                                 }
                                  unsigned char pval = ( ( head->palette[b] & 0xff ) +
                                        ( ( head->palette[b] & 0xff00 )>>8 ) +
                                        ( ( head->palette[b] & 0xff0000 )>>16 ) )/3;   // to 256 color grayscale
                                  pval >>= 4;
                                  if( ( niboff & 0x1 ) == 0 ) { // odd
                                        nibbles[niboff>>1] |= pval << 4;
                                  } else {
                                        nibbles[niboff>>1] |= pval;                   
                                  }
                                  niboff++;
                                  break;
                           }
                           case 8:
                           {
                                  if( hasGreyPalette ) {
                                        nibbles[niboff++] = pixdata[nbyte++];
                                  } else {
                                        b = pixdata[nbyte];
                                        // Blue, green, red.
                                        nibbles[niboff++] = head->palette[b] & 0xff;
                                        nibbles[niboff++] = ( head->palette[b] & 0xff00 ) >> 8;
                                        nibbles[niboff++] = ( head->palette[b] & 0xff0000 ) >> 16;
                                        nbyte++;
                                  }
                                  break;
                           }
                           case 24:
                           {
                                  for( int i = 0; i < 3; i++ ) {
                                        nibbles[niboff++] = pixdata[nbyte++];
                                  }
                                  break;
                           }
                    } //switch
             }           
            
             while( nbyte % 4 != 0 ) {
                    nbyte++;
             }
       }
       return nibbles;
}

unsigned char* bmpRead( char* bmp, int bmpsize,
             int* xsize, int* ysize, int* bitsPerPix, int* imageByteWidth )
{
       struct bmp_header_256* head;
       unsigned char* pixdata;
       unsigned char* nibbles;

       head = (struct bmp_header_256*) bmp;

       if( head->magic1 != 'B' || head->magic2 != 'M' ) {
             return 0;
       }
      
       pixdata = (unsigned char*)( bmp + head->offbits );
       *xsize = head->xsize;
       *ysize = abs( static_cast<int>( head->ysize ) );
      
       *bitsPerPix = head->cBitCount;

       bool hasGreyPalette = false;
       if( head->cBitCount == 8 ) {
             hasGreyPalette = true;
             for( int i = 0; i < UCHAR_MAX && hasGreyPalette; i++ ) {
                    hasGreyPalette = hasGreyPalette && ((head->palette[i] & 0xffffff) == i * (1 + (1 << 8) + (1 << 16)));
             }
             if( !hasGreyPalette ) { // 256-color convert to full-color
                    *bitsPerPix = 24;
             }
       }
      
       if( *bitsPerPix == 1 ) {
             *imageByteWidth = ( ( head->xsize + 7 ) / 8 + 3 ) / 4 * 4;
       } else {
             *imageByteWidth = ( *xsize * *bitsPerPix + CHAR_BIT - 1 ) / CHAR_BIT;
       }
       nibbles = (unsigned char *)malloc( *ysize * ( *imageByteWidth ) );
       if( nibbles == 0 ) {
             return NULL;
       }     
       if( *bitsPerPix == 1 ) {
             return processBW( nibbles, head, pixdata, *imageByteWidth );
       }

       if( head->compression == BI_RLE4 ) {
             return processBI_RLE4( nibbles, head, pixdata, *imageByteWidth );
       } else {
             return processGrayAndColor( nibbles, head, bmpsize, pixdata, *imageByteWidth, hasGreyPalette );
       }
       return NULL;
}


Postar um comentário