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
32Icons are just like BMP's, except for the header.
33
34Known 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
54These structures are actually dummies. These are according to
55the "Windows API reference guide volume II" as written by
56Borland International, but GCC fiddles with the alignment of
57the internal members.
58
59*/
60
61struct BitmapFileHeader {
62 gushort bfType;
63 guint bfSize;
64 guint reserverd;
65 guint bfOffbits;
66};
67
68struct 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
85DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
86debugging purposes.
87
88*/
89static 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 */
122struct 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 */
131struct 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
140struct 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
175static gpointer
176gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
177 GdkPixbufModulePreparedFunc prepared_func,
178 GdkPixbufModuleUpdatedFunc updated_func,
179 gpointer user_data,
180 GError **error);
181static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
182static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
183 const guchar * buf, guint size,
184 GError **error);
185
186static void
187context_free (struct ico_progressive_state *context)
188{
189 g_free (mem: context->LineBuf);
190 context->LineBuf = NULL;
191 g_free (mem: context->HeaderBuf);
192 g_list_free_full (list: context->entries, free_func: g_free);
193 if (context->pixbuf)
194 g_object_unref (object: context->pixbuf);
195
196 g_free (mem: context);
197}
198
199static gint
200compare_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
214static 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 (err: error,
236 GDK_PIXBUF_ERROR,
237 code: 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 (err: error,
250 GDK_PIXBUF_ERROR,
251 code: 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(mem: State->HeaderBuf,n_bytes: State->HeaderSize);
264 if (!tmp) {
265 g_set_error_literal (err: error,
266 GDK_PIXBUF_ERROR,
267 code: 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 (list: State->entries, free_func: 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 (list: State->entries, data: entry, func: 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 (err: error,
340 GDK_PIXBUF_ERROR,
341 code: 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(mem: State->HeaderBuf,n_bytes: State->HeaderSize);
351 if (!tmp) {
352 g_set_error_literal (err: error,
353 GDK_PIXBUF_ERROR,
354 code: 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 (err: error,
383 GDK_PIXBUF_ERROR,
384 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
385 message: 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 (err: error,
424 GDK_PIXBUF_ERROR,
425 code: 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 (err: error,
449 GDK_PIXBUF_ERROR,
450 code: 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(mem: State->HeaderBuf,n_bytes: State->HeaderSize);
457 if (!tmp) {
458 g_set_error_literal (err: error,
459 GDK_PIXBUF_ERROR,
460 code: 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 (err: error,
487 GDK_PIXBUF_ERROR,
488 code: 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(n_bytes: State->LineWidth);
500 if (!State->LineBuf) {
501 g_set_error_literal (err: error,
502 GDK_PIXBUF_ERROR,
503 code: 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 {
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(colorspace: GDK_COLORSPACE_RGB, TRUE, bits_per_sample: 8,
526 width: State->Header.width,
527 height: State->Header.height);
528 if (!State->pixbuf) {
529 g_set_error_literal (err: error,
530 GDK_PIXBUF_ERROR,
531 code: 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 (string: hot, n: 10, format: "%d", State->x_hot);
538 gdk_pixbuf_set_option (pixbuf: State->pixbuf, key: "x_hot", value: hot);
539 g_snprintf (string: hot, n: 10, format: "%d", State->y_hot);
540 gdk_pixbuf_set_option (pixbuf: State->pixbuf, key: "y_hot", value: hot);
541 }
542
543 /* Notify the client that we are ready to go */
544 (*State->prepared_func) (State->pixbuf,
545 NULL,
546 State->user_data);
547 }
548
549}
550
551/*
552 * func - called when we have pixmap created (but no image data)
553 * user_data - passed as arg 1 to func
554 * return context (opaque to user)
555 */
556
557static gpointer
558gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
559 GdkPixbufModulePreparedFunc prepared_func,
560 GdkPixbufModuleUpdatedFunc updated_func,
561 gpointer user_data,
562 GError **error)
563{
564 struct ico_progressive_state *context;
565
566 g_assert (size_func != NULL);
567 g_assert (prepared_func != NULL);
568 g_assert (updated_func != NULL);
569
570 context = g_new0(struct ico_progressive_state, 1);
571 context->size_func = size_func;
572 context->prepared_func = prepared_func;
573 context->updated_func = updated_func;
574 context->user_data = user_data;
575
576 context->HeaderSize = 54;
577 context->HeaderBuf = g_try_malloc(n_bytes: 14 + INFOHEADER_SIZE + 4*256 + 512);
578 if (!context->HeaderBuf) {
579 g_free (mem: context);
580 g_set_error_literal (err: error,
581 GDK_PIXBUF_ERROR,
582 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
583 _("Not enough memory to load ICO file"));
584 return NULL;
585 }
586 /* 4*256 for the colormap */
587 context->BytesInHeaderBuf = 14 + INFOHEADER_SIZE + 4*256 + 512 ;
588 context->HeaderDone = 0;
589
590 context->LineWidth = 0;
591 context->LineBuf = NULL;
592 context->LineDone = 0;
593 context->Lines = 0;
594
595 context->Type = 0;
596
597 memset(s: &context->Header, c: 0, n: sizeof(struct headerpair));
598
599
600 context->pixbuf = NULL;
601
602
603 return (gpointer) context;
604}
605
606/*
607 * context - returned from image_begin_load
608 *
609 * free context, unref gdk_pixbuf
610 */
611static gboolean
612gdk_pixbuf__ico_image_stop_load(gpointer data,
613 GError **error)
614{
615 struct ico_progressive_state *context =
616 (struct ico_progressive_state *) data;
617 gboolean ret = TRUE;
618
619 /* FIXME this thing needs to report errors if
620 * we have unused image data
621 */
622
623 g_return_val_if_fail(context != NULL, TRUE);
624
625 if (context->HeaderDone < context->HeaderSize) {
626 g_set_error_literal (err: error,
627 GDK_PIXBUF_ERROR,
628 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
629 _("ICO image was truncated or incomplete."));
630 ret = FALSE;
631 }
632
633 context_free (context);
634 return ret;
635}
636
637static void
638OneLine32 (struct ico_progressive_state *context)
639{
640 gint X;
641 guchar *Pixels;
642 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
643
644 X = 0;
645 if (context->Header.Negative == 0)
646 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
647 rowstride * (context->Header.height - context->Lines - 1));
648 else
649 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
650 rowstride * context->Lines);
651 while (X < context->Header.width) {
652 /* BGRA */
653 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
654 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
655 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
656 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
657 X++;
658 }
659}
660
661static void OneLine24(struct ico_progressive_state *context)
662{
663 gint X;
664 guchar *Pixels;
665 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
666
667 X = 0;
668 if (context->Header.Negative == 0)
669 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
670 rowstride * (context->Header.height - context->Lines - 1));
671 else
672 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
673 rowstride * context->Lines);
674 while (X < context->Header.width) {
675 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
676 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
677 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
678 Pixels[X * 4 + 3] = 0xff;
679 X++;
680 }
681
682}
683
684static void
685OneLine16 (struct ico_progressive_state *context)
686{
687 int i;
688 guchar *pixels;
689 guchar *src;
690 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
691
692 if (context->Header.Negative == 0)
693 pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
694 rowstride * (context->Header.height - context->Lines - 1));
695 else
696 pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
697 rowstride * context->Lines);
698
699 src = context->LineBuf;
700
701 for (i = 0; i < context->Header.width; i++) {
702 int v, r, g, b;
703
704 v = (int) src[0] | ((int) src[1] << 8);
705 src += 2;
706
707 /* Extract 5-bit RGB values */
708
709 r = (v >> 10) & 0x1f;
710 g = (v >> 5) & 0x1f;
711 b = v & 0x1f;
712
713 /* Fill the rightmost bits to form 8-bit values */
714
715 *pixels++ = (r << 3) | (r >> 2);
716 *pixels++ = (g << 3) | (g >> 2);
717 *pixels++ = (b << 3) | (b >> 2);
718 *pixels++ = 0xff;
719 }
720}
721
722
723static void OneLine8(struct ico_progressive_state *context)
724{
725 gint X;
726 guchar *Pixels;
727 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
728
729 X = 0;
730 if (context->Header.Negative == 0)
731 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
732 rowstride * (context->Header.height - context->Lines - 1));
733 else
734 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
735 rowstride * context->Lines);
736 while (X < context->Header.width) {
737 /* The joys of having a BGR byteorder */
738 Pixels[X * 4 + 0] =
739 context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 2 + context->DIBoffset];
740 Pixels[X * 4 + 1] =
741 context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 1 +context->DIBoffset];
742 Pixels[X * 4 + 2] =
743 context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE +context->DIBoffset];
744 Pixels[X * 4 + 3] = 0xff;
745 X++;
746 }
747}
748static void OneLine4(struct ico_progressive_state *context)
749{
750 gint X;
751 guchar *Pixels;
752 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
753
754 X = 0;
755 if (context->Header.Negative == 0)
756 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
757 rowstride * (context->Header.height - context->Lines - 1));
758 else
759 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
760 rowstride * context->Lines);
761
762 while (X < context->Header.width) {
763 guchar Pix;
764
765 Pix = context->LineBuf[X/2];
766
767 Pixels[X * 4 + 0] =
768 context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 2 + context->DIBoffset];
769 Pixels[X * 4 + 1] =
770 context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 1 +context->DIBoffset];
771 Pixels[X * 4 + 2] =
772 context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + context->DIBoffset];
773 Pixels[X * 4 + 3] = 0xff;
774 X++;
775 if (X<context->Header.width) {
776 /* Handle the other 4 bit pixel only when there is one */
777 Pixels[X * 4 + 0] =
778 context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 2 + context->DIBoffset];
779 Pixels[X * 4 + 1] =
780 context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 1 + context->DIBoffset];
781 Pixels[X * 4 + 2] =
782 context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + context->DIBoffset];
783 Pixels[X * 4 + 3] = 0xff;
784 X++;
785 }
786 }
787
788}
789
790static void OneLine1(struct ico_progressive_state *context)
791{
792 gint X;
793 guchar *Pixels;
794 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
795
796 X = 0;
797 if (context->Header.Negative == 0)
798 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
799 rowstride * (context->Header.height - context->Lines - 1));
800 else
801 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
802 rowstride * context->Lines);
803 while (X < context->Header.width) {
804 int Bit;
805
806 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
807 Bit = Bit & 1;
808 /* The joys of having a BGR byteorder */
809 Pixels[X * 4 + 0] = Bit*255;
810 Pixels[X * 4 + 1] = Bit*255;
811 Pixels[X * 4 + 2] = Bit*255;
812 Pixels[X * 4 + 3] = 0xff;
813 X++;
814 }
815}
816
817static void OneLineTransp(struct ico_progressive_state *context)
818{
819 gint X;
820 guchar *Pixels;
821 gsize rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf);
822
823 /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
824 if (context->Header.depth == 32)
825 return;
826
827 X = 0;
828 if (context->Header.Negative == 0)
829 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
830 rowstride * (2*context->Header.height - context->Lines - 1));
831 else
832 Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) +
833 rowstride * (context->Lines-context->Header.height));
834 while (X < context->Header.width) {
835 int Bit;
836
837 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
838 Bit = Bit & 1;
839 /* The joys of having a BGR byteorder */
840 Pixels[X * 4 + 3] = 255-Bit*255;
841#if 0
842 if (Bit){
843 Pixels[X*4+0] = 255;
844 Pixels[X*4+1] = 255;
845 } else {
846 Pixels[X*4+0] = 0;
847 Pixels[X*4+1] = 0;
848 }
849#endif
850 X++;
851 }
852}
853
854
855static void OneLine(struct ico_progressive_state *context)
856{
857 context->LineDone = 0;
858
859 if (context->Lines >= context->Header.height*2) {
860 return;
861 }
862
863 if (context->Lines <context->Header.height) {
864 if (context->Type == 32)
865 OneLine32 (context);
866 else if (context->Type == 24)
867 OneLine24(context);
868 else if (context->Type == 16)
869 OneLine16 (context);
870 else if (context->Type == 8)
871 OneLine8(context);
872 else if (context->Type == 4)
873 OneLine4(context);
874 else if (context->Type == 1)
875 OneLine1(context);
876 else
877 g_assert_not_reached ();
878 } else
879 OneLineTransp(context);
880
881 context->Lines++;
882 if (context->Lines>=context->Header.height) {
883 context->Type = 1;
884 context->LineWidth = context->Header.width / 8;
885 if ((context->Header.width & 7) != 0)
886 context->LineWidth++;
887 /* Pad to a 32 bit boundary */
888 if (((context->LineWidth % 4) > 0))
889 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
890 }
891
892 {
893 int y;
894
895 y = context->Lines % context->Header.height;
896 if (context->Header.Negative == 0 &&
897 context->Lines < context->Header.height)
898 y = context->Header.height - y;
899 (*context->updated_func) (context->pixbuf,
900 0,
901 y,
902 context->Header.width,
903 1,
904 context->user_data);
905
906 }
907}
908
909/*
910 * context - from image_begin_load
911 * buf - new image data
912 * size - length of new image data
913 *
914 * append image data onto inrecrementally built output image
915 */
916static gboolean
917gdk_pixbuf__ico_image_load_increment(gpointer data,
918 const guchar * buf,
919 guint size,
920 GError **error)
921{
922 struct ico_progressive_state *context =
923 (struct ico_progressive_state *) data;
924
925 gint BytesToCopy;
926
927 while (size > 0) {
928 g_assert(context->LineDone >= 0);
929 if (context->HeaderDone < context->HeaderSize) { /* We still
930 have headerbytes to do */
931 BytesToCopy =
932 context->HeaderSize - context->HeaderDone;
933 if (BytesToCopy > size)
934 BytesToCopy = size;
935
936 memmove(dest: context->HeaderBuf + context->HeaderDone,
937 src: buf, n: BytesToCopy);
938
939 size -= BytesToCopy;
940 buf += BytesToCopy;
941 context->HeaderDone += BytesToCopy;
942 }
943 else {
944 BytesToCopy =
945 context->LineWidth - context->LineDone;
946 if (BytesToCopy > size)
947 BytesToCopy = size;
948
949 if (BytesToCopy > 0) {
950 /* Should be non-NULL once the header is decoded, as below. */
951 g_assert (context->LineBuf != NULL);
952
953 memmove(dest: context->LineBuf +
954 context->LineDone, src: buf,
955 n: BytesToCopy);
956
957 size -= BytesToCopy;
958 buf += BytesToCopy;
959 context->LineDone += BytesToCopy;
960 }
961 if ((context->LineDone >= context->LineWidth) && (context->LineWidth > 0)) {
962 /* By this point, DecodeHeader() will have been called, and should have returned successfully
963 * or set a #GError, as its only return-FALSE-without-setting-a-GError paths are when
964 * (context->HeaderDone < context->HeaderSize) or (context->LineWidth == 0).
965 * If it’s returned a #GError, we will have bailed already; otherwise, pixbuf will be set. */
966 g_assert (context->pixbuf != NULL);
967 OneLine(context);
968 }
969
970
971 }
972
973 if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
974 GError *decode_err = NULL;
975 DecodeHeader(Data: context->HeaderBuf,
976 Bytes: context->HeaderDone, State: context, error: &decode_err);
977 if (context->LineBuf != NULL && context->LineWidth == 0)
978 return TRUE;
979
980 if (decode_err) {
981 g_propagate_error (dest: error, src: decode_err);
982 return FALSE;
983 }
984 }
985 }
986
987 return TRUE;
988}
989
990/* saving ICOs */
991
992static gint
993write8 (FILE *f,
994 guint8 *data,
995 gint count)
996{
997 gint bytes;
998 gint written;
999
1000 written = 0;
1001 while (count > 0)
1002 {
1003 bytes = fwrite (ptr: (char*) data, size: sizeof (char), n: count, s: f);
1004 if (bytes <= 0)
1005 break;
1006 count -= bytes;
1007 data += bytes;
1008 written += bytes;
1009 }
1010
1011 return written;
1012}
1013
1014static gint
1015write16 (FILE *f,
1016 guint16 *data,
1017 gint count)
1018{
1019 gint i;
1020
1021 for (i = 0; i < count; i++)
1022 data[i] = GUINT16_TO_LE (data[i]);
1023
1024 return write8 (f, data: (guint8*) data, count: count * 2);
1025}
1026
1027static gint
1028write32 (FILE *f,
1029 guint32 *data,
1030 gint count)
1031{
1032 gint i;
1033
1034 for (i = 0; i < count; i++)
1035 data[i] = GUINT32_TO_LE (data[i]);
1036
1037 return write8 (f, data: (guint8*) data, count: count * 4);
1038}
1039
1040typedef struct _IconEntry IconEntry;
1041struct _IconEntry {
1042 gint width;
1043 gint height;
1044 gint depth;
1045 gint hot_x;
1046 gint hot_y;
1047
1048 guint8 n_colors;
1049 guint32 *colors;
1050 guint xor_rowstride;
1051 guint8 *xor;
1052 guint and_rowstride;
1053 guint8 *and;
1054};
1055
1056static gboolean
1057fill_entry (IconEntry *icon,
1058 GdkPixbuf *pixbuf,
1059 gint hot_x,
1060 gint hot_y,
1061 GError **error)
1062 {
1063 guchar *p, *pixels, *and, *xor;
1064 gint n_channels, v, x, y;
1065
1066 if (icon->width > 256 || icon->height > 256) {
1067 g_set_error_literal (err: error,
1068 GDK_PIXBUF_ERROR,
1069 code: GDK_PIXBUF_ERROR_BAD_OPTION,
1070 _("Image too large to be saved as ICO"));
1071 return FALSE;
1072 }
1073
1074 if (hot_x > -1 && hot_y > -1) {
1075 icon->hot_x = hot_x;
1076 icon->hot_y = hot_y;
1077 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
1078 g_set_error_literal (err: error,
1079 GDK_PIXBUF_ERROR,
1080 code: GDK_PIXBUF_ERROR_BAD_OPTION,
1081 _("Cursor hotspot outside image"));
1082 return FALSE;
1083 }
1084 }
1085 else {
1086 icon->hot_x = -1;
1087 icon->hot_y = -1;
1088 }
1089
1090 switch (icon->depth) {
1091 case 32:
1092 icon->xor_rowstride = icon->width * 4;
1093 break;
1094 case 24:
1095 icon->xor_rowstride = icon->width * 3;
1096 break;
1097 case 16:
1098 icon->xor_rowstride = icon->width * 2;
1099 break;
1100 default:
1101 g_set_error (err: error,
1102 GDK_PIXBUF_ERROR,
1103 code: GDK_PIXBUF_ERROR_BAD_OPTION,
1104 _("Unsupported depth for ICO file: %d"), icon->depth);
1105 return FALSE;
1106 }
1107
1108 if ((icon->xor_rowstride % 4) != 0)
1109 icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
1110 icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
1111
1112 icon->and_rowstride = (icon->width + 7) / 8;
1113 if ((icon->and_rowstride % 4) != 0)
1114 icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
1115 icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
1116
1117 pixels = gdk_pixbuf_get_pixels (pixbuf);
1118 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1119 for (y = 0; y < icon->height; y++) {
1120 p = pixels + (gsize) gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
1121 and = icon->and + icon->and_rowstride * y;
1122 xor = icon->xor + icon->xor_rowstride * y;
1123 for (x = 0; x < icon->width; x++) {
1124 switch (icon->depth) {
1125 case 32:
1126 xor[0] = p[2];
1127 xor[1] = p[1];
1128 xor[2] = p[0];
1129 xor[3] = 0xff;
1130 if (n_channels == 4) {
1131 xor[3] = p[3];
1132 if (p[3] < 0x80)
1133 *and |= 1 << (7 - x % 8);
1134 }
1135 xor += 4;
1136 break;
1137 case 24:
1138 xor[0] = p[2];
1139 xor[1] = p[1];
1140 xor[2] = p[0];
1141 if (n_channels == 4 && p[3] < 0x80)
1142 *and |= 1 << (7 - x % 8);
1143 xor += 3;
1144 break;
1145 case 16:
1146 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1147 xor[0] = v & 0xff;
1148 xor[1] = v >> 8;
1149 if (n_channels == 4 && p[3] < 0x80)
1150 *and |= 1 << (7 - x % 8);
1151 xor += 2;
1152 break;
1153 }
1154
1155 p += n_channels;
1156 if (x % 8 == 7)
1157 and++;
1158 }
1159 }
1160
1161 return TRUE;
1162}
1163
1164static void
1165free_entry (IconEntry *icon)
1166{
1167 g_free (mem: icon->colors);
1168 g_free (mem: icon->and);
1169 g_free (mem: icon->xor);
1170 g_free (mem: icon);
1171}
1172
1173static void
1174write_icon (FILE *f, GSList *entries)
1175{
1176 IconEntry *icon;
1177 GSList *entry;
1178 guint8 bytes[4];
1179 guint16 words[4];
1180 guint32 dwords[6];
1181 gint type;
1182 gint n_entries;
1183 gint offset;
1184 gint size;
1185
1186 if (((IconEntry *)entries->data)->hot_x > -1)
1187 type = 2;
1188 else
1189 type = 1;
1190 n_entries = g_slist_length (list: entries);
1191
1192 /* header */
1193 words[0] = 0;
1194 words[1] = type;
1195 words[2] = n_entries;
1196 write16 (f, data: words, count: 3);
1197
1198 offset = 6 + 16 * n_entries;
1199
1200 for (entry = entries; entry; entry = entry->next) {
1201 icon = (IconEntry *)entry->data;
1202 size = INFOHEADER_SIZE + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1203
1204 /* directory entry */
1205 if (icon->width == 256)
1206 bytes[0] = 0;
1207 else
1208 bytes[0] = icon->width;
1209 if (icon->height == 256)
1210 bytes[1] = 0;
1211 else
1212 bytes[1] = icon->height;
1213 bytes[2] = icon->n_colors;
1214 bytes[3] = 0;
1215 write8 (f, data: bytes, count: 4);
1216 if (type == 1) {
1217 words[0] = 1;
1218 words[1] = icon->depth;
1219 }
1220 else {
1221 words[0] = icon->hot_x;
1222 words[1] = icon->hot_y;
1223 }
1224 write16 (f, data: words, count: 2);
1225 dwords[0] = size;
1226 dwords[1] = offset;
1227 write32 (f, data: dwords, count: 2);
1228
1229 offset += size;
1230 }
1231
1232 for (entry = entries; entry; entry = entry->next) {
1233 icon = (IconEntry *)entry->data;
1234
1235 /* bitmap header */
1236 dwords[0] = INFOHEADER_SIZE;
1237 dwords[1] = icon->width;
1238 dwords[2] = icon->height * 2;
1239 write32 (f, data: dwords, count: 3);
1240 words[0] = 1;
1241 words[1] = icon->depth;
1242 write16 (f, data: words, count: 2);
1243 dwords[0] = 0;
1244 dwords[1] = 0;
1245 dwords[2] = 0;
1246 dwords[3] = 0;
1247 dwords[4] = 0;
1248 dwords[5] = 0;
1249 write32 (f, data: dwords, count: 6);
1250
1251 /* image data */
1252 write8 (f, data: icon->xor, count: icon->xor_rowstride * icon->height);
1253 write8 (f, data: icon->and, count: icon->and_rowstride * icon->height);
1254 }
1255}
1256
1257/* Locale-independent signed integer string parser, base 10.
1258 * @minimum and @maximum are valid inclusively. */
1259static gboolean
1260ascii_strtoll (const gchar *str,
1261 gint64 minimum,
1262 gint64 maximum,
1263 gint64 *out,
1264 GError **error)
1265{
1266 gint64 retval;
1267 const gchar *end_ptr;
1268
1269 errno = 0;
1270 retval = g_ascii_strtoll (nptr: str, endptr: (gchar **) &end_ptr, base: 10);
1271
1272 if (errno != 0) {
1273 g_set_error_literal (err: error,
1274 G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
1275 message: g_strerror (errno));
1276 return FALSE;
1277 } else if (end_ptr == str || *end_ptr != '\0') {
1278 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
1279 format: "Argument is not an integer: %s", str);
1280 return FALSE;
1281 } else if ((maximum < G_MAXINT64 && retval > maximum) ||
1282 (minimum > G_MININT64 && retval < minimum)) {
1283 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_INVALID_ARGUMENT,
1284 format: "Argument should be in range [%" G_GINT64_FORMAT
1285 ", %" G_GINT64_FORMAT "]: %s",
1286 minimum, maximum, str);
1287 return FALSE;
1288 }
1289
1290 g_assert (retval >= minimum && retval <= maximum);
1291
1292 if (out != NULL)
1293 *out = retval;
1294
1295 return TRUE;
1296}
1297
1298static gboolean
1299gdk_pixbuf__ico_image_save (FILE *f,
1300 GdkPixbuf *pixbuf,
1301 gchar **keys,
1302 gchar **values,
1303 GError **error)
1304{
1305 gint hot_x, hot_y;
1306 IconEntry *icon;
1307 GSList *entries = NULL;
1308
1309 /* support only single-image ICOs for now */
1310 icon = g_new0 (IconEntry, 1);
1311 icon->width = gdk_pixbuf_get_width (pixbuf);
1312 icon->height = gdk_pixbuf_get_height (pixbuf);
1313 icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1314 hot_x = -1;
1315 hot_y = -1;
1316
1317 /* parse options */
1318 if (keys && *keys) {
1319 gchar **kiter;
1320 gchar **viter;
1321
1322 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1323 gint64 out;
1324 if (strcmp (s1: *kiter, s2: "depth") == 0) {
1325 if (!ascii_strtoll (str: *viter, minimum: 1, maximum: 32,
1326 out: &out, error))
1327 return FALSE;
1328 icon->depth = out;
1329 }
1330 else if (strcmp (s1: *kiter, s2: "x_hot") == 0) {
1331 if (!ascii_strtoll (str: *viter, G_MININT, G_MAXINT,
1332 out: &out, error))
1333 return FALSE;
1334 hot_x = out;
1335 }
1336 else if (strcmp (s1: *kiter, s2: "y_hot") == 0) {
1337 if (!ascii_strtoll (str: *viter, G_MININT, G_MAXINT,
1338 out: &out, error))
1339 return FALSE;
1340 hot_y = out;
1341 }
1342
1343 }
1344 }
1345
1346 if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1347 free_entry (icon);
1348 return FALSE;
1349 }
1350
1351 entries = g_slist_append (list: entries, data: icon);
1352 write_icon (f, entries);
1353
1354 g_slist_foreach (list: entries, func: (GFunc)free_entry, NULL);
1355 g_slist_free (list: entries);
1356
1357 return TRUE;
1358}
1359
1360static gboolean
1361gdk_pixbuf__ico_is_save_option_supported (const gchar *option_key)
1362{
1363 if (g_strcmp0 (str1: option_key, str2: "depth") == 0 ||
1364 g_strcmp0 (str1: option_key, str2: "x_hot") == 0 ||
1365 g_strcmp0 (str1: option_key, str2: "y_hot") == 0)
1366 return TRUE;
1367
1368 return FALSE;
1369}
1370
1371#ifndef INCLUDE_ico
1372#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1373#else
1374#define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
1375#endif
1376
1377MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1378{
1379 module->begin_load = gdk_pixbuf__ico_image_begin_load;
1380 module->stop_load = gdk_pixbuf__ico_image_stop_load;
1381 module->load_increment = gdk_pixbuf__ico_image_load_increment;
1382 module->save = gdk_pixbuf__ico_image_save;
1383 module->is_save_option_supported = gdk_pixbuf__ico_is_save_option_supported;
1384}
1385
1386MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1387{
1388 static const GdkPixbufModulePattern signature[] = {
1389 { " \x1 ", "zz znz", 100 },
1390 { " \x2 ", "zz znz", 100 },
1391 { NULL, NULL, 0 }
1392 };
1393 static const gchar *mime_types[] = {
1394 "image/x-icon",
1395 "image/x-ico",
1396 "image/x-win-bitmap",
1397 "image/vnd.microsoft.icon",
1398 "application/ico",
1399 "image/ico",
1400 "image/icon",
1401 "text/ico",
1402 NULL
1403 };
1404 static const gchar *extensions[] = {
1405 "ico",
1406 "cur",
1407 NULL
1408 };
1409
1410 info->name = "ico";
1411 info->signature = (GdkPixbufModulePattern *) signature;
1412 info->description = NC_("image format", "Windows icon");
1413 info->mime_types = (gchar **) mime_types;
1414 info->extensions = (gchar **) extensions;
1415 info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1416 info->license = "LGPL";
1417}
1418
1419
1420
1421
1422

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/io-ico.c