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