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