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
WidthBytes(int bits)69 WidthBytes(int bits) {
70 return ((((bits) + 31)>>5)<<2);
71 }
72
73 /** Calculates the size of a single icon image
74 @return Returns the size for that image
75 */
76 static DWORD
CalculateImageSize(FIBITMAP * icon_dib)77 CalculateImageSize(FIBITMAP* icon_dib) {
78 DWORD dwNumBytes = 0;
79
80 unsigned colors = FreeImage_GetColorsUsed(icon_dib);
81 unsigned width = FreeImage_GetWidth(icon_dib);
82 unsigned height = FreeImage_GetHeight(icon_dib);
83 unsigned pitch = FreeImage_GetPitch(icon_dib);
84
85 dwNumBytes = sizeof( BITMAPINFOHEADER ); // header
86 dwNumBytes += colors * sizeof(RGBQUAD); // palette
87 dwNumBytes += height * pitch; // XOR mask
88 dwNumBytes += height * WidthBytes(width); // AND mask
89
90 return dwNumBytes;
91 }
92
93 /** Calculates the file offset for an icon image
94 @return Returns the file offset for that image
95 */
96 static DWORD
CalculateImageOffset(std::vector<FIBITMAP * > & vPages,int nIndex)97 CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) {
98 DWORD dwSize;
99
100 // calculate the ICO header size
101 dwSize = sizeof(ICONHEADER);
102 // add the ICONDIRENTRY's
103 dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY) );
104 // add the sizes of the previous images
105 for(int k = 0; k < nIndex; k++) {
106 FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
107 dwSize += CalculateImageSize(icon_dib);
108 }
109
110 return dwSize;
111 }
112
113 /**
114 Vista icon support
115 @return Returns TRUE if the bitmap data is stored in PNG format
116 */
117 static BOOL
IsPNG(FreeImageIO * io,fi_handle handle)118 IsPNG(FreeImageIO *io, fi_handle handle) {
119 BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
120 BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
121
122 long tell = io->tell_proc(handle);
123 io->read_proc(&signature, 1, 8, handle);
124 BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0);
125 io->seek_proc(handle, tell, SEEK_SET);
126
127 return bIsPNG;
128 }
129
130 #ifdef FREEIMAGE_BIGENDIAN
131 static void
SwapInfoHeader(BITMAPINFOHEADER * header)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
SwapIconHeader(ICONHEADER * header)147 SwapIconHeader(ICONHEADER *header) {
148 SwapShort(&header->idReserved);
149 SwapShort(&header->idType);
150 SwapShort(&header->idCount);
151 }
152
153 static void
SwapIconDirEntries(ICONDIRENTRY * ent,int num)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
Format()177 Format() {
178 return "ICO";
179 }
180
181 static const char * DLL_CALLCONV
Description()182 Description() {
183 return "Windows Icon";
184 }
185
186 static const char * DLL_CALLCONV
Extension()187 Extension() {
188 return "ico";
189 }
190
191 static const char * DLL_CALLCONV
RegExpr()192 RegExpr() {
193 return NULL;
194 }
195
196 static const char * DLL_CALLCONV
MimeType()197 MimeType() {
198 return "image/vnd.microsoft.icon";
199 }
200
201 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)202 Validate(FreeImageIO *io, fi_handle handle) {
203 ICONHEADER icon_header;
204
205 io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle);
206 #ifdef FREEIMAGE_BIGENDIAN
207 SwapIconHeader(&icon_header);
208 #endif
209
210 return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0));
211 }
212
213 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)214 SupportsExportDepth(int depth) {
215 return (
216 (depth == 1) ||
217 (depth == 4) ||
218 (depth == 8) ||
219 (depth == 16) ||
220 (depth == 24) ||
221 (depth == 32)
222 );
223 }
224
225 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)226 SupportsExportType(FREE_IMAGE_TYPE type) {
227 return (type == FIT_BITMAP) ? TRUE : FALSE;
228 }
229
230 static BOOL DLL_CALLCONV
SupportsNoPixels()231 SupportsNoPixels() {
232 return TRUE;
233 }
234
235 // ----------------------------------------------------------
236
237 static void * DLL_CALLCONV
Open(FreeImageIO * io,fi_handle handle,BOOL read)238 Open(FreeImageIO *io, fi_handle handle, BOOL read) {
239 // Allocate memory for the header structure
240 ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER));
241 if(lpIH == NULL) {
242 return NULL;
243 }
244
245 if (read) {
246 // Read in the header
247 io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle);
248 #ifdef FREEIMAGE_BIGENDIAN
249 SwapIconHeader(lpIH);
250 #endif
251
252 if(!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) {
253 // Not an ICO file
254 free(lpIH);
255 return NULL;
256 }
257 }
258 else {
259 // Fill the header
260 lpIH->idReserved = 0;
261 lpIH->idType = 1;
262 lpIH->idCount = 0;
263 }
264
265 return lpIH;
266 }
267
268 static void DLL_CALLCONV
Close(FreeImageIO * io,fi_handle handle,void * data)269 Close(FreeImageIO *io, fi_handle handle, void *data) {
270 // free the header structure
271 ICONHEADER *lpIH = (ICONHEADER*)data;
272 free(lpIH);
273 }
274
275 // ----------------------------------------------------------
276
277 static int DLL_CALLCONV
PageCount(FreeImageIO * io,fi_handle handle,void * data)278 PageCount(FreeImageIO *io, fi_handle handle, void *data) {
279 ICONHEADER *lpIH = (ICONHEADER*)data;
280
281 if(lpIH) {
282 return lpIH->idCount;
283 }
284 return 1;
285 }
286
287 // ----------------------------------------------------------
288
289 static FIBITMAP*
LoadStandardIcon(FreeImageIO * io,fi_handle handle,int flags,BOOL header_only)290 LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) {
291 FIBITMAP *dib = NULL;
292
293 // load the BITMAPINFOHEADER
294 BITMAPINFOHEADER bmih;
295 io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle);
296 #ifdef FREEIMAGE_BIGENDIAN
297 SwapInfoHeader(&bmih);
298 #endif
299
300 // allocate the bitmap
301 int width = bmih.biWidth;
302 int height = bmih.biHeight / 2; // height == xor + and mask
303 unsigned bit_count = bmih.biBitCount;
304 unsigned line = CalculateLine(width, bit_count);
305 unsigned pitch = CalculatePitch(line);
306
307 // allocate memory for one icon
308
309 dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
310
311 if (dib == NULL) {
312 return NULL;
313 }
314
315 if( bmih.biBitCount <= 8 ) {
316 // read the palette data
317 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 }
325
326 if(header_only) {
327 // header only mode
328 return dib;
329 }
330
331 // read the icon
332 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 if(((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) && (bit_count < 32)) {
361 FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib);
362 FreeImage_Unload(dib);
363
364 if (dib32 == NULL) {
365 return NULL;
366 }
367
368 int width_and = WidthBytes(width);
369 BYTE *line_and = (BYTE *)malloc(width_and);
370
371 if( line_and == NULL ) {
372 FreeImage_Unload(dib32);
373 return NULL;
374 }
375
376 //loop through each line of the AND-mask generating the alpha channel, invert XOR-mask
377 for(int y = 0; y < height; y++) {
378 RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y);
379 io->read_proc(line_and, width_and, 1, handle);
380 for(int x = 0; x < width; x++) {
381 quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF;
382 if( quad->rgbReserved == 0 ) {
383 quad->rgbBlue ^= 0xFF;
384 quad->rgbGreen ^= 0xFF;
385 quad->rgbRed ^= 0xFF;
386 }
387 quad++;
388 }
389 }
390 free(line_and);
391
392 return dib32;
393 }
394
395 return dib;
396 }
397
398 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)399 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
400 if (page == -1) {
401 page = 0;
402 }
403
404 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
405
406 if (handle != NULL) {
407 FIBITMAP *dib = NULL;
408
409 // get the icon header
410 ICONHEADER *icon_header = (ICONHEADER*)data;
411
412 if (icon_header) {
413 // load the icon descriptions
414 ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
415 if(icon_list == NULL) {
416 return NULL;
417 }
418 io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET);
419 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 if (page < icon_header->idCount) {
426 // seek to the start of the bitmap data for the icon
427 io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_SET);
428
429 if( IsPNG(io, handle) ) {
430 // Vista icon support
431 // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
432 dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT);
433 }
434 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 dib = LoadStandardIcon(io, handle, flags, header_only);
439 }
440
441 free(icon_list);
442
443 return dib;
444
445 } else {
446 free(icon_list);
447 FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist");
448 }
449 } else {
450 FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file");
451 }
452 }
453
454 return NULL;
455 }
456
457 // ----------------------------------------------------------
458
459 static BOOL
SaveStandardIcon(FreeImageIO * io,FIBITMAP * dib,fi_handle handle)460 SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) {
461 BITMAPINFOHEADER *bmih = NULL;
462
463 // write the BITMAPINFOHEADER
464 bmih = FreeImage_GetInfoHeader(dib);
465 bmih->biHeight *= 2; // height == xor + and mask
466 #ifdef FREEIMAGE_BIGENDIAN
467 SwapInfoHeader(bmih);
468 #endif
469 io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle);
470 #ifdef FREEIMAGE_BIGENDIAN
471 SwapInfoHeader(bmih);
472 #endif
473 bmih->biHeight /= 2;
474
475 // write the palette data
476 if (FreeImage_GetPalette(dib) != NULL) {
477 RGBQUAD *pal = FreeImage_GetPalette(dib);
478 FILE_BGRA bgra;
479 for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
480 bgra.b = pal[i].rgbBlue;
481 bgra.g = pal[i].rgbGreen;
482 bgra.r = pal[i].rgbRed;
483 bgra.a = pal[i].rgbReserved;
484 io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
485 }
486 }
487
488 // write the bits
489 int width = bmih->biWidth;
490 int height = bmih->biHeight;
491 unsigned bit_count = bmih->biBitCount;
492 unsigned line = CalculateLine(width, bit_count);
493 unsigned pitch = CalculatePitch(line);
494 int size_xor = height * pitch;
495 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 BYTE *xor_mask = FreeImage_GetBits(dib);
546 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 BYTE *and_mask = (BYTE*)malloc(size_and);
552 if(!and_mask) {
553 return FALSE;
554 }
555
556 if(FreeImage_IsTransparent(dib)) {
557
558 if(bit_count == 32) {
559 // create the AND mask from the alpha channel
560
561 int width_and = WidthBytes(width);
562 BYTE *and_bits = and_mask;
563
564 // clear the mask
565 memset(and_mask, 0, size_and);
566
567 for(int y = 0; y < height; y++) {
568 RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y);
569
570 for(int x = 0; x < width; x++) {
571 if(bits[x].rgbReserved != 0xFF) {
572 // set any transparent color to full transparency
573 and_bits[x >> 3] |= (0x80 >> (x & 0x7));
574 }
575 }
576
577 and_bits += width_and;
578 }
579 }
580 else if(bit_count <= 8) {
581 // create the AND mask from the transparency table
582
583 BYTE *trns = FreeImage_GetTransparencyTable(dib);
584
585 int width_and = WidthBytes(width);
586 BYTE *and_bits = and_mask;
587
588 // clear the mask
589 memset(and_mask, 0, size_and);
590
591 switch(FreeImage_GetBPP(dib)) {
592 case 1:
593 {
594 for(int y = 0; y < height; y++) {
595 BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
596 for(int x = 0; x < width; x++) {
597 // get pixel at (x, y)
598 BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
599 if(trns[index] != 0xFF) {
600 // set any transparent color to full transparency
601 and_bits[x >> 3] |= (0x80 >> (x & 0x7));
602 }
603 }
604 and_bits += width_and;
605 }
606 }
607 break;
608
609 case 4:
610 {
611 for(int y = 0; y < height; y++) {
612 BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
613 for(int x = 0; x < width; x++) {
614 // get pixel at (x, y)
615 BYTE shift = (BYTE)((1 - x % 2) << 2);
616 BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift;
617 if(trns[index] != 0xFF) {
618 // set any transparent color to full transparency
619 and_bits[x >> 3] |= (0x80 >> (x & 0x7));
620 }
621 }
622 and_bits += width_and;
623 }
624 }
625 break;
626
627 case 8:
628 {
629 for(int y = 0; y < height; y++) {
630 BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
631 for(int x = 0; x < width; x++) {
632 // get pixel at (x, y)
633 BYTE index = bits[x];
634 if(trns[index] != 0xFF) {
635 // set any transparent color to full transparency
636 and_bits[x >> 3] |= (0x80 >> (x & 0x7));
637 }
638 }
639 and_bits += width_and;
640 }
641 }
642 break;
643
644 }
645 }
646 }
647 else {
648 // empty AND mask
649 memset(and_mask, 0, size_and);
650 }
651
652 io->write_proc(and_mask, size_and, 1, handle);
653 free(and_mask);
654
655 return TRUE;
656 }
657
658 static BOOL DLL_CALLCONV
Save(FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int page,int flags,void * data)659 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
660 ICONHEADER *icon_header = NULL;
661 std::vector<FIBITMAP*> vPages;
662 int k;
663
664 if(!dib || !handle || !data) {
665 return FALSE;
666 }
667
668 // check format limits
669 unsigned w = FreeImage_GetWidth(dib);
670 unsigned h = FreeImage_GetHeight(dib);
671 if((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) {
672 FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h);
673 return FALSE;
674 }
675
676 if (page == -1) {
677 page = 0;
678 }
679
680 // get the icon header
681 icon_header = (ICONHEADER*)data;
682
683 try {
684 FIBITMAP *icon_dib = NULL;
685
686 // load all icons
687 for(k = 0; k < icon_header->idCount; k++) {
688 icon_dib = Load(io, handle, k, flags, data);
689 if(!icon_dib) {
690 throw FI_MSG_ERROR_DIB_MEMORY;
691 }
692 vPages.push_back(icon_dib);
693 }
694
695 // add the page
696 icon_dib = FreeImage_Clone(dib);
697 vPages.push_back(icon_dib);
698 icon_header->idCount++;
699
700 // write the header
701 io->seek_proc(handle, 0, SEEK_SET);
702 #ifdef FREEIMAGE_BIGENDIAN
703 SwapIconHeader(icon_header);
704 #endif
705 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 ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
716 if(!icon_list) {
717 throw FI_MSG_ERROR_MEMORY;
718 }
719 memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY));
720
721 for(k = 0; k < icon_header->idCount; k++) {
722 icon_dib = (FIBITMAP*)vPages[k];
723
724 // convert internal format to ICONDIRENTRY
725 // take into account Vista icons whose size is 256x256
726 const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib);
727 icon_list[k].bWidth = (bmih->biWidth > 255) ? 0 : (BYTE)bmih->biWidth;
728 icon_list[k].bHeight = (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight;
729 icon_list[k].bReserved = 0;
730 icon_list[k].wPlanes = bmih->biPlanes;
731 icon_list[k].wBitCount = bmih->biBitCount;
732 if( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) {
733 icon_list[k].bColorCount = 0;
734 } else {
735 icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount));
736 }
737 // initial guess (correct only for standard icons)
738 icon_list[k].dwBytesInRes = CalculateImageSize(icon_dib);
739 icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k);
740 }
741
742 // make a room for icon dir entries, until later update
743 const long directory_start = io->tell_proc(handle);
744 io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
745
746 // write the image bits for each image
747
748 DWORD dwImageOffset = (DWORD)io->tell_proc(handle);
749
750 for(k = 0; k < icon_header->idCount; k++) {
751 icon_dib = (FIBITMAP*)vPages[k];
752
753 if((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) {
754 // Vista icon support
755 FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT);
756 }
757 else {
758 // standard icon support
759 // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
760 SaveStandardIcon(io, icon_dib, handle);
761 }
762
763 // update ICONDIRENTRY members
764 DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset;
765 icon_list[k].dwImageOffset = dwImageOffset;
766 icon_list[k].dwBytesInRes = dwBytesInRes;
767 dwImageOffset += dwBytesInRes;
768 }
769
770 // update the icon descriptions
771 const long current_pos = io->tell_proc(handle);
772 io->seek_proc(handle, directory_start, SEEK_SET);
773 #ifdef FREEIMAGE_BIGENDIAN
774 SwapIconDirEntries(icon_list, icon_header->idCount);
775 #endif
776 io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
777 io->seek_proc(handle, current_pos, SEEK_SET);
778
779 free(icon_list);
780
781 // free the vector class
782 for(k = 0; k < icon_header->idCount; k++) {
783 icon_dib = (FIBITMAP*)vPages[k];
784 FreeImage_Unload(icon_dib);
785 }
786
787 return TRUE;
788
789 } catch(const char *text) {
790 // free the vector class
791 for(size_t k = 0; k < vPages.size(); k++) {
792 FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
793 FreeImage_Unload(icon_dib);
794 }
795 FreeImage_OutputMessageProc(s_format_id, text);
796 return FALSE;
797 }
798 }
799
800 // ==========================================================
801 // Init
802 // ==========================================================
803
804 void DLL_CALLCONV
InitICO(Plugin * plugin,int format_id)805 InitICO(Plugin *plugin, int format_id) {
806 s_format_id = format_id;
807
808 plugin->format_proc = Format;
809 plugin->description_proc = Description;
810 plugin->extension_proc = Extension;
811 plugin->regexpr_proc = RegExpr;
812 plugin->open_proc = Open;
813 plugin->close_proc = Close;
814 plugin->pagecount_proc = PageCount;
815 plugin->pagecapability_proc = NULL;
816 plugin->load_proc = Load;
817 plugin->save_proc = Save;
818 plugin->validate_proc = Validate;
819 plugin->mime_proc = MimeType;
820 plugin->supports_export_bpp_proc = SupportsExportDepth;
821 plugin->supports_export_type_proc = SupportsExportType;
822 plugin->supports_icc_profiles_proc = NULL;
823 plugin->supports_no_pixels_proc = SupportsNoPixels;
824 }
825