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 = NULL;
231
232 XPelsPerMeter = 0;
233 YPelsPerMeter = 0;
234
235 MetaData1 = NULL;
236 SizeOfMetaData1 = 0;
237 MetaData2 = NULL;
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 = NULL;
252 XPelsPerMeter = 0;
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
~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 = NULL; }
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 == 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++;
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 == 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);
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);
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
CreateStandardColorTable(void)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
SafeFread(char * buffer,int size,int number,FILE * fp)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
SetDPI(int HorizontalDPI,int VerticalDPI)1270 void BMP::SetDPI( int HorizontalDPI, int VerticalDPI )
1271 {
1272 XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 );
1273 YPelsPerMeter = (int) ( VerticalDPI * 39.37007874015748 );
1274 }
1275
1276 // int BMP::TellVerticalDPI( void ) const
TellVerticalDPI(void)1277 int BMP::TellVerticalDPI( void )
1278 {
1279 if( !YPelsPerMeter )
1280 { YPelsPerMeter = DefaultYPelsPerMeter; }
1281 return (int) ( YPelsPerMeter / (double) 39.37007874015748 );
1282 }
1283
1284 // int BMP::TellHorizontalDPI( void ) const
TellHorizontalDPI(void)1285 int BMP::TellHorizontalDPI( void )
1286 {
1287 if( !XPelsPerMeter )
1288 { XPelsPerMeter = DefaultXPelsPerMeter; }
1289 return (int) ( XPelsPerMeter / (double) 39.37007874015748 );
1290 }
1291
1292 /* These functions are defined in EasyBMP_VariousBMPutilities.h */
1293
GetBMFH(const char * szFileNameIn)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
GetBMIH(const char * szFileNameIn)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
DisplayBitmapInfo(const char * szFileNameIn)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
GetBitmapColorDepth(const char * szFileNameIn)1430 int GetBitmapColorDepth( const char* szFileNameIn )
1431 {
1432 BMIH bmih = GetBMIH( szFileNameIn );
1433 return (int) bmih.biBitCount;
1434 }
1435
PixelToPixelCopy(BMP & From,int FromX,int FromY,BMP & To,int ToX,int ToY)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
PixelToPixelCopyTransparent(BMP & From,int FromX,int FromY,BMP & To,int ToX,int ToY,RGBApixel & Transparent)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
RangedPixelToPixelCopy(BMP & From,int FromL,int FromR,int FromB,int FromT,BMP & To,int ToX,int ToY)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
RangedPixelToPixelCopyTransparent(BMP & From,int FromL,int FromR,int FromB,int FromT,BMP & To,int ToX,int ToY,RGBApixel & Transparent)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
CreateGrayscaleColorTable(BMP & InputImage)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
Read32bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Read24bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Read8bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Read4bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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 }
Read1bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Write32bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Write24bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Write8bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Write4bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
Write1bitRow(ebmpBYTE * Buffer,int BufferSize,int Row)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
FindClosestColor(RGBApixel & input)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
EasyBMPcheckDataSize(void)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
Rescale(BMP & InputImage,char mode,int NewDimension)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 }
1906