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 *************************************************/
20 
21 #include "EasyBMP.h"
22 
23 /* These functions are defined in EasyBMP.h */
24 
25 bool EasyBMPwarnings = true;
26 
SetEasyBMPwarningsOff(void)27 void SetEasyBMPwarningsOff( void )
28 { EasyBMPwarnings = false; }
SetEasyBMPwarningsOn(void)29 void SetEasyBMPwarningsOn( void )
30 { EasyBMPwarnings = true; }
GetEasyBMPwarningState(void)31 bool GetEasyBMPwarningState( void )
32 { return EasyBMPwarnings; }
33 
34 /* These functions are defined in EasyBMP_DataStructures.h */
35 
IntPow(int base,int exponent)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 }
44 
BMFH()45 BMFH::BMFH()
46 {
47  bfType = 19778;
48  bfReserved1 = 0;
49  bfReserved2 = 0;
50 }
51 
SwitchEndianess(void)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 
BMIH()62 BMIH::BMIH()
63 {
64  biPlanes = 1;
65  biCompression = 0;
66  biXPelsPerMeter = DefaultXPelsPerMeter;
67  biYPelsPerMeter = DefaultYPelsPerMeter;
68  biClrUsed = 0;
69  biClrImportant = 0;
70 }
71 
SwitchEndianess(void)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 
display(void)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 
display(void)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 
114 /* These functions are defined in EasyBMP_BMP.h */
115 
GetPixel(int i,int j) const116 RGBApixel BMP::GetPixel( int i, int j ) const
117 {
118  using namespace std;
119  bool Warn = false;
120  if( i >= Width )
121  { i = Width-1; Warn = true; }
122  if( i < 0 )
123  { i = 0; Warn = true; }
124  if( j >= Height )
125  { j = Height-1; Warn = true; }
126  if( j < 0 )
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 
SetPixel(int i,int j,RGBApixel NewPixel)137 bool BMP::SetPixel( int i, int j, RGBApixel NewPixel )
138 {
139  Pixels[i][j] = NewPixel;
140  return true;
141 }
142 
143 
SetColor(int ColorNumber,RGBApixel NewColor)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
GetColor(int ColorNumber)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 
BMP()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 = nullptr;
231 
232  XPelsPerMeter = 0;
233  YPelsPerMeter = 0;
234 
235  MetaData1 = nullptr;
236  SizeOfMetaData1 = 0;
237  MetaData2 = nullptr;
238  SizeOfMetaData2 = 0;
239 }
240 
241 // BMP::BMP( const BMP& Input )
BMP(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 = nullptr;
252  XPelsPerMeter = 0;
253  YPelsPerMeter = 0;
254 
255  MetaData1 = nullptr;
256  SizeOfMetaData1 = 0;
257  MetaData2 = nullptr;
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 
~BMP()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 
operator ()(int i,int j)310 RGBApixel* BMP::operator()(int i, int j)
311 {
312  using namespace std;
313  bool Warn = false;
314  if( i >= Width )
315  { i = Width-1; Warn = true; }
316  if( i < 0 )
317  { i = 0; Warn = true; }
318  if( j >= Height )
319  { j = Height-1; Warn = true; }
320  if( j < 0 )
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
TellBitDepth(void)332 int BMP::TellBitDepth( void )
333 { return BitDepth; }
334 
335 // int BMP::TellHeight( void ) const
TellHeight(void)336 int BMP::TellHeight( void )
337 { return Height; }
338 
339 // int BMP::TellWidth( void ) const
TellWidth(void)340 int BMP::TellWidth( void )
341 { return Width; }
342 
343 // int BMP::TellNumberOfColors( void ) const
TellNumberOfColors(void)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 
SetBitDepth(int NewDepth)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 = nullptr; }
377  if( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
378  { CreateStandardColorTable(); }
379 
380  return true;
381 }
382 
SetSize(int NewWidth,int NewHeight)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 
WriteToFile(const char * FileName)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 == nullptr )
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++;
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 
ReadFromFile(const char * FileName)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 == nullptr )
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  // if < 16 bits, read the palette
853 
854  if( BitDepth < 16 )
855  {
856   // determine the number of colors specified in the
857   // color table
858 
859   int NumberOfColorsToRead = ((int) bmfh.bfOffBits - 54 )/4;
860   if( NumberOfColorsToRead > IntPow(2,BitDepth) )
861   { NumberOfColorsToRead = IntPow(2,BitDepth); }
862 
863   if( NumberOfColorsToRead < TellNumberOfColors() )
864   {
865    if( EasyBMPwarnings )
866    {
867     cout << "EasyBMP Warning: file " << FileName << " has an underspecified" << endl
868          << "                 color table. The table will be padded with extra" << endl
869           << "                 white (255,255,255,0) entries." << endl;
870    }
871   }
872 
873   int n;
874   for( n=0; n < NumberOfColorsToRead ; n++ )
875   {
876    SafeFread( (char*) &(Colors[n]) , 4 , 1 , fp);
877   }
878   for( n=NumberOfColorsToRead ; n < TellNumberOfColors() ; n++ )
879   {
880    RGBApixel WHITE;
881    WHITE.Red = 255;
882    WHITE.Green = 255;
883    WHITE.Blue = 255;
884    WHITE.Alpha = 0;
885    SetColor( n , WHITE );
886   }
887 
888 
889  }
890 
891  // skip blank data if bfOffBits so indicates
892 
893  int BytesToSkip = bmfh.bfOffBits - 54;;
894  if( BitDepth < 16 )
895  { BytesToSkip -= 4*IntPow(2,BitDepth); }
896  if( BitDepth == 16 && bmih.biCompression == 3 )
897  { BytesToSkip -= 3*4; }
898  if( BytesToSkip < 0 )
899  { BytesToSkip = 0; }
900  if( BytesToSkip > 0 && BitDepth != 16 )
901  {
902   if( EasyBMPwarnings )
903   {
904    cout << "EasyBMP Warning: Extra meta data detected in file " << FileName << endl
905         << "                 Data will be skipped." << endl;
906   }
907   ebmpBYTE* TempSkipBYTE;
908   TempSkipBYTE = new ebmpBYTE [BytesToSkip];
909   SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);
910   delete [] TempSkipBYTE;
911  }
912 
913  // This code reads 1, 4, 8, 24, and 32-bpp files
914  // with a more-efficient buffered technique.
915 
916  int i,j;
917  if( BitDepth != 16 )
918  {
919   int BufferSize = (int) ( (Width*BitDepth) / 8.0 );
920   while( 8*BufferSize < Width*BitDepth )
921   { BufferSize++; }
922   while( BufferSize % 4 )
923   { BufferSize++; }
924   ebmpBYTE* Buffer;
925   Buffer = new ebmpBYTE [BufferSize];
926   j= Height-1;
927   while( j > -1 )
928   {
929    int BytesRead = (int) fread( (char*) Buffer, 1, BufferSize, fp );
930    if( BytesRead < BufferSize )
931    {
932     j = -1;
933     if( EasyBMPwarnings )
934     {
935      cout << "EasyBMP Error: Could not read proper amount of data." << endl;
936     }
937    }
938    else
939    {
940     bool Success = false;
941     if( BitDepth == 1  )
942     { Success = Read1bitRow(  Buffer, BufferSize, j ); }
943     if( BitDepth == 4  )
944     { Success = Read4bitRow(  Buffer, BufferSize, j ); }
945     if( BitDepth == 8  )
946     { Success = Read8bitRow(  Buffer, BufferSize, j ); }
947     if( BitDepth == 24 )
948     { Success = Read24bitRow( Buffer, BufferSize, j ); }
949     if( BitDepth == 32 )
950     { Success = Read32bitRow( Buffer, BufferSize, j ); }
951     if( !Success )
952     {
953      if( EasyBMPwarnings )
954      {
955       cout << "EasyBMP Error: Could not read enough pixel data!" << endl;
956      }
957      j = -1;
958     }
959    }
960    j--;
961   }
962   delete [] Buffer;
963  }
964 
965  if( BitDepth == 16 )
966  {
967   int DataBytes = Width*2;
968   int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;
969 
970   // set the default mask
971 
972   ebmpWORD BlueMask = 31; // bits 12-16
973   ebmpWORD GreenMask = 992; // bits 7-11
974   ebmpWORD RedMask = 31744; // bits 2-6
975 
976   // read the bit fields, if necessary, to
977   // override the default 5-5-5 mask
978 
979   if( bmih.biCompression != 0 )
980   {
981    // read the three bit masks
982 
983    ebmpWORD TempMaskWORD;
984 
985    SafeFread( (char*) &RedMask , 2 , 1 , fp );
986    if( IsBigEndian() )
987    { RedMask = FlipWORD(RedMask); }
988    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
989 
990    SafeFread( (char*) &GreenMask , 2 , 1 , fp );
991    if( IsBigEndian() )
992    { GreenMask = FlipWORD(GreenMask); }
993    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
994 
995    SafeFread( (char*) &BlueMask , 2 , 1 , fp );
996    if( IsBigEndian() )
997    { BlueMask = FlipWORD(BlueMask); }
998    SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
999   }
1000 
1001   // read and skip any meta data
1002 
1003   if( BytesToSkip > 0 )
1004   {
1005    if( EasyBMPwarnings )
1006    {
1007     cout << "EasyBMP Warning: Extra meta data detected in file "
1008          << FileName << endl
1009          << "                 Data will be skipped." << endl;
1010    }
1011    ebmpBYTE* TempSkipBYTE;
1012    TempSkipBYTE = new ebmpBYTE [BytesToSkip];
1013    SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);
1014    delete [] TempSkipBYTE;
1015   }
1016 
1017   // determine the red, green and blue shifts
1018 
1019   int GreenShift = 0;
1020   ebmpWORD TempShiftWORD = GreenMask;
1021   while( TempShiftWORD > 31 )
1022   { TempShiftWORD = TempShiftWORD>>1; GreenShift++; }
1023   int BlueShift = 0;
1024   TempShiftWORD = BlueMask;
1025   while( TempShiftWORD > 31 )
1026   { TempShiftWORD = TempShiftWORD>>1; BlueShift++; }
1027   int RedShift = 0;
1028   TempShiftWORD = RedMask;
1029   while( TempShiftWORD > 31 )
1030   { TempShiftWORD = TempShiftWORD>>1; RedShift++; }
1031 
1032   // read the actual pixels
1033 
1034   for( j=Height-1 ; j >= 0 ; j-- )
1035   {
1036    i=0;
1037    int ReadNumber = 0;
1038    while( ReadNumber < DataBytes )
1039    {
1040     ebmpWORD TempWORD;
1041     SafeFread( (char*) &TempWORD , 2 , 1 , fp );
1042     if( IsBigEndian() )
1043     { TempWORD = FlipWORD(TempWORD); }
1044     ReadNumber += 2;
1045 
1046     ebmpWORD Red = RedMask & TempWORD;
1047     ebmpWORD Green = GreenMask & TempWORD;
1048     ebmpWORD Blue = BlueMask & TempWORD;
1049 
1050     ebmpBYTE BlueBYTE = (ebmpBYTE) 8*(Blue>>BlueShift);
1051     ebmpBYTE GreenBYTE = (ebmpBYTE) 8*(Green>>GreenShift);
1052     ebmpBYTE RedBYTE = (ebmpBYTE) 8*(Red>>RedShift);
1053 
1054     (Pixels[i][j]).Red = RedBYTE;
1055     (Pixels[i][j]).Green = GreenBYTE;
1056     (Pixels[i][j]).Blue = BlueBYTE;
1057 
1058     i++;
1059    }
1060    ReadNumber = 0;
1061    while( ReadNumber < PaddingBytes )
1062    {
1063     ebmpBYTE TempBYTE;
1064     SafeFread( (char*) &TempBYTE , 1, 1, fp);
1065     ReadNumber++;
1066    }
1067   }
1068 
1069  }
1070 
1071  fclose(fp);
1072  return true;
1073 }
1074 
CreateStandardColorTable(void)1075 bool BMP::CreateStandardColorTable( void )
1076 {
1077  using namespace std;
1078  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
1079  {
1080   if( EasyBMPwarnings )
1081   {
1082    cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
1083         << "                 depth that does not require a color table." << endl
1084         << "                 Ignoring request." << endl;
1085   }
1086   return false;
1087  }
1088 
1089  if( BitDepth == 1 )
1090  {
1091   int i;
1092   for( i=0 ; i < 2 ; i++ )
1093   {
1094    Colors[i].Red = i*255;
1095    Colors[i].Green = i*255;
1096    Colors[i].Blue = i*255;
1097    Colors[i].Alpha = 0;
1098   }
1099   return true;
1100  }
1101 
1102  if( BitDepth == 4 )
1103  {
1104   int i = 0;
1105   int j,k,ell;
1106 
1107   // simplify the code for the first 8 colors
1108   for( ell=0 ; ell < 2 ; ell++ )
1109   {
1110    for( k=0 ; k < 2 ; k++ )
1111    {
1112     for( j=0 ; j < 2 ; j++ )
1113     {
1114      Colors[i].Red = j*128;
1115      Colors[i].Green = k*128;
1116       Colors[i].Blue = ell*128;
1117         i++;
1118     }
1119    }
1120   }
1121 
1122   // simplify the code for the last 8 colors
1123   for( ell=0 ; ell < 2 ; ell++ )
1124   {
1125    for( k=0 ; k < 2 ; k++ )
1126    {
1127     for( j=0 ; j < 2 ; j++ )
1128     {
1129      Colors[i].Red = j*255;
1130      Colors[i].Green = k*255;
1131      Colors[i].Blue = ell*255;
1132      i++;
1133     }
1134    }
1135   }
1136 
1137   // overwrite the duplicate color
1138   i=8;
1139   Colors[i].Red = 192;
1140   Colors[i].Green = 192;
1141   Colors[i].Blue = 192;
1142 
1143   for( i=0 ; i < 16 ; i++ )
1144   { Colors[i].Alpha = 0; }
1145   return true;
1146  }
1147 
1148  if( BitDepth == 8 )
1149  {
1150   int i=0;
1151   int j,k,ell;
1152 
1153   // do an easy loop, which works for all but colors
1154   // 0 to 9 and 246 to 255
1155   for( ell=0 ; ell < 4 ; ell++ )
1156   {
1157    for( k=0 ; k < 8 ; k++ )
1158    {
1159     for( j=0; j < 8 ; j++ )
1160     {
1161      Colors[i].Red = j*32;
1162      Colors[i].Green = k*32;
1163      Colors[i].Blue = ell*64;
1164      Colors[i].Alpha = 0;
1165      i++;
1166     }
1167    }
1168   }
1169 
1170   // now redo the first 8 colors
1171   i=0;
1172   for( ell=0 ; ell < 2 ; ell++ )
1173   {
1174    for( k=0 ; k < 2 ; k++ )
1175    {
1176     for( j=0; j < 2 ; j++ )
1177     {
1178      Colors[i].Red = j*128;
1179      Colors[i].Green = k*128;
1180      Colors[i].Blue = ell*128;
1181      i++;
1182     }
1183    }
1184   }
1185 
1186   // overwrite colors 7, 8, 9
1187   i=7;
1188   Colors[i].Red = 192;
1189   Colors[i].Green = 192;
1190   Colors[i].Blue = 192;
1191   i++; // 8
1192   Colors[i].Red = 192;
1193   Colors[i].Green = 220;
1194   Colors[i].Blue = 192;
1195   i++; // 9
1196   Colors[i].Red = 166;
1197   Colors[i].Green = 202;
1198   Colors[i].Blue = 240;
1199 
1200   // overwrite colors 246 to 255
1201   i=246;
1202   Colors[i].Red = 255;
1203   Colors[i].Green = 251;
1204   Colors[i].Blue = 240;
1205   i++; // 247
1206   Colors[i].Red = 160;
1207   Colors[i].Green = 160;
1208   Colors[i].Blue = 164;
1209   i++; // 248
1210   Colors[i].Red = 128;
1211   Colors[i].Green = 128;
1212   Colors[i].Blue = 128;
1213   i++; // 249
1214   Colors[i].Red = 255;
1215   Colors[i].Green = 0;
1216   Colors[i].Blue = 0;
1217   i++; // 250
1218   Colors[i].Red = 0;
1219   Colors[i].Green = 255;
1220   Colors[i].Blue = 0;
1221   i++; // 251
1222   Colors[i].Red = 255;
1223   Colors[i].Green = 255;
1224   Colors[i].Blue = 0;
1225   i++; // 252
1226   Colors[i].Red = 0;
1227   Colors[i].Green = 0;
1228   Colors[i].Blue = 255;
1229   i++; // 253
1230   Colors[i].Red = 255;
1231   Colors[i].Green = 0;
1232   Colors[i].Blue = 255;
1233   i++; // 254
1234   Colors[i].Red = 0;
1235   Colors[i].Green = 255;
1236   Colors[i].Blue = 255;
1237   i++; // 255
1238   Colors[i].Red = 255;
1239   Colors[i].Green = 255;
1240   Colors[i].Blue = 255;
1241 
1242   return true;
1243  }
1244  return true;
1245 }
1246 
SafeFread(char * buffer,int size,int number,FILE * fp)1247 bool SafeFread( char* buffer, int size, int number, FILE* fp )
1248 {
1249  using namespace std;
1250  int ItemsRead;
1251  if( feof(fp) )
1252  { return false; }
1253  ItemsRead = (int) fread( buffer , size , number , fp );
1254  if( ItemsRead < number )
1255  { return false; }
1256  return true;
1257 }
1258 
SetDPI(int HorizontalDPI,int VerticalDPI)1259 void BMP::SetDPI( int HorizontalDPI, int VerticalDPI )
1260 {
1261  XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 );
1262  YPelsPerMeter = (int) (   VerticalDPI * 39.37007874015748 );
1263 }
1264 
1265 // int BMP::TellVerticalDPI( void ) const
TellVerticalDPI(void)1266 int BMP::TellVerticalDPI( void )
1267 {
1268  if( !YPelsPerMeter )
1269  { YPelsPerMeter = DefaultYPelsPerMeter; }
1270  return (int) ( YPelsPerMeter / (double) 39.37007874015748 );
1271 }
1272 
1273 // int BMP::TellHorizontalDPI( void ) const
TellHorizontalDPI(void)1274 int BMP::TellHorizontalDPI( void )
1275 {
1276  if( !XPelsPerMeter )
1277  { XPelsPerMeter = DefaultXPelsPerMeter; }
1278  return (int) ( XPelsPerMeter / (double) 39.37007874015748 );
1279 }
1280 
1281 /* These functions are defined in EasyBMP_VariousBMPutilities.h */
1282 
GetBMFH(const char * szFileNameIn)1283 BMFH GetBMFH( const char* szFileNameIn )
1284 {
1285  using namespace std;
1286  BMFH bmfh;
1287 
1288  FILE* fp;
1289  fp = fopen( szFileNameIn,"rb");
1290 
1291  if( !fp  )
1292  {
1293   if( EasyBMPwarnings )
1294   {
1295    cout << "EasyBMP Error: Cannot initialize from file "
1296         << szFileNameIn << "." << endl
1297         << "               File cannot be opened or does not exist."
1298         << endl;
1299   }
1300   bmfh.bfType = 0;
1301   return bmfh;
1302  }
1303 
1304  SafeFread( (char*) &(bmfh.bfType) , sizeof(ebmpWORD) , 1 , fp );
1305  SafeFread( (char*) &(bmfh.bfSize) , sizeof(ebmpDWORD) , 1 , fp );
1306  SafeFread( (char*) &(bmfh.bfReserved1) , sizeof(ebmpWORD) , 1 , fp );
1307  SafeFread( (char*) &(bmfh.bfReserved2) , sizeof(ebmpWORD) , 1 , fp );
1308  SafeFread( (char*) &(bmfh.bfOffBits) , sizeof(ebmpDWORD) , 1 , fp );
1309 
1310  fclose( fp );
1311 
1312  if( IsBigEndian() )
1313  { bmfh.SwitchEndianess(); }
1314 
1315  return bmfh;
1316 }
1317 
GetBMIH(const char * szFileNameIn)1318 BMIH GetBMIH( const char* szFileNameIn )
1319 {
1320  using namespace std;
1321  BMFH bmfh;
1322  BMIH bmih;
1323 
1324  FILE* fp;
1325  fp = fopen( szFileNameIn,"rb");
1326 
1327  if( !fp  )
1328  {
1329   if( EasyBMPwarnings )
1330   {
1331    cout << "EasyBMP Error: Cannot initialize from file "
1332         << szFileNameIn << "." << endl
1333         << "               File cannot be opened or does not exist."
1334         << endl;
1335   }
1336   return bmih;
1337  }
1338 
1339  // read the bmfh, i.e., first 14 bytes (just to get it out of the way);
1340 
1341  ebmpBYTE TempBYTE;
1342  int i;
1343  for( i = 14 ; i > 0 ; i-- )
1344  { SafeFread( (char*) &TempBYTE , sizeof(ebmpBYTE) , 1, fp ); }
1345 
1346  // read the bmih
1347 
1348  SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
1349  SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp );
1350  SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp );
1351  SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp );
1352 
1353  SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
1354  SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp );
1355  SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp );
1356  SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
1357 
1358  SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
1359  SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp );
1360  SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp );
1361 
1362  fclose( fp );
1363 
1364  if( IsBigEndian() )
1365  { bmih.SwitchEndianess(); }
1366 
1367  return bmih;
1368 }
1369 
DisplayBitmapInfo(const char * szFileNameIn)1370 void DisplayBitmapInfo( const char* szFileNameIn )
1371 {
1372  using namespace std;
1373  FILE* fp;
1374  fp = fopen( szFileNameIn,"rb");
1375 
1376  if( !fp  )
1377  {
1378   if( EasyBMPwarnings )
1379   {
1380    cout << "EasyBMP Error: Cannot initialize from file "
1381         << szFileNameIn << "." << endl
1382         << "               File cannot be opened or does not exist."
1383         << endl;
1384   }
1385   return;
1386  }
1387  fclose( fp );
1388 
1389  // don't duplicate work! Just use the functions from above!
1390 
1391  BMFH bmfh = GetBMFH(szFileNameIn);
1392  BMIH bmih = GetBMIH(szFileNameIn);
1393 
1394  cout << "File information for file " << szFileNameIn
1395       << ":" << endl << endl;
1396 
1397  cout << "BITMAPFILEHEADER:" << endl
1398       << "bfType: " << bmfh.bfType << endl
1399       << "bfSize: " << bmfh.bfSize << endl
1400       << "bfReserved1: " << bmfh.bfReserved1 << endl
1401       << "bfReserved2: " << bmfh.bfReserved2 << endl
1402       << "bfOffBits: " << bmfh.bfOffBits << endl << endl;
1403 
1404  cout << "BITMAPINFOHEADER:" << endl
1405       << "biSize: " << bmih.biSize << endl
1406       << "biWidth: " << bmih.biWidth << endl
1407       << "biHeight: " << bmih.biHeight << endl
1408       << "biPlanes: " << bmih.biPlanes << endl
1409       << "biBitCount: " << bmih.biBitCount << endl
1410       << "biCompression: " << bmih.biCompression << endl
1411       << "biSizeImage: " << bmih.biSizeImage << endl
1412       << "biXPelsPerMeter: " << bmih.biXPelsPerMeter << endl
1413       << "biYPelsPerMeter: " << bmih.biYPelsPerMeter << endl
1414       << "biClrUsed: " << bmih.biClrUsed << endl
1415       << "biClrImportant: " << bmih.biClrImportant << endl << endl;
1416  return;
1417 }
1418 
GetBitmapColorDepth(const char * szFileNameIn)1419 int GetBitmapColorDepth( const char* szFileNameIn )
1420 {
1421  BMIH bmih = GetBMIH( szFileNameIn );
1422  return (int) bmih.biBitCount;
1423 }
1424 
PixelToPixelCopy(BMP & From,int FromX,int FromY,BMP & To,int ToX,int ToY)1425 void PixelToPixelCopy( BMP& From, int FromX, int FromY,
1426                        BMP& To, int ToX, int ToY)
1427 {
1428  *To(ToX,ToY) = *From(FromX,FromY);
1429  return;
1430 }
1431 
PixelToPixelCopyTransparent(BMP & From,int FromX,int FromY,BMP & To,int ToX,int ToY,RGBApixel & Transparent)1432 void PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY,
1433                                   BMP& To, int ToX, int ToY,
1434                                   RGBApixel& Transparent )
1435 {
1436  if( From(FromX,FromY)->Red != Transparent.Red ||
1437      From(FromX,FromY)->Green != Transparent.Green ||
1438      From(FromX,FromY)->Blue != Transparent.Blue )
1439  { *To(ToX,ToY) = *From(FromX,FromY); }
1440  return;
1441 }
1442 
RangedPixelToPixelCopy(BMP & From,int FromL,int FromR,int FromB,int FromT,BMP & To,int ToX,int ToY)1443 void RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT,
1444                              BMP& To, int ToX, int ToY )
1445 {
1446  // make sure the conventions are followed
1447  if( FromB < FromT )
1448  { int Temp = FromT; FromT = FromB; FromB = Temp; }
1449 
1450  // make sure that the copied regions exist in both bitmaps
1451  if( FromR >= From.TellWidth() )
1452  { FromR = From.TellWidth()-1; }
1453  if( FromL < 0 ){ FromL = 0; }
1454 
1455  if( FromB >= From.TellHeight() )
1456  { FromB = From.TellHeight()-1; }
1457  if( FromT < 0 ){ FromT = 0; }
1458 
1459  if( ToX+(FromR-FromL) >= To.TellWidth() )
1460  { FromR = To.TellWidth()-1+FromL-ToX; }
1461  if( ToY+(FromB-FromT) >= To.TellHeight() )
1462  { FromB = To.TellHeight()-1+FromT-ToY; }
1463 
1464  int i,j;
1465  for( j=FromT ; j <= FromB ; j++ )
1466  {
1467   for( i=FromL ; i <= FromR ; i++ )
1468   {
1469    PixelToPixelCopy( From, i,j,
1470                      To, ToX+(i-FromL), ToY+(j-FromT) );
1471   }
1472  }
1473 
1474  return;
1475 }
1476 
RangedPixelToPixelCopyTransparent(BMP & From,int FromL,int FromR,int FromB,int FromT,BMP & To,int ToX,int ToY,RGBApixel & Transparent)1477 void RangedPixelToPixelCopyTransparent(
1478      BMP& From, int FromL , int FromR, int FromB, int FromT,
1479      BMP& To, int ToX, int ToY ,
1480      RGBApixel& Transparent )
1481 {
1482  // make sure the conventions are followed
1483  if( FromB < FromT )
1484  { int Temp = FromT; FromT = FromB; FromB = Temp; }
1485 
1486  // make sure that the copied regions exist in both bitmaps
1487  if( FromR >= From.TellWidth() )
1488  { FromR = From.TellWidth()-1; }
1489  if( FromL < 0 ){ FromL = 0; }
1490 
1491  if( FromB >= From.TellHeight() )
1492  { FromB = From.TellHeight()-1; }
1493  if( FromT < 0 ){ FromT = 0; }
1494 
1495  if( ToX+(FromR-FromL) >= To.TellWidth() )
1496  { FromR = To.TellWidth()-1+FromL-ToX; }
1497  if( ToY+(FromB-FromT) >= To.TellHeight() )
1498  { FromB = To.TellHeight()-1+FromT-ToY; }
1499 
1500  int i,j;
1501  for( j=FromT ; j <= FromB ; j++ )
1502  {
1503   for( i=FromL ; i <= FromR ; i++ )
1504   {
1505    PixelToPixelCopyTransparent( From, i,j,
1506                      To, ToX+(i-FromL), ToY+(j-FromT) ,
1507                      Transparent);
1508   }
1509  }
1510 
1511  return;
1512 }
1513 
CreateGrayscaleColorTable(BMP & InputImage)1514 bool CreateGrayscaleColorTable( BMP& InputImage )
1515 {
1516  using namespace std;
1517  int BitDepth = InputImage.TellBitDepth();
1518  if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
1519  {
1520   if( EasyBMPwarnings )
1521   {
1522    cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl
1523         << "                 depth that does not require a color table." << endl
1524            << "                 Ignoring request." << endl;
1525   }
1526   return false;
1527  }
1528  int i;
1529  int NumberOfColors = InputImage.TellNumberOfColors();
1530 
1531  ebmpBYTE StepSize;
1532  if( BitDepth != 1 )
1533  { StepSize = 255/(NumberOfColors-1); }
1534  else
1535  { StepSize = 255; }
1536 
1537  for( i=0 ; i < NumberOfColors ; i++ )
1538  {
1539   ebmpBYTE TempBYTE = i*StepSize;
1540   RGBApixel TempColor;
1541   TempColor.Red = TempBYTE;
1542   TempColor.Green = TempBYTE;
1543   TempColor.Blue = TempBYTE;
1544   TempColor.Alpha = 0;
1545   InputImage.SetColor( i , TempColor );
1546  }
1547  return true;
1548 }
1549 
Read32bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1550 bool BMP::Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1551 {
1552  int i;
1553  if( Width*4 > BufferSize )
1554  { return false; }
1555  for( i=0 ; i < Width ; i++ )
1556  { memcpy( (char*) &(Pixels[i][Row]), (char*) Buffer+4*i, 4 ); }
1557  return true;
1558 }
1559 
Read24bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1560 bool BMP::Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1561 {
1562  int i;
1563  if( Width*3 > BufferSize )
1564  { return false; }
1565  for( i=0 ; i < Width ; i++ )
1566  { memcpy( (char*) &(Pixels[i][Row]), Buffer+3*i, 3 ); }
1567  return true;
1568 }
1569 
Read8bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1570 bool BMP::Read8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1571 {
1572  int i;
1573  if( Width > BufferSize )
1574  { return false; }
1575  for( i=0 ; i < Width ; i++ )
1576  {
1577   int Index = Buffer[i];
1578   *( this->operator()(i,Row) )= GetColor(Index);
1579  }
1580  return true;
1581 }
1582 
Read4bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1583 bool BMP::Read4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1584 {
1585  int Shifts[2] = {4  ,0 };
1586  int Masks[2]  = {240,15};
1587 
1588  int i=0;
1589  int j;
1590  int k=0;
1591  if( Width > 2*BufferSize )
1592  { return false; }
1593  while( i < Width )
1594  {
1595   j=0;
1596   while( j < 2 && i < Width )
1597   {
1598    int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
1599    *( this->operator()(i,Row) )= GetColor(Index);
1600    i++; j++;
1601   }
1602   k++;
1603  }
1604  return true;
1605 }
Read1bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1606 bool BMP::Read1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1607 {
1608  int Shifts[8] = {7  ,6 ,5 ,4 ,3,2,1,0};
1609  int Masks[8]  = {128,64,32,16,8,4,2,1};
1610 
1611  int i=0;
1612  int j;
1613  int k=0;
1614 
1615  if( Width > 8*BufferSize )
1616  { return false; }
1617  while( i < Width )
1618  {
1619   j=0;
1620   while( j < 8 && i < Width )
1621   {
1622    int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
1623    *( this->operator()(i,Row) )= GetColor(Index);
1624    i++; j++;
1625   }
1626   k++;
1627  }
1628  return true;
1629 }
1630 
Write32bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1631 bool BMP::Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1632 {
1633  int i;
1634  if( Width*4 > BufferSize )
1635  { return false; }
1636  for( i=0 ; i < Width ; i++ )
1637  { memcpy( (char*) Buffer+4*i, (char*) &(Pixels[i][Row]), 4 ); }
1638  return true;
1639 }
1640 
Write24bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1641 bool BMP::Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
1642 {
1643  int i;
1644  if( Width*3 > BufferSize )
1645  { return false; }
1646  for( i=0 ; i < Width ; i++ )
1647  { memcpy( (char*) Buffer+3*i,  (char*) &(Pixels[i][Row]), 3 ); }
1648  return true;
1649 }
1650 
Write8bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1651 bool BMP::Write8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1652 {
1653  int i;
1654  if( Width > BufferSize )
1655  { return false; }
1656  for( i=0 ; i < Width ; i++ )
1657  { Buffer[i] = FindClosestColor( Pixels[i][Row] ); }
1658  return true;
1659 }
1660 
Write4bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1661 bool BMP::Write4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1662 {
1663  int PositionWeights[2]  = {16,1};
1664 
1665  int i=0;
1666  int j;
1667  int k=0;
1668  if( Width > 2*BufferSize )
1669  { return false; }
1670  while( i < Width )
1671  {
1672   j=0;
1673   int Index = 0;
1674   while( j < 2 && i < Width )
1675   {
1676    Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) );
1677    i++; j++;
1678   }
1679   Buffer[k] = (ebmpBYTE) Index;
1680   k++;
1681  }
1682  return true;
1683 }
1684 
Write1bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)1685 bool BMP::Write1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
1686 {
1687  int PositionWeights[8]  = {128,64,32,16,8,4,2,1};
1688 
1689  int i=0;
1690  int j;
1691  int k=0;
1692  if( Width > 8*BufferSize )
1693  { return false; }
1694  while( i < Width )
1695  {
1696   j=0;
1697   int Index = 0;
1698   while( j < 8 && i < Width )
1699   {
1700    Index += ( PositionWeights[j]* (int) FindClosestColor( Pixels[i][Row] ) );
1701    i++; j++;
1702   }
1703   Buffer[k] = (ebmpBYTE) Index;
1704   k++;
1705  }
1706  return true;
1707 }
1708 
FindClosestColor(RGBApixel & input)1709 ebmpBYTE BMP::FindClosestColor( RGBApixel& input )
1710 {
1711  using namespace std;
1712 
1713  int i=0;
1714  int NumberOfColors = TellNumberOfColors();
1715  ebmpBYTE BestI = 0;
1716  int BestMatch = 999999;
1717 
1718  while( i < NumberOfColors )
1719  {
1720   RGBApixel Attempt = GetColor( i );
1721   int TempMatch = IntSquare( (int) Attempt.Red - (int) input.Red )
1722                 + IntSquare( (int) Attempt.Green - (int) input.Green )
1723                 + IntSquare( (int) Attempt.Blue - (int) input.Blue );
1724   if( TempMatch < BestMatch )
1725   { BestI = (ebmpBYTE) i; BestMatch = TempMatch; }
1726   if( BestMatch < 1 )
1727   { break; }
1728   i++;
1729  }
1730  return BestI;
1731 }
1732 
EasyBMPcheckDataSize(void)1733 bool EasyBMPcheckDataSize( void )
1734 {
1735  using namespace std;
1736  bool ReturnValue = true;
1737  if( sizeof( ebmpBYTE ) != 1 )
1738  {
1739   if( EasyBMPwarnings )
1740   {
1741    cout << "EasyBMP Error: ebmpBYTE has the wrong size ("
1742         << sizeof( ebmpBYTE ) << " bytes)," << endl
1743         << "               Compared to the expected 1 byte value" << endl;
1744   }
1745   ReturnValue = false;
1746  }
1747  if( sizeof( ebmpWORD ) != 2 )
1748  {
1749   if( EasyBMPwarnings )
1750   {
1751    cout << "EasyBMP Error: ebmpWORD has the wrong size ("
1752         << sizeof( ebmpWORD ) << " bytes)," << endl
1753         << "               Compared to the expected 2 byte value" << endl;
1754   }
1755   ReturnValue = false;
1756  }
1757  if( sizeof( ebmpDWORD ) != 4 )
1758  {
1759   if( EasyBMPwarnings )
1760   {
1761    cout << "EasyBMP Error: ebmpDWORD has the wrong size ("
1762         << sizeof( ebmpDWORD ) << " bytes)," << endl
1763         << "               Compared to the expected 4 byte value" << endl;
1764   }
1765   ReturnValue = false;
1766  }
1767  return ReturnValue;
1768 }
1769 
Rescale(BMP & InputImage,char mode,int NewDimension)1770 bool Rescale( BMP& InputImage , char mode, int NewDimension )
1771 {
1772  using namespace std;
1773  int CapMode = toupper( mode );
1774 
1775  BMP OldImage( InputImage );
1776 
1777  if( CapMode != 'P' &&
1778      CapMode != 'W' &&
1779      CapMode != 'H' &&
1780      CapMode != 'F' )
1781  {
1782   if( EasyBMPwarnings )
1783   {
1784    char ErrorMessage [1024];
1785    sprintf( ErrorMessage, "EasyBMP Error: Unknown rescale mode %c requested\n" , mode );
1786    cout << ErrorMessage;
1787   }
1788   return false;
1789  }
1790 
1791  int NewWidth  =0;
1792  int NewHeight =0;
1793 
1794  int OldWidth = OldImage.TellWidth();
1795  int OldHeight= OldImage.TellHeight();
1796 
1797  if( CapMode == 'P' )
1798  {
1799   NewWidth = (int) floor( OldWidth * NewDimension / 100.0 );
1800   NewHeight = (int) floor( OldHeight * NewDimension / 100.0 );
1801  }
1802  if( CapMode == 'F' )
1803  {
1804   if( OldWidth > OldHeight )
1805   { CapMode = 'W'; }
1806   else
1807   { CapMode = 'H'; }
1808  }
1809 
1810  if( CapMode == 'W' )
1811  {
1812   double percent = (double) NewDimension / (double) OldWidth;
1813   NewWidth = NewDimension;
1814   NewHeight = (int) floor( OldHeight * percent );
1815  }
1816  if( CapMode == 'H' )
1817  {
1818   double percent = (double) NewDimension / (double) OldHeight;
1819   NewHeight = NewDimension;
1820   NewWidth = (int) floor( OldWidth * percent );
1821  }
1822 
1823  if( NewWidth < 1 )
1824  { NewWidth = 1; }
1825  if( NewHeight < 1 )
1826  { NewHeight = 1; }
1827 
1828  InputImage.SetSize( NewWidth, NewHeight );
1829  InputImage.SetBitDepth( 24 );
1830 
1831  int I,J;
1832  double ThetaI,ThetaJ;
1833 
1834  for( int j=0; j < NewHeight-1 ; j++ )
1835  {
1836   ThetaJ = (double)(j*(OldHeight-1.0))
1837           /(double)(NewHeight-1.0);
1838   J    = (int) floor( ThetaJ );
1839   ThetaJ -= J;
1840 
1841   for( int i=0; i < NewWidth-1 ; i++ )
1842   {
1843    ThetaI = (double)(i*(OldWidth-1.0))
1844            /(double)(NewWidth-1.0);
1845    I = (int) floor( ThetaI );
1846    ThetaI -= I;
1847 
1848    InputImage(i,j)->Red = (ebmpBYTE)
1849                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*(OldImage(I,J)->Red)
1850                            +(ThetaI-ThetaI*ThetaJ)*(OldImage(I+1,J)->Red)
1851                            +(ThetaJ-ThetaI*ThetaJ)*(OldImage(I,J+1)->Red)
1852                            +(ThetaI*ThetaJ)*(OldImage(I+1,J+1)->Red) );
1853    InputImage(i,j)->Green = (ebmpBYTE)
1854                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Green
1855                            +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Green
1856                            +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Green
1857                            +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Green );
1858    InputImage(i,j)->Blue = (ebmpBYTE)
1859                           ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Blue
1860                            +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Blue
1861                            +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Blue
1862                            +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Blue );
1863   }
1864    InputImage(NewWidth-1,j)->Red = (ebmpBYTE)
1865                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Red)
1866                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Red) );
1867    InputImage(NewWidth-1,j)->Green = (ebmpBYTE)
1868                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Green)
1869                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Green) );
1870    InputImage(NewWidth-1,j)->Blue = (ebmpBYTE)
1871                             ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Blue)
1872                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Blue) );
1873  }
1874 
1875  for( int i=0 ; i < NewWidth-1 ; i++ )
1876  {
1877   ThetaI = (double)(i*(OldWidth-1.0))
1878           /(double)(NewWidth-1.0);
1879   I = (int) floor( ThetaI );
1880   ThetaI -= I;
1881   InputImage(i,NewHeight-1)->Red = (ebmpBYTE)
1882                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Red)
1883                           + ThetaI*(OldImage(I,OldHeight-1)->Red) );
1884   InputImage(i,NewHeight-1)->Green = (ebmpBYTE)
1885                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Green)
1886                           + ThetaI*(OldImage(I,OldHeight-1)->Green) );
1887   InputImage(i,NewHeight-1)->Blue = (ebmpBYTE)
1888                             ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Blue)
1889                           + ThetaI*(OldImage(I,OldHeight-1)->Blue) );
1890  }
1891 
1892  *InputImage(NewWidth-1,NewHeight-1) = *OldImage(OldWidth-1,OldHeight-1);
1893  return true;
1894 }
1895