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