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