1 // ==========================================================
2 // BMP Loader and Writer
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Markus Loibl (markus.loibl@epost.de)
7 // - Martin Weber (martweb@gmx.net)
8 // - Herv� Drolon (drolon@infonie.fr)
9 // - Michal Novotny (michal@etc.cz)
10 // - Mihail Naydenov (mnaydenov@users.sourceforge.net)
11 //
12 // This file is part of FreeImage 3
13 //
14 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
15 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
16 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
17 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
18 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
19 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
20 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
21 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
22 // THIS DISCLAIMER.
23 //
24 // Use at your own risk!
25 // ==========================================================
26
27 #include "FreeImage.h"
28 #include "Utilities.h"
29
30 // ----------------------------------------------------------
31 // Constants + headers
32 // ----------------------------------------------------------
33
34 static const BYTE RLE_COMMAND = 0;
35 static const BYTE RLE_ENDOFLINE = 0;
36 static const BYTE RLE_ENDOFBITMAP = 1;
37 static const BYTE RLE_DELTA = 2;
38
39 static const BYTE BI_RGB = 0; // compression: none
40 static const BYTE BI_RLE8 = 1; // compression: RLE 8-bit/pixel
41 static const BYTE BI_RLE4 = 2; // compression: RLE 4-bit/pixel
42 static const BYTE BI_BITFIELDS = 3; // compression: Bit field or Huffman 1D compression for BITMAPCOREHEADER2
43 static const BYTE BI_JPEG = 4; // compression: JPEG or RLE-24 compression for BITMAPCOREHEADER2
44 static const BYTE BI_PNG = 5; // compression: PNG
45 static const BYTE BI_ALPHABITFIELDS = 6; // compression: Bit field (this value is valid in Windows CE .NET 4.0 and later)
46
47 // ----------------------------------------------------------
48
49 #ifdef _WIN32
50 #pragma pack(push, 1)
51 #else
52 #pragma pack(1)
53 #endif
54
55 typedef struct tagBITMAPCOREHEADER {
56 DWORD bcSize;
57 WORD bcWidth;
58 WORD bcHeight;
59 WORD bcPlanes;
60 WORD bcBitCnt;
61 } BITMAPCOREHEADER, *PBITMAPCOREHEADER;
62
63 typedef struct tagBITMAPINFOOS2_1X_HEADER {
64 DWORD biSize;
65 WORD biWidth;
66 WORD biHeight;
67 WORD biPlanes;
68 WORD biBitCount;
69 } BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER;
70
71 typedef struct tagBITMAPFILEHEADER {
72 WORD bfType; //! The file type
73 DWORD bfSize; //! The size, in bytes, of the bitmap file
74 WORD bfReserved1; //! Reserved; must be zero
75 WORD bfReserved2; //! Reserved; must be zero
76 DWORD bfOffBits; //! The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits
77 } BITMAPFILEHEADER, *PBITMAPFILEHEADER;
78
79 #ifdef _WIN32
80 #pragma pack(pop)
81 #else
82 #pragma pack()
83 #endif
84
85 // ==========================================================
86 // Plugin Interface
87 // ==========================================================
88
89 static int s_format_id;
90
91 // ==========================================================
92 // Internal functions
93 // ==========================================================
94
95 #ifdef FREEIMAGE_BIGENDIAN
96 static void
SwapInfoHeader(BITMAPINFOHEADER * header)97 SwapInfoHeader(BITMAPINFOHEADER *header) {
98 SwapLong(&header->biSize);
99 SwapLong((DWORD *)&header->biWidth);
100 SwapLong((DWORD *)&header->biHeight);
101 SwapShort(&header->biPlanes);
102 SwapShort(&header->biBitCount);
103 SwapLong(&header->biCompression);
104 SwapLong(&header->biSizeImage);
105 SwapLong((DWORD *)&header->biXPelsPerMeter);
106 SwapLong((DWORD *)&header->biYPelsPerMeter);
107 SwapLong(&header->biClrUsed);
108 SwapLong(&header->biClrImportant);
109 }
110
111 static void
SwapCoreHeader(BITMAPCOREHEADER * header)112 SwapCoreHeader(BITMAPCOREHEADER *header) {
113 SwapLong(&header->bcSize);
114 SwapShort(&header->bcWidth);
115 SwapShort(&header->bcHeight);
116 SwapShort(&header->bcPlanes);
117 SwapShort(&header->bcBitCnt);
118 }
119
120 static void
SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER * header)121 SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header) {
122 SwapLong(&header->biSize);
123 SwapShort(&header->biWidth);
124 SwapShort(&header->biHeight);
125 SwapShort(&header->biPlanes);
126 SwapShort(&header->biBitCount);
127 }
128
129 static void
SwapFileHeader(BITMAPFILEHEADER * header)130 SwapFileHeader(BITMAPFILEHEADER *header) {
131 SwapShort(&header->bfType);
132 SwapLong(&header->bfSize);
133 SwapShort(&header->bfReserved1);
134 SwapShort(&header->bfReserved2);
135 SwapLong(&header->bfOffBits);
136 }
137 #endif
138
139 // --------------------------------------------------------------------------
140
141 /**
142 Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
143 @param io FreeImage IO
144 @param handle FreeImage IO handle
145 @param dib Image to be loaded
146 @param height Image height
147 @param pitch Image pitch
148 @param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
149 @return Returns TRUE if successful, returns FALSE otherwise
150 */
151 static BOOL
LoadPixelData(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,int height,unsigned pitch,unsigned bit_count)152 LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, unsigned pitch, unsigned bit_count) {
153 unsigned count = 0;
154
155 // Load pixel data
156 // NB: height can be < 0 for BMP data
157 if (height > 0) {
158 count = io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
159 if(count != 1) {
160 return FALSE;
161 }
162 } else {
163 int positiveHeight = abs(height);
164 for (int c = 0; c < positiveHeight; ++c) {
165 count = io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, handle);
166 if(count != 1) {
167 return FALSE;
168 }
169 }
170 }
171
172 // swap as needed
173 #ifdef FREEIMAGE_BIGENDIAN
174 if (bit_count == 16) {
175 for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
176 WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
177 for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
178 SwapShort(pixel);
179 pixel++;
180 }
181 }
182 }
183 #endif
184 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
185 if (bit_count == 24 || bit_count == 32) {
186 for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
187 BYTE *pixel = FreeImage_GetScanLine(dib, y);
188 for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
189 INPLACESWAP(pixel[0], pixel[2]);
190 pixel += (bit_count >> 3);
191 }
192 }
193 }
194 #endif
195
196 return TRUE;
197 }
198
199 /**
200 Load image pixels for 4-bit RLE compressed dib
201 @param io FreeImage IO
202 @param handle FreeImage IO handle
203 @param width Image width
204 @param height Image height
205 @param dib Image to be loaded
206 @return Returns TRUE if successful, returns FALSE otherwise
207 */
208 static BOOL
LoadPixelDataRLE4(FreeImageIO * io,fi_handle handle,int width,int height,FIBITMAP * dib)209 LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
210 int status_byte = 0;
211 BYTE second_byte = 0;
212 int bits = 0;
213
214 BYTE *pixels = NULL; // temporary 8-bit buffer
215
216 try {
217 height = abs(height);
218
219 pixels = (BYTE*)malloc(width * height * sizeof(BYTE));
220 if(!pixels) throw(1);
221 memset(pixels, 0, width * height * sizeof(BYTE));
222
223 BYTE *q = pixels;
224 BYTE *end = pixels + height * width;
225
226 for (int scanline = 0; scanline < height; ) {
227 if (q < pixels || q >= end) {
228 break;
229 }
230 if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
231 throw(1);
232 }
233 if (status_byte != 0) {
234 status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
235 // Encoded mode
236 if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
237 throw(1);
238 }
239 for (int i = 0; i < status_byte; i++) {
240 *q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
241 }
242 bits += status_byte;
243 }
244 else {
245 // Escape mode
246 if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
247 throw(1);
248 }
249 switch (status_byte) {
250 case RLE_ENDOFLINE:
251 {
252 // End of line
253 bits = 0;
254 scanline++;
255 q = pixels + scanline*width;
256 }
257 break;
258
259 case RLE_ENDOFBITMAP:
260 // End of bitmap
261 q = end;
262 break;
263
264 case RLE_DELTA:
265 {
266 // read the delta values
267
268 BYTE delta_x = 0;
269 BYTE delta_y = 0;
270
271 if(io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
272 throw(1);
273 }
274 if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
275 throw(1);
276 }
277
278 // apply them
279
280 bits += delta_x;
281 scanline += delta_y;
282 q = pixels + scanline*width+bits;
283 }
284 break;
285
286 default:
287 {
288 // Absolute mode
289 status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
290 for (int i = 0; i < status_byte; i++) {
291 if ((i & 0x01) == 0) {
292 if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
293 throw(1);
294 }
295 }
296 *q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
297 }
298 bits += status_byte;
299 // Read pad byte
300 if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) {
301 BYTE padding = 0;
302 if(io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) {
303 throw(1);
304 }
305 }
306 }
307 break;
308 }
309 }
310 }
311
312 {
313 // Convert to 4-bit
314 for(int y = 0; y < height; y++) {
315 const BYTE *src = (BYTE*)pixels + y * width;
316 BYTE *dst = FreeImage_GetScanLine(dib, y);
317
318 BOOL hinibble = TRUE;
319
320 for (int cols = 0; cols < width; cols++){
321 if (hinibble) {
322 dst[cols >> 1] = (src[cols] << 4);
323 } else {
324 dst[cols >> 1] |= src[cols];
325 }
326
327 hinibble = !hinibble;
328 }
329 }
330 }
331
332 free(pixels);
333
334 return TRUE;
335
336 } catch(int) {
337 if(pixels) free(pixels);
338 return FALSE;
339 }
340 }
341
342 /**
343 Load image pixels for 8-bit RLE compressed dib
344 @param io FreeImage IO
345 @param handle FreeImage IO handle
346 @param width Image width
347 @param height Image height
348 @param dib Image to be loaded
349 @return Returns TRUE if successful, returns FALSE otherwise
350 */
351 static BOOL
LoadPixelDataRLE8(FreeImageIO * io,fi_handle handle,int width,int height,FIBITMAP * dib)352 LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
353 BYTE status_byte = 0;
354 BYTE second_byte = 0;
355 int scanline = 0;
356 int bits = 0;
357
358 for (;;) {
359 if( io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
360 return FALSE;
361 }
362
363 switch (status_byte) {
364 case RLE_COMMAND :
365 if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
366 return FALSE;
367 }
368
369 switch (status_byte) {
370 case RLE_ENDOFLINE :
371 bits = 0;
372 scanline++;
373 break;
374
375 case RLE_ENDOFBITMAP :
376 return TRUE;
377
378 case RLE_DELTA :
379 {
380 // read the delta values
381
382 BYTE delta_x = 0;
383 BYTE delta_y = 0;
384
385 if(io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
386 return FALSE;
387 }
388 if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
389 return FALSE;
390 }
391
392 // apply them
393
394 bits += delta_x;
395 scanline += delta_y;
396
397 break;
398 }
399
400 default :
401 {
402 if(scanline >= abs(height)) {
403 return TRUE;
404 }
405
406 int count = MIN((int)status_byte, width - bits);
407
408 BYTE *sline = FreeImage_GetScanLine(dib, scanline);
409
410 if(io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
411 return FALSE;
412 }
413
414 // align run length to even number of bytes
415
416 if ((status_byte & 1) == 1) {
417 if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
418 return FALSE;
419 }
420 }
421
422 bits += status_byte;
423
424 break;
425 }
426 }
427
428 break;
429
430 default :
431 {
432 if(scanline >= abs(height)) {
433 return TRUE;
434 }
435
436 int count = MIN((int)status_byte, width - bits);
437
438 BYTE *sline = FreeImage_GetScanLine(dib, scanline);
439
440 if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
441 return FALSE;
442 }
443
444 for (int i = 0; i < count; i++) {
445 *(sline + bits) = second_byte;
446
447 bits++;
448 }
449
450 break;
451 }
452 }
453 }
454 }
455
456 // --------------------------------------------------------------------------
457
458 static FIBITMAP *
LoadWindowsBMP(FreeImageIO * io,fi_handle handle,int flags,unsigned bitmap_bits_offset,int type)459 LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset, int type) {
460 FIBITMAP *dib = NULL;
461
462 try {
463 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
464
465 // load the info header
466
467 BITMAPINFOHEADER bih;
468
469 io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
470 #ifdef FREEIMAGE_BIGENDIAN
471 SwapInfoHeader(&bih);
472 #endif
473
474 // keep some general information about the bitmap
475
476 unsigned used_colors = bih.biClrUsed;
477 int width = bih.biWidth;
478 int height = bih.biHeight; // WARNING: height can be < 0 => check each call using 'height' as a parameter
479 unsigned bit_count = bih.biBitCount;
480 unsigned compression = bih.biCompression;
481 unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
482
483 switch (bit_count) {
484 case 1 :
485 case 4 :
486 case 8 :
487 {
488 if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) {
489 used_colors = CalculateUsedPaletteEntries(bit_count);
490 }
491
492 // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
493
494 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
495 if (dib == NULL) {
496 throw FI_MSG_ERROR_DIB_MEMORY;
497 }
498
499 // set resolution information
500 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
501 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
502
503 // seek to the end of the header (depending on the BMP header version)
504 // type == sizeof(BITMAPVxINFOHEADER)
505 switch(type) {
506 case 40: // sizeof(BITMAPINFOHEADER) - all Windows versions since Windows 3.0
507 break;
508 case 52: // sizeof(BITMAPV2INFOHEADER) (undocumented)
509 case 56: // sizeof(BITMAPV3INFOHEADER) (undocumented)
510 case 108: // sizeof(BITMAPV4HEADER) - all Windows versions since Windows 95/NT4 (not supported)
511 case 124: // sizeof(BITMAPV5HEADER) - Windows 98/2000 and newer (not supported)
512 io->seek_proc(handle, (long)(type - sizeof(BITMAPINFOHEADER)), SEEK_CUR);
513 break;
514 }
515
516 // load the palette
517
518 io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
519 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
520 RGBQUAD *pal = FreeImage_GetPalette(dib);
521 for(int i = 0; i < used_colors; i++) {
522 INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
523 }
524 #endif
525
526 if(header_only) {
527 // header only mode
528 return dib;
529 }
530
531 // seek to the actual pixel data.
532 // this is needed because sometimes the palette is larger than the entries it contains predicts
533 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
534
535 // read the pixel data
536
537 switch (compression) {
538 case BI_RGB :
539 if( LoadPixelData(io, handle, dib, height, pitch, bit_count) ) {
540 return dib;
541 } else {
542 throw "Error encountered while decoding BMP data";
543 }
544 break;
545
546 case BI_RLE4 :
547 if( LoadPixelDataRLE4(io, handle, width, height, dib) ) {
548 return dib;
549 } else {
550 throw "Error encountered while decoding RLE4 BMP data";
551 }
552 break;
553
554 case BI_RLE8 :
555 if( LoadPixelDataRLE8(io, handle, width, height, dib) ) {
556 return dib;
557 } else {
558 throw "Error encountered while decoding RLE8 BMP data";
559 }
560 break;
561
562 default :
563 throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
564 }
565 }
566 break; // 1-, 4-, 8-bit
567
568 case 16 :
569 {
570 int use_bitfields = 0;
571 if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
572 else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
573 else if (type == 52) use_bitfields = 3;
574 else if (type >= 56) use_bitfields = 4;
575
576 if (use_bitfields > 0) {
577 DWORD bitfields[4];
578 io->read_proc(bitfields, use_bitfields * sizeof(DWORD), 1, handle);
579 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
580 } else {
581 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
582 }
583
584 if (dib == NULL) {
585 throw FI_MSG_ERROR_DIB_MEMORY;
586 }
587
588 // set resolution information
589 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
590 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
591
592 if(header_only) {
593 // header only mode
594 return dib;
595 }
596
597 // seek to the actual pixel data
598 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
599
600 // load pixel data and swap as needed if OS is Big Endian
601 LoadPixelData(io, handle, dib, height, pitch, bit_count);
602
603 return dib;
604 }
605 break; // 16-bit
606
607 case 24 :
608 case 32 :
609 {
610 int use_bitfields = 0;
611 if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
612 else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
613 else if (type == 52) use_bitfields = 3;
614 else if (type >= 56) use_bitfields = 4;
615
616 if (use_bitfields > 0) {
617 DWORD bitfields[4];
618 io->read_proc(bitfields, use_bitfields * sizeof(DWORD), 1, handle);
619 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
620 } else {
621 if( bit_count == 32 ) {
622 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
623 } else {
624 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
625 }
626 }
627
628 if (dib == NULL) {
629 throw FI_MSG_ERROR_DIB_MEMORY;
630 }
631
632 // set resolution information
633 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
634 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
635
636 if(header_only) {
637 // header only mode
638 return dib;
639 }
640
641 // Skip over the optional palette
642 // A 24 or 32 bit DIB may contain a palette for faster color reduction
643 // i.e. you can have (FreeImage_GetColorsUsed(dib) > 0)
644
645 // seek to the actual pixel data
646 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
647
648 // read in the bitmap bits
649 // load pixel data and swap as needed if OS is Big Endian
650 LoadPixelData(io, handle, dib, height, pitch, bit_count);
651
652 // check if the bitmap contains transparency, if so enable it in the header
653
654 FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
655
656 return dib;
657 }
658 break; // 24-, 32-bit
659 }
660 } catch(const char *message) {
661 if(dib) {
662 FreeImage_Unload(dib);
663 }
664 if(message) {
665 FreeImage_OutputMessageProc(s_format_id, message);
666 }
667 }
668
669 return NULL;
670 }
671
672 // --------------------------------------------------------------------------
673
674 static FIBITMAP *
LoadOS22XBMP(FreeImageIO * io,fi_handle handle,int flags,unsigned bitmap_bits_offset)675 LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
676 FIBITMAP *dib = NULL;
677
678 try {
679 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
680
681 // load the info header
682
683 BITMAPINFOHEADER bih;
684
685 io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
686 #ifdef FREEIMAGE_BIGENDIAN
687 SwapInfoHeader(&bih);
688 #endif
689
690 // keep some general information about the bitmap
691
692 unsigned used_colors = bih.biClrUsed;
693 int width = bih.biWidth;
694 int height = bih.biHeight; // WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
695 unsigned bit_count = bih.biBitCount;
696 unsigned compression = bih.biCompression;
697 unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
698
699 switch (bit_count) {
700 case 1 :
701 case 4 :
702 case 8 :
703 {
704 if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
705 used_colors = CalculateUsedPaletteEntries(bit_count);
706
707 // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
708
709 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
710
711 if (dib == NULL) {
712 throw FI_MSG_ERROR_DIB_MEMORY;
713 }
714
715 // set resolution information
716 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
717 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
718
719 // load the palette
720 // note that it may contain RGB or RGBA values : we will calculate this
721 unsigned pal_size = (bitmap_bits_offset - sizeof(BITMAPFILEHEADER) - bih.biSize) / used_colors;
722
723 io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);
724
725 RGBQUAD *pal = FreeImage_GetPalette(dib);
726
727 if(pal_size == 4) {
728 for (unsigned count = 0; count < used_colors; count++) {
729 FILE_BGRA bgra;
730
731 io->read_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
732
733 pal[count].rgbRed = bgra.r;
734 pal[count].rgbGreen = bgra.g;
735 pal[count].rgbBlue = bgra.b;
736 }
737 } else if(pal_size == 3) {
738 for (unsigned count = 0; count < used_colors; count++) {
739 FILE_BGR bgr;
740
741 io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
742
743 pal[count].rgbRed = bgr.r;
744 pal[count].rgbGreen = bgr.g;
745 pal[count].rgbBlue = bgr.b;
746 }
747 }
748
749 if(header_only) {
750 // header only mode
751 return dib;
752 }
753
754 // seek to the actual pixel data.
755 // this is needed because sometimes the palette is larger than the entries it contains predicts
756
757 if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
758 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
759 }
760
761 // read the pixel data
762
763 switch (compression) {
764 case BI_RGB :
765 // load pixel data
766 LoadPixelData(io, handle, dib, height, pitch, bit_count);
767 return dib;
768
769 case BI_RLE4 :
770 if( LoadPixelDataRLE4(io, handle, width, height, dib) ) {
771 return dib;
772 } else {
773 throw "Error encountered while decoding RLE4 BMP data";
774 }
775 break;
776
777 case BI_RLE8 :
778 if( LoadPixelDataRLE8(io, handle, width, height, dib) ) {
779 return dib;
780 } else {
781 throw "Error encountered while decoding RLE8 BMP data";
782 }
783 break;
784
785 default :
786 throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
787 }
788 }
789
790 case 16 :
791 {
792 if (bih.biCompression == 3) {
793 DWORD bitfields[3];
794
795 io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
796
797 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
798 } else {
799 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
800 }
801
802 if (dib == NULL) {
803 throw FI_MSG_ERROR_DIB_MEMORY;
804 }
805
806 // set resolution information
807 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
808 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
809
810 if(header_only) {
811 // header only mode
812 return dib;
813 }
814
815 if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
816 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
817 }
818
819 // load pixel data and swap as needed if OS is Big Endian
820 LoadPixelData(io, handle, dib, height, pitch, bit_count);
821
822 return dib;
823 }
824
825 case 24 :
826 case 32 :
827 {
828 if( bit_count == 32 ) {
829 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
830 } else {
831 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
832 }
833
834 if (dib == NULL) {
835 throw FI_MSG_ERROR_DIB_MEMORY;
836 }
837
838 // set resolution information
839 FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
840 FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
841
842 if(header_only) {
843 // header only mode
844 return dib;
845 }
846
847 // Skip over the optional palette
848 // A 24 or 32 bit DIB may contain a palette for faster color reduction
849
850 if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
851 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
852 }
853
854 // read in the bitmap bits
855 // load pixel data and swap as needed if OS is Big Endian
856 LoadPixelData(io, handle, dib, height, pitch, bit_count);
857
858 // check if the bitmap contains transparency, if so enable it in the header
859
860 FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
861
862 return dib;
863 }
864 }
865 } catch(const char *message) {
866 if(dib)
867 FreeImage_Unload(dib);
868
869 FreeImage_OutputMessageProc(s_format_id, message);
870 }
871
872 return NULL;
873 }
874
875 // --------------------------------------------------------------------------
876
877 static FIBITMAP *
LoadOS21XBMP(FreeImageIO * io,fi_handle handle,int flags,unsigned bitmap_bits_offset)878 LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
879 FIBITMAP *dib = NULL;
880
881 try {
882 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
883
884 BITMAPINFOOS2_1X_HEADER bios2_1x;
885
886 io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
887 #ifdef FREEIMAGE_BIGENDIAN
888 SwapOS21XHeader(&bios2_1x);
889 #endif
890 // keep some general information about the bitmap
891
892 unsigned used_colors = 0;
893 unsigned width = bios2_1x.biWidth;
894 unsigned height = bios2_1x.biHeight; // WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
895 unsigned bit_count = bios2_1x.biBitCount;
896 unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
897
898 switch (bit_count) {
899 case 1 :
900 case 4 :
901 case 8 :
902 {
903 used_colors = CalculateUsedPaletteEntries(bit_count);
904
905 // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
906
907 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
908
909 if (dib == NULL) {
910 throw FI_MSG_ERROR_DIB_MEMORY;
911 }
912
913 // set resolution information to default values (72 dpi in english units)
914 FreeImage_SetDotsPerMeterX(dib, 2835);
915 FreeImage_SetDotsPerMeterY(dib, 2835);
916
917 // load the palette
918
919 RGBQUAD *pal = FreeImage_GetPalette(dib);
920
921 for (unsigned count = 0; count < used_colors; count++) {
922 FILE_BGR bgr;
923
924 io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
925
926 pal[count].rgbRed = bgr.r;
927 pal[count].rgbGreen = bgr.g;
928 pal[count].rgbBlue = bgr.b;
929 }
930
931 if(header_only) {
932 // header only mode
933 return dib;
934 }
935
936 // Skip over the optional palette
937 // A 24 or 32 bit DIB may contain a palette for faster color reduction
938
939 io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
940
941 // read the pixel data
942
943 // load pixel data
944 LoadPixelData(io, handle, dib, height, pitch, bit_count);
945
946 return dib;
947 }
948
949 case 16 :
950 {
951 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
952
953 if (dib == NULL) {
954 throw FI_MSG_ERROR_DIB_MEMORY;
955 }
956
957 // set resolution information to default values (72 dpi in english units)
958 FreeImage_SetDotsPerMeterX(dib, 2835);
959 FreeImage_SetDotsPerMeterY(dib, 2835);
960
961 if(header_only) {
962 // header only mode
963 return dib;
964 }
965
966 // load pixel data and swap as needed if OS is Big Endian
967 LoadPixelData(io, handle, dib, height, pitch, bit_count);
968
969 return dib;
970 }
971
972 case 24 :
973 case 32 :
974 {
975 if( bit_count == 32 ) {
976 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
977 } else {
978 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
979 }
980
981 if (dib == NULL) {
982 throw FI_MSG_ERROR_DIB_MEMORY;
983 }
984
985 // set resolution information to default values (72 dpi in english units)
986 FreeImage_SetDotsPerMeterX(dib, 2835);
987 FreeImage_SetDotsPerMeterY(dib, 2835);
988
989 if(header_only) {
990 // header only mode
991 return dib;
992 }
993
994 // Skip over the optional palette
995 // A 24 or 32 bit DIB may contain a palette for faster color reduction
996
997 // load pixel data and swap as needed if OS is Big Endian
998 LoadPixelData(io, handle, dib, height, pitch, bit_count);
999
1000 // check if the bitmap contains transparency, if so enable it in the header
1001
1002 FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
1003
1004 return dib;
1005 }
1006 }
1007 } catch(const char *message) {
1008 if(dib)
1009 FreeImage_Unload(dib);
1010
1011 FreeImage_OutputMessageProc(s_format_id, message);
1012 }
1013
1014 return NULL;
1015 }
1016
1017 // ==========================================================
1018 // Plugin Implementation
1019 // ==========================================================
1020
1021 static const char * DLL_CALLCONV
Format()1022 Format() {
1023 return "BMP";
1024 }
1025
1026 static const char * DLL_CALLCONV
Description()1027 Description() {
1028 return "Windows or OS/2 Bitmap";
1029 }
1030
1031 static const char * DLL_CALLCONV
Extension()1032 Extension() {
1033 return "bmp";
1034 }
1035
1036 static const char * DLL_CALLCONV
RegExpr()1037 RegExpr() {
1038 return "^BM";
1039 }
1040
1041 static const char * DLL_CALLCONV
MimeType()1042 MimeType() {
1043 return "image/bmp";
1044 }
1045
1046 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)1047 Validate(FreeImageIO *io, fi_handle handle) {
1048 BYTE bmp_signature1[] = { 0x42, 0x4D };
1049 BYTE bmp_signature2[] = { 0x42, 0x41 };
1050 BYTE signature[2] = { 0, 0 };
1051
1052 io->read_proc(signature, 1, sizeof(bmp_signature1), handle);
1053
1054 if (memcmp(bmp_signature1, signature, sizeof(bmp_signature1)) == 0)
1055 return TRUE;
1056
1057 if (memcmp(bmp_signature2, signature, sizeof(bmp_signature2)) == 0)
1058 return TRUE;
1059
1060 return FALSE;
1061 }
1062
1063 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)1064 SupportsExportDepth(int depth) {
1065 return (
1066 (depth == 1) ||
1067 (depth == 4) ||
1068 (depth == 8) ||
1069 (depth == 16) ||
1070 (depth == 24) ||
1071 (depth == 32)
1072 );
1073 }
1074
1075 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)1076 SupportsExportType(FREE_IMAGE_TYPE type) {
1077 return (type == FIT_BITMAP) ? TRUE : FALSE;
1078 }
1079
1080 static BOOL DLL_CALLCONV
SupportsNoPixels()1081 SupportsNoPixels() {
1082 return TRUE;
1083 }
1084
1085 // ----------------------------------------------------------
1086
1087 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)1088 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
1089 if (handle != NULL) {
1090 BITMAPFILEHEADER bitmapfileheader;
1091 DWORD type = 0;
1092
1093 // we use this offset value to make seemingly absolute seeks relative in the file
1094
1095 long offset_in_file = io->tell_proc(handle);
1096
1097 // read the fileheader
1098
1099 io->read_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle);
1100 #ifdef FREEIMAGE_BIGENDIAN
1101 SwapFileHeader(&bitmapfileheader);
1102 #endif
1103
1104 // check the signature
1105
1106 if((bitmapfileheader.bfType != 0x4D42) && (bitmapfileheader.bfType != 0x4142)) {
1107 FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_MAGIC_NUMBER);
1108 return NULL;
1109 }
1110
1111 // read the first byte of the infoheader
1112
1113 io->read_proc(&type, sizeof(DWORD), 1, handle);
1114 io->seek_proc(handle, 0 - (long)sizeof(DWORD), SEEK_CUR);
1115 #ifdef FREEIMAGE_BIGENDIAN
1116 SwapLong(&type);
1117 #endif
1118
1119 // call the appropriate load function for the found bitmap type
1120
1121 switch(type) {
1122 case 12:
1123 // OS/2 and also all Windows versions since Windows 3.0
1124 return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
1125
1126 case 64:
1127 // OS/2
1128 return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
1129
1130 case 40: // BITMAPINFOHEADER - all Windows versions since Windows 3.0
1131 case 52: // BITMAPV2INFOHEADER (undocumented, partially supported)
1132 case 56: // BITMAPV3INFOHEADER (undocumented, partially supported)
1133 case 108: // BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (partially supported)
1134 case 124: // BITMAPV5HEADER - Windows 98/2000 and newer (partially supported)
1135 return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits, type);
1136
1137 default:
1138 break;
1139 }
1140
1141 FreeImage_OutputMessageProc(s_format_id, "unknown bmp subtype with id %d", type);
1142 }
1143
1144 return NULL;
1145 }
1146
1147 // ----------------------------------------------------------
1148
1149 /**
1150 Encode a 8-bit source buffer into a 8-bit target buffer using a RLE compression algorithm.
1151 The size of the target buffer must be equal to the size of the source buffer.
1152 On return, the function will return the real size of the target buffer, which should be less that or equal to the source buffer size.
1153 @param target 8-bit Target buffer
1154 @param source 8-bit Source buffer
1155 @param size Source/Target input buffer size
1156 @return Returns the target buffer size
1157 */
1158 static int
RLEEncodeLine(BYTE * target,BYTE * source,int size)1159 RLEEncodeLine(BYTE *target, BYTE *source, int size) {
1160 BYTE buffer[256];
1161 int buffer_size = 0;
1162 int target_pos = 0;
1163
1164 for (int i = 0; i < size; ++i) {
1165 if ((i < size - 1) && (source[i] == source[i + 1])) {
1166 // find a solid block of same bytes
1167
1168 int j = i + 1;
1169 int jmax = 254 + i;
1170
1171 while ((j < size - 1) && (j < jmax) && (source[j] == source[j + 1]))
1172 ++j;
1173
1174 // if the block is larger than 3 bytes, use it
1175 // else put the data into the larger pool
1176
1177 if (((j - i) + 1) > 3) {
1178 // don't forget to write what we already have in the buffer
1179
1180 switch(buffer_size) {
1181 case 0 :
1182 break;
1183
1184 case RLE_DELTA :
1185 target[target_pos++] = 1;
1186 target[target_pos++] = buffer[0];
1187 target[target_pos++] = 1;
1188 target[target_pos++] = buffer[1];
1189 break;
1190
1191 case RLE_ENDOFBITMAP :
1192 target[target_pos++] = (BYTE)buffer_size;
1193 target[target_pos++] = buffer[0];
1194 break;
1195
1196 default :
1197 target[target_pos++] = RLE_COMMAND;
1198 target[target_pos++] = (BYTE)buffer_size;
1199 memcpy(target + target_pos, buffer, buffer_size);
1200
1201 // prepare for next run
1202
1203 target_pos += buffer_size;
1204
1205 if ((buffer_size & 1) == 1)
1206 target_pos++;
1207
1208 break;
1209 }
1210
1211 // write the continuous data
1212
1213 target[target_pos++] = (BYTE)((j - i) + 1);
1214 target[target_pos++] = source[i];
1215
1216 buffer_size = 0;
1217 } else {
1218 for (int k = 0; k < (j - i) + 1; ++k) {
1219 buffer[buffer_size++] = source[i + k];
1220
1221 if (buffer_size == 254) {
1222 // write what we have
1223
1224 target[target_pos++] = RLE_COMMAND;
1225 target[target_pos++] = (BYTE)buffer_size;
1226 memcpy(target + target_pos, buffer, buffer_size);
1227
1228 // prepare for next run
1229
1230 target_pos += buffer_size;
1231 buffer_size = 0;
1232 }
1233 }
1234 }
1235
1236 i = j;
1237 } else {
1238 buffer[buffer_size++] = source[i];
1239 }
1240
1241 // write the buffer if it's full
1242
1243 if (buffer_size == 254) {
1244 target[target_pos++] = RLE_COMMAND;
1245 target[target_pos++] = (BYTE)buffer_size;
1246 memcpy(target + target_pos, buffer, buffer_size);
1247
1248 // prepare for next run
1249
1250 target_pos += buffer_size;
1251 buffer_size = 0;
1252 }
1253 }
1254
1255 // write the last bytes
1256
1257 switch(buffer_size) {
1258 case 0 :
1259 break;
1260
1261 case RLE_DELTA :
1262 target[target_pos++] = 1;
1263 target[target_pos++] = buffer[0];
1264 target[target_pos++] = 1;
1265 target[target_pos++] = buffer[1];
1266 break;
1267
1268 case RLE_ENDOFBITMAP :
1269 target[target_pos++] = (BYTE)buffer_size;
1270 target[target_pos++] = buffer[0];
1271 break;
1272
1273 default :
1274 target[target_pos++] = RLE_COMMAND;
1275 target[target_pos++] = (BYTE)buffer_size;
1276 memcpy(target + target_pos, buffer, buffer_size);
1277
1278 // prepare for next run
1279
1280 target_pos += buffer_size;
1281
1282 if ((buffer_size & 1) == 1)
1283 target_pos++;
1284
1285 break;
1286 }
1287
1288 // write the END_OF_LINE marker
1289
1290 target[target_pos++] = RLE_COMMAND;
1291 target[target_pos++] = RLE_ENDOFLINE;
1292
1293 // return the written size
1294
1295 return target_pos;
1296 }
1297
1298 static BOOL DLL_CALLCONV
Save(FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int page,int flags,void * data)1299 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
1300 if ((dib != NULL) && (handle != NULL)) {
1301 // write the file header
1302
1303 const unsigned dst_width = FreeImage_GetWidth(dib);
1304 const unsigned dst_height = FreeImage_GetHeight(dib);
1305
1306 // note that the dib may have been created using FreeImage_CreateView
1307 // we need to recalculate the dst pitch here
1308 const unsigned dst_bpp = FreeImage_GetBPP(dib);
1309 const unsigned dst_pitch = CalculatePitch(CalculateLine(dst_width, dst_bpp));
1310
1311 BITMAPFILEHEADER bitmapfileheader;
1312 bitmapfileheader.bfType = 0x4D42;
1313 bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD);
1314 bitmapfileheader.bfSize = bitmapfileheader.bfOffBits + dst_height * dst_pitch;
1315 bitmapfileheader.bfReserved1 = 0;
1316 bitmapfileheader.bfReserved2 = 0;
1317
1318 // take care of the bit fields data of any
1319
1320 bool bit_fields = (dst_bpp == 16) ? true : false;
1321
1322 if (bit_fields) {
1323 bitmapfileheader.bfSize += 3 * sizeof(DWORD);
1324 bitmapfileheader.bfOffBits += 3 * sizeof(DWORD);
1325 }
1326
1327 #ifdef FREEIMAGE_BIGENDIAN
1328 SwapFileHeader(&bitmapfileheader);
1329 #endif
1330 if (io->write_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle) != 1) {
1331 return FALSE;
1332 }
1333
1334 // update the bitmap info header
1335
1336 BITMAPINFOHEADER bih;
1337 memcpy(&bih, FreeImage_GetInfoHeader(dib), sizeof(BITMAPINFOHEADER));
1338
1339 if (bit_fields) {
1340 bih.biCompression = BI_BITFIELDS;
1341 }
1342 else if ((bih.biBitCount == 8) && ((flags & BMP_SAVE_RLE) == BMP_SAVE_RLE)) {
1343 bih.biCompression = BI_RLE8;
1344 }
1345 else {
1346 bih.biCompression = BI_RGB;
1347 }
1348
1349 // write the bitmap info header
1350
1351 #ifdef FREEIMAGE_BIGENDIAN
1352 SwapInfoHeader(&bih);
1353 #endif
1354 if (io->write_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle) != 1) {
1355 return FALSE;
1356 }
1357
1358 // write the bit fields when we are dealing with a 16 bit BMP
1359
1360 if (bit_fields) {
1361 DWORD d;
1362
1363 d = FreeImage_GetRedMask(dib);
1364
1365 if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) {
1366 return FALSE;
1367 }
1368
1369 d = FreeImage_GetGreenMask(dib);
1370
1371 if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) {
1372 return FALSE;
1373 }
1374
1375 d = FreeImage_GetBlueMask(dib);
1376
1377 if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) {
1378 return FALSE;
1379 }
1380 }
1381
1382 // write the palette
1383
1384 if (FreeImage_GetPalette(dib) != NULL) {
1385 RGBQUAD *pal = FreeImage_GetPalette(dib);
1386 FILE_BGRA bgra;
1387 for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) {
1388 bgra.b = pal[i].rgbBlue;
1389 bgra.g = pal[i].rgbGreen;
1390 bgra.r = pal[i].rgbRed;
1391 bgra.a = pal[i].rgbReserved;
1392 if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1) {
1393 return FALSE;
1394 }
1395 }
1396 }
1397
1398 // write the bitmap data... if RLE compression is enable, use it
1399
1400 if ((dst_bpp == 8) && ((flags & BMP_SAVE_RLE) == BMP_SAVE_RLE)) {
1401 BYTE *buffer = (BYTE*)malloc(dst_pitch * 2 * sizeof(BYTE));
1402
1403 for (unsigned i = 0; i < dst_height; ++i) {
1404 int size = RLEEncodeLine(buffer, FreeImage_GetScanLine(dib, i), FreeImage_GetLine(dib));
1405
1406 if (io->write_proc(buffer, size, 1, handle) != 1) {
1407 free(buffer);
1408 return FALSE;
1409 }
1410 }
1411
1412 buffer[0] = RLE_COMMAND;
1413 buffer[1] = RLE_ENDOFBITMAP;
1414
1415 if (io->write_proc(buffer, 2, 1, handle) != 1) {
1416 free(buffer);
1417 return FALSE;
1418 }
1419
1420 free(buffer);
1421 #ifdef FREEIMAGE_BIGENDIAN
1422 } else if (dst_bpp == 16) {
1423 int padding = dst_pitch - dst_width * sizeof(WORD);
1424 WORD pad = 0;
1425 WORD pixel;
1426 for(unsigned y = 0; y < dst_height; y++) {
1427 BYTE *line = FreeImage_GetScanLine(dib, y);
1428 for(unsigned x = 0; x < dst_width; x++) {
1429 pixel = ((WORD *)line)[x];
1430 SwapShort(&pixel);
1431 if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1) {
1432 return FALSE;
1433 }
1434 }
1435 if(padding != 0) {
1436 if(io->write_proc(&pad, padding, 1, handle) != 1) {
1437 return FALSE;
1438 }
1439 }
1440 }
1441 #endif
1442 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
1443 } else if (dst_bpp == 24) {
1444 int padding = dst_pitch - dst_width * sizeof(FILE_BGR);
1445 DWORD pad = 0;
1446 FILE_BGR bgr;
1447 for(unsigned y = 0; y < dst_height; y++) {
1448 BYTE *line = FreeImage_GetScanLine(dib, y);
1449 for(unsigned x = 0; x < dst_width; x++) {
1450 RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
1451 bgr.b = triple->rgbtBlue;
1452 bgr.g = triple->rgbtGreen;
1453 bgr.r = triple->rgbtRed;
1454 if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1) {
1455 return FALSE;
1456 }
1457 }
1458 if(padding != 0) {
1459 if(io->write_proc(&pad, padding, 1, handle) != 1) {
1460 return FALSE;
1461 }
1462 }
1463 }
1464 } else if (dst_bpp == 32) {
1465 FILE_BGRA bgra;
1466 for(unsigned y = 0; y < dst_height; y++) {
1467 BYTE *line = FreeImage_GetScanLine(dib, y);
1468 for(unsigned x = 0; x < dst_width; x++) {
1469 RGBQUAD *quad = ((RGBQUAD *)line)+x;
1470 bgra.b = quad->rgbBlue;
1471 bgra.g = quad->rgbGreen;
1472 bgra.r = quad->rgbRed;
1473 bgra.a = quad->rgbReserved;
1474 if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1) {
1475 return FALSE;
1476 }
1477 }
1478 }
1479 #endif
1480 }
1481 else if (FreeImage_GetPitch(dib) == dst_pitch) {
1482 return (io->write_proc(FreeImage_GetBits(dib), dst_height * dst_pitch, 1, handle) != 1) ? FALSE : TRUE;
1483 }
1484 else {
1485 for (unsigned y = 0; y < dst_height; y++) {
1486 BYTE *line = (BYTE*)FreeImage_GetScanLine(dib, y);
1487
1488 if (io->write_proc(line, dst_pitch, 1, handle) != 1) {
1489 return FALSE;
1490 }
1491 }
1492 }
1493
1494 return TRUE;
1495
1496 } else {
1497 return FALSE;
1498 }
1499 }
1500
1501 // ==========================================================
1502 // Init
1503 // ==========================================================
1504
1505 void DLL_CALLCONV
InitBMP(Plugin * plugin,int format_id)1506 InitBMP(Plugin *plugin, int format_id) {
1507 s_format_id = format_id;
1508
1509 plugin->format_proc = Format;
1510 plugin->description_proc = Description;
1511 plugin->extension_proc = Extension;
1512 plugin->regexpr_proc = RegExpr;
1513 plugin->open_proc = NULL;
1514 plugin->close_proc = NULL;
1515 plugin->pagecount_proc = NULL;
1516 plugin->pagecapability_proc = NULL;
1517 plugin->load_proc = Load;
1518 plugin->save_proc = Save;
1519 plugin->validate_proc = Validate;
1520 plugin->mime_proc = MimeType;
1521 plugin->supports_export_bpp_proc = SupportsExportDepth;
1522 plugin->supports_export_type_proc = SupportsExportType;
1523 plugin->supports_icc_profiles_proc = NULL; // not implemented yet;
1524 plugin->supports_no_pixels_proc = SupportsNoPixels;
1525 }
1526