/*************************************************
*                                                *
*  EasyBMP Cross-Platform Windows Bitmap Library * 
*                                                *
*  Author: Paul Macklin                          *
*   email: macklin01@users.sourceforge.net       *
* support: http://easybmp.sourceforge.net        *
*                                                *
*          file: EasyBMP.cpp                     * 
*    date added: 03-31-2006                      *
* date modified: 12-01-2006                      *
*       version: 1.06                            *
*                                                *
*   License: BSD (revised/modified)              *
* Copyright: 2005-6 by the EasyBMP Project       * 
*                                                *
* description: Actual source file                *
*                                                *
*************************************************/

#include "EasyBMP.h"

/* These functions are defined in EasyBMP.h */

bool EasyBMPwarnings = true;

void SetEasyBMPwarningsOff( void )
{ EasyBMPwarnings = false; }
void SetEasyBMPwarningsOn( void )
{ EasyBMPwarnings = true; }
bool GetEasyBMPwarningState( void )
{ return EasyBMPwarnings; }

/* These functions are defined in EasyBMP_DataStructures.h */

int IntPow( int base, int exponent )
{
 int i;
 int output = 1;
 for( i=0 ; i < exponent ; i++ )
 { output *= base; }
 return output;
}

BMFH::BMFH()
{
 bfType = 19778;
 bfReserved1 = 0;
 bfReserved2 = 0;
}

void BMFH::SwitchEndianess( void )
{
 bfType = FlipWORD( bfType );
 bfSize = FlipDWORD( bfSize );
 bfReserved1 = FlipWORD( bfReserved1 );
 bfReserved2 = FlipWORD( bfReserved2 );
 bfOffBits = FlipDWORD( bfOffBits );
 return;
}

BMIH::BMIH()
{
 biPlanes = 1;
 biCompression = 0;
 biXPelsPerMeter = DefaultXPelsPerMeter;  
 biYPelsPerMeter = DefaultYPelsPerMeter;
 biClrUsed = 0;
 biClrImportant = 0;
}

void BMIH::SwitchEndianess( void )
{
 biSize = FlipDWORD( biSize );
 biWidth = FlipDWORD( biWidth );
 biHeight = FlipDWORD( biHeight );
 biPlanes = FlipWORD( biPlanes );
 biBitCount = FlipWORD( biBitCount );
 biCompression = FlipDWORD( biCompression );
 biSizeImage = FlipDWORD( biSizeImage );
 biXPelsPerMeter = FlipDWORD( biXPelsPerMeter );
 biYPelsPerMeter = FlipDWORD( biYPelsPerMeter );
 biClrUsed = FlipDWORD( biClrUsed );
 biClrImportant = FlipDWORD( biClrImportant );
 return;
}

void BMIH::display( void )
{
 using namespace std;
 cout << "biSize: " << (int) biSize << endl
      << "biWidth: " << (int) biWidth << endl
      << "biHeight: " << (int) biHeight << endl
      << "biPlanes: " << (int) biPlanes << endl
      << "biBitCount: " << (int) biBitCount << endl
      << "biCompression: " << (int) biCompression << endl
      << "biSizeImage: " << (int) biSizeImage << endl
      << "biXPelsPerMeter: " << (int) biXPelsPerMeter << endl
      << "biYPelsPerMeter: " << (int) biYPelsPerMeter << endl
      << "biClrUsed: " << (int) biClrUsed << endl
      << "biClrImportant: " << (int) biClrImportant << endl << endl;
}

void BMFH::display( void )
{
 using namespace std;
 cout << "bfType: " << (int) bfType << endl
      << "bfSize: " << (int) bfSize << endl
      << "bfReserved1: " << (int) bfReserved1 << endl
      << "bfReserved2: " << (int) bfReserved2 << endl
      << "bfOffBits: " << (int) bfOffBits << endl << endl;
}

/* These functions are defined in EasyBMP_BMP.h */

RGBApixel BMP::GetPixel( int i, int j ) const
{
 using namespace std;
 bool Warn = false;
 if( i >= Width )
 { i = Width-1; Warn = true; }
 if( i < 0 )
 { i = 0; Warn = true; }
 if( j >= Height )
 { j = Height-1; Warn = true; }
 if( j < 0 )
 { j = 0; Warn = true; }
 if( Warn && EasyBMPwarnings )
 {
  cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
       << "                 Truncating request to fit in the range [0,"
       << Width-1 << "] x [0," << Height-1 << "]." << endl;
 }	
 return Pixels[i][j];
}

bool BMP::SetPixel( int i, int j, RGBApixel NewPixel )
{
 Pixels[i][j] = NewPixel;
 return true;
}


bool BMP::SetColor( int ColorNumber , RGBApixel NewColor )
{
 using namespace std;
 if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Attempted to change color table for a BMP object" << endl
        << "                 that lacks a color table. Ignoring request." << endl;
  }
  return false;
 }
 if( !Colors )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Attempted to set a color, but the color table" << endl
        << "                 is not defined. Ignoring request." << endl; 
  }
  return false; 
 }
 if( ColorNumber >= TellNumberOfColors() )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Requested color number " 
        << ColorNumber << " is outside the allowed" << endl
        << "                 range [0," << TellNumberOfColors()-1 
	    << "]. Ignoring request to set this color." << endl;
  }
  return false;
 }
 Colors[ColorNumber] = NewColor;
 return true;
}

// RGBApixel BMP::GetColor( int ColorNumber ) const
RGBApixel BMP::GetColor( int ColorNumber )
{ 
 RGBApixel Output;
 Output.Red   = 255;
 Output.Green = 255;
 Output.Blue  = 255;
 Output.Alpha = 0;
 
 using namespace std;
 if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Attempted to access color table for a BMP object" << endl
        << "                 that lacks a color table. Ignoring request." << endl;
  }
  return Output;
 }
 if( !Colors )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Requested a color, but the color table" << endl
        << "                 is not defined. Ignoring request." << endl;
  }
  return Output; 
 }
 if( ColorNumber >= TellNumberOfColors() )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Requested color number " 
        << ColorNumber << " is outside the allowed" << endl
        << "                 range [0," << TellNumberOfColors()-1 
	    << "]. Ignoring request to get this color." << endl;
  }
  return Output;
 }
 Output = Colors[ColorNumber];
 return Output;
}

BMP::BMP()
{
 Width = 1;
 Height = 1;
 BitDepth = 24;
 Pixels = new RGBApixel* [Width];
 Pixels[0] = new RGBApixel [Height];
 Colors = NULL;
 
 XPelsPerMeter = 0;
 YPelsPerMeter = 0;
 
 MetaData1 = NULL;
 SizeOfMetaData1 = 0;
 MetaData2 = NULL;
 SizeOfMetaData2 = 0;
}

// BMP::BMP( const BMP& Input )
BMP::BMP( BMP& Input )
{
 // first, make the image empty.

 Width = 1;
 Height = 1;
 BitDepth = 24;
 Pixels = new RGBApixel* [Width];
 Pixels[0] = new RGBApixel [Height];
 Colors = NULL; 
 XPelsPerMeter = 0;
 YPelsPerMeter = 0;
 
 MetaData1 = NULL;
 SizeOfMetaData1 = 0;
 MetaData2 = NULL;
 SizeOfMetaData2 = 0;

 // now, set the correct bit depth
 
 SetBitDepth( Input.TellBitDepth() );
 
 // set the correct pixel size 
 
 SetSize( Input.TellWidth() , Input.TellHeight() );

 // set the DPI information from Input
 
 SetDPI( Input.TellHorizontalDPI() , Input.TellVerticalDPI() );
 
 // if there is a color table, get all the colors

 if( BitDepth == 1 || BitDepth == 4 ||  
     BitDepth == 8 )
 {
  for( int k=0 ; k < TellNumberOfColors() ; k++ )
  {
   SetColor( k, Input.GetColor( k )); 
  }
 }
 
 // get all the pixels 
 
 for( int j=0; j < Height ; j++ )
 {
  for( int i=0; i < Width ; i++ )
  {
   Pixels[i][j] = *Input(i,j);
//   Pixels[i][j] = Input.GetPixel(i,j); // *Input(i,j);
  }
 }
}

BMP::~BMP()
{
 int i;
 for(i=0;i<Width;i++)
 { delete [] Pixels[i]; }
 delete [] Pixels;
 if( Colors )
 { delete [] Colors; }
 
 if( MetaData1 )
 { delete [] MetaData1; }
 if( MetaData2 )
 { delete [] MetaData2; }
} 

RGBApixel* BMP::operator()(int i, int j)
{
 using namespace std;
 bool Warn = false;
 if( i >= Width )
 { i = Width-1; Warn = true; }
 if( i < 0 )
 { i = 0; Warn = true; }
 if( j >= Height )
 { j = Height-1; Warn = true; }
 if( j < 0 )
 { j = 0; Warn = true; }
 if( Warn && EasyBMPwarnings )
 {
  cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
       << "                 Truncating request to fit in the range [0,"
       << Width-1 << "] x [0," << Height-1 << "]." << endl;
 }	
 return &(Pixels[i][j]);
}

// int BMP::TellBitDepth( void ) const
int BMP::TellBitDepth( void )
{ return BitDepth; }

// int BMP::TellHeight( void ) const
int BMP::TellHeight( void )
{ return Height; }

// int BMP::TellWidth( void ) const
int BMP::TellWidth( void )
{ return Width; }

// int BMP::TellNumberOfColors( void ) const
int BMP::TellNumberOfColors( void )
{
 int output = IntPow( 2, BitDepth );
 if( BitDepth == 32 )
 { output = IntPow( 2, 24 ); }
 return output;
}

bool BMP::SetBitDepth( int NewDepth )
{
 using namespace std;
 if( NewDepth != 1 && NewDepth != 4 && 
     NewDepth != 8 && NewDepth != 16 && 
     NewDepth != 24 && NewDepth != 32 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: User attempted to set unsupported bit depth " 
        << NewDepth << "." << endl
        << "                 Bit depth remains unchanged at " 
	    << BitDepth << "." << endl;
  }
  return false;
 }
 
 BitDepth = NewDepth;
 if( Colors )
 { delete [] Colors; }
 int NumberOfColors = IntPow( 2, BitDepth );
 if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
 { Colors = new RGBApixel [NumberOfColors]; }
 else
 { Colors = NULL; } 
 if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
 { CreateStandardColorTable(); }
 
 return true;
}

bool BMP::SetSize(int NewWidth , int NewHeight )
{
 using namespace std;
 if( NewWidth <= 0 || NewHeight <= 0 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: User attempted to set a non-positive width or height." << endl
        << "                 Size remains unchanged at " 
        << Width << " x " << Height << "." << endl;
  }
  return false;
 }

 int i,j; 

 for(i=0;i<Width;i++)
 { delete [] Pixels[i]; }
 delete [] Pixels;

 Width = NewWidth;
 Height = NewHeight;
 Pixels = new RGBApixel* [ Width ]; 
 
 for(i=0; i<Width; i++)
 { Pixels[i] = new RGBApixel [ Height ]; }
 
 for( i=0 ; i < Width ; i++)
 {
  for( j=0 ; j < Height ; j++ )
  {
   Pixels[i][j].Red = 255; 
   Pixels[i][j].Green = 255; 
   Pixels[i][j].Blue = 255; 
   Pixels[i][j].Alpha = 0;    
  }
 }

 return true; 
}

bool BMP::WriteToFile( const char* FileName )
{
 using namespace std;
 if( !EasyBMPcheckDataSize() )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Data types are wrong size!" << endl
        << "               You may need to mess with EasyBMP_DataTypes.h" << endl
	    << "               to fix these errors, and then recompile." << endl
	    << "               All 32-bit and 64-bit machines should be" << endl
	    << "               supported, however." << endl << endl;
  }
  return false; 
 }
 
 FILE* fp = fopen( FileName, "wb" );
 if( fp == NULL )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Cannot open file " 
        << FileName << " for output." << endl;
  }
  fclose( fp );
  return false;
 }
  
 // some preliminaries
 
 double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
 double dBytesPerRow = dBytesPerPixel * (Width+0.0);
 dBytesPerRow = ceil(dBytesPerRow);
  
 int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
 if( BytePaddingPerRow == 4 )
 { BytePaddingPerRow = 0; } 
 
 double dActualBytesPerRow = dBytesPerRow + BytePaddingPerRow;
 
 double dTotalPixelBytes = Height * dActualBytesPerRow;
 
 double dPaletteSize = 0;
 if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
 { dPaletteSize = IntPow(2,BitDepth)*4.0; }

 // leave some room for 16-bit masks 
 if( BitDepth == 16 )
 { dPaletteSize = 3*4; }
 
 double dTotalFileSize = 14 + 40 + dPaletteSize + dTotalPixelBytes;
 
 // write the file header 
 
 BMFH bmfh;
 bmfh.bfSize = (ebmpDWORD) dTotalFileSize; 
 bmfh.bfReserved1 = 0; 
 bmfh.bfReserved2 = 0; 
 bmfh.bfOffBits = (ebmpDWORD) (14+40+dPaletteSize);  
 
 if( IsBigEndian() )
 { bmfh.SwitchEndianess(); }
 
 fwrite( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp );
 fwrite( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp );
 fwrite( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp );
 fwrite( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp );
 
 // write the info header 
 
 BMIH bmih;
 bmih.biSize = 40;
 bmih.biWidth = Width;
 bmih.biHeight = Height;
 bmih.biPlanes = 1;
 bmih.biBitCount = BitDepth;
 bmih.biCompression = 0;
 bmih.biSizeImage = (ebmpDWORD) dTotalPixelBytes;
 if( XPelsPerMeter )
 { bmih.biXPelsPerMeter = XPelsPerMeter; }
 else
 { bmih.biXPelsPerMeter = DefaultXPelsPerMeter; }
 if( YPelsPerMeter )
 { bmih.biYPelsPerMeter = YPelsPerMeter; }
 else
 { bmih.biYPelsPerMeter = DefaultYPelsPerMeter; }

 bmih.biClrUsed = 0;
 bmih.biClrImportant = 0;

 // indicates that we'll be using bit fields for 16-bit files
 if( BitDepth == 16 )
 { bmih.biCompression = 3; }
 
 if( IsBigEndian() )
 { bmih.SwitchEndianess(); }
 
 fwrite( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
 fwrite( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
 fwrite( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
 fwrite( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);
 
 // write the palette 
 if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
 {
  int NumberOfColors = IntPow(2,BitDepth);
  
  // if there is no palette, create one 
  if( !Colors )
  {
   if( !Colors )
   { Colors = new RGBApixel [NumberOfColors]; }
   CreateStandardColorTable(); 
  }
   
  int n;
  for( n=0 ; n < NumberOfColors ; n++ )
  { fwrite( (char*) &(Colors[n]) , 4 , 1 , fp ); }
 }
 
 // write the pixels 
 int i,j;
 if( BitDepth != 16 )
 {  
  ebmpBYTE* Buffer;
  int BufferSize = (int) ( (Width*BitDepth)/8.0 );
  while( 8*BufferSize < Width*BitDepth )
  { BufferSize++; }
  while( BufferSize % 4 )
  { BufferSize++; }
  
  Buffer = new ebmpBYTE [BufferSize];
  for( j=0 ; j < BufferSize; j++ )
  { Buffer[j] = 0; }
    
  j=Height-1;
  
  while( j > -1 )
  {
   bool Success = false;
   if( BitDepth == 32 )
   { Success = Write32bitRow( Buffer, BufferSize, j ); }
   if( BitDepth == 24 )
   { Success = Write24bitRow( Buffer, BufferSize, j ); }
   if( BitDepth == 8  )
   { Success = Write8bitRow( Buffer, BufferSize, j ); }
   if( BitDepth == 4  )
   { Success = Write4bitRow( Buffer, BufferSize, j ); }
   if( BitDepth == 1  )
   { Success = Write1bitRow( Buffer, BufferSize, j ); }
   if( Success )
   {
    int BytesWritten = (int) fwrite( (char*) Buffer, 1, BufferSize, fp );
    if( BytesWritten != BufferSize )
    { Success = false; }
   }
   if( !Success )
   {
    if( EasyBMPwarnings )
    {
     cout << "EasyBMP Error: Could not write proper amount of data." << endl;
	}
    j = -1; 
   }
   j--; 
  }
  
  delete [] Buffer;
 }
 
 if( BitDepth == 16 )
 {
  // write the bit masks

  ebmpWORD BlueMask = 31;    // bits 12-16
  ebmpWORD GreenMask = 2016; // bits 6-11
  ebmpWORD RedMask = 63488;  // bits 1-5
  ebmpWORD ZeroWORD;
  
  if( IsBigEndian() )
  { RedMask = FlipWORD( RedMask ); }
  fwrite( (char*) &RedMask , 2 , 1 , fp );
  fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

  if( IsBigEndian() )
  { GreenMask = FlipWORD( GreenMask ); }
  fwrite( (char*) &GreenMask , 2 , 1 , fp );
  fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

  if( IsBigEndian() )
  { BlueMask = FlipWORD( BlueMask ); }
  fwrite( (char*) &BlueMask , 2 , 1 , fp );
  fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

  int DataBytes = Width*2;
  int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;
  
  // write the actual pixels
  
  for( j=Height-1 ; j >= 0 ; j-- )
  {
   // write all row pixel data
   i=0;
   int WriteNumber = 0;
   while( WriteNumber < DataBytes )
   {
    ebmpWORD TempWORD;
	
	ebmpWORD RedWORD = (ebmpWORD) ((Pixels[i][j]).Red / 8);
	ebmpWORD GreenWORD = (ebmpWORD) ((Pixels[i][j]).Green / 4);
	ebmpWORD BlueWORD = (ebmpWORD) ((Pixels[i][j]).Blue / 8);
	
    TempWORD = (RedWORD<<11) + (GreenWORD<<5) + BlueWORD;
	if( IsBigEndian() )
	{ TempWORD = FlipWORD( TempWORD ); }
	
    fwrite( (char*) &TempWORD , 2, 1, fp);
    WriteNumber += 2;
	i++;
   }
   // write any necessary row padding
   WriteNumber = 0;
   while( WriteNumber < PaddingBytes )
   {
    ebmpBYTE TempBYTE;
    fwrite( (char*) &TempBYTE , 1, 1, fp);
    WriteNumber++;
   }
  }
  
 }

 fclose(fp);
 return true;
}

bool BMP::ReadFromFile( const char* FileName )
{ 
 using namespace std;
 if( !EasyBMPcheckDataSize() )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Data types are wrong size!" << endl
        << "               You may need to mess with EasyBMP_DataTypes.h" << endl
	    << "               to fix these errors, and then recompile." << endl
	    << "               All 32-bit and 64-bit machines should be" << endl
	    << "               supported, however." << endl << endl;
  }
  return false; 
 }

 FILE* fp = fopen( FileName, "rb" );
 if( fp == NULL )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Cannot open file " 
        << FileName << " for input." << endl;
  }
  SetBitDepth(1);
  SetSize(1,1);
  return false;
 }
 
 // read the file header 
 
 BMFH bmfh;
 bool NotCorrupted = true;
 
 NotCorrupted &= SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD), 1, fp);
 
 bool IsBitmap = false;
 
 if( IsBigEndian() && bmfh.bfType == 16973 )
 { IsBitmap = true; }
 if( !IsBigEndian() && bmfh.bfType == 19778 )
 { IsBitmap = true; }
 
 if( !IsBitmap ) 
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName 
        << " is not a Windows BMP file!" << endl; 
  }
  fclose( fp ); 
  return false;
 }

 NotCorrupted &= SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1, fp); 
 NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1, fp);
 NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1, fp);
 NotCorrupted &= SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp);
 
 if( IsBigEndian() ) 
 { bmfh.SwitchEndianess(); }
  
 // read the info header

 BMIH bmih; 
 
 NotCorrupted &= SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp); 
 NotCorrupted &= SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1, fp); 
 NotCorrupted &= SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1, fp);

 NotCorrupted &= SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
 NotCorrupted &= SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);
 
 if( IsBigEndian() ) 
 { bmih.SwitchEndianess(); }

 // a safety catch: if any of the header information didn't read properly, abort
 // future idea: check to see if at least most is self-consistent
  
 if( !NotCorrupted )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName 
        << " is obviously corrupted." << endl;
  }
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false;
 } 
 
 XPelsPerMeter = bmih.biXPelsPerMeter;
 YPelsPerMeter = bmih.biYPelsPerMeter;
 
 // if bmih.biCompression 1 or 2, then the file is RLE compressed
 
 if( bmih.biCompression == 1 || bmih.biCompression == 2 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName << " is (RLE) compressed." << endl
        << "               EasyBMP does not support compression." << endl;
  }
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false; 
 }
 
 // if bmih.biCompression > 3, then something strange is going on 
 // it's probably an OS2 bitmap file.
 
 if( bmih.biCompression > 3 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName << " is in an unsupported format." 
        << endl
        << "               (bmih.biCompression = " 
	    << bmih.biCompression << ")" << endl
	    << "               The file is probably an old OS2 bitmap or corrupted." 
	    << endl;
  }		
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false; 
 }
 
 if( bmih.biCompression == 3 && bmih.biBitCount != 16 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName 
        << " uses bit fields and is not a" << endl
        << "               16-bit file. This is not supported." << endl;
  }
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false; 
 }

 // set the bit depth
 
 int TempBitDepth = (int) bmih.biBitCount;
 if(    TempBitDepth != 1  && TempBitDepth != 4 
     && TempBitDepth != 8  && TempBitDepth != 16
     && TempBitDepth != 24 && TempBitDepth != 32 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName << " has unrecognized bit depth." << endl;
  }
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false;
 }
 SetBitDepth( (int) bmih.biBitCount ); 
 
 // set the size

 if( (int) bmih.biWidth <= 0 || (int) bmih.biHeight <= 0 ) 
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: " << FileName 
        << " has a non-positive width or height." << endl;
  }
  SetSize(1,1);
  SetBitDepth(1);
  fclose(fp);
  return false;
 } 
 SetSize( (int) bmih.biWidth , (int) bmih.biHeight );
  
 // some preliminaries
 
 double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
 double dBytesPerRow = dBytesPerPixel * (Width+0.0);
 dBytesPerRow = ceil(dBytesPerRow);
  
 int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
 if( BytePaddingPerRow == 4 )
 { BytePaddingPerRow = 0; }  
 
 // if < 16 bits, read the palette
 
 if( BitDepth < 16 )
 {
  // determine the number of colors specified in the 
  // color table
  
  int NumberOfColorsToRead = ((int) bmfh.bfOffBits - 54 )/4;  
  if( NumberOfColorsToRead > IntPow(2,BitDepth) )
  { NumberOfColorsToRead = IntPow(2,BitDepth); }
 
  if( NumberOfColorsToRead < TellNumberOfColors() )
  {
   if( EasyBMPwarnings )
   {
    cout << "EasyBMP Warning: file " << FileName << " has an underspecified" << endl
         << "                 color table. The table will be padded with extra" << endl
	 	 << "                 white (255,255,255,0) entries." << endl;
   }
  }
 
  int n;
  for( n=0; n < NumberOfColorsToRead ; n++ )
  {
   SafeFread( (char*) &(Colors[n]) , 4 , 1 , fp);     
  }
  for( n=NumberOfColorsToRead ; n < TellNumberOfColors() ; n++ )
  {
   RGBApixel WHITE; 
   WHITE.Red = 255;
   WHITE.Green = 255;
   WHITE.Blue = 255;
   WHITE.Alpha = 0;
   SetColor( n , WHITE );
  }
  
  
 }
 
 // skip blank data if bfOffBits so indicates
 
 int BytesToSkip = bmfh.bfOffBits - 54;;
 if( BitDepth < 16 )
 { BytesToSkip -= 4*IntPow(2,BitDepth); }
 if( BitDepth == 16 && bmih.biCompression == 3 )
 { BytesToSkip -= 3*4; }
 if( BytesToSkip < 0 )
 { BytesToSkip = 0; }
 if( BytesToSkip > 0 && BitDepth != 16 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Extra meta data detected in file " << FileName << endl
        << "                 Data will be skipped." << endl;
  }
  ebmpBYTE* TempSkipBYTE;
  TempSkipBYTE = new ebmpBYTE [BytesToSkip];
  SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);   
  delete [] TempSkipBYTE;
 } 
  
 // This code reads 1, 4, 8, 24, and 32-bpp files 
 // with a more-efficient buffered technique.

 int i,j;
 if( BitDepth != 16 )
 {
  int BufferSize = (int) ( (Width*BitDepth) / 8.0 );
  while( 8*BufferSize < Width*BitDepth )
  { BufferSize++; }
  while( BufferSize % 4 )
  { BufferSize++; }
  ebmpBYTE* Buffer;
  Buffer = new ebmpBYTE [BufferSize];
  j= Height-1;
  while( j > -1 )
  {
   int BytesRead = (int) fread( (char*) Buffer, 1, BufferSize, fp );
   if( BytesRead < BufferSize )
   {
    j = -1; 
    if( EasyBMPwarnings )
    {
     cout << "EasyBMP Error: Could not read proper amount of data." << endl;
	}
   }
   else
   {
    bool Success = false;
    if( BitDepth == 1  )
	{ Success = Read1bitRow(  Buffer, BufferSize, j ); }
    if( BitDepth == 4  )
	{ Success = Read4bitRow(  Buffer, BufferSize, j ); }
    if( BitDepth == 8  )
	{ Success = Read8bitRow(  Buffer, BufferSize, j ); }
    if( BitDepth == 24 )
	{ Success = Read24bitRow( Buffer, BufferSize, j ); }
	if( BitDepth == 32 )
	{ Success = Read32bitRow( Buffer, BufferSize, j ); }
    if( !Success )
    {
     if( EasyBMPwarnings )
     {
      cout << "EasyBMP Error: Could not read enough pixel data!" << endl;
	 }
	 j = -1;
    }
   }   
   j--;
  }
  delete [] Buffer; 
 }

 if( BitDepth == 16 )
 {
  int DataBytes = Width*2;
  int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;

  // set the default mask
  
  ebmpWORD BlueMask = 31; // bits 12-16
  ebmpWORD GreenMask = 992; // bits 7-11
  ebmpWORD RedMask = 31744; // bits 2-6

  // read the bit fields, if necessary, to 
  // override the default 5-5-5 mask
  
  if( bmih.biCompression != 0 )
  {
   // read the three bit masks

   ebmpWORD TempMaskWORD;
//   ebmpWORD ZeroWORD;
  
   SafeFread( (char*) &RedMask , 2 , 1 , fp );
   if( IsBigEndian() )
   { RedMask = FlipWORD(RedMask); }
   SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
  
   SafeFread( (char*) &GreenMask , 2 , 1 , fp );
   if( IsBigEndian() )
   { GreenMask = FlipWORD(GreenMask); }
   SafeFread( (char*) &TempMaskWORD , 2, 1, fp );

   SafeFread( (char*) &BlueMask , 2 , 1 , fp );
   if( IsBigEndian() )
   { BlueMask = FlipWORD(BlueMask); }
   SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
  }
  
  // read and skip any meta data

  if( BytesToSkip > 0 )
  {
   if( EasyBMPwarnings )
   {
    cout << "EasyBMP Warning: Extra meta data detected in file " 
         << FileName << endl
         << "                 Data will be skipped." << endl;
   }
   ebmpBYTE* TempSkipBYTE;
   TempSkipBYTE = new ebmpBYTE [BytesToSkip];
   SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);
   delete [] TempSkipBYTE;   
  } 
  
  // determine the red, green and blue shifts
  
  int GreenShift = 0; 
  ebmpWORD TempShiftWORD = GreenMask;
  while( TempShiftWORD > 31 )
  { TempShiftWORD = TempShiftWORD>>1; GreenShift++; }  
  int BlueShift = 0;
  TempShiftWORD = BlueMask;
  while( TempShiftWORD > 31 )
  { TempShiftWORD = TempShiftWORD>>1; BlueShift++; }  
  int RedShift = 0;  
  TempShiftWORD = RedMask;
  while( TempShiftWORD > 31 )
  { TempShiftWORD = TempShiftWORD>>1; RedShift++; }  
  
  // read the actual pixels
  
  for( j=Height-1 ; j >= 0 ; j-- )
  {
   i=0;
   int ReadNumber = 0;
   while( ReadNumber < DataBytes )
   {
	ebmpWORD TempWORD;
	SafeFread( (char*) &TempWORD , 2 , 1 , fp );
	if( IsBigEndian() )
	{ TempWORD = FlipWORD(TempWORD); }
    ReadNumber += 2;
  
    ebmpWORD Red = RedMask & TempWORD;
    ebmpWORD Green = GreenMask & TempWORD;
    ebmpWORD Blue = BlueMask & TempWORD;
				
	ebmpBYTE BlueBYTE = (ebmpBYTE) 8*(Blue>>BlueShift);
    ebmpBYTE GreenBYTE = (ebmpBYTE) 8*(Green>>GreenShift);
    ebmpBYTE RedBYTE = (ebmpBYTE) 8*(Red>>RedShift);
		
	(Pixels[i][j]).Red = RedBYTE;
	(Pixels[i][j]).Green = GreenBYTE;
	(Pixels[i][j]).Blue = BlueBYTE;
	
	i++;
   }
   ReadNumber = 0;
   while( ReadNumber < PaddingBytes )
   {
    ebmpBYTE TempBYTE;
    SafeFread( (char*) &TempBYTE , 1, 1, fp);
    ReadNumber++;
   }
  }

 }
 
 fclose(fp);
 return true;
}

bool BMP::CreateStandardColorTable( void )
{
 using namespace std;
 if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
        << "                 depth that does not require a color table." << endl
    	<< "                 Ignoring request." << endl;
  }
  return false;
 }

 if( BitDepth == 1 )
 {
  int i;
  for( i=0 ; i < 2 ; i++ )
  {
   Colors[i].Red = i*255;
   Colors[i].Green = i*255;
   Colors[i].Blue = i*255;
   Colors[i].Alpha = 0;
  } 
  return true;
 } 

 if( BitDepth == 4 )
 {
  int i = 0;
  int j,k,ell;
  
  // simplify the code for the first 8 colors
  for( ell=0 ; ell < 2 ; ell++ )
  {
   for( k=0 ; k < 2 ; k++ )
   {
    for( j=0 ; j < 2 ; j++ )
    {
     Colors[i].Red = j*128; 
	 Colors[i].Green = k*128;
 	 Colors[i].Blue = ell*128;
   	 i++;
    }
   }
  }
 
  // simplify the code for the last 8 colors
  for( ell=0 ; ell < 2 ; ell++ )
  {
   for( k=0 ; k < 2 ; k++ )
   {
    for( j=0 ; j < 2 ; j++ )
    {
     Colors[i].Red = j*255;
     Colors[i].Green = k*255; 
     Colors[i].Blue = ell*255;
	 i++;
    }
   }
  }
 
  // overwrite the duplicate color
  i=8; 
  Colors[i].Red = 192;
  Colors[i].Green = 192;
  Colors[i].Blue = 192;
   
  for( i=0 ; i < 16 ; i++ )
  { Colors[i].Alpha = 0; }
  return true;
 }

 if( BitDepth == 8 )
 {
  int i=0;
  int j,k,ell;
    
  // do an easy loop, which works for all but colors 
  // 0 to 9 and 246 to 255
  for( ell=0 ; ell < 4 ; ell++ ) 
  {
   for( k=0 ; k < 8 ; k++ )
   {
    for( j=0; j < 8 ; j++ )
    {
     Colors[i].Red = j*32; 
     Colors[i].Green = k*32;
     Colors[i].Blue = ell*64;
     Colors[i].Alpha = 0;
     i++;
    }
   }
  } 
   
  // now redo the first 8 colors  
  i=0;
  for( ell=0 ; ell < 2 ; ell++ ) 
  {
   for( k=0 ; k < 2 ; k++ )
   {
    for( j=0; j < 2 ; j++ )
    {
     Colors[i].Red = j*128;
     Colors[i].Green = k*128;
     Colors[i].Blue = ell*128;
     i++;
    }
   }
  } 
 
  // overwrite colors 7, 8, 9
  i=7;
  Colors[i].Red = 192;
  Colors[i].Green = 192;
  Colors[i].Blue = 192;
  i++; // 8
  Colors[i].Red = 192;
  Colors[i].Green = 220;
  Colors[i].Blue = 192;
  i++; // 9
  Colors[i].Red = 166;
  Colors[i].Green = 202;
  Colors[i].Blue = 240;
   
  // overwrite colors 246 to 255 
  i=246;
  Colors[i].Red = 255;
  Colors[i].Green = 251;
  Colors[i].Blue = 240;
  i++; // 247
  Colors[i].Red = 160;
  Colors[i].Green = 160;
  Colors[i].Blue = 164;
  i++; // 248
  Colors[i].Red = 128;
  Colors[i].Green = 128;
  Colors[i].Blue = 128;
  i++; // 249
  Colors[i].Red = 255;
  Colors[i].Green = 0;
  Colors[i].Blue = 0;
  i++; // 250
  Colors[i].Red = 0;
  Colors[i].Green = 255;
  Colors[i].Blue = 0;
  i++; // 251
  Colors[i].Red = 255;
  Colors[i].Green = 255;
  Colors[i].Blue = 0;
  i++; // 252
  Colors[i].Red = 0;
  Colors[i].Green = 0;
  Colors[i].Blue = 255;
  i++; // 253
  Colors[i].Red = 255;
  Colors[i].Green = 0;
  Colors[i].Blue = 255;
  i++; // 254
  Colors[i].Red = 0;
  Colors[i].Green = 255;
  Colors[i].Blue = 255;
  i++; // 255
  Colors[i].Red = 255;
  Colors[i].Green = 255;
  Colors[i].Blue = 255;
  
  return true;
 }
 return true;
}

bool SafeFread( char* buffer, int size, int number, FILE* fp )
{
 using namespace std;
 int ItemsRead;
 if( feof(fp) )
 { return false; }
 ItemsRead = (int) fread( buffer , size , number , fp );
 if( ItemsRead < number )
 { return false; }
 return true;
}

void BMP::SetDPI( int HorizontalDPI, int VerticalDPI )
{
 XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 );
 YPelsPerMeter = (int) (   VerticalDPI * 39.37007874015748 );
}

// int BMP::TellVerticalDPI( void ) const
int BMP::TellVerticalDPI( void )
{
 if( !YPelsPerMeter )
 { YPelsPerMeter = DefaultYPelsPerMeter; }
 return (int) ( YPelsPerMeter / (double) 39.37007874015748 ); 
}

// int BMP::TellHorizontalDPI( void ) const
int BMP::TellHorizontalDPI( void )
{
 if( !XPelsPerMeter )
 { XPelsPerMeter = DefaultXPelsPerMeter; }
 return (int) ( XPelsPerMeter / (double) 39.37007874015748 );
}

/* These functions are defined in EasyBMP_VariousBMPutilities.h */

BMFH GetBMFH( const char* szFileNameIn )
{
 using namespace std;
 BMFH bmfh;

 FILE* fp;
 fp = fopen( szFileNameIn,"rb");
 
 if( !fp  )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Cannot initialize from file " 
        << szFileNameIn << "." << endl
        << "               File cannot be opened or does not exist." 
	    << endl;
  }
  bmfh.bfType = 0;
  return bmfh;
 } 
 
 SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp );
 SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp ); 
 
 fclose( fp );
 
 if( IsBigEndian() )
 { bmfh.SwitchEndianess(); }

 return bmfh;
}

BMIH GetBMIH( const char* szFileNameIn )
{
 using namespace std;
 BMFH bmfh;
 BMIH bmih;

 FILE* fp;
 fp = fopen( szFileNameIn,"rb");

 if( !fp  )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Cannot initialize from file " 
        << szFileNameIn << "." << endl
        << "               File cannot be opened or does not exist." 
	    << endl;
  }
  return bmih;
 } 
 
 // read the bmfh, i.e., first 14 bytes (just to get it out of the way);
 
 ebmpBYTE TempBYTE;
 int i;
 for( i = 14 ; i > 0 ; i-- )
 { SafeFread( (char*) &TempBYTE , sizeof(ebmpBYTE) , 1, fp ); }

 // read the bmih 

 SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
 SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp ); 
 
 SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
 SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
 
 SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp ); 
 SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp ); 
 
 fclose( fp );
 
 if( IsBigEndian() )
 { bmih.SwitchEndianess(); }

 return bmih;
}

void DisplayBitmapInfo( const char* szFileNameIn )
{
 using namespace std;
 FILE* fp;
 fp = fopen( szFileNameIn,"rb");
 
 if( !fp  )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: Cannot initialize from file " 
        << szFileNameIn << "." << endl
        << "               File cannot be opened or does not exist." 
	    << endl;
  }
  return;
 } 
 fclose( fp );

 // don't duplicate work! Just use the functions from above!
 
 BMFH bmfh = GetBMFH(szFileNameIn);
 BMIH bmih = GetBMIH(szFileNameIn);

 cout << "File information for file " << szFileNameIn 
      << ":" << endl << endl;

 cout << "BITMAPFILEHEADER:" << endl
      << "bfType: " << bmfh.bfType << endl
      << "bfSize: " << bmfh.bfSize << endl
      << "bfReserved1: " << bmfh.bfReserved1 << endl
      << "bfReserved2: " << bmfh.bfReserved2 << endl    
      << "bfOffBits: " << bmfh.bfOffBits << endl << endl;

 cout << "BITMAPINFOHEADER:" << endl
      << "biSize: " << bmih.biSize << endl
      << "biWidth: " << bmih.biWidth << endl
      << "biHeight: " << bmih.biHeight << endl
      << "biPlanes: " << bmih.biPlanes << endl
      << "biBitCount: " << bmih.biBitCount << endl
      << "biCompression: " << bmih.biCompression << endl
      << "biSizeImage: " << bmih.biSizeImage << endl
      << "biXPelsPerMeter: " << bmih.biXPelsPerMeter << endl
      << "biYPelsPerMeter: " << bmih.biYPelsPerMeter << endl
      << "biClrUsed: " << bmih.biClrUsed << endl
      << "biClrImportant: " << bmih.biClrImportant << endl << endl;  
 return;
}

int GetBitmapColorDepth( const char* szFileNameIn )
{
 BMIH bmih = GetBMIH( szFileNameIn );
 return (int) bmih.biBitCount;
}

void PixelToPixelCopy( BMP& From, int FromX, int FromY,  
                       BMP& To, int ToX, int ToY)
{
 *To(ToX,ToY) = *From(FromX,FromY);
 return;
}

void PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY,  
                                  BMP& To, int ToX, int ToY,
                                  RGBApixel& Transparent )
{
 if( From(FromX,FromY)->Red != Transparent.Red ||
     From(FromX,FromY)->Green != Transparent.Green ||
     From(FromX,FromY)->Blue != Transparent.Blue )	 
 { *To(ToX,ToY) = *From(FromX,FromY); }
 return;
}

void RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT, 
                             BMP& To, int ToX, int ToY )
{
 // make sure the conventions are followed
 if( FromB < FromT )
 { int Temp = FromT; FromT = FromB; FromB = Temp; }

 // make sure that the copied regions exist in both bitmaps
 if( FromR >= From.TellWidth() )
 { FromR = From.TellWidth()-1; }
 if( FromL < 0 ){ FromL = 0; }

 if( FromB >= From.TellHeight() )
 { FromB = From.TellHeight()-1; }
 if( FromT < 0 ){ FromT = 0; }
 
 if( ToX+(FromR-FromL) >= To.TellWidth() )
 { FromR = To.TellWidth()-1+FromL-ToX; }
 if( ToY+(FromB-FromT) >= To.TellHeight() )
 { FromB = To.TellHeight()-1+FromT-ToY; } 
 
 int i,j;
 for( j=FromT ; j <= FromB ; j++ )
 { 
  for( i=FromL ; i <= FromR ; i++ )
  {
   PixelToPixelCopy( From, i,j,  
                     To, ToX+(i-FromL), ToY+(j-FromT) );
  }
 }

 return;
}

void RangedPixelToPixelCopyTransparent( 
     BMP& From, int FromL , int FromR, int FromB, int FromT, 
     BMP& To, int ToX, int ToY ,
     RGBApixel& Transparent )
{
 // make sure the conventions are followed
 if( FromB < FromT )
 { int Temp = FromT; FromT = FromB; FromB = Temp; }

 // make sure that the copied regions exist in both bitmaps
 if( FromR >= From.TellWidth() )
 { FromR = From.TellWidth()-1; }
 if( FromL < 0 ){ FromL = 0; }

 if( FromB >= From.TellHeight() )
 { FromB = From.TellHeight()-1; }
 if( FromT < 0 ){ FromT = 0; }
 
 if( ToX+(FromR-FromL) >= To.TellWidth() )
 { FromR = To.TellWidth()-1+FromL-ToX; }
 if( ToY+(FromB-FromT) >= To.TellHeight() )
 { FromB = To.TellHeight()-1+FromT-ToY; } 
 
 int i,j;
 for( j=FromT ; j <= FromB ; j++ )
 { 
  for( i=FromL ; i <= FromR ; i++ )
  {
   PixelToPixelCopyTransparent( From, i,j,  
                     To, ToX+(i-FromL), ToY+(j-FromT) , 
                     Transparent);
  }
 }

 return;
}

bool CreateGrayscaleColorTable( BMP& InputImage )
{
 using namespace std;
 int BitDepth = InputImage.TellBitDepth();
 if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
        << "                 depth that does not require a color table." << endl
   	    << "                 Ignoring request." << endl;
  }
  return false;
 }
 int i;
 int NumberOfColors = InputImage.TellNumberOfColors();
 
 ebmpBYTE StepSize;
 if( BitDepth != 1 )
 { StepSize = 255/(NumberOfColors-1); }
 else
 { StepSize = 255; }
  
 for( i=0 ; i < NumberOfColors ; i++ )
 {
  ebmpBYTE TempBYTE = i*StepSize;
  RGBApixel TempColor;
  TempColor.Red = TempBYTE;
  TempColor.Green = TempBYTE;
  TempColor.Blue = TempBYTE;
  TempColor.Alpha = 0;
  InputImage.SetColor( i , TempColor );  
 }
 return true;
}

bool BMP::Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int i;
 if( Width*4 > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 { memcpy( (char*) &(Pixels[i][Row]), (char*) Buffer+4*i, 4 ); }
 return true;
}

bool BMP::Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int i;
 if( Width*3 > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 { memcpy( (char*) &(Pixels[i][Row]), Buffer+3*i, 3 ); }
 return true;
}

bool BMP::Read8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
 int i;
 if( Width > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 {
  int Index = Buffer[i];
  *( this->operator()(i,Row) )= GetColor(Index); 
 }
 return true;
}

bool BMP::Read4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
 int Shifts[2] = {4  ,0 };
 int Masks[2]  = {240,15};
 
 int i=0;
 int j;
 int k=0;
 if( Width > 2*BufferSize )
 { return false; }
 while( i < Width )
 {
  j=0;
  while( j < 2 && i < Width )
  {
   int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
   *( this->operator()(i,Row) )= GetColor(Index); 
   i++; j++;   
  }
  k++;
 }
 return true;
}
bool BMP::Read1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
 int Shifts[8] = {7  ,6 ,5 ,4 ,3,2,1,0};
 int Masks[8]  = {128,64,32,16,8,4,2,1};
 
 int i=0;
 int j;
 int k=0;
 
 if( Width > 8*BufferSize )
 { return false; }
 while( i < Width )
 {
  j=0;
  while( j < 8 && i < Width )
  {
   int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
   *( this->operator()(i,Row) )= GetColor(Index); 
   i++; j++;   
  }
  k++;
 }
 return true;
}

bool BMP::Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int i;
 if( Width*4 > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 { memcpy( (char*) Buffer+4*i, (char*) &(Pixels[i][Row]), 4 ); }
 return true;
}

bool BMP::Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int i;
 if( Width*3 > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 { memcpy( (char*) Buffer+3*i,  (char*) &(Pixels[i][Row]), 3 ); }
 return true;
}

bool BMP::Write8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
 int i;
 if( Width > BufferSize )
 { return false; }
 for( i=0 ; i < Width ; i++ )
 { Buffer[i] = FindClosestColor( Pixels[i][Row] ); }
 return true;
}

bool BMP::Write4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int PositionWeights[2]  = {16,1};
 
 int i=0;
 int j;
 int k=0;
 if( Width > 2*BufferSize )
 { return false; }
 while( i < Width )
 {
  j=0;
  int Index = 0;
  while( j < 2 && i < Width )
  {
   Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); 
   i++; j++;   
  }
  Buffer[k] = (ebmpBYTE) Index;
  k++;
 }
 return true;
}

bool BMP::Write1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{ 
 int PositionWeights[8]  = {128,64,32,16,8,4,2,1};
 
 int i=0;
 int j;
 int k=0;
 if( Width > 8*BufferSize )
 { return false; }
 while( i < Width )
 {
  j=0;
  int Index = 0;
  while( j < 8 && i < Width )
  {
   Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); 
   i++; j++;   
  }
  Buffer[k] = (ebmpBYTE) Index;
  k++;
 }
 return true;
}

ebmpBYTE BMP::FindClosestColor( RGBApixel& input )
{
 using namespace std;
 
 int i=0;
 int NumberOfColors = TellNumberOfColors();
 ebmpBYTE BestI = 0;
 int BestMatch = 999999;
  
 while( (uint64_t)i < (uint64_t)NumberOfColors )
 {
  RGBApixel Attempt = GetColor( i );
  int TempMatch = IntSquare( (int) Attempt.Red - (int) input.Red )
                + IntSquare( (int) Attempt.Green - (int) input.Green )
                + IntSquare( (int) Attempt.Blue - (int) input.Blue );
  if( TempMatch < BestMatch )
  { BestI = (ebmpBYTE) i; BestMatch = TempMatch; }
  if( BestMatch < 1 )
  { i = NumberOfColors; }
  i++;
 }
 return BestI;
}

bool EasyBMPcheckDataSize( void )
{
 using namespace std;
 bool ReturnValue = true;
 if( sizeof( ebmpBYTE ) != 1 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: ebmpBYTE has the wrong size (" 
        << sizeof( ebmpBYTE ) << " bytes)," << endl
	    << "               Compared to the expected 1 byte value" << endl;
  }
  ReturnValue = false;
 }
 if( sizeof( ebmpWORD ) != 2 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: ebmpWORD has the wrong size (" 
        << sizeof( ebmpWORD ) << " bytes)," << endl
	    << "               Compared to the expected 2 byte value" << endl;
  }
  ReturnValue = false;
 }
 if( sizeof( ebmpDWORD ) != 4 )
 {
  if( EasyBMPwarnings )
  {
   cout << "EasyBMP Error: ebmpDWORD has the wrong size (" 
        << sizeof( ebmpDWORD ) << " bytes)," << endl
	    << "               Compared to the expected 4 byte value" << endl;
  }
  ReturnValue = false;
 }
 return ReturnValue;
}

bool Rescale( BMP& InputImage , char mode, int NewDimension )
{
 using namespace std;
 int CapMode = toupper( mode );

 BMP OldImage( InputImage );
 
 if( CapMode != 'P' &&
     CapMode != 'W' &&
     CapMode != 'H' && 
     CapMode != 'F' )
 {
  if( EasyBMPwarnings )
  {
   char ErrorMessage [1024];
   sprintf( ErrorMessage, "EasyBMP Error: Unknown rescale mode %c requested\n" , mode );
   cout << ErrorMessage; 
  }
  return false;
 }

 int NewWidth  =0;
 int NewHeight =0;
 
 int OldWidth = OldImage.TellWidth();
 int OldHeight= OldImage.TellHeight();
 
 if( CapMode == 'P' )
 {
  NewWidth = (int) floor( OldWidth * NewDimension / 100.0 );
  NewHeight = (int) floor( OldHeight * NewDimension / 100.0 );
 }
 if( CapMode == 'F' )
 {
  if( OldWidth > OldHeight )
  { CapMode = 'W'; }
  else
  { CapMode = 'H'; }
 }

 if( CapMode == 'W' )
 {
  double percent = (double) NewDimension / (double) OldWidth;
  NewWidth = NewDimension;
  NewHeight = (int) floor( OldHeight * percent );
 }
 if( CapMode == 'H' )
 {
  double percent = (double) NewDimension / (double) OldHeight;
  NewHeight = NewDimension;
  NewWidth = (int) floor( OldWidth * percent );
 }
 
 if( NewWidth < 1 )
 { NewWidth = 1; }
 if( NewHeight < 1 )
 { NewHeight = 1; }
 
 InputImage.SetSize( NewWidth, NewHeight );
 InputImage.SetBitDepth( 24 );

 int I,J;
 double ThetaI,ThetaJ;
 
 for( int j=0; j < NewHeight-1 ; j++ )
 {
  ThetaJ = (double)(j*(OldHeight-1.0))
          /(double)(NewHeight-1.0);
  J	= (int) floor( ThetaJ );
  ThetaJ -= J;  
  
  for( int i=0; i < NewWidth-1 ; i++ )
  {
   ThetaI = (double)(i*(OldWidth-1.0))
           /(double)(NewWidth-1.0);
   I = (int) floor( ThetaI );
   ThetaI -= I;  
   
   InputImage(i,j)->Red = (ebmpBYTE) 
                          ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*(OldImage(I,J)->Red)
                           +(ThetaI-ThetaI*ThetaJ)*(OldImage(I+1,J)->Red)   
                           +(ThetaJ-ThetaI*ThetaJ)*(OldImage(I,J+1)->Red)   
                           +(ThetaI*ThetaJ)*(OldImage(I+1,J+1)->Red) );
   InputImage(i,j)->Green = (ebmpBYTE) 
                          ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Green
                           +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Green   
                           +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Green   
                           +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Green );  
   InputImage(i,j)->Blue = (ebmpBYTE) 
                          ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Blue
                           +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Blue   
                           +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Blue   
                           +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Blue ); 
  }
   InputImage(NewWidth-1,j)->Red = (ebmpBYTE) 
                            ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Red)
                          + ThetaJ*(OldImage(OldWidth-1,J+1)->Red) ); 
   InputImage(NewWidth-1,j)->Green = (ebmpBYTE) 
                            ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Green)
                          + ThetaJ*(OldImage(OldWidth-1,J+1)->Green) ); 
   InputImage(NewWidth-1,j)->Blue = (ebmpBYTE) 
                            ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Blue)
                          + ThetaJ*(OldImage(OldWidth-1,J+1)->Blue) ); 
 } 

 for( int i=0 ; i < NewWidth-1 ; i++ )
 {
  ThetaI = (double)(i*(OldWidth-1.0))
          /(double)(NewWidth-1.0);
  I = (int) floor( ThetaI );
  ThetaI -= I;  
  InputImage(i,NewHeight-1)->Red = (ebmpBYTE) 
                            ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Red)
                          + ThetaI*(OldImage(I,OldHeight-1)->Red) ); 
  InputImage(i,NewHeight-1)->Green = (ebmpBYTE) 
                            ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Green)
                          + ThetaI*(OldImage(I,OldHeight-1)->Green) ); 
  InputImage(i,NewHeight-1)->Blue = (ebmpBYTE) 
                            ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Blue)
                          + ThetaI*(OldImage(I,OldHeight-1)->Blue) ); 
 }
 
 *InputImage(NewWidth-1,NewHeight-1) = *OldImage(OldWidth-1,OldHeight-1);
 return true;
}
