/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginICO.cpp
Line | Count | Source |
1 | | // ========================================================== |
2 | | // ICO Loader and Writer |
3 | | // |
4 | | // Design and implementation by |
5 | | // - Floris van den Berg (flvdberg@wxs.nl) |
6 | | // - Hervé Drolon (drolon@infonie.fr) |
7 | | // |
8 | | // This file is part of FreeImage 3 |
9 | | // |
10 | | // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
11 | | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
12 | | // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
13 | | // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
14 | | // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
15 | | // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
16 | | // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
17 | | // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
18 | | // THIS DISCLAIMER. |
19 | | // |
20 | | // Use at your own risk! |
21 | | // ========================================================== |
22 | | |
23 | | #include "FreeImage.h" |
24 | | #include "Utilities.h" |
25 | | |
26 | | // ---------------------------------------------------------- |
27 | | // Constants + headers |
28 | | // ---------------------------------------------------------- |
29 | | |
30 | | #ifdef _WIN32 |
31 | | #pragma pack(push, 1) |
32 | | #else |
33 | | #pragma pack(1) |
34 | | #endif |
35 | | |
36 | | // These next two structs represent how the icon information is stored |
37 | | // in an ICO file. |
38 | | |
39 | | typedef struct tagICONHEADER { |
40 | | WORD idReserved; // reserved |
41 | | WORD idType; // resource type (1 for icons) |
42 | | WORD idCount; // how many images? |
43 | | } ICONHEADER; |
44 | | |
45 | | typedef struct tagICONDIRECTORYENTRY { |
46 | | BYTE bWidth; // width of the image |
47 | | BYTE bHeight; // height of the image (times 2) |
48 | | BYTE bColorCount; // number of colors in image (0 if >=8bpp) |
49 | | BYTE bReserved; // reserved |
50 | | WORD wPlanes; // color Planes |
51 | | WORD wBitCount; // bits per pixel |
52 | | DWORD dwBytesInRes; // how many bytes in this resource? |
53 | | DWORD dwImageOffset; // where in the file is this image |
54 | | } ICONDIRENTRY; |
55 | | |
56 | | #ifdef _WIN32 |
57 | | #pragma pack(pop) |
58 | | #else |
59 | | #pragma pack() |
60 | | #endif |
61 | | |
62 | | // ========================================================== |
63 | | // Static helpers |
64 | | // ========================================================== |
65 | | |
66 | | /** How wide, in bytes, would this many bits be, DWORD aligned ? |
67 | | */ |
68 | | static int |
69 | 0 | WidthBytes(int bits) { |
70 | 0 | return ((((bits) + 31)>>5)<<2); |
71 | 0 | } |
72 | | |
73 | | /** Calculates the size of a single icon image |
74 | | @return Returns the size for that image |
75 | | */ |
76 | | static DWORD |
77 | 0 | CalculateImageSize(FIBITMAP* icon_dib) { |
78 | 0 | DWORD dwNumBytes = 0; |
79 | |
|
80 | 0 | unsigned colors = FreeImage_GetColorsUsed(icon_dib); |
81 | 0 | unsigned width = FreeImage_GetWidth(icon_dib); |
82 | 0 | unsigned height = FreeImage_GetHeight(icon_dib); |
83 | 0 | unsigned pitch = FreeImage_GetPitch(icon_dib); |
84 | |
|
85 | 0 | dwNumBytes = sizeof( BITMAPINFOHEADER ); // header |
86 | 0 | dwNumBytes += colors * sizeof(RGBQUAD); // palette |
87 | 0 | dwNumBytes += height * pitch; // XOR mask |
88 | 0 | dwNumBytes += height * WidthBytes(width); // AND mask |
89 | |
|
90 | 0 | return dwNumBytes; |
91 | 0 | } |
92 | | |
93 | | /** Calculates the file offset for an icon image |
94 | | @return Returns the file offset for that image |
95 | | */ |
96 | | static DWORD |
97 | 0 | CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) { |
98 | 0 | DWORD dwSize; |
99 | | |
100 | | // calculate the ICO header size |
101 | 0 | dwSize = sizeof(ICONHEADER); |
102 | | // add the ICONDIRENTRY's |
103 | 0 | dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY) ); |
104 | | // add the sizes of the previous images |
105 | 0 | for(int k = 0; k < nIndex; k++) { |
106 | 0 | FIBITMAP *icon_dib = (FIBITMAP*)vPages[k]; |
107 | 0 | dwSize += CalculateImageSize(icon_dib); |
108 | 0 | } |
109 | |
|
110 | 0 | return dwSize; |
111 | 0 | } |
112 | | |
113 | | /** |
114 | | Vista icon support |
115 | | @return Returns TRUE if the bitmap data is stored in PNG format |
116 | | */ |
117 | | static BOOL |
118 | 0 | IsPNG(FreeImageIO *io, fi_handle handle) { |
119 | 0 | BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; |
120 | 0 | BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
121 | |
|
122 | 0 | long tell = io->tell_proc(handle); |
123 | 0 | io->read_proc(&signature, 1, 8, handle); |
124 | 0 | BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0); |
125 | 0 | io->seek_proc(handle, tell, SEEK_SET); |
126 | |
|
127 | 0 | return bIsPNG; |
128 | 0 | } |
129 | | |
130 | | #ifdef FREEIMAGE_BIGENDIAN |
131 | | static void |
132 | | SwapInfoHeader(BITMAPINFOHEADER *header) { |
133 | | SwapLong(&header->biSize); |
134 | | SwapLong((DWORD *)&header->biWidth); |
135 | | SwapLong((DWORD *)&header->biHeight); |
136 | | SwapShort(&header->biPlanes); |
137 | | SwapShort(&header->biBitCount); |
138 | | SwapLong(&header->biCompression); |
139 | | SwapLong(&header->biSizeImage); |
140 | | SwapLong((DWORD *)&header->biXPelsPerMeter); |
141 | | SwapLong((DWORD *)&header->biYPelsPerMeter); |
142 | | SwapLong(&header->biClrUsed); |
143 | | SwapLong(&header->biClrImportant); |
144 | | } |
145 | | |
146 | | static void |
147 | | SwapIconHeader(ICONHEADER *header) { |
148 | | SwapShort(&header->idReserved); |
149 | | SwapShort(&header->idType); |
150 | | SwapShort(&header->idCount); |
151 | | } |
152 | | |
153 | | static void |
154 | | SwapIconDirEntries(ICONDIRENTRY *ent, int num) { |
155 | | while(num) { |
156 | | SwapShort(&ent->wPlanes); |
157 | | SwapShort(&ent->wBitCount); |
158 | | SwapLong(&ent->dwBytesInRes); |
159 | | SwapLong(&ent->dwImageOffset); |
160 | | num--; |
161 | | ent++; |
162 | | } |
163 | | } |
164 | | #endif |
165 | | |
166 | | // ========================================================== |
167 | | // Plugin Interface |
168 | | // ========================================================== |
169 | | |
170 | | static int s_format_id; |
171 | | |
172 | | // ========================================================== |
173 | | // Plugin Implementation |
174 | | // ========================================================== |
175 | | |
176 | | static const char * DLL_CALLCONV |
177 | 2 | Format() { |
178 | 2 | return "ICO"; |
179 | 2 | } |
180 | | |
181 | | static const char * DLL_CALLCONV |
182 | 0 | Description() { |
183 | 0 | return "Windows Icon"; |
184 | 0 | } |
185 | | |
186 | | static const char * DLL_CALLCONV |
187 | 0 | Extension() { |
188 | 0 | return "ico"; |
189 | 0 | } |
190 | | |
191 | | static const char * DLL_CALLCONV |
192 | 0 | RegExpr() { |
193 | 0 | return NULL; |
194 | 0 | } |
195 | | |
196 | | static const char * DLL_CALLCONV |
197 | 0 | MimeType() { |
198 | 0 | return "image/vnd.microsoft.icon"; |
199 | 0 | } |
200 | | |
201 | | static BOOL DLL_CALLCONV |
202 | 37.1k | Validate(FreeImageIO *io, fi_handle handle) { |
203 | 37.1k | ICONHEADER icon_header; |
204 | | |
205 | 37.1k | io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle); |
206 | | #ifdef FREEIMAGE_BIGENDIAN |
207 | | SwapIconHeader(&icon_header); |
208 | | #endif |
209 | | |
210 | 37.1k | return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0)); |
211 | 37.1k | } |
212 | | |
213 | | static BOOL DLL_CALLCONV |
214 | 0 | SupportsExportDepth(int depth) { |
215 | 0 | return ( |
216 | 0 | (depth == 1) || |
217 | 0 | (depth == 4) || |
218 | 0 | (depth == 8) || |
219 | 0 | (depth == 16) || |
220 | 0 | (depth == 24) || |
221 | 0 | (depth == 32) |
222 | 0 | ); |
223 | 0 | } |
224 | | |
225 | | static BOOL DLL_CALLCONV |
226 | 0 | SupportsExportType(FREE_IMAGE_TYPE type) { |
227 | 0 | return (type == FIT_BITMAP) ? TRUE : FALSE; |
228 | 0 | } |
229 | | |
230 | | static BOOL DLL_CALLCONV |
231 | 0 | SupportsNoPixels() { |
232 | 0 | return TRUE; |
233 | 0 | } |
234 | | |
235 | | // ---------------------------------------------------------- |
236 | | |
237 | | static void * DLL_CALLCONV |
238 | 0 | Open(FreeImageIO *io, fi_handle handle, BOOL read) { |
239 | | // Allocate memory for the header structure |
240 | 0 | ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER)); |
241 | 0 | if(lpIH == NULL) { |
242 | 0 | return NULL; |
243 | 0 | } |
244 | | |
245 | 0 | if (read) { |
246 | | // Read in the header |
247 | 0 | io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle); |
248 | | #ifdef FREEIMAGE_BIGENDIAN |
249 | | SwapIconHeader(lpIH); |
250 | | #endif |
251 | |
|
252 | 0 | if(!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) { |
253 | | // Not an ICO file |
254 | 0 | free(lpIH); |
255 | 0 | return NULL; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | else { |
259 | | // Fill the header |
260 | 0 | lpIH->idReserved = 0; |
261 | 0 | lpIH->idType = 1; |
262 | 0 | lpIH->idCount = 0; |
263 | 0 | } |
264 | | |
265 | 0 | return lpIH; |
266 | 0 | } |
267 | | |
268 | | static void DLL_CALLCONV |
269 | 0 | Close(FreeImageIO *io, fi_handle handle, void *data) { |
270 | | // free the header structure |
271 | 0 | ICONHEADER *lpIH = (ICONHEADER*)data; |
272 | 0 | free(lpIH); |
273 | 0 | } |
274 | | |
275 | | // ---------------------------------------------------------- |
276 | | |
277 | | static int DLL_CALLCONV |
278 | 0 | PageCount(FreeImageIO *io, fi_handle handle, void *data) { |
279 | 0 | ICONHEADER *lpIH = (ICONHEADER*)data; |
280 | |
|
281 | 0 | if(lpIH) { |
282 | 0 | return lpIH->idCount; |
283 | 0 | } |
284 | 0 | return 1; |
285 | 0 | } |
286 | | |
287 | | // ---------------------------------------------------------- |
288 | | |
289 | | static FIBITMAP* |
290 | 0 | LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) { |
291 | 0 | FIBITMAP *dib = NULL; |
292 | | |
293 | | // load the BITMAPINFOHEADER |
294 | 0 | BITMAPINFOHEADER bmih; |
295 | 0 | io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle); |
296 | | #ifdef FREEIMAGE_BIGENDIAN |
297 | | SwapInfoHeader(&bmih); |
298 | | #endif |
299 | | |
300 | | // allocate the bitmap |
301 | 0 | int width = bmih.biWidth; |
302 | 0 | int height = bmih.biHeight / 2; // height == xor + and mask |
303 | 0 | unsigned bit_count = bmih.biBitCount; |
304 | 0 | unsigned line = CalculateLine(width, bit_count); |
305 | 0 | unsigned pitch = CalculatePitch(line); |
306 | | |
307 | | // allocate memory for one icon |
308 | |
|
309 | 0 | dib = FreeImage_AllocateHeader(header_only, width, height, bit_count); |
310 | |
|
311 | 0 | if (dib == NULL) { |
312 | 0 | return NULL; |
313 | 0 | } |
314 | | |
315 | 0 | if( bmih.biBitCount <= 8 ) { |
316 | | // read the palette data |
317 | 0 | io->read_proc(FreeImage_GetPalette(dib), CalculateUsedPaletteEntries(bit_count) * sizeof(RGBQUAD), 1, handle); |
318 | | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB |
319 | | RGBQUAD *pal = FreeImage_GetPalette(dib); |
320 | | for(unsigned i = 0; i < CalculateUsedPaletteEntries(bit_count); i++) { |
321 | | INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue); |
322 | | } |
323 | | #endif |
324 | 0 | } |
325 | | |
326 | 0 | if(header_only) { |
327 | | // header only mode |
328 | 0 | return dib; |
329 | 0 | } |
330 | | |
331 | | // read the icon |
332 | 0 | io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); |
333 | |
|
334 | | #ifdef FREEIMAGE_BIGENDIAN |
335 | | if (bit_count == 16) { |
336 | | for(int y = 0; y < height; y++) { |
337 | | WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); |
338 | | for(int x = 0; x < width; x++) { |
339 | | SwapShort(pixel); |
340 | | pixel++; |
341 | | } |
342 | | } |
343 | | } |
344 | | #endif |
345 | | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB |
346 | | if (bit_count == 24 || bit_count == 32) { |
347 | | for(int y = 0; y < height; y++) { |
348 | | BYTE *pixel = FreeImage_GetScanLine(dib, y); |
349 | | for(int x = 0; x < width; x++) { |
350 | | INPLACESWAP(pixel[0], pixel[2]); |
351 | | pixel += (bit_count>>3); |
352 | | } |
353 | | } |
354 | | } |
355 | | #endif |
356 | | // bitmap has been loaded successfully! |
357 | | |
358 | | // convert to 32bpp and generate an alpha channel |
359 | | // apply the AND mask only if the image is not 32 bpp |
360 | 0 | if(((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) && (bit_count < 32)) { |
361 | 0 | FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib); |
362 | 0 | FreeImage_Unload(dib); |
363 | |
|
364 | 0 | if (dib32 == NULL) { |
365 | 0 | return NULL; |
366 | 0 | } |
367 | | |
368 | 0 | int width_and = WidthBytes(width); |
369 | 0 | BYTE *line_and = (BYTE *)malloc(width_and); |
370 | |
|
371 | 0 | if( line_and == NULL ) { |
372 | 0 | FreeImage_Unload(dib32); |
373 | 0 | return NULL; |
374 | 0 | } |
375 | | |
376 | | //loop through each line of the AND-mask generating the alpha channel, invert XOR-mask |
377 | 0 | for(int y = 0; y < height; y++) { |
378 | 0 | RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y); |
379 | 0 | io->read_proc(line_and, width_and, 1, handle); |
380 | 0 | for(int x = 0; x < width; x++) { |
381 | 0 | quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF; |
382 | 0 | if( quad->rgbReserved == 0 ) { |
383 | 0 | quad->rgbBlue ^= 0xFF; |
384 | 0 | quad->rgbGreen ^= 0xFF; |
385 | 0 | quad->rgbRed ^= 0xFF; |
386 | 0 | } |
387 | 0 | quad++; |
388 | 0 | } |
389 | 0 | } |
390 | 0 | free(line_and); |
391 | |
|
392 | 0 | return dib32; |
393 | 0 | } |
394 | | |
395 | 0 | return dib; |
396 | 0 | } |
397 | | |
398 | | static FIBITMAP * DLL_CALLCONV |
399 | 0 | Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
400 | 0 | if (page == -1) { |
401 | 0 | page = 0; |
402 | 0 | } |
403 | |
|
404 | 0 | BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; |
405 | |
|
406 | 0 | if (handle != NULL) { |
407 | 0 | FIBITMAP *dib = NULL; |
408 | | |
409 | | // get the icon header |
410 | 0 | ICONHEADER *icon_header = (ICONHEADER*)data; |
411 | |
|
412 | 0 | if (icon_header) { |
413 | | // load the icon descriptions |
414 | 0 | ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY)); |
415 | 0 | if(icon_list == NULL) { |
416 | 0 | return NULL; |
417 | 0 | } |
418 | 0 | io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET); |
419 | 0 | io->read_proc(icon_list, icon_header->idCount * sizeof(ICONDIRENTRY), 1, handle); |
420 | | #ifdef FREEIMAGE_BIGENDIAN |
421 | | SwapIconDirEntries(icon_list, icon_header->idCount); |
422 | | #endif |
423 | | |
424 | | // load the requested icon |
425 | 0 | if (page < icon_header->idCount) { |
426 | | // seek to the start of the bitmap data for the icon |
427 | 0 | io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_SET); |
428 | |
|
429 | 0 | if( IsPNG(io, handle) ) { |
430 | | // Vista icon support |
431 | | // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx |
432 | 0 | dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT); |
433 | 0 | } |
434 | 0 | else { |
435 | | // standard icon support |
436 | | // see http://msdn.microsoft.com/en-us/library/ms997538.aspx |
437 | | // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx |
438 | 0 | dib = LoadStandardIcon(io, handle, flags, header_only); |
439 | 0 | } |
440 | |
|
441 | 0 | free(icon_list); |
442 | |
|
443 | 0 | return dib; |
444 | |
|
445 | 0 | } else { |
446 | 0 | free(icon_list); |
447 | 0 | FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist"); |
448 | 0 | } |
449 | 0 | } else { |
450 | 0 | FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file"); |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | 0 | return NULL; |
455 | 0 | } |
456 | | |
457 | | // ---------------------------------------------------------- |
458 | | |
459 | | static BOOL |
460 | 0 | SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) { |
461 | 0 | BITMAPINFOHEADER *bmih = NULL; |
462 | | |
463 | | // write the BITMAPINFOHEADER |
464 | 0 | bmih = FreeImage_GetInfoHeader(dib); |
465 | 0 | bmih->biHeight *= 2; // height == xor + and mask |
466 | | #ifdef FREEIMAGE_BIGENDIAN |
467 | | SwapInfoHeader(bmih); |
468 | | #endif |
469 | 0 | io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle); |
470 | | #ifdef FREEIMAGE_BIGENDIAN |
471 | | SwapInfoHeader(bmih); |
472 | | #endif |
473 | 0 | bmih->biHeight /= 2; |
474 | | |
475 | | // write the palette data |
476 | 0 | if (FreeImage_GetPalette(dib) != NULL) { |
477 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
478 | 0 | FILE_BGRA bgra; |
479 | 0 | for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) { |
480 | 0 | bgra.b = pal[i].rgbBlue; |
481 | 0 | bgra.g = pal[i].rgbGreen; |
482 | 0 | bgra.r = pal[i].rgbRed; |
483 | 0 | bgra.a = pal[i].rgbReserved; |
484 | 0 | io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle); |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | // write the bits |
489 | 0 | int width = bmih->biWidth; |
490 | 0 | int height = bmih->biHeight; |
491 | 0 | unsigned bit_count = bmih->biBitCount; |
492 | 0 | unsigned line = CalculateLine(width, bit_count); |
493 | 0 | unsigned pitch = CalculatePitch(line); |
494 | 0 | int size_xor = height * pitch; |
495 | 0 | int size_and = height * WidthBytes(width); |
496 | | |
497 | | // XOR mask |
498 | | #ifdef FREEIMAGE_BIGENDIAN |
499 | | if (bit_count == 16) { |
500 | | WORD pixel; |
501 | | for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { |
502 | | BYTE *line = FreeImage_GetScanLine(dib, y); |
503 | | for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { |
504 | | pixel = ((WORD *)line)[x]; |
505 | | SwapShort(&pixel); |
506 | | if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1) |
507 | | return FALSE; |
508 | | } |
509 | | } |
510 | | } else |
511 | | #endif |
512 | | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB |
513 | | if (bit_count == 24) { |
514 | | FILE_BGR bgr; |
515 | | for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { |
516 | | BYTE *line = FreeImage_GetScanLine(dib, y); |
517 | | for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { |
518 | | RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x; |
519 | | bgr.b = triple->rgbtBlue; |
520 | | bgr.g = triple->rgbtGreen; |
521 | | bgr.r = triple->rgbtRed; |
522 | | if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1) |
523 | | return FALSE; |
524 | | } |
525 | | } |
526 | | } else if (bit_count == 32) { |
527 | | FILE_BGRA bgra; |
528 | | for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { |
529 | | BYTE *line = FreeImage_GetScanLine(dib, y); |
530 | | for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { |
531 | | RGBQUAD *quad = ((RGBQUAD *)line)+x; |
532 | | bgra.b = quad->rgbBlue; |
533 | | bgra.g = quad->rgbGreen; |
534 | | bgra.r = quad->rgbRed; |
535 | | bgra.a = quad->rgbReserved; |
536 | | if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1) |
537 | | return FALSE; |
538 | | } |
539 | | } |
540 | | } else |
541 | | #endif |
542 | | #if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB |
543 | | { |
544 | | #endif |
545 | 0 | BYTE *xor_mask = FreeImage_GetBits(dib); |
546 | 0 | io->write_proc(xor_mask, size_xor, 1, handle); |
547 | | #if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB |
548 | | } |
549 | | #endif |
550 | | // AND mask |
551 | 0 | BYTE *and_mask = (BYTE*)malloc(size_and); |
552 | 0 | if(!and_mask) { |
553 | 0 | return FALSE; |
554 | 0 | } |
555 | | |
556 | 0 | if(FreeImage_IsTransparent(dib)) { |
557 | |
|
558 | 0 | if(bit_count == 32) { |
559 | | // create the AND mask from the alpha channel |
560 | |
|
561 | 0 | int width_and = WidthBytes(width); |
562 | 0 | BYTE *and_bits = and_mask; |
563 | | |
564 | | // clear the mask |
565 | 0 | memset(and_mask, 0, size_and); |
566 | |
|
567 | 0 | for(int y = 0; y < height; y++) { |
568 | 0 | RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y); |
569 | |
|
570 | 0 | for(int x = 0; x < width; x++) { |
571 | 0 | if(bits[x].rgbReserved != 0xFF) { |
572 | | // set any transparent color to full transparency |
573 | 0 | and_bits[x >> 3] |= (0x80 >> (x & 0x7)); |
574 | 0 | } |
575 | 0 | } |
576 | |
|
577 | 0 | and_bits += width_and; |
578 | 0 | } |
579 | 0 | } |
580 | 0 | else if(bit_count <= 8) { |
581 | | // create the AND mask from the transparency table |
582 | |
|
583 | 0 | BYTE *trns = FreeImage_GetTransparencyTable(dib); |
584 | |
|
585 | 0 | int width_and = WidthBytes(width); |
586 | 0 | BYTE *and_bits = and_mask; |
587 | | |
588 | | // clear the mask |
589 | 0 | memset(and_mask, 0, size_and); |
590 | |
|
591 | 0 | switch(FreeImage_GetBPP(dib)) { |
592 | 0 | case 1: |
593 | 0 | { |
594 | 0 | for(int y = 0; y < height; y++) { |
595 | 0 | BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y); |
596 | 0 | for(int x = 0; x < width; x++) { |
597 | | // get pixel at (x, y) |
598 | 0 | BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0; |
599 | 0 | if(trns[index] != 0xFF) { |
600 | | // set any transparent color to full transparency |
601 | 0 | and_bits[x >> 3] |= (0x80 >> (x & 0x7)); |
602 | 0 | } |
603 | 0 | } |
604 | 0 | and_bits += width_and; |
605 | 0 | } |
606 | 0 | } |
607 | 0 | break; |
608 | | |
609 | 0 | case 4: |
610 | 0 | { |
611 | 0 | for(int y = 0; y < height; y++) { |
612 | 0 | BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y); |
613 | 0 | for(int x = 0; x < width; x++) { |
614 | | // get pixel at (x, y) |
615 | 0 | BYTE shift = (BYTE)((1 - x % 2) << 2); |
616 | 0 | BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift; |
617 | 0 | if(trns[index] != 0xFF) { |
618 | | // set any transparent color to full transparency |
619 | 0 | and_bits[x >> 3] |= (0x80 >> (x & 0x7)); |
620 | 0 | } |
621 | 0 | } |
622 | 0 | and_bits += width_and; |
623 | 0 | } |
624 | 0 | } |
625 | 0 | break; |
626 | | |
627 | 0 | case 8: |
628 | 0 | { |
629 | 0 | for(int y = 0; y < height; y++) { |
630 | 0 | BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y); |
631 | 0 | for(int x = 0; x < width; x++) { |
632 | | // get pixel at (x, y) |
633 | 0 | BYTE index = bits[x]; |
634 | 0 | if(trns[index] != 0xFF) { |
635 | | // set any transparent color to full transparency |
636 | 0 | and_bits[x >> 3] |= (0x80 >> (x & 0x7)); |
637 | 0 | } |
638 | 0 | } |
639 | 0 | and_bits += width_and; |
640 | 0 | } |
641 | 0 | } |
642 | 0 | break; |
643 | |
|
644 | 0 | } |
645 | 0 | } |
646 | 0 | } |
647 | 0 | else { |
648 | | // empty AND mask |
649 | 0 | memset(and_mask, 0, size_and); |
650 | 0 | } |
651 | | |
652 | 0 | io->write_proc(and_mask, size_and, 1, handle); |
653 | 0 | free(and_mask); |
654 | |
|
655 | 0 | return TRUE; |
656 | 0 | } |
657 | | |
658 | | static BOOL DLL_CALLCONV |
659 | 0 | Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { |
660 | 0 | ICONHEADER *icon_header = NULL; |
661 | 0 | std::vector<FIBITMAP*> vPages; |
662 | 0 | int k; |
663 | |
|
664 | 0 | if(!dib || !handle || !data) { |
665 | 0 | return FALSE; |
666 | 0 | } |
667 | | |
668 | | // check format limits |
669 | 0 | unsigned w = FreeImage_GetWidth(dib); |
670 | 0 | unsigned h = FreeImage_GetHeight(dib); |
671 | 0 | if((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) { |
672 | 0 | FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h); |
673 | 0 | return FALSE; |
674 | 0 | } |
675 | | |
676 | 0 | if (page == -1) { |
677 | 0 | page = 0; |
678 | 0 | } |
679 | | |
680 | | // get the icon header |
681 | 0 | icon_header = (ICONHEADER*)data; |
682 | |
|
683 | 0 | try { |
684 | 0 | FIBITMAP *icon_dib = NULL; |
685 | | |
686 | | // load all icons |
687 | 0 | for(k = 0; k < icon_header->idCount; k++) { |
688 | 0 | icon_dib = Load(io, handle, k, flags, data); |
689 | 0 | if(!icon_dib) { |
690 | 0 | throw FI_MSG_ERROR_DIB_MEMORY; |
691 | 0 | } |
692 | 0 | vPages.push_back(icon_dib); |
693 | 0 | } |
694 | | |
695 | | // add the page |
696 | 0 | icon_dib = FreeImage_Clone(dib); |
697 | 0 | vPages.push_back(icon_dib); |
698 | 0 | icon_header->idCount++; |
699 | | |
700 | | // write the header |
701 | 0 | io->seek_proc(handle, 0, SEEK_SET); |
702 | | #ifdef FREEIMAGE_BIGENDIAN |
703 | | SwapIconHeader(icon_header); |
704 | | #endif |
705 | 0 | io->write_proc(icon_header, sizeof(ICONHEADER), 1, handle); |
706 | | #ifdef FREEIMAGE_BIGENDIAN |
707 | | SwapIconHeader(icon_header); |
708 | | #endif |
709 | | |
710 | | // write all icons |
711 | | // ... |
712 | | |
713 | | // save the icon descriptions |
714 | |
|
715 | 0 | ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY)); |
716 | 0 | if(!icon_list) { |
717 | 0 | throw FI_MSG_ERROR_MEMORY; |
718 | 0 | } |
719 | 0 | memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY)); |
720 | |
|
721 | 0 | for(k = 0; k < icon_header->idCount; k++) { |
722 | 0 | icon_dib = (FIBITMAP*)vPages[k]; |
723 | | |
724 | | // convert internal format to ICONDIRENTRY |
725 | | // take into account Vista icons whose size is 256x256 |
726 | 0 | const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib); |
727 | 0 | icon_list[k].bWidth = (bmih->biWidth > 255) ? 0 : (BYTE)bmih->biWidth; |
728 | 0 | icon_list[k].bHeight = (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight; |
729 | 0 | icon_list[k].bReserved = 0; |
730 | 0 | icon_list[k].wPlanes = bmih->biPlanes; |
731 | 0 | icon_list[k].wBitCount = bmih->biBitCount; |
732 | 0 | if( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) { |
733 | 0 | icon_list[k].bColorCount = 0; |
734 | 0 | } else { |
735 | 0 | icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount)); |
736 | 0 | } |
737 | | // initial guess (correct only for standard icons) |
738 | 0 | icon_list[k].dwBytesInRes = CalculateImageSize(icon_dib); |
739 | 0 | icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k); |
740 | 0 | } |
741 | | |
742 | | // make a room for icon dir entries, until later update |
743 | 0 | const long directory_start = io->tell_proc(handle); |
744 | 0 | io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle); |
745 | | |
746 | | // write the image bits for each image |
747 | | |
748 | 0 | DWORD dwImageOffset = (DWORD)io->tell_proc(handle); |
749 | |
|
750 | 0 | for(k = 0; k < icon_header->idCount; k++) { |
751 | 0 | icon_dib = (FIBITMAP*)vPages[k]; |
752 | | |
753 | 0 | if((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) { |
754 | | // Vista icon support |
755 | 0 | FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT); |
756 | 0 | } |
757 | 0 | else { |
758 | | // standard icon support |
759 | | // see http://msdn.microsoft.com/en-us/library/ms997538.aspx |
760 | 0 | SaveStandardIcon(io, icon_dib, handle); |
761 | 0 | } |
762 | | |
763 | | // update ICONDIRENTRY members |
764 | 0 | DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset; |
765 | 0 | icon_list[k].dwImageOffset = dwImageOffset; |
766 | 0 | icon_list[k].dwBytesInRes = dwBytesInRes; |
767 | 0 | dwImageOffset += dwBytesInRes; |
768 | 0 | } |
769 | | |
770 | | // update the icon descriptions |
771 | 0 | const long current_pos = io->tell_proc(handle); |
772 | 0 | io->seek_proc(handle, directory_start, SEEK_SET); |
773 | | #ifdef FREEIMAGE_BIGENDIAN |
774 | | SwapIconDirEntries(icon_list, icon_header->idCount); |
775 | | #endif |
776 | 0 | io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle); |
777 | 0 | io->seek_proc(handle, current_pos, SEEK_SET); |
778 | |
|
779 | 0 | free(icon_list); |
780 | | |
781 | | // free the vector class |
782 | 0 | for(k = 0; k < icon_header->idCount; k++) { |
783 | 0 | icon_dib = (FIBITMAP*)vPages[k]; |
784 | 0 | FreeImage_Unload(icon_dib); |
785 | 0 | } |
786 | |
|
787 | 0 | return TRUE; |
788 | |
|
789 | 0 | } catch(const char *text) { |
790 | | // free the vector class |
791 | 0 | for(size_t k = 0; k < vPages.size(); k++) { |
792 | 0 | FIBITMAP *icon_dib = (FIBITMAP*)vPages[k]; |
793 | 0 | FreeImage_Unload(icon_dib); |
794 | 0 | } |
795 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
796 | 0 | return FALSE; |
797 | 0 | } |
798 | 0 | } |
799 | | |
800 | | // ========================================================== |
801 | | // Init |
802 | | // ========================================================== |
803 | | |
804 | | void DLL_CALLCONV |
805 | 2 | InitICO(Plugin *plugin, int format_id) { |
806 | 2 | s_format_id = format_id; |
807 | | |
808 | 2 | plugin->format_proc = Format; |
809 | 2 | plugin->description_proc = Description; |
810 | 2 | plugin->extension_proc = Extension; |
811 | 2 | plugin->regexpr_proc = RegExpr; |
812 | 2 | plugin->open_proc = Open; |
813 | 2 | plugin->close_proc = Close; |
814 | 2 | plugin->pagecount_proc = PageCount; |
815 | 2 | plugin->pagecapability_proc = NULL; |
816 | 2 | plugin->load_proc = Load; |
817 | 2 | plugin->save_proc = Save; |
818 | 2 | plugin->validate_proc = Validate; |
819 | 2 | plugin->mime_proc = MimeType; |
820 | 2 | plugin->supports_export_bpp_proc = SupportsExportDepth; |
821 | 2 | plugin->supports_export_type_proc = SupportsExportType; |
822 | | plugin->supports_icc_profiles_proc = NULL; |
823 | 2 | plugin->supports_no_pixels_proc = SupportsNoPixels; |
824 | 2 | } |