1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - Windows Icon/Cursor image loader
3  *
4  * Copyright (C) 1999 The Free Software Foundation
5  *
6  * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
7  *          Federico Mena-Quintero <federico@gimp.org>
8  *
9  * Based on io-bmp.c
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #undef DUMPBIH
26 #define DEBUG(s)
27 
28 #define INFOHEADER_SIZE 40
29 
30 /*
31 
32 Icons are just like BMP's, except for the header.
33 
34 Known bugs:
35 	* bi-tonal files aren't tested
36 
37 */
38 
39 #include "config.h"
40 #include <stdio.h>
41 #include <stdlib.h>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #include <string.h>
46 #include <errno.h>
47 #include <glib/gi18n-lib.h>
48 #include "gdk-pixbuf-io.h"
49 
50 
51 
52 /*
53 
54 These structures are actually dummies. These are according to
55 the "Windows API reference guide volume II" as written by
56 Borland International, but GCC fiddles with the alignment of
57 the internal members.
58 
59 */
60 
61 struct BitmapFileHeader {
62 	gushort bfType;
63 	guint bfSize;
64 	guint reserverd;
65 	guint bfOffbits;
66 };
67 
68 struct BitmapInfoHeader {
69 	guint biSize;
70 	guint biWidth;
71 	guint biHeight;
72 	gushort biPlanes;
73 	gushort biBitCount;
74 	guint biCompression;
75 	guint biSizeImage;
76 	guint biXPelsPerMeter;
77 	guint biYPelsPerMeter;
78 	guint biClrUsed;
79 	guint biClrImportant;
80 };
81 
82 #ifdef DUMPBIH
83 /*
84 
85 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
86 debugging purposes.
87 
88 */
DumpBIH(unsigned char * BIH)89 static void DumpBIH(unsigned char *BIH)
90 {
91 	printf("biSize      = %i \n",
92 	       (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
93 	printf("biWidth     = %i \n",
94 	       (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
95 	printf("biHeight    = %i \n",
96 	       (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
97 	       (BIH[8]));
98 	printf("biPlanes    = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
99 	printf("biBitCount  = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
100 	printf("biCompress  = %i \n",
101 	       (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
102 	       (BIH[16]));
103 	printf("biSizeImage = %i \n",
104 	       (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
105 	       (BIH[20]));
106 	printf("biXPels     = %i \n",
107 	       (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
108 	       (BIH[24]));
109 	printf("biYPels     = %i \n",
110 	       (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
111 	       (BIH[28]));
112 	printf("biClrUsed   = %i \n",
113 	       (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
114 	       (BIH[32]));
115 	printf("biClrImprtnt= %i \n",
116 	       (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
117 	       (BIH[36]));
118 }
119 #endif
120 
121 /* Progressive loading */
122 struct headerpair {
123 	gint width;
124 	gint height;
125 	guint depth;
126 	guint Negative;		/* Negative = 1 -> top down BMP,
127 				   Negative = 0 -> bottom up BMP */
128 };
129 
130 /* Score the various parts of the icon */
131 struct ico_direntry_data {
132 	gint ImageScore;
133         gint width;
134         gint height;
135 	guint DIBoffset;
136 	gint x_hot;
137 	gint y_hot;
138 };
139 
140 struct ico_progressive_state {
141 	GdkPixbufModuleSizeFunc size_func;
142 	GdkPixbufModulePreparedFunc prepared_func;
143 	GdkPixbufModuleUpdatedFunc updated_func;
144 	gpointer user_data;
145 
146 	gint HeaderSize;	/* The size of the header-part (incl colormap) */
147 	guchar *HeaderBuf;	/* The buffer for the header (incl colormap) */
148 	gint BytesInHeaderBuf;  /* The size of the allocated HeaderBuf */
149 	gint HeaderDone;	/* The nr of bytes actually in HeaderBuf */
150 
151 	gint LineWidth;		/* The width of a line in bytes */
152 	guchar *LineBuf;	/* Buffer for 1 line */
153 	gint LineDone;		/* # of bytes in LineBuf */
154 	gint Lines;		/* # of finished lines */
155 
156 	gint Type;		/*
157 				   32 = RGBA
158 				   24 = RGB
159 				   16 = 555 RGB
160 				   8 = 8 bit colormapped
161 				   4 = 4 bpp colormapped
162 				   1  = 1 bit bitonal
163 				 */
164         gboolean cursor;
165         gint x_hot;
166         gint y_hot;
167 
168 	struct headerpair Header;	/* Decoded (BE->CPU) header */
169 	GList *entries;
170 	guint			DIBoffset;
171 
172 	GdkPixbuf *pixbuf;	/* Our "target" */
173 };
174 
175 static gpointer
176 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
177                                  GdkPixbufModulePreparedFunc prepared_func,
178 				 GdkPixbufModuleUpdatedFunc updated_func,
179 				 gpointer user_data,
180                                  GError **error);
181 static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
182 static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
183                                                      const guchar * buf, guint size,
184                                                      GError **error);
185 
186 static void
context_free(struct ico_progressive_state * context)187 context_free (struct ico_progressive_state *context)
188 {
189 	g_free (context->LineBuf);
190 	context->LineBuf = NULL;
191 	g_free (context->HeaderBuf);
192 	g_list_free_full (context->entries, g_free);
193 	if (context->pixbuf)
194 		g_object_unref (context->pixbuf);
195 
196 	g_free (context);
197 }
198 
199 static gint
compare_direntry_scores(gconstpointer a,gconstpointer b)200 compare_direntry_scores (gconstpointer a,
201                          gconstpointer b)
202 {
203 	const struct ico_direntry_data *ia = a;
204 	const struct ico_direntry_data *ib = b;
205 
206 	/* Backwards, so largest first */
207 	if (ib->ImageScore < ia->ImageScore)
208 		return -1;
209 	else if (ib->ImageScore > ia->ImageScore)
210 		return 1;
211 	return 0;
212 }
213 
DecodeHeader(guchar * Data,gint Bytes,struct ico_progressive_state * State,GError ** error)214 static void DecodeHeader(guchar *Data, gint Bytes,
215 			 struct ico_progressive_state *State,
216 			 GError **error)
217 {
218 /* For ICO's we have to be very clever. There are multiple images possible
219    in an .ICO. As a simple heuristic, we select the image which is the largest
220    in pixels.
221  */
222 	struct ico_direntry_data *entry;
223 	gint IconCount = 0; /* The number of icon-versions in the file */
224 	guchar *BIH; /* The DIB for the used icon */
225  	guchar *Ptr;
226  	gint I;
227 	guint16 imgtype; /* 1 = icon, 2 = cursor */
228 	GList *l;
229 	gboolean got_broken_header = FALSE;
230 
231  	/* Step 1: The ICO header */
232 
233 	/* First word should be 0 according to specs */
234 	if (((Data[1] << 8) + Data[0]) != 0) {
235 		g_set_error (error,
236 			     GDK_PIXBUF_ERROR,
237 			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
238 			     _("Invalid header in icon (%s)"), "first word");
239 		return;
240 
241 	}
242 
243 	imgtype = (Data[3] << 8) + Data[2];
244 
245 	State->cursor = (imgtype == 2) ? TRUE : FALSE;
246 
247 	/* If it is not a cursor make sure it is actually an icon */
248 	if (!State->cursor && imgtype != 1) {
249 		g_set_error (error,
250 			     GDK_PIXBUF_ERROR,
251 			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
252 			     _("Invalid header in icon (%s)"), "image type");
253 		return;
254 	}
255 
256  	IconCount = (Data[5] << 8) + (Data[4]);
257 
258  	State->HeaderSize = 6 + IconCount*16;
259 
260         DEBUG(g_print ("Image type: %d (%s)\nImage count: %d\n", imgtype, imgtype == 2 ? "cursor" : "icon", IconCount));
261 
262  	if (State->HeaderSize>State->BytesInHeaderBuf) {
263  		guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
264 		if (!tmp) {
265 			g_set_error_literal (error,
266                                              GDK_PIXBUF_ERROR,
267                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
268                                              _("Not enough memory to load icon"));
269 			return;
270 		}
271 		State->HeaderBuf = tmp;
272  		State->BytesInHeaderBuf = State->HeaderSize;
273  	}
274  	if (Bytes < State->HeaderSize) {
275  		return;
276 	}
277 
278 	/* Now iterate through the ICONDIRENTRY structures, and sort them by
279 	 * which one we think is "best" (essentially the largest) */
280 	g_list_free_full (State->entries, g_free);
281 	State->entries = 0;
282 	Ptr = Data + 6;
283 	for (I=0;I<IconCount;I++) {
284                 int width;
285                 int height;
286                 int depth;
287                 int x_hot;
288                 int y_hot;
289                 guint data_size G_GNUC_UNUSED;
290                 guint data_offset;
291 
292                 width = Ptr[0];
293                 height = Ptr[1];
294                 depth = Ptr[2];
295 		x_hot = (Ptr[5] << 8) + Ptr[4];
296 		y_hot = (Ptr[7] << 8) + Ptr[6];
297                 data_size = ((guint) (Ptr[11]) << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
298 		data_offset = ((guint) (Ptr[15]) << 24) + (Ptr[14] << 16) + (Ptr[13] << 8) + (Ptr[12]);
299                 DEBUG(g_print ("Image %d: %d x %d\n\tDepth: %d\n", I, width, height, depth);
300                 if (imgtype == 2)
301                   g_print ("\tHotspot: %d x %d\n", x_hot, y_hot);
302                 else
303                   g_print ("\tColor planes: %d\n\tBits per pixel: %d\n", x_hot, y_hot);
304                 g_print ("\tSize: %d\n\tOffset: %d\n", data_size, data_offset);)
305 
306                 if (depth == 0)
307                         depth = 32;
308                 else if (depth <= 2)
309                         depth = 1;
310                 else if (depth <= 16)
311                         depth = 4;
312                 else if (depth <= 256)
313                         depth = 8;
314 
315 		/* We check whether the HeaderSize (int) would overflow */
316                 if (data_offset > INT_MAX - INFOHEADER_SIZE) {
317 			got_broken_header = TRUE;
318 			continue;
319 		}
320 
321 		entry = g_new0 (struct ico_direntry_data, 1);
322                 entry->width = width ? width : 256;
323                 entry->height = height ? height : 256;
324                 entry->ImageScore = entry->width * entry->height * depth;
325 		entry->x_hot = x_hot;
326 		entry->y_hot = y_hot;
327 		entry->DIBoffset = data_offset;
328 		State->entries = g_list_insert_sorted (State->entries, entry, compare_direntry_scores);
329 		Ptr += 16;
330 	}
331 
332 	/* Now go through and find one we can parse */
333 	entry = NULL;
334 	for (l = State->entries; l != NULL; l = g_list_next (l)) {
335 		entry = l->data;
336 
337 		/* Avoid invoking undefined behavior in the State->HeaderSize calculation below */
338 		if (entry->DIBoffset > G_MAXINT - INFOHEADER_SIZE) {
339 			g_set_error (error,
340 			             GDK_PIXBUF_ERROR,
341 			             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
342 			             _("Invalid header in icon (%s)"), "header size");
343 			return;
344 		}
345 
346 		/* We know how many bytes are in the "header" part. */
347 		State->HeaderSize = entry->DIBoffset + INFOHEADER_SIZE;
348 
349 		if (State->HeaderSize>State->BytesInHeaderBuf) {
350 			guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
351 			if (!tmp) {
352 				g_set_error_literal (error,
353 				                     GDK_PIXBUF_ERROR,
354 				                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
355 				                     _("Not enough memory to load icon"));
356 				return;
357 			}
358 			State->HeaderBuf = tmp;
359 			State->BytesInHeaderBuf = State->HeaderSize;
360 		}
361 		if (Bytes<State->HeaderSize)
362 			return;
363 
364 		BIH = Data+entry->DIBoffset;
365 
366 		/* A compressed icon, try the next one */
367 		if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
368 		    || (BIH[19] != 0)) {
369 			DEBUG(g_print("Skipping icon with score %d, as it is compressed\n", entry->ImageScore));
370 			continue;
371 		}
372 
373 		DEBUG(g_print("Selecting icon with score %d\n", entry->ImageScore));
374 
375 		/* If we made it to here then we have selected a BIH structure
376 		 * in a format that we can parse */
377 		break;
378 	}
379 
380 	/* No valid icon found, because all are compressed? */
381 	if (l == NULL) {
382 		g_set_error_literal (error,
383 				     GDK_PIXBUF_ERROR,
384 				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
385 				     got_broken_header ?
386 					_("Invalid header in icon") :
387 					_("Compressed icons are not supported"));
388 		return;
389 	}
390 
391 	/* This is the one we're going with */
392 	State->DIBoffset = entry->DIBoffset;
393 	State->x_hot = entry->x_hot;
394 	State->y_hot = entry->y_hot;
395 
396 #ifdef DUMPBIH
397 	DumpBIH(BIH);
398 #endif
399 	/* Add the palette to the headersize */
400 
401 	State->Header.width =
402 	    (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
403 	if (State->Header.width == 0)
404 		State->Header.width = 256;
405 
406 	State->Header.height =
407 	    (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
408 	    /* /2 because the BIH height includes the transparency mask */
409 	if (State->Header.height == 0)
410 		State->Header.height = 256;
411 
412 	/* Negative heights mean top-down pixel-order */
413 	if (State->Header.height < 0) {
414 		State->Header.height = -State->Header.height;
415 		State->Header.Negative = 1;
416 	}
417 	if (State->Header.width < 0) {
418 		State->Header.width = -State->Header.width;
419 	}
420 
421         if (State->Header.width != entry->width ||
422             State->Header.height != entry->height) {
423 		g_set_error (error,
424                              GDK_PIXBUF_ERROR,
425                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
426                              _("Invalid header in icon (%s)"), "image size");
427 		return;
428         }
429 
430 	State->Header.depth = (BIH[15] << 8) + (BIH[14]);
431 	State->Type = State->Header.depth;
432 
433 	/* Determine the  palette size. If the header indicates 0, it
434 	   is actually the maximum for the bpp. You have to love the
435 	   guys who made the spec. */
436 	I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
437 	I = I*4;
438 	if ((I==0)&&(State->Type==1))
439 		I = 2*4;
440 	if ((I==0)&&(State->Type==4))
441 		I = 16*4;
442 	if ((I==0)&&(State->Type==8))
443 		I = 256*4;
444 
445 	State->HeaderSize+=I;
446 
447 	if (State->HeaderSize < 0) {
448 		g_set_error (error,
449                              GDK_PIXBUF_ERROR,
450                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
451                              _("Invalid header in icon (%s)"), "palette size");
452 		return;
453 	}
454 
455  	if (State->HeaderSize>State->BytesInHeaderBuf) {
456 	        guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
457 		if (!tmp) {
458 			g_set_error_literal (error,
459                                              GDK_PIXBUF_ERROR,
460                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
461                                              _("Not enough memory to load icon"));
462 			return;
463 		}
464 		State->HeaderBuf = tmp;
465  		State->BytesInHeaderBuf = State->HeaderSize;
466  	}
467  	if (Bytes < State->HeaderSize) {
468  		return;
469 	}
470 
471         if (State->Type == 32)
472                 State->LineWidth = State->Header.width * 4;
473         else if (State->Type == 24)
474 		State->LineWidth = State->Header.width * 3;
475         else if (State->Type == 16)
476                 State->LineWidth = State->Header.width * 2;
477         else if (State->Type == 8)
478 		State->LineWidth = State->Header.width * 1;
479         else if (State->Type == 4)
480 		State->LineWidth = (State->Header.width+1)/2;
481         else if (State->Type == 1) {
482 		State->LineWidth = State->Header.width / 8;
483 		if ((State->Header.width & 7) != 0)
484 			State->LineWidth++;
485         } else {
486           g_set_error_literal (error,
487                                GDK_PIXBUF_ERROR,
488                                GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
489                                _("Unsupported icon type"));
490           return;
491 	}
492 
493 	/* Pad to a 32 bit boundary */
494 	if (((State->LineWidth % 4) > 0))
495 		State->LineWidth = (State->LineWidth / 4) * 4 + 4;
496 
497 
498 	if (State->LineBuf == NULL) {
499 		State->LineBuf = g_try_malloc(State->LineWidth);
500 		if (!State->LineBuf) {
501 			g_set_error_literal (error,
502                                              GDK_PIXBUF_ERROR,
503                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
504                                              _("Not enough memory to load icon"));
505 			return;
506 		}
507 	}
508 
509 	g_assert(State->LineBuf != NULL);
510 
511 
512 	if (State->pixbuf == NULL) {
513 		if (State->size_func) {
514 			gint width = State->Header.width;
515 			gint height = State->Header.height;
516 
517 			(*State->size_func) (&width, &height, State->user_data);
518 			if (width == 0 || height == 0) {
519 				State->LineWidth = 0;
520 				return;
521 			}
522 		}
523 
524 		State->pixbuf =
525 		    gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
526 				   State->Header.width,
527 				   State->Header.height);
528 		if (!State->pixbuf) {
529 			g_set_error_literal (error,
530                                              GDK_PIXBUF_ERROR,
531                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
532                                              _("Not enough memory to load icon"));
533 			return;
534 		}
535 		if (State->cursor) {
536 			gchar hot[10];
537 			g_snprintf (hot, 10, "%d", State->x_hot);
538 			gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
539 			g_snprintf (hot, 10, "%d", State->y_hot);
540 			gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
541 		}
542 
543 		if (State->prepared_func != NULL)
544 			/* Notify the client that we are ready to go */
545 			(*State->prepared_func) (State->pixbuf,
546                                                  NULL,
547 						 State->user_data);
548 
549 	}
550 
551 }
552 
553 /*
554  * func - called when we have pixmap created (but no image data)
555  * user_data - passed as arg 1 to func
556  * return context (opaque to user)
557  */
558 
559 static gpointer
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,GdkPixbufModulePreparedFunc prepared_func,GdkPixbufModuleUpdatedFunc updated_func,gpointer user_data,GError ** error)560 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
561                                  GdkPixbufModulePreparedFunc prepared_func,
562 				 GdkPixbufModuleUpdatedFunc updated_func,
563 				 gpointer user_data,
564                                  GError **error)
565 {
566 	struct ico_progressive_state *context;
567 
568 	context = g_new0(struct ico_progressive_state, 1);
569 	context->size_func = size_func;
570 	context->prepared_func = prepared_func;
571 	context->updated_func = updated_func;
572 	context->user_data = user_data;
573 
574 	context->HeaderSize = 54;
575 	context->HeaderBuf = g_try_malloc(14 + INFOHEADER_SIZE + 4*256 + 512);
576 	if (!context->HeaderBuf) {
577 		g_free (context);
578 		g_set_error_literal (error,
579                                      GDK_PIXBUF_ERROR,
580                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
581                                      _("Not enough memory to load ICO file"));
582 		return NULL;
583 	}
584 	/* 4*256 for the colormap */
585 	context->BytesInHeaderBuf = 14 + INFOHEADER_SIZE + 4*256 + 512 ;
586 	context->HeaderDone = 0;
587 
588 	context->LineWidth = 0;
589 	context->LineBuf = NULL;
590 	context->LineDone = 0;
591 	context->Lines = 0;
592 
593 	context->Type = 0;
594 
595 	memset(&context->Header, 0, sizeof(struct headerpair));
596 
597 
598 	context->pixbuf = NULL;
599 
600 
601 	return (gpointer) context;
602 }
603 
604 /*
605  * context - returned from image_begin_load
606  *
607  * free context, unref gdk_pixbuf
608  */
609 static gboolean
gdk_pixbuf__ico_image_stop_load(gpointer data,GError ** error)610 gdk_pixbuf__ico_image_stop_load(gpointer data,
611 				GError **error)
612 {
613 	struct ico_progressive_state *context =
614 	    (struct ico_progressive_state *) data;
615 	gboolean ret = TRUE;
616 
617         /* FIXME this thing needs to report errors if
618          * we have unused image data
619          */
620 
621 	g_return_val_if_fail(context != NULL, TRUE);
622 
623 	if (context->HeaderDone < context->HeaderSize) {
624 		g_set_error_literal (error,
625 				     GDK_PIXBUF_ERROR,
626 				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
627 				     _("ICO image was truncated or incomplete."));
628 		ret = FALSE;
629 	}
630 
631 	context_free (context);
632         return ret;
633 }
634 
635 static void
OneLine32(struct ico_progressive_state * context)636 OneLine32 (struct ico_progressive_state *context)
637 {
638         gint X;
639         guchar *Pixels;
640 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
641 
642         X = 0;
643         if (context->Header.Negative == 0)
644                 Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
645 			  rowstride * (context->Header.height - context->Lines - 1));
646         else
647                 Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
648 			  rowstride * context->Lines);
649         while (X < context->Header.width) {
650                 /* BGRA */
651                 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
652                 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
653                 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
654                 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
655                 X++;
656         }
657 }
658 
OneLine24(struct ico_progressive_state * context)659 static void OneLine24(struct ico_progressive_state *context)
660 {
661 	gint X;
662 	guchar *Pixels;
663 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
664 
665 	X = 0;
666 	if (context->Header.Negative == 0)
667 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
668 			  rowstride * (context->Header.height - context->Lines - 1));
669 	else
670 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
671 			  rowstride * context->Lines);
672 	while (X < context->Header.width) {
673 		Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
674 		Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
675 		Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
676 		Pixels[X * 4 + 3] = 0xff;
677 		X++;
678 	}
679 
680 }
681 
682 static void
OneLine16(struct ico_progressive_state * context)683 OneLine16 (struct ico_progressive_state *context)
684 {
685         int i;
686         guchar *pixels;
687         guchar *src;
688 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
689 
690         if (context->Header.Negative == 0)
691                 pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
692 			  rowstride * (context->Header.height - context->Lines - 1));
693         else
694                 pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
695 			  rowstride * context->Lines);
696 
697         src = context->LineBuf;
698 
699         for (i = 0; i < context->Header.width; i++) {
700                 int v, r, g, b;
701 
702                 v = (int) src[0] | ((int) src[1] << 8);
703                 src += 2;
704 
705                 /* Extract 5-bit RGB values */
706 
707                 r = (v >> 10) & 0x1f;
708                 g = (v >> 5) & 0x1f;
709                 b = v & 0x1f;
710 
711                 /* Fill the rightmost bits to form 8-bit values */
712 
713                 *pixels++ = (r << 3) | (r >> 2);
714                 *pixels++ = (g << 3) | (g >> 2);
715                 *pixels++ = (b << 3) | (b >> 2);
716                 *pixels++ = 0xff;
717         }
718 }
719 
720 
OneLine8(struct ico_progressive_state * context)721 static void OneLine8(struct ico_progressive_state *context)
722 {
723 	gint X;
724 	guchar *Pixels;
725 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
726 
727 	X = 0;
728 	if (context->Header.Negative == 0)
729 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
730 			  rowstride * (context->Header.height - context->Lines - 1));
731 	else
732 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
733 			  rowstride * context->Lines);
734 	while (X < context->Header.width) {
735 		/* The joys of having a BGR byteorder */
736 		Pixels[X * 4 + 0] =
737 		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 2 + context->DIBoffset];
738 		Pixels[X * 4 + 1] =
739 		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 1 +context->DIBoffset];
740 		Pixels[X * 4 + 2] =
741 		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE +context->DIBoffset];
742 		Pixels[X * 4 + 3] = 0xff;
743 		X++;
744 	}
745 }
OneLine4(struct ico_progressive_state * context)746 static void OneLine4(struct ico_progressive_state *context)
747 {
748 	gint X;
749 	guchar *Pixels;
750 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
751 
752 	X = 0;
753 	if (context->Header.Negative == 0)
754 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
755 			  rowstride * (context->Header.height - context->Lines - 1));
756 	else
757 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
758 			  rowstride * context->Lines);
759 
760 	while (X < context->Header.width) {
761 		guchar Pix;
762 
763 		Pix = context->LineBuf[X/2];
764 
765 		Pixels[X * 4 + 0] =
766 		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 2 + context->DIBoffset];
767 		Pixels[X * 4 + 1] =
768 		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 1 +context->DIBoffset];
769 		Pixels[X * 4 + 2] =
770 		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + context->DIBoffset];
771 		Pixels[X * 4 + 3] = 0xff;
772 		X++;
773 		if (X<context->Header.width) {
774 			/* Handle the other 4 bit pixel only when there is one */
775 			Pixels[X * 4 + 0] =
776 			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 2 + context->DIBoffset];
777 			Pixels[X * 4 + 1] =
778 			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 1 + context->DIBoffset];
779 			Pixels[X * 4 + 2] =
780 			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + context->DIBoffset];
781 			Pixels[X * 4 + 3] = 0xff;
782 			X++;
783 		}
784 	}
785 
786 }
787 
OneLine1(struct ico_progressive_state * context)788 static void OneLine1(struct ico_progressive_state *context)
789 {
790 	gint X;
791 	guchar *Pixels;
792 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
793 
794 	X = 0;
795 	if (context->Header.Negative == 0)
796 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
797 			  rowstride * (context->Header.height - context->Lines - 1));
798 	else
799 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
800 			  rowstride * context->Lines);
801 	while (X < context->Header.width) {
802 		int Bit;
803 
804 		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
805 		Bit = Bit & 1;
806 		/* The joys of having a BGR byteorder */
807 		Pixels[X * 4 + 0] = Bit*255;
808 		Pixels[X * 4 + 1] = Bit*255;
809 		Pixels[X * 4 + 2] = Bit*255;
810 		Pixels[X * 4 + 3] = 0xff;
811 		X++;
812 	}
813 }
814 
OneLineTransp(struct ico_progressive_state * context)815 static void OneLineTransp(struct ico_progressive_state *context)
816 {
817 	gint X;
818 	guchar *Pixels;
819 	gsize rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
820 
821 	/* Ignore the XOR mask for XP style 32-bpp icons with alpha */
822 	if (context->Header.depth == 32)
823 		return;
824 
825 	X = 0;
826 	if (context->Header.Negative == 0)
827 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
828 			  rowstride * (2*context->Header.height - context->Lines - 1));
829 	else
830 		Pixels = (gdk_pixbuf_get_pixels (context->pixbuf) +
831 			  rowstride * (context->Lines-context->Header.height));
832 	while (X < context->Header.width) {
833 		int Bit;
834 
835 		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
836 		Bit = Bit & 1;
837 		/* The joys of having a BGR byteorder */
838 		Pixels[X * 4 + 3] = 255-Bit*255;
839 #if 0
840 		if (Bit){
841 		  Pixels[X*4+0] = 255;
842 		  Pixels[X*4+1] = 255;
843 		} else {
844 		  Pixels[X*4+0] = 0;
845 		  Pixels[X*4+1] = 0;
846 		}
847 #endif
848 		X++;
849 	}
850 }
851 
852 
OneLine(struct ico_progressive_state * context)853 static void OneLine(struct ico_progressive_state *context)
854 {
855 	context->LineDone = 0;
856 
857 	if (context->Lines >= context->Header.height*2) {
858 		return;
859 	}
860 
861 	if (context->Lines <context->Header.height) {
862                 if (context->Type == 32)
863                         OneLine32 (context);
864 		else if (context->Type == 24)
865 			OneLine24(context);
866                 else if (context->Type == 16)
867                         OneLine16 (context);
868 		else if (context->Type == 8)
869 			OneLine8(context);
870 		else if (context->Type == 4)
871 			OneLine4(context);
872 		else if (context->Type == 1)
873 			OneLine1(context);
874 		else
875 			g_assert_not_reached ();
876 	} else
877 		OneLineTransp(context);
878 
879 	context->Lines++;
880 	if (context->Lines>=context->Header.height) {
881 		context->Type = 1;
882 		context->LineWidth = context->Header.width / 8;
883 		if ((context->Header.width & 7) != 0)
884 			context->LineWidth++;
885 		/* Pad to a 32 bit boundary */
886 		if (((context->LineWidth % 4) > 0))
887 			context->LineWidth = (context->LineWidth / 4) * 4 + 4;
888 	}
889 
890 	if (context->updated_func != NULL) {
891 		int y;
892 
893 		y = context->Lines % context->Header.height;
894 		if (context->Header.Negative == 0 &&
895 		    context->Lines < context->Header.height)
896 			y = context->Header.height - y;
897 		(*context->updated_func) (context->pixbuf,
898 					  0,
899 					  y,
900 					  context->Header.width,
901 					  1,
902 					  context->user_data);
903 
904 	}
905 }
906 
907 /*
908  * context - from image_begin_load
909  * buf - new image data
910  * size - length of new image data
911  *
912  * append image data onto inrecrementally built output image
913  */
914 static gboolean
gdk_pixbuf__ico_image_load_increment(gpointer data,const guchar * buf,guint size,GError ** error)915 gdk_pixbuf__ico_image_load_increment(gpointer data,
916                                      const guchar * buf,
917                                      guint size,
918                                      GError **error)
919 {
920 	struct ico_progressive_state *context =
921 	    (struct ico_progressive_state *) data;
922 
923 	gint BytesToCopy;
924 
925 	while (size > 0) {
926 		g_assert(context->LineDone >= 0);
927 		if (context->HeaderDone < context->HeaderSize) {	/* We still
928 									   have headerbytes to do */
929 			BytesToCopy =
930 			    context->HeaderSize - context->HeaderDone;
931 			if (BytesToCopy > size)
932 				BytesToCopy = size;
933 
934 			memmove(context->HeaderBuf + context->HeaderDone,
935 			       buf, BytesToCopy);
936 
937 			size -= BytesToCopy;
938 			buf += BytesToCopy;
939 			context->HeaderDone += BytesToCopy;
940 		}
941 		else {
942 			BytesToCopy =
943 			    context->LineWidth - context->LineDone;
944 			if (BytesToCopy > size)
945 				BytesToCopy = size;
946 
947 			if (BytesToCopy > 0) {
948 				/* Should be non-NULL once the header is decoded, as below. */
949 				g_assert (context->LineBuf != NULL);
950 
951 				memmove(context->LineBuf +
952 				       context->LineDone, buf,
953 				       BytesToCopy);
954 
955 				size -= BytesToCopy;
956 				buf += BytesToCopy;
957 				context->LineDone += BytesToCopy;
958 			}
959 			if ((context->LineDone >= context->LineWidth) && (context->LineWidth > 0)) {
960 				/* By this point, DecodeHeader() will have been called, and should have returned successfully
961 				 * or set a #GError, as its only return-FALSE-without-setting-a-GError paths are when
962 				 * (context->HeaderDone < context->HeaderSize) or (context->LineWidth == 0).
963 				 * If it’s returned a #GError, we will have bailed already; otherwise, pixbuf will be set. */
964 				g_assert (context->pixbuf != NULL);
965 				OneLine(context);
966 			}
967 
968 
969 		}
970 
971 		if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
972 			GError *decode_err = NULL;
973 			DecodeHeader(context->HeaderBuf,
974 				     context->HeaderDone, context, &decode_err);
975 			if (context->LineBuf != NULL && context->LineWidth == 0)
976 				return TRUE;
977 
978 			if (decode_err) {
979 				g_propagate_error (error, decode_err);
980 				return FALSE;
981 			}
982 		}
983 	}
984 
985 	return TRUE;
986 }
987 
988 /* saving ICOs */
989 
990 static gint
write8(FILE * f,guint8 * data,gint count)991 write8 (FILE     *f,
992 	guint8   *data,
993 	gint      count)
994 {
995   gint bytes;
996   gint written;
997 
998   written = 0;
999   while (count > 0)
1000     {
1001       bytes = fwrite ((char*) data, sizeof (char), count, f);
1002       if (bytes <= 0)
1003         break;
1004       count -= bytes;
1005       data += bytes;
1006       written += bytes;
1007     }
1008 
1009   return written;
1010 }
1011 
1012 static gint
write16(FILE * f,guint16 * data,gint count)1013 write16 (FILE     *f,
1014 	 guint16  *data,
1015 	 gint      count)
1016 {
1017   gint i;
1018 
1019   for (i = 0; i < count; i++)
1020 	  data[i] = GUINT16_TO_LE (data[i]);
1021 
1022   return write8 (f, (guint8*) data, count * 2);
1023 }
1024 
1025 static gint
write32(FILE * f,guint32 * data,gint count)1026 write32 (FILE     *f,
1027 	 guint32  *data,
1028 	 gint      count)
1029 {
1030   gint i;
1031 
1032   for (i = 0; i < count; i++)
1033 	  data[i] = GUINT32_TO_LE (data[i]);
1034 
1035   return write8 (f, (guint8*) data, count * 4);
1036 }
1037 
1038 typedef struct _IconEntry IconEntry;
1039 struct _IconEntry {
1040 	gint width;
1041 	gint height;
1042 	gint depth;
1043 	gint hot_x;
1044 	gint hot_y;
1045 
1046 	guint8 n_colors;
1047 	guint32 *colors;
1048 	guint xor_rowstride;
1049 	guint8 *xor;
1050 	guint and_rowstride;
1051 	guint8 *and;
1052 };
1053 
1054 static gboolean
fill_entry(IconEntry * icon,GdkPixbuf * pixbuf,gint hot_x,gint hot_y,GError ** error)1055 fill_entry (IconEntry *icon,
1056 	    GdkPixbuf *pixbuf,
1057 	    gint       hot_x,
1058 	    gint       hot_y,
1059 	    GError   **error)
1060  {
1061 	guchar *p, *pixels, *and, *xor;
1062 	gint n_channels, v, x, y;
1063 
1064 	if (icon->width > 256 || icon->height > 256) {
1065 		g_set_error_literal (error,
1066                                      GDK_PIXBUF_ERROR,
1067                                      GDK_PIXBUF_ERROR_BAD_OPTION,
1068                                      _("Image too large to be saved as ICO"));
1069 		return FALSE;
1070 	}
1071 
1072 	if (hot_x > -1 && hot_y > -1) {
1073 		icon->hot_x = hot_x;
1074 		icon->hot_y = hot_y;
1075 		if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
1076 			g_set_error_literal (error,
1077                                              GDK_PIXBUF_ERROR,
1078                                              GDK_PIXBUF_ERROR_BAD_OPTION,
1079                                              _("Cursor hotspot outside image"));
1080 			return FALSE;
1081 		}
1082 	}
1083 	else {
1084 		icon->hot_x = -1;
1085 		icon->hot_y = -1;
1086 	}
1087 
1088 	switch (icon->depth) {
1089 	case 32:
1090 		icon->xor_rowstride = icon->width * 4;
1091 		break;
1092 	case 24:
1093 		icon->xor_rowstride = icon->width * 3;
1094 		break;
1095 	case 16:
1096 		icon->xor_rowstride = icon->width * 2;
1097 		break;
1098 	default:
1099 		g_set_error (error,
1100 			     GDK_PIXBUF_ERROR,
1101 			     GDK_PIXBUF_ERROR_BAD_OPTION,
1102 			     _("Unsupported depth for ICO file: %d"), icon->depth);
1103 		return FALSE;
1104 	}
1105 
1106 	if ((icon->xor_rowstride % 4) != 0)
1107 		icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
1108 	icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
1109 
1110 	icon->and_rowstride = (icon->width + 7) / 8;
1111 	if ((icon->and_rowstride % 4) != 0)
1112 		icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
1113 	icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
1114 
1115 	pixels = gdk_pixbuf_get_pixels (pixbuf);
1116 	n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1117 	for (y = 0; y < icon->height; y++) {
1118 		p = pixels + (gsize) gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
1119 		and = icon->and + icon->and_rowstride * y;
1120 		xor = icon->xor + icon->xor_rowstride * y;
1121 		for (x = 0; x < icon->width; x++) {
1122 			switch (icon->depth) {
1123 			case 32:
1124 				xor[0] = p[2];
1125 				xor[1] = p[1];
1126 				xor[2] = p[0];
1127 				xor[3] = 0xff;
1128 				if (n_channels == 4) {
1129 					xor[3] = p[3];
1130 					if (p[3] < 0x80)
1131 						*and |= 1 << (7 - x % 8);
1132 				}
1133 				xor += 4;
1134 				break;
1135 			case 24:
1136 				xor[0] = p[2];
1137 				xor[1] = p[1];
1138 				xor[2] = p[0];
1139 				if (n_channels == 4 && p[3] < 0x80)
1140 					*and |= 1 << (7 - x % 8);
1141 				xor += 3;
1142 				break;
1143 			case 16:
1144 				v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1145 				xor[0] = v & 0xff;
1146 				xor[1] = v >> 8;
1147 				if (n_channels == 4 && p[3] < 0x80)
1148 					*and |= 1 << (7 - x % 8);
1149 				xor += 2;
1150 				break;
1151 			}
1152 
1153 			p += n_channels;
1154 			if (x % 8 == 7)
1155 				and++;
1156 		}
1157 	}
1158 
1159 	return TRUE;
1160 }
1161 
1162 static void
free_entry(IconEntry * icon)1163 free_entry (IconEntry *icon)
1164 {
1165 	g_free (icon->colors);
1166 	g_free (icon->and);
1167 	g_free (icon->xor);
1168 	g_free (icon);
1169 }
1170 
1171 static void
write_icon(FILE * f,GSList * entries)1172 write_icon (FILE *f, GSList *entries)
1173 {
1174 	IconEntry *icon;
1175 	GSList *entry;
1176 	guint8 bytes[4];
1177 	guint16 words[4];
1178 	guint32 dwords[6];
1179 	gint type;
1180 	gint n_entries;
1181 	gint offset;
1182 	gint size;
1183 
1184 	if (((IconEntry *)entries->data)->hot_x > -1)
1185 		type = 2;
1186 	else
1187 		type = 1;
1188 	n_entries = g_slist_length (entries);
1189 
1190 	/* header */
1191 	words[0] = 0;
1192 	words[1] = type;
1193 	words[2] = n_entries;
1194 	write16 (f, words, 3);
1195 
1196 	offset = 6 + 16 * n_entries;
1197 
1198 	for (entry = entries; entry; entry = entry->next) {
1199 		icon = (IconEntry *)entry->data;
1200 		size = INFOHEADER_SIZE + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1201 
1202 		/* directory entry */
1203 		if (icon->width == 256)
1204 			bytes[0] = 0;
1205 		else
1206 			bytes[0] = icon->width;
1207 		if (icon->height == 256)
1208 			bytes[1] = 0;
1209 		else
1210 			bytes[1] = icon->height;
1211 		bytes[2] = icon->n_colors;
1212 		bytes[3] = 0;
1213 		write8 (f, bytes, 4);
1214 		if (type == 1) {
1215 			words[0] = 1;
1216 			words[1] = icon->depth;
1217 		}
1218 		else {
1219 			words[0] = icon->hot_x;
1220 			words[1] = icon->hot_y;
1221 		}
1222 		write16 (f, words, 2);
1223 		dwords[0] = size;
1224 		dwords[1] = offset;
1225 		write32 (f, dwords, 2);
1226 
1227 		offset += size;
1228 	}
1229 
1230 	for (entry = entries; entry; entry = entry->next) {
1231 		icon = (IconEntry *)entry->data;
1232 
1233 		/* bitmap header */
1234 		dwords[0] = INFOHEADER_SIZE;
1235 		dwords[1] = icon->width;
1236 		dwords[2] = icon->height * 2;
1237 		write32 (f, dwords, 3);
1238 		words[0] = 1;
1239 		words[1] = icon->depth;
1240 		write16 (f, words, 2);
1241 		dwords[0] = 0;
1242 		dwords[1] = 0;
1243 		dwords[2] = 0;
1244 		dwords[3] = 0;
1245 		dwords[4] = 0;
1246 		dwords[5] = 0;
1247 		write32 (f, dwords, 6);
1248 
1249 		/* image data */
1250 		write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1251 		write8 (f, icon->and, icon->and_rowstride * icon->height);
1252 	}
1253 }
1254 
1255 /* Locale-independent signed integer string parser, base 10.
1256  * @minimum and @maximum are valid inclusively. */
1257 static gboolean
ascii_strtoll(const gchar * str,gint64 minimum,gint64 maximum,gint64 * out,GError ** error)1258 ascii_strtoll (const gchar  *str,
1259                gint64        minimum,
1260                gint64        maximum,
1261                gint64       *out,
1262                GError      **error)
1263 {
1264 	gint64 retval;
1265 	const gchar *end_ptr;
1266 
1267 	errno = 0;
1268 	retval = g_ascii_strtoll (str, (gchar **) &end_ptr, 10);
1269 
1270 	if (errno != 0) {
1271 		g_set_error_literal (error,
1272 		                     G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1273 		                     g_strerror (errno));
1274 		return FALSE;
1275 	} else if (end_ptr == str || *end_ptr != '\0') {
1276 		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1277 		             "Argument is not an integer: %s", str);
1278 		return FALSE;
1279 	} else if ((maximum < G_MAXINT64 && retval > maximum) ||
1280 	           (minimum > G_MININT64 && retval < minimum)) {
1281 		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1282 		             "Argument should be in range [%" G_GINT64_FORMAT
1283 		             ", %" G_GINT64_FORMAT "]: %s",
1284 		             minimum, maximum, str);
1285 		return FALSE;
1286 	}
1287 
1288 	g_assert (retval >= minimum && retval <= maximum);
1289 
1290 	if (out != NULL)
1291 		*out = retval;
1292 
1293 	return TRUE;
1294 }
1295 
1296 static gboolean
gdk_pixbuf__ico_image_save(FILE * f,GdkPixbuf * pixbuf,gchar ** keys,gchar ** values,GError ** error)1297 gdk_pixbuf__ico_image_save (FILE          *f,
1298                             GdkPixbuf     *pixbuf,
1299                             gchar        **keys,
1300                             gchar        **values,
1301                             GError       **error)
1302 {
1303 	gint hot_x, hot_y;
1304 	IconEntry *icon;
1305 	GSList *entries = NULL;
1306 
1307 	/* support only single-image ICOs for now */
1308 	icon = g_new0 (IconEntry, 1);
1309 	icon->width = gdk_pixbuf_get_width (pixbuf);
1310 	icon->height = gdk_pixbuf_get_height (pixbuf);
1311 	icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1312 	hot_x = -1;
1313 	hot_y = -1;
1314 
1315 	/* parse options */
1316 	if (keys && *keys) {
1317 		gchar **kiter;
1318 		gchar **viter;
1319 
1320 		for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1321 			gint64 out;
1322 			if (strcmp (*kiter, "depth") == 0) {
1323 				if (!ascii_strtoll (*viter, 1, 32,
1324 				                    &out, error))
1325 					return FALSE;
1326 				icon->depth = out;
1327 			}
1328 			else if (strcmp (*kiter, "x_hot") == 0) {
1329 				if (!ascii_strtoll (*viter, G_MININT, G_MAXINT,
1330 				                    &out, error))
1331 					return FALSE;
1332 				hot_x = out;
1333 			}
1334 			else if (strcmp (*kiter, "y_hot") == 0) {
1335 				if (!ascii_strtoll (*viter, G_MININT, G_MAXINT,
1336 				                    &out, error))
1337 					return FALSE;
1338 				hot_y = out;
1339 			}
1340 
1341 		}
1342 	}
1343 
1344 	if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1345 		free_entry (icon);
1346 		return FALSE;
1347 	}
1348 
1349 	entries = g_slist_append (entries, icon);
1350 	write_icon (f, entries);
1351 
1352 	g_slist_foreach (entries, (GFunc)free_entry, NULL);
1353 	g_slist_free (entries);
1354 
1355 	return TRUE;
1356 }
1357 
1358 static gboolean
gdk_pixbuf__ico_is_save_option_supported(const gchar * option_key)1359 gdk_pixbuf__ico_is_save_option_supported (const gchar *option_key)
1360 {
1361         if (g_strcmp0 (option_key, "depth") == 0 ||
1362             g_strcmp0 (option_key, "x_hot") == 0 ||
1363             g_strcmp0 (option_key, "y_hot") == 0)
1364                 return TRUE;
1365 
1366         return FALSE;
1367 }
1368 
1369 #ifndef INCLUDE_ico
1370 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1371 #else
1372 #define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
1373 #endif
1374 
MODULE_ENTRY(fill_vtable)1375 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1376 {
1377 	module->begin_load = gdk_pixbuf__ico_image_begin_load;
1378 	module->stop_load = gdk_pixbuf__ico_image_stop_load;
1379 	module->load_increment = gdk_pixbuf__ico_image_load_increment;
1380         module->save = gdk_pixbuf__ico_image_save;
1381         module->is_save_option_supported = gdk_pixbuf__ico_is_save_option_supported;
1382 }
1383 
MODULE_ENTRY(fill_info)1384 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1385 {
1386 	static const GdkPixbufModulePattern signature[] = {
1387 		{ "  \x1   ", "zz znz", 100 },
1388 		{ "  \x2   ", "zz znz", 100 },
1389 		{ NULL, NULL, 0 }
1390 	};
1391 	static const gchar *mime_types[] = {
1392 		"image/x-icon",
1393 		"image/x-ico",
1394 		"image/x-win-bitmap",
1395                 "image/vnd.microsoft.icon",
1396                 "application/ico",
1397                 "image/ico",
1398                 "image/icon",
1399                 "text/ico",
1400 		NULL
1401 	};
1402 	static const gchar *extensions[] = {
1403 		"ico",
1404 		"cur",
1405 		NULL
1406 	};
1407 
1408 	info->name = "ico";
1409 	info->signature = (GdkPixbufModulePattern *) signature;
1410 	info->description = NC_("image format", "Windows icon");
1411 	info->mime_types = (gchar **) mime_types;
1412 	info->extensions = (gchar **) extensions;
1413 	info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1414 	info->license = "LGPL";
1415 }
1416 
1417 
1418 
1419 
1420