version 0.0.1
[fms.git] / src / freenet / captcha / easybmp / EasyBMP.cpp
1 /*************************************************
2 *                                                *
3 *  EasyBMP Cross-Platform Windows Bitmap Library * 
4 *                                                *
5 *  Author: Paul Macklin                          *
6 *   email: macklin01@users.sourceforge.net       *
7 * support: http://easybmp.sourceforge.net        *
8 *                                                *
9 *          file: EasyBMP.cpp                     * 
10 *    date added: 03-31-2006                      *
11 * date modified: 12-01-2006                      *
12 *       version: 1.06                            *
13 *                                                *
14 *   License: BSD (revised/modified)              *
15 * Copyright: 2005-6 by the EasyBMP Project       * 
16 *                                                *
17 * description: Actual source file                *
18 *                                                *
19 *************************************************/\r
20 \r
21 #include "../../../../include/freenet/captcha/easybmp/EasyBMP.h"\r
22
23 /* These functions are defined in EasyBMP.h */
24
25 bool EasyBMPwarnings = true;
26
27 void SetEasyBMPwarningsOff( void )
28 { EasyBMPwarnings = false; }
29 void SetEasyBMPwarningsOn( void )
30 { EasyBMPwarnings = true; }
31 bool GetEasyBMPwarningState( void )
32 { return EasyBMPwarnings; }
33
34 /* These functions are defined in EasyBMP_DataStructures.h */\r
35 \r
36 int IntPow( int base, int exponent )
37 {
38  int i;
39  int output = 1;
40  for( i=0 ; i < exponent ; i++ )
41  { output *= base; }
42  return output;
43 }\r
44 \r
45 BMFH::BMFH()
46 {
47  bfType = 19778;
48  bfReserved1 = 0;
49  bfReserved2 = 0;
50 }\r
51 \r
52 void BMFH::SwitchEndianess( void )
53 {
54  bfType = FlipWORD( bfType );
55  bfSize = FlipDWORD( bfSize );
56  bfReserved1 = FlipWORD( bfReserved1 );
57  bfReserved2 = FlipWORD( bfReserved2 );
58  bfOffBits = FlipDWORD( bfOffBits );
59  return;
60 }
61 \r
62 BMIH::BMIH()
63 {\r
64  biPlanes = 1;
65  biCompression = 0;
66  biXPelsPerMeter = DefaultXPelsPerMeter;  
67  biYPelsPerMeter = DefaultYPelsPerMeter;
68  biClrUsed = 0;
69  biClrImportant = 0;
70 }
71
72 void BMIH::SwitchEndianess( void )
73 {
74  biSize = FlipDWORD( biSize );
75  biWidth = FlipDWORD( biWidth );
76  biHeight = FlipDWORD( biHeight );
77  biPlanes = FlipWORD( biPlanes );
78  biBitCount = FlipWORD( biBitCount );
79  biCompression = FlipDWORD( biCompression );
80  biSizeImage = FlipDWORD( biSizeImage );
81  biXPelsPerMeter = FlipDWORD( biXPelsPerMeter );
82  biYPelsPerMeter = FlipDWORD( biYPelsPerMeter );
83  biClrUsed = FlipDWORD( biClrUsed );
84  biClrImportant = FlipDWORD( biClrImportant );
85  return;
86 }
87
88 void BMIH::display( void )
89 {
90  using namespace std;
91  cout << "biSize: " << (int) biSize << endl
92       << "biWidth: " << (int) biWidth << endl
93       << "biHeight: " << (int) biHeight << endl
94       << "biPlanes: " << (int) biPlanes << endl
95       << "biBitCount: " << (int) biBitCount << endl
96       << "biCompression: " << (int) biCompression << endl
97       << "biSizeImage: " << (int) biSizeImage << endl
98       << "biXPelsPerMeter: " << (int) biXPelsPerMeter << endl
99       << "biYPelsPerMeter: " << (int) biYPelsPerMeter << endl
100       << "biClrUsed: " << (int) biClrUsed << endl
101       << "biClrImportant: " << (int) biClrImportant << endl << endl;
102 }
103
104 void BMFH::display( void )
105 {
106  using namespace std;
107  cout << "bfType: " << (int) bfType << endl
108       << "bfSize: " << (int) bfSize << endl
109       << "bfReserved1: " << (int) bfReserved1 << endl
110       << "bfReserved2: " << (int) bfReserved2 << endl
111       << "bfOffBits: " << (int) bfOffBits << endl << endl;
112 }
113 \r
114 /* These functions are defined in EasyBMP_BMP.h */
115
116 RGBApixel BMP::GetPixel( int i, int j ) const
117 {
118  using namespace std;
119  bool Warn = false;
120  if( i >= Width )\r
121  { i = Width-1; Warn = true; }
122  if( i < 0 )\r
123  { i = 0; Warn = true; }
124  if( j >= Height )\r
125  { j = Height-1; Warn = true; }
126  if( j < 0 )\r
127  { j = 0; Warn = true; }
128  if( Warn && EasyBMPwarnings )
129  {
130   cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
131        << "                 Truncating request to fit in the range [0,"
132        << Width-1 << "] x [0," << Height-1 << "]." << endl;
133  }      
134  return Pixels[i][j];
135 }
136
137 bool BMP::SetPixel( int i, int j, RGBApixel NewPixel )
138 {
139  Pixels[i][j] = NewPixel;
140  return true;
141 }
142 \r
143 \r
144 bool BMP::SetColor( int ColorNumber , RGBApixel NewColor )
145 {
146  using namespace std;
147  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
148  {
149   if( EasyBMPwarnings )
150   {
151    cout << "EasyBMP Warning: Attempted to change color table for a BMP object" << endl
152         << "                 that lacks a color table. Ignoring request." << endl;
153   }
154   return false;
155  }
156  if( !Colors )
157  {
158   if( EasyBMPwarnings )
159   {
160    cout << "EasyBMP Warning: Attempted to set a color, but the color table" << endl
161         << "                 is not defined. Ignoring request." << endl; 
162   }
163   return false; 
164  }
165  if( ColorNumber >= TellNumberOfColors() )
166  {
167   if( EasyBMPwarnings )
168   {
169    cout << "EasyBMP Warning: Requested color number " 
170         << ColorNumber << " is outside the allowed" << endl
171         << "                 range [0," << TellNumberOfColors()-1 
172             << "]. Ignoring request to set this color." << endl;
173   }
174   return false;
175  }
176  Colors[ColorNumber] = NewColor;
177  return true;
178 }
179
180 // RGBApixel BMP::GetColor( int ColorNumber ) const
181 RGBApixel BMP::GetColor( int ColorNumber )
182
183  RGBApixel Output;
184  Output.Red   = 255;
185  Output.Green = 255;
186  Output.Blue  = 255;
187  Output.Alpha = 0;
188  
189  using namespace std;
190  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
191  {
192   if( EasyBMPwarnings )
193   {
194    cout << "EasyBMP Warning: Attempted to access color table for a BMP object" << endl
195         << "                 that lacks a color table. Ignoring request." << endl;
196   }
197   return Output;
198  }
199  if( !Colors )
200  {
201   if( EasyBMPwarnings )
202   {
203    cout << "EasyBMP Warning: Requested a color, but the color table" << endl
204         << "                 is not defined. Ignoring request." << endl;
205   }
206   return Output; 
207  }
208  if( ColorNumber >= TellNumberOfColors() )
209  {
210   if( EasyBMPwarnings )
211   {
212    cout << "EasyBMP Warning: Requested color number " 
213         << ColorNumber << " is outside the allowed" << endl
214         << "                 range [0," << TellNumberOfColors()-1 
215             << "]. Ignoring request to get this color." << endl;
216   }
217   return Output;
218  }
219  Output = Colors[ColorNumber];
220  return Output;
221 }
222
223 BMP::BMP()
224 {
225  Width = 1;
226  Height = 1;
227  BitDepth = 24;
228  Pixels = new RGBApixel* [Width];
229  Pixels[0] = new RGBApixel [Height];
230  Colors = NULL;\r
231  \r
232  XPelsPerMeter = 0;\r
233  YPelsPerMeter = 0;
234  
235  MetaData1 = NULL;
236  SizeOfMetaData1 = 0;
237  MetaData2 = NULL;
238  SizeOfMetaData2 = 0;
239 }
240
241 // BMP::BMP( const BMP& Input )
242 BMP::BMP( BMP& Input )
243 {
244  // first, make the image empty.
245
246  Width = 1;
247  Height = 1;
248  BitDepth = 24;
249  Pixels = new RGBApixel* [Width];
250  Pixels[0] = new RGBApixel [Height];
251  Colors = NULL; 
252  XPelsPerMeter = 0;\r
253  YPelsPerMeter = 0;
254  
255  MetaData1 = NULL;
256  SizeOfMetaData1 = 0;
257  MetaData2 = NULL;
258  SizeOfMetaData2 = 0;
259
260  // now, set the correct bit depth
261  
262  SetBitDepth( Input.TellBitDepth() );
263  
264  // set the correct pixel size 
265  
266  SetSize( Input.TellWidth() , Input.TellHeight() );
267
268  // set the DPI information from Input
269  
270  SetDPI( Input.TellHorizontalDPI() , Input.TellVerticalDPI() );
271  
272  // if there is a color table, get all the colors
273
274  if( BitDepth == 1 || BitDepth == 4 ||  
275      BitDepth == 8 )
276  {
277   for( int k=0 ; k < TellNumberOfColors() ; k++ )
278   {
279    SetColor( k, Input.GetColor( k )); 
280   }
281  }
282  
283  // get all the pixels 
284  
285  for( int j=0; j < Height ; j++ )
286  {
287   for( int i=0; i < Width ; i++ )
288   {
289    Pixels[i][j] = *Input(i,j);
290 //   Pixels[i][j] = Input.GetPixel(i,j); // *Input(i,j);
291   }
292  }
293 }
294
295 BMP::~BMP()
296 {
297  int i;
298  for(i=0;i<Width;i++)
299  { delete [] Pixels[i]; }
300  delete [] Pixels;
301  if( Colors )
302  { delete [] Colors; }
303  
304  if( MetaData1 )
305  { delete [] MetaData1; }
306  if( MetaData2 )
307  { delete [] MetaData2; }
308
309
310 RGBApixel* BMP::operator()(int i, int j)
311 {
312  using namespace std;
313  bool Warn = false;
314  if( i >= Width )\r
315  { i = Width-1; Warn = true; }
316  if( i < 0 )\r
317  { i = 0; Warn = true; }
318  if( j >= Height )\r
319  { j = Height-1; Warn = true; }
320  if( j < 0 )\r
321  { j = 0; Warn = true; }
322  if( Warn && EasyBMPwarnings )
323  {
324   cout << "EasyBMP Warning: Attempted to access non-existent pixel;" << endl
325        << "                 Truncating request to fit in the range [0,"
326        << Width-1 << "] x [0," << Height-1 << "]." << endl;
327  }      
328  return &(Pixels[i][j]);
329 }
330
331 // int BMP::TellBitDepth( void ) const
332 int BMP::TellBitDepth( void )
333 { return BitDepth; }
334
335 // int BMP::TellHeight( void ) const
336 int BMP::TellHeight( void )
337 { return Height; }
338
339 // int BMP::TellWidth( void ) const
340 int BMP::TellWidth( void )
341 { return Width; }
342
343 // int BMP::TellNumberOfColors( void ) const
344 int BMP::TellNumberOfColors( void )
345 {
346  int output = IntPow( 2, BitDepth );
347  if( BitDepth == 32 )
348  { output = IntPow( 2, 24 ); }
349  return output;
350 }
351
352 bool BMP::SetBitDepth( int NewDepth )
353 {
354  using namespace std;
355  if( NewDepth != 1 && NewDepth != 4 && 
356      NewDepth != 8 && NewDepth != 16 && 
357      NewDepth != 24 && NewDepth != 32 )
358  {
359   if( EasyBMPwarnings )
360   {
361    cout << "EasyBMP Warning: User attempted to set unsupported bit depth " 
362         << NewDepth << "." << endl
363         << "                 Bit depth remains unchanged at " 
364             << BitDepth << "." << endl;
365   }
366   return false;
367  }
368  
369  BitDepth = NewDepth;
370  if( Colors )
371  { delete [] Colors; }
372  int NumberOfColors = IntPow( 2, BitDepth );
373  if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
374  { Colors = new RGBApixel [NumberOfColors]; }
375  else
376  { Colors = NULL; } 
377  if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
378  { CreateStandardColorTable(); }
379  
380  return true;
381 }
382
383 bool BMP::SetSize(int NewWidth , int NewHeight )
384 {
385  using namespace std;
386  if( NewWidth <= 0 || NewHeight <= 0 )
387  {
388   if( EasyBMPwarnings )
389   {
390    cout << "EasyBMP Warning: User attempted to set a non-positive width or height." << endl
391         << "                 Size remains unchanged at " 
392         << Width << " x " << Height << "." << endl;
393   }
394   return false;
395  }
396
397  int i,j; 
398
399  for(i=0;i<Width;i++)
400  { delete [] Pixels[i]; }
401  delete [] Pixels;
402
403  Width = NewWidth;
404  Height = NewHeight;
405  Pixels = new RGBApixel* [ Width ]; 
406  
407  for(i=0; i<Width; i++)
408  { Pixels[i] = new RGBApixel [ Height ]; }
409  
410  for( i=0 ; i < Width ; i++)
411  {
412   for( j=0 ; j < Height ; j++ )
413   {
414    Pixels[i][j].Red = 255; 
415    Pixels[i][j].Green = 255; 
416    Pixels[i][j].Blue = 255; 
417    Pixels[i][j].Alpha = 0;    
418   }
419  }
420
421  return true; 
422 }
423
424 bool BMP::WriteToFile( const char* FileName )
425 {
426  using namespace std;
427  if( !EasyBMPcheckDataSize() )
428  {
429   if( EasyBMPwarnings )
430   {
431    cout << "EasyBMP Error: Data types are wrong size!" << endl
432         << "               You may need to mess with EasyBMP_DataTypes.h" << endl
433             << "               to fix these errors, and then recompile." << endl
434             << "               All 32-bit and 64-bit machines should be" << endl
435             << "               supported, however." << endl << endl;
436   }
437   return false; 
438  }
439  
440  FILE* fp = fopen( FileName, "wb" );
441  if( fp == NULL )
442  {
443   if( EasyBMPwarnings )
444   {
445    cout << "EasyBMP Error: Cannot open file " 
446         << FileName << " for output." << endl;
447   }
448   fclose( fp );
449   return false;
450  }
451   
452  // some preliminaries
453  
454  double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
455  double dBytesPerRow = dBytesPerPixel * (Width+0.0);
456  dBytesPerRow = ceil(dBytesPerRow);
457   
458  int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
459  if( BytePaddingPerRow == 4 )
460  { BytePaddingPerRow = 0; } 
461  
462  double dActualBytesPerRow = dBytesPerRow + BytePaddingPerRow;
463  
464  double dTotalPixelBytes = Height * dActualBytesPerRow;
465  
466  double dPaletteSize = 0;
467  if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
468  { dPaletteSize = IntPow(2,BitDepth)*4.0; }
469
470  // leave some room for 16-bit masks 
471  if( BitDepth == 16 )
472  { dPaletteSize = 3*4; }
473  
474  double dTotalFileSize = 14 + 40 + dPaletteSize + dTotalPixelBytes;
475  
476  // write the file header 
477  
478  BMFH bmfh;
479  bmfh.bfSize = (ebmpDWORD) dTotalFileSize; 
480  bmfh.bfReserved1 = 0; 
481  bmfh.bfReserved2 = 0; 
482  bmfh.bfOffBits = (ebmpDWORD) (14+40+dPaletteSize);  
483  
484  if( IsBigEndian() )
485  { bmfh.SwitchEndianess(); }
486  
487  fwrite( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp );
488  fwrite( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp );
489  fwrite( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp );
490  fwrite( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp );
491  fwrite( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp );
492  
493  // write the info header 
494  
495  BMIH bmih;
496  bmih.biSize = 40;
497  bmih.biWidth = Width;
498  bmih.biHeight = Height;
499  bmih.biPlanes = 1;
500  bmih.biBitCount = BitDepth;
501  bmih.biCompression = 0;
502  bmih.biSizeImage = (ebmpDWORD) dTotalPixelBytes;
503  if( XPelsPerMeter )
504  { bmih.biXPelsPerMeter = XPelsPerMeter; }
505  else
506  { bmih.biXPelsPerMeter = DefaultXPelsPerMeter; }
507  if( YPelsPerMeter )
508  { bmih.biYPelsPerMeter = YPelsPerMeter; }
509  else
510  { bmih.biYPelsPerMeter = DefaultYPelsPerMeter; }
511
512  bmih.biClrUsed = 0;
513  bmih.biClrImportant = 0;
514
515  // indicates that we'll be using bit fields for 16-bit files
516  if( BitDepth == 16 )
517  { bmih.biCompression = 3; }
518  
519  if( IsBigEndian() )
520  { bmih.SwitchEndianess(); }
521  
522  fwrite( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
523  fwrite( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp );
524  fwrite( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp );
525  fwrite( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp );
526  fwrite( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
527  fwrite( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp );
528  fwrite( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp );
529  fwrite( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
530  fwrite( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
531  fwrite( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
532  fwrite( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);
533  
534  // write the palette 
535  if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
536  {
537   int NumberOfColors = IntPow(2,BitDepth);
538   
539   // if there is no palette, create one 
540   if( !Colors )
541   {
542    if( !Colors )
543    { Colors = new RGBApixel [NumberOfColors]; }
544    CreateStandardColorTable(); 
545   }
546    
547   int n;
548   for( n=0 ; n < NumberOfColors ; n++ )
549   { fwrite( (char*) &(Colors[n]) , 4 , 1 , fp ); }
550  }
551  
552  // write the pixels 
553  int i,j;
554  if( BitDepth != 16 )
555  {  
556   ebmpBYTE* Buffer;
557   int BufferSize = (int) ( (Width*BitDepth)/8.0 );
558   while( 8*BufferSize < Width*BitDepth )
559   { BufferSize++; }
560   while( BufferSize % 4 )
561   { BufferSize++; }
562   
563   Buffer = new ebmpBYTE [BufferSize];
564   for( j=0 ; j < BufferSize; j++ )
565   { Buffer[j] = 0; }
566     
567   j=Height-1;
568   
569   while( j > -1 )
570   {
571    bool Success = false;
572    if( BitDepth == 32 )
573    { Success = Write32bitRow( Buffer, BufferSize, j ); }
574    if( BitDepth == 24 )
575    { Success = Write24bitRow( Buffer, BufferSize, j ); }
576    if( BitDepth == 8  )
577    { Success = Write8bitRow( Buffer, BufferSize, j ); }
578    if( BitDepth == 4  )
579    { Success = Write4bitRow( Buffer, BufferSize, j ); }
580    if( BitDepth == 1  )
581    { Success = Write1bitRow( Buffer, BufferSize, j ); }
582    if( Success )
583    {
584     int BytesWritten = (int) fwrite( (char*) Buffer, 1, BufferSize, fp );
585     if( BytesWritten != BufferSize )
586     { Success = false; }
587    }
588    if( !Success )
589    {
590     if( EasyBMPwarnings )
591     {
592      cout << "EasyBMP Error: Could not write proper amount of data." << endl;
593         }
594     j = -1; 
595    }
596    j--; 
597   }
598   
599   delete [] Buffer;
600  }
601  
602  if( BitDepth == 16 )
603  {
604   // write the bit masks
605
606   ebmpWORD BlueMask = 31;    // bits 12-16
607   ebmpWORD GreenMask = 2016; // bits 6-11
608   ebmpWORD RedMask = 63488;  // bits 1-5
609   ebmpWORD ZeroWORD;
610   
611   if( IsBigEndian() )
612   { RedMask = FlipWORD( RedMask ); }
613   fwrite( (char*) &RedMask , 2 , 1 , fp );
614   fwrite( (char*) &ZeroWORD , 2 , 1 , fp );
615
616   if( IsBigEndian() )
617   { GreenMask = FlipWORD( GreenMask ); }
618   fwrite( (char*) &GreenMask , 2 , 1 , fp );
619   fwrite( (char*) &ZeroWORD , 2 , 1 , fp );
620
621   if( IsBigEndian() )
622   { BlueMask = FlipWORD( BlueMask ); }
623   fwrite( (char*) &BlueMask , 2 , 1 , fp );
624   fwrite( (char*) &ZeroWORD , 2 , 1 , fp );
625
626   int DataBytes = Width*2;
627   int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;
628   
629   // write the actual pixels
630   
631   for( j=Height-1 ; j >= 0 ; j-- )
632   {
633    // write all row pixel data
634    i=0;
635    int WriteNumber = 0;
636    while( WriteNumber < DataBytes )
637    {
638     ebmpWORD TempWORD;
639         
640         ebmpWORD RedWORD = (ebmpWORD) ((Pixels[i][j]).Red / 8);
641         ebmpWORD GreenWORD = (ebmpWORD) ((Pixels[i][j]).Green / 4);
642         ebmpWORD BlueWORD = (ebmpWORD) ((Pixels[i][j]).Blue / 8);
643         
644     TempWORD = (RedWORD<<11) + (GreenWORD<<5) + BlueWORD;
645         if( IsBigEndian() )
646         { TempWORD = FlipWORD( TempWORD ); }
647         
648     fwrite( (char*) &TempWORD , 2, 1, fp);
649     WriteNumber += 2;
650         i++;\r
651    }
652    // write any necessary row padding
653    WriteNumber = 0;
654    while( WriteNumber < PaddingBytes )
655    {
656     ebmpBYTE TempBYTE;
657     fwrite( (char*) &TempBYTE , 1, 1, fp);
658     WriteNumber++;
659    }
660   }
661   
662  }
663
664  fclose(fp);
665  return true;
666 }
667
668 bool BMP::ReadFromFile( const char* FileName )
669
670  using namespace std;
671  if( !EasyBMPcheckDataSize() )
672  {
673   if( EasyBMPwarnings )
674   {
675    cout << "EasyBMP Error: Data types are wrong size!" << endl
676         << "               You may need to mess with EasyBMP_DataTypes.h" << endl
677             << "               to fix these errors, and then recompile." << endl
678             << "               All 32-bit and 64-bit machines should be" << endl
679             << "               supported, however." << endl << endl;
680   }
681   return false; 
682  }
683
684  FILE* fp = fopen( FileName, "rb" );
685  if( fp == NULL )
686  {
687   if( EasyBMPwarnings )
688   {
689    cout << "EasyBMP Error: Cannot open file " 
690         << FileName << " for input." << endl;
691   }
692   SetBitDepth(1);
693   SetSize(1,1);
694   return false;
695  }
696  
697  // read the file header 
698  
699  BMFH bmfh;
700  bool NotCorrupted = true;
701  
702  NotCorrupted &= SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD), 1, fp);
703  
704  bool IsBitmap = false;
705  
706  if( IsBigEndian() && bmfh.bfType == 16973 )
707  { IsBitmap = true; }
708  if( !IsBigEndian() && bmfh.bfType == 19778 )
709  { IsBitmap = true; }
710  
711  if( !IsBitmap ) 
712  {
713   if( EasyBMPwarnings )
714   {
715    cout << "EasyBMP Error: " << FileName 
716         << " is not a Windows BMP file!" << endl; 
717   }
718   fclose( fp ); 
719   return false;
720  }
721
722  NotCorrupted &= SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1, fp); 
723  NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1, fp);
724  NotCorrupted &= SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1, fp);
725  NotCorrupted &= SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp);
726  
727  if( IsBigEndian() ) 
728  { bmfh.SwitchEndianess(); }
729   
730  // read the info header
731
732  BMIH bmih; 
733  
734  NotCorrupted &= SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp);
735  NotCorrupted &= SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp); 
736  NotCorrupted &= SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp);
737  NotCorrupted &= SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1, fp); 
738  NotCorrupted &= SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1, fp);
739
740  NotCorrupted &= SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp);
741  NotCorrupted &= SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp);
742  NotCorrupted &= SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
743  NotCorrupted &= SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
744  NotCorrupted &= SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
745  NotCorrupted &= SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);
746  
747  if( IsBigEndian() ) 
748  { bmih.SwitchEndianess(); }
749
750  // a safety catch: if any of the header information didn't read properly, abort
751  // future idea: check to see if at least most is self-consistent
752   
753  if( !NotCorrupted )
754  {
755   if( EasyBMPwarnings )
756   {
757    cout << "EasyBMP Error: " << FileName 
758         << " is obviously corrupted." << endl;
759   }
760   SetSize(1,1);
761   SetBitDepth(1);
762   fclose(fp);
763   return false;
764  } 
765  
766  XPelsPerMeter = bmih.biXPelsPerMeter;
767  YPelsPerMeter = bmih.biYPelsPerMeter;
768  
769  // if bmih.biCompression 1 or 2, then the file is RLE compressed
770  
771  if( bmih.biCompression == 1 || bmih.biCompression == 2 )
772  {
773   if( EasyBMPwarnings )
774   {
775    cout << "EasyBMP Error: " << FileName << " is (RLE) compressed." << endl
776         << "               EasyBMP does not support compression." << endl;
777   }
778   SetSize(1,1);
779   SetBitDepth(1);
780   fclose(fp);
781   return false; 
782  }
783  
784  // if bmih.biCompression > 3, then something strange is going on 
785  // it's probably an OS2 bitmap file.
786  
787  if( bmih.biCompression > 3 )
788  {
789   if( EasyBMPwarnings )
790   {
791    cout << "EasyBMP Error: " << FileName << " is in an unsupported format." 
792         << endl
793         << "               (bmih.biCompression = " 
794             << bmih.biCompression << ")" << endl
795             << "               The file is probably an old OS2 bitmap or corrupted." 
796             << endl;
797   }             
798   SetSize(1,1);
799   SetBitDepth(1);
800   fclose(fp);
801   return false; 
802  }
803  
804  if( bmih.biCompression == 3 && bmih.biBitCount != 16 )
805  {
806   if( EasyBMPwarnings )
807   {
808    cout << "EasyBMP Error: " << FileName 
809         << " uses bit fields and is not a" << endl
810         << "               16-bit file. This is not supported." << endl;
811   }
812   SetSize(1,1);
813   SetBitDepth(1);
814   fclose(fp);
815   return false; 
816  }
817
818  // set the bit depth
819  
820  int TempBitDepth = (int) bmih.biBitCount;
821  if(    TempBitDepth != 1  && TempBitDepth != 4 
822      && TempBitDepth != 8  && TempBitDepth != 16
823      && TempBitDepth != 24 && TempBitDepth != 32 )
824  {
825   if( EasyBMPwarnings )
826   {
827    cout << "EasyBMP Error: " << FileName << " has unrecognized bit depth." << endl;
828   }
829   SetSize(1,1);
830   SetBitDepth(1);
831   fclose(fp);
832   return false;
833  }
834  SetBitDepth( (int) bmih.biBitCount ); 
835  
836  // set the size
837
838  if( (int) bmih.biWidth <= 0 || (int) bmih.biHeight <= 0 ) 
839  {
840   if( EasyBMPwarnings )
841   {
842    cout << "EasyBMP Error: " << FileName 
843         << " has a non-positive width or height." << endl;
844   }
845   SetSize(1,1);
846   SetBitDepth(1);
847   fclose(fp);
848   return false;
849  } 
850  SetSize( (int) bmih.biWidth , (int) bmih.biHeight );
851   
852  // some preliminaries
853  
854  double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
855  double dBytesPerRow = dBytesPerPixel * (Width+0.0);
856  dBytesPerRow = ceil(dBytesPerRow);
857   
858  int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
859  if( BytePaddingPerRow == 4 )
860  { BytePaddingPerRow = 0; }  
861  
862  // if < 16 bits, read the palette
863  
864  if( BitDepth < 16 )
865  {
866   // determine the number of colors specified in the 
867   // color table
868   
869   int NumberOfColorsToRead = ((int) bmfh.bfOffBits - 54 )/4;  
870   if( NumberOfColorsToRead > IntPow(2,BitDepth) )
871   { NumberOfColorsToRead = IntPow(2,BitDepth); }
872  
873   if( NumberOfColorsToRead < TellNumberOfColors() )
874   {
875    if( EasyBMPwarnings )
876    {
877     cout << "EasyBMP Warning: file " << FileName << " has an underspecified" << endl
878          << "                 color table. The table will be padded with extra" << endl
879                  << "                 white (255,255,255,0) entries." << endl;
880    }
881   }
882  
883   int n;
884   for( n=0; n < NumberOfColorsToRead ; n++ )
885   {
886    SafeFread( (char*) &(Colors[n]) , 4 , 1 , fp);     
887   }
888   for( n=NumberOfColorsToRead ; n < TellNumberOfColors() ; n++ )
889   {
890    RGBApixel WHITE; 
891    WHITE.Red = 255;
892    WHITE.Green = 255;
893    WHITE.Blue = 255;
894    WHITE.Alpha = 0;
895    SetColor( n , WHITE );
896   }
897   
898   
899  }
900  
901  // skip blank data if bfOffBits so indicates
902  
903  int BytesToSkip = bmfh.bfOffBits - 54;;
904  if( BitDepth < 16 )
905  { BytesToSkip -= 4*IntPow(2,BitDepth); }
906  if( BitDepth == 16 && bmih.biCompression == 3 )
907  { BytesToSkip -= 3*4; }
908  if( BytesToSkip < 0 )
909  { BytesToSkip = 0; }
910  if( BytesToSkip > 0 && BitDepth != 16 )
911  {
912   if( EasyBMPwarnings )
913   {
914    cout << "EasyBMP Warning: Extra meta data detected in file " << FileName << endl
915         << "                 Data will be skipped." << endl;
916   }
917   ebmpBYTE* TempSkipBYTE;
918   TempSkipBYTE = new ebmpBYTE [BytesToSkip];
919   SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);   \r
920   delete [] TempSkipBYTE;
921  } 
922   
923  // This code reads 1, 4, 8, 24, and 32-bpp files 
924  // with a more-efficient buffered technique.
925
926  int i,j;
927  if( BitDepth != 16 )
928  {
929   int BufferSize = (int) ( (Width*BitDepth) / 8.0 );
930   while( 8*BufferSize < Width*BitDepth )
931   { BufferSize++; }
932   while( BufferSize % 4 )
933   { BufferSize++; }
934   ebmpBYTE* Buffer;
935   Buffer = new ebmpBYTE [BufferSize];
936   j= Height-1;
937   while( j > -1 )
938   {
939    int BytesRead = (int) fread( (char*) Buffer, 1, BufferSize, fp );
940    if( BytesRead < BufferSize )
941    {
942     j = -1; 
943     if( EasyBMPwarnings )
944     {
945      cout << "EasyBMP Error: Could not read proper amount of data." << endl;
946         }
947    }
948    else
949    {
950     bool Success = false;
951     if( BitDepth == 1  )
952         { Success = Read1bitRow(  Buffer, BufferSize, j ); }
953     if( BitDepth == 4  )
954         { Success = Read4bitRow(  Buffer, BufferSize, j ); }
955     if( BitDepth == 8  )
956         { Success = Read8bitRow(  Buffer, BufferSize, j ); }
957     if( BitDepth == 24 )
958         { Success = Read24bitRow( Buffer, BufferSize, j ); }
959         if( BitDepth == 32 )
960         { Success = Read32bitRow( Buffer, BufferSize, j ); }
961     if( !Success )
962     {
963      if( EasyBMPwarnings )
964      {
965       cout << "EasyBMP Error: Could not read enough pixel data!" << endl;
966          }
967          j = -1;
968     }
969    }   
970    j--;
971   }
972   delete [] Buffer; 
973  }
974
975  if( BitDepth == 16 )
976  {
977   int DataBytes = Width*2;
978   int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;
979
980   // set the default mask
981   
982   ebmpWORD BlueMask = 31; // bits 12-16
983   ebmpWORD GreenMask = 992; // bits 7-11
984   ebmpWORD RedMask = 31744; // bits 2-6
985
986   // read the bit fields, if necessary, to 
987   // override the default 5-5-5 mask
988   
989   if( bmih.biCompression != 0 )
990   {
991    // read the three bit masks
992
993    ebmpWORD TempMaskWORD;
994    ebmpWORD ZeroWORD;
995   
996    SafeFread( (char*) &RedMask , 2 , 1 , fp );
997    if( IsBigEndian() )
998    { RedMask = FlipWORD(RedMask); }
999    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
1000   
1001    SafeFread( (char*) &GreenMask , 2 , 1 , fp );
1002    if( IsBigEndian() )
1003    { GreenMask = FlipWORD(GreenMask); }
1004    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
1005
1006    SafeFread( (char*) &BlueMask , 2 , 1 , fp );
1007    if( IsBigEndian() )
1008    { BlueMask = FlipWORD(BlueMask); }
1009    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
1010   }
1011   
1012   // read and skip any meta data
1013
1014   if( BytesToSkip > 0 )
1015   {
1016    if( EasyBMPwarnings )
1017    {
1018     cout << "EasyBMP Warning: Extra meta data detected in file " 
1019          << FileName << endl
1020          << "                 Data will be skipped." << endl;
1021    }
1022    ebmpBYTE* TempSkipBYTE;
1023    TempSkipBYTE = new ebmpBYTE [BytesToSkip];
1024    SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);\r
1025    delete [] TempSkipBYTE;   
1026   } 
1027   
1028   // determine the red, green and blue shifts
1029   
1030   int GreenShift = 0; 
1031   ebmpWORD TempShiftWORD = GreenMask;
1032   while( TempShiftWORD > 31 )
1033   { TempShiftWORD = TempShiftWORD>>1; GreenShift++; }  
1034   int BlueShift = 0;
1035   TempShiftWORD = BlueMask;
1036   while( TempShiftWORD > 31 )
1037   { TempShiftWORD = TempShiftWORD>>1; BlueShift++; }  
1038   int RedShift = 0;  
1039   TempShiftWORD = RedMask;
1040   while( TempShiftWORD > 31 )
1041   { TempShiftWORD = TempShiftWORD>>1; RedShift++; }  
1042   
1043   // read the actual pixels
1044   
1045   for( j=Height-1 ; j >= 0 ; j-- )
1046   {
1047    i=0;
1048    int ReadNumber = 0;
1049    while( ReadNumber < DataBytes )
1050    {
1051         ebmpWORD TempWORD;
1052         SafeFread( (char*) &TempWORD , 2 , 1 , fp );
1053         if( IsBigEndian() )
1054         { TempWORD = FlipWORD(TempWORD); }
1055     ReadNumber += 2;
1056   
1057     ebmpWORD Red = RedMask & TempWORD;
1058     ebmpWORD Green = GreenMask & TempWORD;
1059     ebmpWORD Blue = BlueMask & TempWORD;
1060                                 
1061         ebmpBYTE BlueBYTE = (ebmpBYTE) 8*(Blue>>BlueShift);
1062     ebmpBYTE GreenBYTE = (ebmpBYTE) 8*(Green>>GreenShift);
1063     ebmpBYTE RedBYTE = (ebmpBYTE) 8*(Red>>RedShift);
1064                 
1065         (Pixels[i][j]).Red = RedBYTE;
1066         (Pixels[i][j]).Green = GreenBYTE;
1067         (Pixels[i][j]).Blue = BlueBYTE;
1068         
1069         i++;
1070    }
1071    ReadNumber = 0;
1072    while( ReadNumber < PaddingBytes )
1073    {
1074     ebmpBYTE TempBYTE;
1075     SafeFread( (char*) &TempBYTE , 1, 1, fp);
1076     ReadNumber++;
1077    }
1078   }
1079
1080  }
1081  
1082  fclose(fp);
1083  return true;
1084 }
1085
1086 bool BMP::CreateStandardColorTable( void )
1087 {
1088  using namespace std;
1089  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
1090  {
1091   if( EasyBMPwarnings )
1092   {
1093    cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
1094         << "                 depth that does not require a color table." << endl
1095         << "                 Ignoring request." << endl;
1096   }
1097   return false;
1098  }
1099
1100  if( BitDepth == 1 )
1101  {
1102   int i;
1103   for( i=0 ; i < 2 ; i++ )
1104   {
1105    Colors[i].Red = i*255;
1106    Colors[i].Green = i*255;
1107    Colors[i].Blue = i*255;
1108    Colors[i].Alpha = 0;
1109   } 
1110   return true;
1111  } 
1112
1113  if( BitDepth == 4 )
1114  {
1115   int i = 0;
1116   int j,k,ell;
1117   
1118   // simplify the code for the first 8 colors
1119   for( ell=0 ; ell < 2 ; ell++ )
1120   {
1121    for( k=0 ; k < 2 ; k++ )
1122    {
1123     for( j=0 ; j < 2 ; j++ )
1124     {
1125      Colors[i].Red = j*128; 
1126          Colors[i].Green = k*128;
1127          Colors[i].Blue = ell*128;
1128          i++;
1129     }
1130    }
1131   }
1132  
1133   // simplify the code for the last 8 colors
1134   for( ell=0 ; ell < 2 ; ell++ )
1135   {
1136    for( k=0 ; k < 2 ; k++ )
1137    {
1138     for( j=0 ; j < 2 ; j++ )
1139     {
1140      Colors[i].Red = j*255;
1141      Colors[i].Green = k*255; 
1142      Colors[i].Blue = ell*255;
1143          i++;
1144     }
1145    }
1146   }
1147  
1148   // overwrite the duplicate color
1149   i=8; 
1150   Colors[i].Red = 192;
1151   Colors[i].Green = 192;
1152   Colors[i].Blue = 192;
1153    
1154   for( i=0 ; i < 16 ; i++ )
1155   { Colors[i].Alpha = 0; }
1156   return true;
1157  }
1158
1159  if( BitDepth == 8 )
1160  {
1161   int i=0;
1162   int j,k,ell;
1163     
1164   // do an easy loop, which works for all but colors 
1165   // 0 to 9 and 246 to 255
1166   for( ell=0 ; ell < 4 ; ell++ ) 
1167   {
1168    for( k=0 ; k < 8 ; k++ )
1169    {
1170     for( j=0; j < 8 ; j++ )
1171     {
1172      Colors[i].Red = j*32; 
1173      Colors[i].Green = k*32;
1174      Colors[i].Blue = ell*64;
1175      Colors[i].Alpha = 0;
1176      i++;
1177     }
1178    }
1179   } 
1180    
1181   // now redo the first 8 colors  
1182   i=0;
1183   for( ell=0 ; ell < 2 ; ell++ ) 
1184   {
1185    for( k=0 ; k < 2 ; k++ )
1186    {
1187     for( j=0; j < 2 ; j++ )
1188     {
1189      Colors[i].Red = j*128;
1190      Colors[i].Green = k*128;
1191      Colors[i].Blue = ell*128;
1192      i++;
1193     }
1194    }
1195   } 
1196  
1197   // overwrite colors 7, 8, 9
1198   i=7;
1199   Colors[i].Red = 192;
1200   Colors[i].Green = 192;
1201   Colors[i].Blue = 192;
1202   i++; // 8
1203   Colors[i].Red = 192;
1204   Colors[i].Green = 220;
1205   Colors[i].Blue = 192;
1206   i++; // 9
1207   Colors[i].Red = 166;
1208   Colors[i].Green = 202;
1209   Colors[i].Blue = 240;
1210    
1211   // overwrite colors 246 to 255 
1212   i=246;
1213   Colors[i].Red = 255;
1214   Colors[i].Green = 251;
1215   Colors[i].Blue = 240;
1216   i++; // 247
1217   Colors[i].Red = 160;
1218   Colors[i].Green = 160;
1219   Colors[i].Blue = 164;
1220   i++; // 248
1221   Colors[i].Red = 128;
1222   Colors[i].Green = 128;
1223   Colors[i].Blue = 128;
1224   i++; // 249
1225   Colors[i].Red = 255;
1226   Colors[i].Green = 0;
1227   Colors[i].Blue = 0;
1228   i++; // 250
1229   Colors[i].Red = 0;
1230   Colors[i].Green = 255;
1231   Colors[i].Blue = 0;
1232   i++; // 251
1233   Colors[i].Red = 255;
1234   Colors[i].Green = 255;
1235   Colors[i].Blue = 0;
1236   i++; // 252
1237   Colors[i].Red = 0;
1238   Colors[i].Green = 0;
1239   Colors[i].Blue = 255;
1240   i++; // 253
1241   Colors[i].Red = 255;
1242   Colors[i].Green = 0;
1243   Colors[i].Blue = 255;
1244   i++; // 254
1245   Colors[i].Red = 0;
1246   Colors[i].Green = 255;
1247   Colors[i].Blue = 255;
1248   i++; // 255
1249   Colors[i].Red = 255;
1250   Colors[i].Green = 255;
1251   Colors[i].Blue = 255;
1252   
1253   return true;
1254  }
1255  return true;
1256 }
1257
1258 bool SafeFread( char* buffer, int size, int number, FILE* fp )
1259 {
1260  using namespace std;
1261  int ItemsRead;
1262  if( feof(fp) )
1263  { return false; }
1264  ItemsRead = (int) fread( buffer , size , number , fp );
1265  if( ItemsRead < number )
1266  { return false; }
1267  return true;
1268 }
1269
1270 void BMP::SetDPI( int HorizontalDPI, int VerticalDPI )
1271 {
1272  XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 );
1273  YPelsPerMeter = (int) (   VerticalDPI * 39.37007874015748 );
1274 }\r
1275 \r
1276 // int BMP::TellVerticalDPI( void ) const\r
1277 int BMP::TellVerticalDPI( void )\r
1278 {\r
1279  if( !YPelsPerMeter )\r
1280  { YPelsPerMeter = DefaultYPelsPerMeter; }\r
1281  return (int) ( YPelsPerMeter / (double) 39.37007874015748 ); \r
1282 }
1283
1284 // int BMP::TellHorizontalDPI( void ) const\r
1285 int BMP::TellHorizontalDPI( void )\r
1286 {\r
1287  if( !XPelsPerMeter )\r
1288  { XPelsPerMeter = DefaultXPelsPerMeter; }\r
1289  return (int) ( XPelsPerMeter / (double) 39.37007874015748 );\r
1290 }\r
1291 \r
1292 /* These functions are defined in EasyBMP_VariousBMPutilities.h */\r
1293 \r
1294 BMFH GetBMFH( const char* szFileNameIn )
1295 {
1296  using namespace std;
1297  BMFH bmfh;
1298
1299  FILE* fp;
1300  fp = fopen( szFileNameIn,"rb");
1301  
1302  if( !fp  )
1303  {
1304   if( EasyBMPwarnings )
1305   {
1306    cout << "EasyBMP Error: Cannot initialize from file " 
1307         << szFileNameIn << "." << endl
1308         << "               File cannot be opened or does not exist." 
1309             << endl;
1310   }
1311   bmfh.bfType = 0;
1312   return bmfh;
1313  } 
1314  
1315  SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp );
1316  SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp ); 
1317  SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp ); 
1318  SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp ); 
1319  SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp ); 
1320  
1321  fclose( fp );
1322  
1323  if( IsBigEndian() )
1324  { bmfh.SwitchEndianess(); }
1325
1326  return bmfh;
1327 }
1328
1329 BMIH GetBMIH( const char* szFileNameIn )
1330 {
1331  using namespace std;
1332  BMFH bmfh;
1333  BMIH bmih;
1334
1335  FILE* fp;
1336  fp = fopen( szFileNameIn,"rb");
1337
1338  if( !fp  )
1339  {
1340   if( EasyBMPwarnings )
1341   {
1342    cout << "EasyBMP Error: Cannot initialize from file " 
1343         << szFileNameIn << "." << endl
1344         << "               File cannot be opened or does not exist." 
1345             << endl;
1346   }
1347   return bmih;
1348  } 
1349  
1350  // read the bmfh, i.e., first 14 bytes (just to get it out of the way);
1351  
1352  ebmpBYTE TempBYTE;
1353  int i;
1354  for( i = 14 ; i > 0 ; i-- )
1355  { SafeFread( (char*) &TempBYTE , sizeof(ebmpBYTE) , 1, fp ); }
1356
1357  // read the bmih 
1358
1359  SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
1360  SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp ); 
1361  SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp ); 
1362  SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp ); 
1363  
1364  SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
1365  SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp ); 
1366  SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp ); 
1367  SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
1368  
1369  SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp ); 
1370  SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp ); 
1371  SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp ); 
1372  
1373  fclose( fp );
1374  
1375  if( IsBigEndian() )
1376  { bmih.SwitchEndianess(); }
1377
1378  return bmih;
1379 }
1380
1381 void DisplayBitmapInfo( const char* szFileNameIn )
1382 {
1383  using namespace std;
1384  FILE* fp;
1385  fp = fopen( szFileNameIn,"rb");
1386  
1387  if( !fp  )
1388  {
1389   if( EasyBMPwarnings )
1390   {
1391    cout << "EasyBMP Error: Cannot initialize from file " 
1392         << szFileNameIn << "." << endl
1393         << "               File cannot be opened or does not exist." 
1394             << endl;
1395   }
1396   return;
1397  } 
1398  fclose( fp );
1399
1400  // don't duplicate work! Just use the functions from above!
1401  
1402  BMFH bmfh = GetBMFH(szFileNameIn);
1403  BMIH bmih = GetBMIH(szFileNameIn);
1404
1405  cout << "File information for file " << szFileNameIn 
1406       << ":" << endl << endl;
1407
1408  cout << "BITMAPFILEHEADER:" << endl
1409       << "bfType: " << bmfh.bfType << endl
1410       << "bfSize: " << bmfh.bfSize << endl
1411       << "bfReserved1: " << bmfh.bfReserved1 << endl
1412       << "bfReserved2: " << bmfh.bfReserved2 << endl    
1413       << "bfOffBits: " << bmfh.bfOffBits << endl << endl;
1414
1415  cout << "BITMAPINFOHEADER:" << endl
1416       << "biSize: " << bmih.biSize << endl
1417       << "biWidth: " << bmih.biWidth << endl
1418       << "biHeight: " << bmih.biHeight << endl
1419       << "biPlanes: " << bmih.biPlanes << endl
1420       << "biBitCount: " << bmih.biBitCount << endl
1421       << "biCompression: " << bmih.biCompression << endl
1422       << "biSizeImage: " << bmih.biSizeImage << endl
1423       << "biXPelsPerMeter: " << bmih.biXPelsPerMeter << endl
1424       << "biYPelsPerMeter: " << bmih.biYPelsPerMeter << endl
1425       << "biClrUsed: " << bmih.biClrUsed << endl
1426       << "biClrImportant: " << bmih.biClrImportant << endl << endl;  
1427  return;
1428 }
1429
1430 int GetBitmapColorDepth( const char* szFileNameIn )
1431 {
1432  BMIH bmih = GetBMIH( szFileNameIn );
1433  return (int) bmih.biBitCount;
1434 }
1435
1436 void PixelToPixelCopy( BMP& From, int FromX, int FromY,  
1437                        BMP& To, int ToX, int ToY)
1438 {
1439  *To(ToX,ToY) = *From(FromX,FromY);
1440  return;
1441 }
1442
1443 void PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY,  
1444                                   BMP& To, int ToX, int ToY,
1445                                   RGBApixel& Transparent )
1446 {
1447  if( From(FromX,FromY)->Red != Transparent.Red ||
1448      From(FromX,FromY)->Green != Transparent.Green ||
1449      From(FromX,FromY)->Blue != Transparent.Blue )       
1450  { *To(ToX,ToY) = *From(FromX,FromY); }
1451  return;
1452 }
1453
1454 void RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT, 
1455                              BMP& To, int ToX, int ToY )
1456 {
1457  // make sure the conventions are followed
1458  if( FromB < FromT )
1459  { int Temp = FromT; FromT = FromB; FromB = Temp; }
1460
1461  // make sure that the copied regions exist in both bitmaps
1462  if( FromR >= From.TellWidth() )
1463  { FromR = From.TellWidth()-1; }
1464  if( FromL < 0 ){ FromL = 0; }
1465
1466  if( FromB >= From.TellHeight() )
1467  { FromB = From.TellHeight()-1; }
1468  if( FromT < 0 ){ FromT = 0; }
1469  
1470  if( ToX+(FromR-FromL) >= To.TellWidth() )
1471  { FromR = To.TellWidth()-1+FromL-ToX; }
1472  if( ToY+(FromB-FromT) >= To.TellHeight() )
1473  { FromB = To.TellHeight()-1+FromT-ToY; } 
1474  
1475  int i,j;
1476  for( j=FromT ; j <= FromB ; j++ )
1477  { 
1478   for( i=FromL ; i <= FromR ; i++ )
1479   {
1480    PixelToPixelCopy( From, i,j,  
1481                      To, ToX+(i-FromL), ToY+(j-FromT) );
1482   }
1483  }
1484
1485  return;
1486 }
1487
1488 void RangedPixelToPixelCopyTransparent( 
1489      BMP& From, int FromL , int FromR, int FromB, int FromT, 
1490      BMP& To, int ToX, int ToY ,
1491      RGBApixel& Transparent )
1492 {
1493  // make sure the conventions are followed
1494  if( FromB < FromT )
1495  { int Temp = FromT; FromT = FromB; FromB = Temp; }
1496
1497  // make sure that the copied regions exist in both bitmaps
1498  if( FromR >= From.TellWidth() )
1499  { FromR = From.TellWidth()-1; }
1500  if( FromL < 0 ){ FromL = 0; }
1501
1502  if( FromB >= From.TellHeight() )
1503  { FromB = From.TellHeight()-1; }
1504  if( FromT < 0 ){ FromT = 0; }
1505  
1506  if( ToX+(FromR-FromL) >= To.TellWidth() )
1507  { FromR = To.TellWidth()-1+FromL-ToX; }
1508  if( ToY+(FromB-FromT) >= To.TellHeight() )
1509  { FromB = To.TellHeight()-1+FromT-ToY; } 
1510  
1511  int i,j;
1512  for( j=FromT ; j <= FromB ; j++ )
1513  { 
1514   for( i=FromL ; i <= FromR ; i++ )
1515   {
1516    PixelToPixelCopyTransparent( From, i,j,  
1517                      To, ToX+(i-FromL), ToY+(j-FromT) , 
1518                      Transparent);
1519   }
1520  }
1521
1522  return;
1523 }
1524
1525 bool CreateGrayscaleColorTable( BMP& InputImage )
1526 {
1527  using namespace std;
1528  int BitDepth = InputImage.TellBitDepth();
1529  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
1530  {
1531   if( EasyBMPwarnings )
1532   {
1533    cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
1534         << "                 depth that does not require a color table." << endl
1535             << "                 Ignoring request." << endl;
1536   }
1537   return false;
1538  }
1539  int i;
1540  int NumberOfColors = InputImage.TellNumberOfColors();
1541  
1542  ebmpBYTE StepSize;
1543  if( BitDepth != 1 )
1544  { StepSize = 255/(NumberOfColors-1); }
1545  else
1546  { StepSize = 255; }
1547   
1548  for( i=0 ; i < NumberOfColors ; i++ )
1549  {
1550   ebmpBYTE TempBYTE = i*StepSize;
1551   RGBApixel TempColor;
1552   TempColor.Red = TempBYTE;
1553   TempColor.Green = TempBYTE;
1554   TempColor.Blue = TempBYTE;
1555   TempColor.Alpha = 0;
1556   InputImage.SetColor( i , TempColor );  
1557  }
1558  return true;
1559 }
1560
1561 bool BMP::Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1562
1563  int i;
1564  if( Width*4 > BufferSize )
1565  { return false; }
1566  for( i=0 ; i < Width ; i++ )
1567  { memcpy( (char*) &(Pixels[i][Row]), (char*) Buffer+4*i, 4 ); }
1568  return true;
1569 }
1570
1571 bool BMP::Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1572
1573  int i;
1574  if( Width*3 > BufferSize )
1575  { return false; }
1576  for( i=0 ; i < Width ; i++ )
1577  { memcpy( (char*) &(Pixels[i][Row]), Buffer+3*i, 3 ); }
1578  return true;
1579 }
1580
1581 bool BMP::Read8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1582 {
1583  int i;
1584  if( Width > BufferSize )
1585  { return false; }
1586  for( i=0 ; i < Width ; i++ )
1587  {
1588   int Index = Buffer[i];
1589   *( this->operator()(i,Row) )= GetColor(Index); 
1590  }
1591  return true;
1592 }
1593
1594 bool BMP::Read4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1595 {
1596  int Shifts[2] = {4  ,0 };
1597  int Masks[2]  = {240,15};
1598  
1599  int i=0;
1600  int j;
1601  int k=0;
1602  if( Width > 2*BufferSize )
1603  { return false; }
1604  while( i < Width )
1605  {
1606   j=0;
1607   while( j < 2 && i < Width )
1608   {
1609    int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
1610    *( this->operator()(i,Row) )= GetColor(Index); 
1611    i++; j++;   
1612   }
1613   k++;
1614  }
1615  return true;
1616 }
1617 bool BMP::Read1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1618 {
1619  int Shifts[8] = {7  ,6 ,5 ,4 ,3,2,1,0};
1620  int Masks[8]  = {128,64,32,16,8,4,2,1};
1621  
1622  int i=0;
1623  int j;
1624  int k=0;
1625  
1626  if( Width > 8*BufferSize )
1627  { return false; }
1628  while( i < Width )
1629  {
1630   j=0;
1631   while( j < 8 && i < Width )
1632   {
1633    int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
1634    *( this->operator()(i,Row) )= GetColor(Index); 
1635    i++; j++;   
1636   }
1637   k++;
1638  }
1639  return true;
1640 }
1641
1642 bool BMP::Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1643
1644  int i;
1645  if( Width*4 > BufferSize )
1646  { return false; }
1647  for( i=0 ; i < Width ; i++ )
1648  { memcpy( (char*) Buffer+4*i, (char*) &(Pixels[i][Row]), 4 ); }
1649  return true;
1650 }
1651
1652 bool BMP::Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1653
1654  int i;
1655  if( Width*3 > BufferSize )
1656  { return false; }
1657  for( i=0 ; i < Width ; i++ )
1658  { memcpy( (char*) Buffer+3*i,  (char*) &(Pixels[i][Row]), 3 ); }
1659  return true;
1660 }
1661
1662 bool BMP::Write8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1663 {
1664  int i;
1665  if( Width > BufferSize )
1666  { return false; }
1667  for( i=0 ; i < Width ; i++ )
1668  { Buffer[i] = FindClosestColor( Pixels[i][Row] ); }
1669  return true;
1670 }
1671
1672 bool BMP::Write4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1673
1674  int PositionWeights[2]  = {16,1};
1675  
1676  int i=0;
1677  int j;
1678  int k=0;
1679  if( Width > 2*BufferSize )
1680  { return false; }
1681  while( i < Width )
1682  {
1683   j=0;
1684   int Index = 0;
1685   while( j < 2 && i < Width )
1686   {
1687    Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); 
1688    i++; j++;   
1689   }
1690   Buffer[k] = (ebmpBYTE) Index;
1691   k++;
1692  }
1693  return true;
1694 }
1695
1696 bool BMP::Write1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1697
1698  int PositionWeights[8]  = {128,64,32,16,8,4,2,1};
1699  
1700  int i=0;
1701  int j;
1702  int k=0;
1703  if( Width > 8*BufferSize )
1704  { return false; }
1705  while( i < Width )
1706  {
1707   j=0;
1708   int Index = 0;
1709   while( j < 8 && i < Width )
1710   {
1711    Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) ); 
1712    i++; j++;   
1713   }
1714   Buffer[k] = (ebmpBYTE) Index;
1715   k++;
1716  }
1717  return true;
1718 }
1719
1720 ebmpBYTE BMP::FindClosestColor( RGBApixel& input )
1721 {
1722  using namespace std;
1723  
1724  int i=0;
1725  int NumberOfColors = TellNumberOfColors();
1726  ebmpBYTE BestI = 0;
1727  int BestMatch = 999999;
1728   
1729  while( i < NumberOfColors )
1730  {
1731   RGBApixel Attempt = GetColor( i );
1732   int TempMatch = IntSquare( (int) Attempt.Red - (int) input.Red )
1733                 + IntSquare( (int) Attempt.Green - (int) input.Green )
1734                 + IntSquare( (int) Attempt.Blue - (int) input.Blue );
1735   if( TempMatch < BestMatch )
1736   { BestI = (ebmpBYTE) i; BestMatch = TempMatch; }
1737   if( BestMatch < 1 )
1738   { i = NumberOfColors; }
1739   i++;
1740  }
1741  return BestI;
1742 }
1743
1744 bool EasyBMPcheckDataSize( void )
1745 {
1746  using namespace std;
1747  bool ReturnValue = true;
1748  if( sizeof( ebmpBYTE ) != 1 )
1749  {
1750   if( EasyBMPwarnings )
1751   {
1752    cout << "EasyBMP Error: ebmpBYTE has the wrong size (" 
1753         << sizeof( ebmpBYTE ) << " bytes)," << endl
1754             << "               Compared to the expected 1 byte value" << endl;
1755   }
1756   ReturnValue = false;
1757  }
1758  if( sizeof( ebmpWORD ) != 2 )
1759  {
1760   if( EasyBMPwarnings )
1761   {
1762    cout << "EasyBMP Error: ebmpWORD has the wrong size (" 
1763         << sizeof( ebmpWORD ) << " bytes)," << endl
1764             << "               Compared to the expected 2 byte value" << endl;
1765   }
1766   ReturnValue = false;
1767  }
1768  if( sizeof( ebmpDWORD ) != 4 )
1769  {
1770   if( EasyBMPwarnings )
1771   {
1772    cout << "EasyBMP Error: ebmpDWORD has the wrong size (" 
1773         << sizeof( ebmpDWORD ) << " bytes)," << endl
1774             << "               Compared to the expected 4 byte value" << endl;
1775   }
1776   ReturnValue = false;
1777  }
1778  return ReturnValue;
1779 }
1780
1781 bool Rescale( BMP& InputImage , char mode, int NewDimension )
1782 {
1783  using namespace std;
1784  int CapMode = toupper( mode );
1785
1786  BMP OldImage( InputImage );
1787  
1788  if( CapMode != 'P' &&
1789      CapMode != 'W' &&
1790      CapMode != 'H' && 
1791      CapMode != 'F' )
1792  {
1793   if( EasyBMPwarnings )
1794   {
1795    char ErrorMessage [1024];
1796    sprintf( ErrorMessage, "EasyBMP Error: Unknown rescale mode %c requested\n" , mode );
1797    cout << ErrorMessage; 
1798   }
1799   return false;
1800  }
1801
1802  int NewWidth  =0;
1803  int NewHeight =0;
1804  
1805  int OldWidth = OldImage.TellWidth();
1806  int OldHeight= OldImage.TellHeight();
1807  
1808  if( CapMode == 'P' )
1809  {
1810   NewWidth = (int) floor( OldWidth * NewDimension / 100.0 );
1811   NewHeight = (int) floor( OldHeight * NewDimension / 100.0 );
1812  }
1813  if( CapMode == 'F' )
1814  {
1815   if( OldWidth > OldHeight )
1816   { CapMode = 'W'; }
1817   else
1818   { CapMode = 'H'; }
1819  }
1820
1821  if( CapMode == 'W' )
1822  {
1823   double percent = (double) NewDimension / (double) OldWidth;
1824   NewWidth = NewDimension;
1825   NewHeight = (int) floor( OldHeight * percent );
1826  }
1827  if( CapMode == 'H' )
1828  {
1829   double percent = (double) NewDimension / (double) OldHeight;
1830   NewHeight = NewDimension;
1831   NewWidth = (int) floor( OldWidth * percent );
1832  }
1833  
1834  if( NewWidth < 1 )
1835  { NewWidth = 1; }
1836  if( NewHeight < 1 )
1837  { NewHeight = 1; }
1838  
1839  InputImage.SetSize( NewWidth, NewHeight );
1840  InputImage.SetBitDepth( 24 );
1841
1842  int I,J;
1843  double ThetaI,ThetaJ;
1844  
1845  for( int j=0; j < NewHeight-1 ; j++ )
1846  {
1847   ThetaJ = (double)(j*(OldHeight-1.0))
1848           /(double)(NewHeight-1.0);
1849   J     = (int) floor( ThetaJ );
1850   ThetaJ -= J;  
1851   
1852   for( int i=0; i < NewWidth-1 ; i++ )
1853   {
1854    ThetaI = (double)(i*(OldWidth-1.0))
1855            /(double)(NewWidth-1.0);
1856    I = (int) floor( ThetaI );
1857    ThetaI -= I;  
1858    
1859    InputImage(i,j)->Red = (ebmpBYTE) 
1860                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*(OldImage(I,J)->Red)
1861                            +(ThetaI-ThetaI*ThetaJ)*(OldImage(I+1,J)->Red)   
1862                            +(ThetaJ-ThetaI*ThetaJ)*(OldImage(I,J+1)->Red)   
1863                            +(ThetaI*ThetaJ)*(OldImage(I+1,J+1)->Red) );
1864    InputImage(i,j)->Green = (ebmpBYTE) 
1865                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Green
1866                            +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Green   
1867                            +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Green   
1868                            +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Green );  
1869    InputImage(i,j)->Blue = (ebmpBYTE) 
1870                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Blue
1871                            +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Blue   
1872                            +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Blue   
1873                            +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Blue ); 
1874   }
1875    InputImage(NewWidth-1,j)->Red = (ebmpBYTE) 
1876                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Red)
1877                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Red) ); 
1878    InputImage(NewWidth-1,j)->Green = (ebmpBYTE) 
1879                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Green)
1880                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Green) ); 
1881    InputImage(NewWidth-1,j)->Blue = (ebmpBYTE) 
1882                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Blue)
1883                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Blue) ); 
1884  } 
1885
1886  for( int i=0 ; i < NewWidth-1 ; i++ )
1887  {
1888   ThetaI = (double)(i*(OldWidth-1.0))
1889           /(double)(NewWidth-1.0);
1890   I = (int) floor( ThetaI );
1891   ThetaI -= I;  
1892   InputImage(i,NewHeight-1)->Red = (ebmpBYTE) 
1893                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Red)
1894                           + ThetaI*(OldImage(I,OldHeight-1)->Red) ); 
1895   InputImage(i,NewHeight-1)->Green = (ebmpBYTE) 
1896                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Green)
1897                           + ThetaI*(OldImage(I,OldHeight-1)->Green) ); 
1898   InputImage(i,NewHeight-1)->Blue = (ebmpBYTE) 
1899                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Blue)
1900                           + ThetaI*(OldImage(I,OldHeight-1)->Blue) ); 
1901  }
1902  
1903  *InputImage(NewWidth-1,NewHeight-1) = *OldImage(OldWidth-1,OldHeight-1);
1904  return true;
1905 }