1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - Windows Icon/Cursor image loader
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
7 * Federico Mena-Quintero <federico@gimp.org>
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.
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.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
30 Icons are just like BMP's, except for the header.
33 * bi-tonal files aren't tested
44 #include "gdk-pixbuf-private.h"
45 #include "gdk-pixbuf-io.h"
52 These structures are actually dummies. These are according to
53 the "Windows API reference guide volume II" as written by
54 Borland International, but GCC fiddles with the alignment of
59 struct BitmapFileHeader {
66 struct BitmapInfoHeader {
74 guint biXPelsPerMeter;
75 guint biYPelsPerMeter;
83 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
87 static void DumpBIH(unsigned char *BIH)
89 printf("biSize = %i \n",
90 (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
91 printf("biWidth = %i \n",
92 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
93 printf("biHeight = %i \n",
94 (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
96 printf("biPlanes = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
97 printf("biBitCount = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
98 printf("biCompress = %i \n",
99 (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
101 printf("biSizeImage = %i \n",
102 (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
104 printf("biXPels = %i \n",
105 (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
107 printf("biYPels = %i \n",
108 (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
110 printf("biClrUsed = %i \n",
111 (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
113 printf("biClrImprtnt= %i \n",
114 (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
119 /* Progressive loading */
124 guint Negative; /* Negative = 1 -> top down BMP,
125 Negative = 0 -> bottom up BMP */
128 struct ico_progressive_state {
129 GdkPixbufModuleSizeFunc size_func;
130 GdkPixbufModulePreparedFunc prepared_func;
131 GdkPixbufModuleUpdatedFunc updated_func;
134 gint HeaderSize; /* The size of the header-part (incl colormap) */
135 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
136 gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */
137 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
139 gint LineWidth; /* The width of a line in bytes */
140 guchar *LineBuf; /* Buffer for 1 line */
141 gint LineDone; /* # of bytes in LineBuf */
142 gint Lines; /* # of finished lines */
148 8 = 8 bit colormapped
149 4 = 4 bpp colormapped
156 struct headerpair Header; /* Decoded (BE->CPU) header */
162 GdkPixbuf *pixbuf; /* Our "target" */
166 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
167 GdkPixbufModulePreparedFunc prepared_func,
168 GdkPixbufModuleUpdatedFunc updated_func,
171 static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
172 static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
173 const guchar * buf, guint size,
177 context_free (struct ico_progressive_state *context)
179 if (context->LineBuf != NULL)
180 g_free (context->LineBuf);
181 context->LineBuf = NULL;
182 if (context->HeaderBuf != NULL)
183 g_free (context->HeaderBuf);
186 g_object_unref (context->pixbuf);
191 static void DecodeHeader(guchar *Data, gint Bytes,
192 struct ico_progressive_state *State,
195 /* For ICO's we have to be very clever. There are multiple images possible
196 in an .ICO. As a simple heuristic, we select the image which occupies the
197 largest number of bytes.
200 gint IconCount = 0; /* The number of icon-versions in the file */
201 guchar *BIH; /* The DIB for the used icon */
205 /* Step 1: The ICO header */
207 State->cursor = ((Data[3] << 8) + Data[2] == 2) ? TRUE : FALSE;
209 IconCount = (Data[5] << 8) + (Data[4]);
211 State->HeaderSize = 6 + IconCount*16;
213 if (State->HeaderSize>State->BytesInHeaderBuf) {
214 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
218 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
219 _("Not enough memory to load icon"));
222 State->HeaderBuf = tmp;
223 State->BytesInHeaderBuf = State->HeaderSize;
225 if (Bytes < State->HeaderSize)
228 /* We now have all the "short-specs" of the versions
229 So we iterate through them and select the best one */
231 State->ImageScore = 0;
232 State->DIBoffset = 0;
234 for (I=0;I<IconCount;I++) {
237 ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
239 if (ThisScore>=State->ImageScore) {
240 State->ImageScore = ThisScore;
241 State->x_hot = (Ptr[5] << 8) + Ptr[4];
242 State->y_hot = (Ptr[7] << 8) + Ptr[6];
243 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
244 (Ptr[13]<<8) + (Ptr[12]);
252 if (State->DIBoffset < 0) {
255 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
256 _("Invalid header in icon"));
260 /* We now have a winner, pointed to in State->DIBoffset,
261 so we know how many bytes are in the "header" part. */
263 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
265 if (State->HeaderSize < 0) {
268 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
269 _("Invalid header in icon"));
273 if (State->HeaderSize>State->BytesInHeaderBuf) {
274 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
278 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
279 _("Not enough memory to load icon"));
282 State->HeaderBuf = tmp;
283 State->BytesInHeaderBuf = State->HeaderSize;
285 if (Bytes<State->HeaderSize)
288 BIH = Data+State->DIBoffset;
293 /* Add the palette to the headersize */
295 State->Header.width =
296 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
297 if (State->Header.width == 0) {
300 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
301 _("Icon has zero width"));
304 State->Header.height =
305 (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
306 /* /2 because the BIH height includes the transparency mask */
307 if (State->Header.height == 0) {
310 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
311 _("Icon has zero height"));
314 State->Header.depth = (BIH[15] << 8) + (BIH[14]);
316 State->Type = State->Header.depth;
317 if (State->Lines>=State->Header.height)
318 State->Type = 1; /* The transparency mask is 1 bpp */
320 /* Determine the palette size. If the header indicates 0, it
321 is actually the maximum for the bpp. You have to love the
322 guys who made the spec. */
323 I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
325 if ((I==0)&&(State->Type==1))
327 if ((I==0)&&(State->Type==4))
329 if ((I==0)&&(State->Type==8))
332 State->HeaderSize+=I;
334 if (State->HeaderSize < 0) {
337 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
338 _("Invalid header in icon"));
342 if (State->HeaderSize>State->BytesInHeaderBuf) {
343 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
347 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
348 _("Not enough memory to load icon"));
351 State->HeaderBuf = tmp;
352 State->BytesInHeaderBuf = State->HeaderSize;
354 if (Bytes < State->HeaderSize)
357 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
359 /* FIXME: is this the correct message? */
362 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
363 _("Compressed icons are not supported"));
367 /* Negative heights mean top-down pixel-order */
368 if (State->Header.height < 0) {
369 State->Header.height = -State->Header.height;
370 State->Header.Negative = 1;
372 if (State->Header.width < 0) {
373 State->Header.width = -State->Header.width;
375 g_assert (State->Header.width > 0);
376 g_assert (State->Header.height > 0);
378 if (State->Type == 32)
379 State->LineWidth = State->Header.width * 4;
380 else if (State->Type == 24)
381 State->LineWidth = State->Header.width * 3;
382 else if (State->Type == 16)
383 State->LineWidth = State->Header.width * 2;
384 else if (State->Type == 8)
385 State->LineWidth = State->Header.width * 1;
386 else if (State->Type == 4)
387 State->LineWidth = (State->Header.width+1)/2;
388 else if (State->Type == 1) {
389 State->LineWidth = State->Header.width / 8;
390 if ((State->Header.width & 7) != 0)
395 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
396 _("Unsupported icon type"));
400 /* Pad to a 32 bit boundary */
401 if (((State->LineWidth % 4) > 0))
402 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
405 if (State->LineBuf == NULL) {
406 State->LineBuf = g_try_malloc(State->LineWidth);
407 if (!State->LineBuf) {
410 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
411 _("Not enough memory to load icon"));
416 g_assert(State->LineBuf != NULL);
419 if (State->pixbuf == NULL) {
421 if (State->size_func) {
422 gint width = State->Header.width;
423 gint height = State->Header.height;
425 (*State->size_func) (&width, &height, State->user_data);
426 if (width == 0 || height == 0) {
427 State->LineWidth = 0;
434 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
436 State->Header.height);
437 if (!State->pixbuf) {
440 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
441 _("Not enough memory to load icon"));
446 g_snprintf (hot, 10, "%d", State->x_hot);
447 gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
448 g_snprintf (hot, 10, "%d", State->y_hot);
449 gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
452 if (State->prepared_func != NULL)
453 /* Notify the client that we are ready to go */
454 (*State->prepared_func) (State->pixbuf,
463 * func - called when we have pixmap created (but no image data)
464 * user_data - passed as arg 1 to func
465 * return context (opaque to user)
469 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
470 GdkPixbufModulePreparedFunc prepared_func,
471 GdkPixbufModuleUpdatedFunc updated_func,
475 struct ico_progressive_state *context;
477 context = g_new0(struct ico_progressive_state, 1);
478 context->size_func = size_func;
479 context->prepared_func = prepared_func;
480 context->updated_func = updated_func;
481 context->user_data = user_data;
483 context->HeaderSize = 54;
484 context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
485 if (!context->HeaderBuf) {
489 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
490 _("Not enough memory to load ICO file"));
493 /* 4*256 for the colormap */
494 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
495 context->HeaderDone = 0;
497 context->LineWidth = 0;
498 context->LineBuf = NULL;
499 context->LineDone = 0;
504 memset(&context->Header, 0, sizeof(struct headerpair));
507 context->pixbuf = NULL;
510 return (gpointer) context;
514 * context - returned from image_begin_load
516 * free context, unref gdk_pixbuf
519 gdk_pixbuf__ico_image_stop_load(gpointer data,
522 struct ico_progressive_state *context =
523 (struct ico_progressive_state *) data;
525 /* FIXME this thing needs to report errors if
526 * we have unused image data
529 g_return_val_if_fail(context != NULL, TRUE);
531 context_free (context);
536 OneLine32 (struct ico_progressive_state *context)
542 if (context->Header.Negative == 0)
543 Pixels = (context->pixbuf->pixels +
544 context->pixbuf->rowstride *
545 (context->Header.height - context->Lines - 1));
547 Pixels = (context->pixbuf->pixels +
548 context->pixbuf->rowstride *
550 while (X < context->Header.width) {
551 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
552 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
553 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
554 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
559 static void OneLine24(struct ico_progressive_state *context)
565 if (context->Header.Negative == 0)
566 Pixels = (context->pixbuf->pixels +
567 context->pixbuf->rowstride *
568 (context->Header.height - context->Lines - 1));
570 Pixels = (context->pixbuf->pixels +
571 context->pixbuf->rowstride *
573 while (X < context->Header.width) {
574 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
575 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
576 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
583 OneLine16 (struct ico_progressive_state *context)
589 if (context->Header.Negative == 0)
590 pixels = (context->pixbuf->pixels +
591 context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
593 pixels = (context->pixbuf->pixels +
594 context->pixbuf->rowstride * context->Lines);
596 src = context->LineBuf;
598 for (i = 0; i < context->Header.width; i++) {
601 v = (int) src[0] | ((int) src[1] << 8);
604 /* Extract 5-bit RGB values */
606 r = (v >> 10) & 0x1f;
610 /* Fill the rightmost bits to form 8-bit values */
612 *pixels++ = (r << 3) | (r >> 2);
613 *pixels++ = (g << 3) | (g >> 2);
614 *pixels++ = (b << 3) | (b >> 2);
615 pixels++; /* skip alpha channel */
620 static void OneLine8(struct ico_progressive_state *context)
626 if (context->Header.Negative == 0)
627 Pixels = (context->pixbuf->pixels +
628 context->pixbuf->rowstride *
629 (context->Header.height - context->Lines - 1));
631 Pixels = (context->pixbuf->pixels +
632 context->pixbuf->rowstride *
634 while (X < context->Header.width) {
635 /* The joys of having a BGR byteorder */
637 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
639 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
641 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
645 static void OneLine4(struct ico_progressive_state *context)
651 if (context->Header.Negative == 0)
652 Pixels = (context->pixbuf->pixels +
653 context->pixbuf->rowstride *
654 (context->Header.height - context->Lines - 1));
656 Pixels = (context->pixbuf->pixels +
657 context->pixbuf->rowstride *
660 while (X < context->Header.width) {
663 Pix = context->LineBuf[X/2];
666 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
668 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
670 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
672 if (X<context->Header.width) {
673 /* Handle the other 4 bit pixel only when there is one */
675 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
677 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
679 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
686 static void OneLine1(struct ico_progressive_state *context)
692 if (context->Header.Negative == 0)
693 Pixels = (context->pixbuf->pixels +
694 context->pixbuf->rowstride *
695 (context->Header.height - context->Lines - 1));
697 Pixels = (context->pixbuf->pixels +
698 context->pixbuf->rowstride *
700 while (X < context->Header.width) {
703 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
705 /* The joys of having a BGR byteorder */
706 Pixels[X * 4 + 0] = Bit*255;
707 Pixels[X * 4 + 1] = Bit*255;
708 Pixels[X * 4 + 2] = Bit*255;
713 static void OneLineTransp(struct ico_progressive_state *context)
718 /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
719 if (context->Header.depth == 32)
723 if (context->Header.Negative == 0)
724 Pixels = (context->pixbuf->pixels +
725 context->pixbuf->rowstride *
726 (2*context->Header.height - context->Lines - 1));
728 Pixels = (context->pixbuf->pixels +
729 context->pixbuf->rowstride *
730 (context->Lines-context->Header.height));
731 while (X < context->Header.width) {
734 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
736 /* The joys of having a BGR byteorder */
737 Pixels[X * 4 + 3] = 255-Bit*255;
752 static void OneLine(struct ico_progressive_state *context)
754 context->LineDone = 0;
756 if (context->Lines >= context->Header.height*2) {
760 if (context->Lines <context->Header.height) {
761 if (context->Type == 32)
763 else if (context->Type == 24)
765 else if (context->Type == 16)
767 else if (context->Type == 8)
769 else if (context->Type == 4)
771 else if (context->Type == 1)
774 g_assert_not_reached ();
776 OneLineTransp(context);
779 if (context->Lines>=context->Header.height) {
781 context->LineWidth = context->Header.width / 8;
782 if ((context->Header.width & 7) != 0)
783 context->LineWidth++;
784 /* Pad to a 32 bit boundary */
785 if (((context->LineWidth % 4) > 0))
786 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
791 if (context->updated_func != NULL) {
792 (*context->updated_func) (context->pixbuf,
794 context->Lines % context->Header.height,
795 context->Header.width,
803 * context - from image_begin_load
804 * buf - new image data
805 * size - length of new image data
807 * append image data onto inrecrementally built output image
810 gdk_pixbuf__ico_image_load_increment(gpointer data,
815 struct ico_progressive_state *context =
816 (struct ico_progressive_state *) data;
821 g_assert(context->LineDone >= 0);
822 if (context->HeaderDone < context->HeaderSize) { /* We still
823 have headerbytes to do */
825 context->HeaderSize - context->HeaderDone;
826 if (BytesToCopy > size)
829 memmove(context->HeaderBuf + context->HeaderDone,
834 context->HeaderDone += BytesToCopy;
838 context->LineWidth - context->LineDone;
839 if (BytesToCopy > size)
842 if (BytesToCopy > 0) {
843 memmove(context->LineBuf +
844 context->LineDone, buf,
849 context->LineDone += BytesToCopy;
851 if ((context->LineDone >= context->LineWidth) &&
852 (context->LineWidth > 0))
858 if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
859 GError *decode_err = NULL;
860 DecodeHeader(context->HeaderBuf,
861 context->HeaderDone, context, &decode_err);
862 if (context->LineBuf != NULL && context->LineWidth == 0)
866 g_propagate_error (error, decode_err);
888 bytes = fwrite ((char*) data, sizeof (char), count, f);
906 for (i = 0; i < count; i++)
907 data[i] = GUINT16_TO_LE (data[i]);
909 return write8 (f, (guint8*) data, count * 2);
919 for (i = 0; i < count; i++)
920 data[i] = GUINT32_TO_LE (data[i]);
922 return write8 (f, (guint8*) data, count * 4);
925 typedef struct _IconEntry IconEntry;
942 fill_entry (IconEntry *icon,
948 guchar *p, *pixels, *and, *xor;
949 gint n_channels, v, x, y;
951 if (icon->width > 255 || icon->height > 255) {
954 GDK_PIXBUF_ERROR_BAD_OPTION,
955 _("Image too large to be saved as ICO"));
959 if (hot_x > -1 && hot_y > -1) {
962 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
965 GDK_PIXBUF_ERROR_BAD_OPTION,
966 _("Cursor hotspot outside image"));
975 switch (icon->depth) {
977 icon->xor_rowstride = icon->width * 4;
980 icon->xor_rowstride = icon->width * 3;
983 icon->xor_rowstride = icon->width * 2;
988 GDK_PIXBUF_ERROR_BAD_OPTION,
989 _("Unsupported depth for ICO file: %d"), icon->depth);
993 if ((icon->xor_rowstride % 4) != 0)
994 icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
995 icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
997 icon->and_rowstride = icon->width / 8;
998 if ((icon->and_rowstride % 4) != 0)
999 icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
1000 icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
1002 pixels = gdk_pixbuf_get_pixels (pixbuf);
1003 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1004 for (y = 0; y < icon->height; y++) {
1005 p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
1006 and = icon->and + icon->and_rowstride * y;
1007 xor = icon->xor + icon->xor_rowstride * y;
1008 for (x = 0; x < icon->width; x++) {
1009 switch (icon->depth) {
1015 if (n_channels == 4) {
1018 *and |= 1 << (7 - x % 8);
1026 if (n_channels == 4 && p[3] < 0x80)
1027 *and |= 1 << (7 - x % 8);
1031 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1034 if (n_channels == 4 && p[3] < 0x80)
1035 *and |= 1 << (7 - x % 8);
1050 free_entry (IconEntry *icon)
1052 g_free (icon->colors);
1059 write_icon (FILE *f, GSList *entries)
1071 if (((IconEntry *)entries->data)->hot_x > -1)
1075 n_entries = g_slist_length (entries);
1080 words[2] = n_entries;
1081 write16 (f, words, 3);
1083 offset = 6 + 16 * n_entries;
1085 for (entry = entries; entry; entry = entry->next) {
1086 icon = (IconEntry *)entry->data;
1087 size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1089 /* directory entry */
1090 bytes[0] = icon->width;
1091 bytes[1] = icon->height;
1092 bytes[2] = icon->n_colors;
1094 write8 (f, bytes, 4);
1097 words[1] = icon->depth;
1100 words[0] = icon->hot_x;
1101 words[1] = icon->hot_y;
1103 write16 (f, words, 2);
1106 write32 (f, dwords, 2);
1111 for (entry = entries; entry; entry = entry->next) {
1112 icon = (IconEntry *)entry->data;
1116 dwords[1] = icon->width;
1117 dwords[2] = icon->height * 2;
1118 write32 (f, dwords, 3);
1120 words[1] = icon->depth;
1121 write16 (f, words, 2);
1128 write32 (f, dwords, 6);
1131 write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1132 write8 (f, icon->and, icon->and_rowstride * icon->height);
1137 gdk_pixbuf__ico_image_save (FILE *f,
1145 GSList *entries = NULL;
1147 /* support only single-image ICOs for now */
1148 icon = g_new0 (IconEntry, 1);
1149 icon->width = gdk_pixbuf_get_width (pixbuf);
1150 icon->height = gdk_pixbuf_get_height (pixbuf);
1151 icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1156 if (keys && *keys) {
1160 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1162 if (strcmp (*kiter, "depth") == 0) {
1163 sscanf (*viter, "%d", &icon->depth);
1165 else if (strcmp (*kiter, "x_hot") == 0) {
1166 hot_x = strtol (*viter, &endptr, 10);
1168 else if (strcmp (*kiter, "y_hot") == 0) {
1169 hot_y = strtol (*viter, &endptr, 10);
1175 if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1180 entries = g_slist_append (entries, icon);
1181 write_icon (f, entries);
1183 g_slist_foreach (entries, (GFunc)free_entry, NULL);
1184 g_slist_free (entries);
1190 #define MODULE_ENTRY(type,function) function
1192 #define MODULE_ENTRY(type,function) _gdk_pixbuf__ ## type ## _ ## function
1196 MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
1198 module->begin_load = gdk_pixbuf__ico_image_begin_load;
1199 module->stop_load = gdk_pixbuf__ico_image_stop_load;
1200 module->load_increment = gdk_pixbuf__ico_image_load_increment;
1201 module->save = gdk_pixbuf__ico_image_save;
1205 MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
1207 static GdkPixbufModulePattern signature[] = {
1208 { " \x1 ", "zz znz", 100 },
1209 { " \x2 ", "zz znz", 100 },
1212 static gchar * mime_types[] = {
1216 static gchar * extensions[] = {
1223 info->signature = signature;
1224 info->description = N_("The ICO image format");
1225 info->mime_types = mime_types;
1226 info->extensions = extensions;
1227 info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1228 info->license = "LGPL";