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>State->BytesInHeaderBuf) {
266 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
270 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
271 _("Not enough memory to load icon"));
274 State->HeaderBuf = tmp;
275 State->BytesInHeaderBuf = State->HeaderSize;
277 if (Bytes<State->HeaderSize)
280 BIH = Data+State->DIBoffset;
285 /* Add the palette to the headersize */
287 State->Header.width =
288 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
289 if (State->Header.width == 0) {
292 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
293 _("Icon has zero width"));
296 State->Header.height =
297 (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
298 /* /2 because the BIH height includes the transparency mask */
299 if (State->Header.height == 0) {
302 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
303 _("Icon has zero height"));
306 State->Header.depth = (BIH[15] << 8) + (BIH[14]);
308 State->Type = State->Header.depth;
309 if (State->Lines>=State->Header.height)
310 State->Type = 1; /* The transparency mask is 1 bpp */
312 /* Determine the palette size. If the header indicates 0, it
313 is actually the maximum for the bpp. You have to love the
314 guys who made the spec. */
315 I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
317 if ((I==0)&&(State->Type==1))
319 if ((I==0)&&(State->Type==4))
321 if ((I==0)&&(State->Type==8))
324 State->HeaderSize+=I;
326 if (State->HeaderSize < 0) {
329 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
330 _("Invalid header in icon"));
334 if (State->HeaderSize>State->BytesInHeaderBuf) {
335 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
339 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
340 _("Not enough memory to load icon"));
343 State->HeaderBuf = tmp;
344 State->BytesInHeaderBuf = State->HeaderSize;
346 if (Bytes < State->HeaderSize)
349 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
351 /* FIXME: is this the correct message? */
354 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
355 _("Compressed icons are not supported"));
359 /* Negative heights mean top-down pixel-order */
360 if (State->Header.height < 0) {
361 State->Header.height = -State->Header.height;
362 State->Header.Negative = 1;
364 if (State->Header.width < 0) {
365 State->Header.width = -State->Header.width;
367 g_assert (State->Header.width > 0);
368 g_assert (State->Header.height > 0);
370 if (State->Type == 32)
371 State->LineWidth = State->Header.width * 4;
372 else if (State->Type == 24)
373 State->LineWidth = State->Header.width * 3;
374 else if (State->Type == 16)
375 State->LineWidth = State->Header.width * 2;
376 else if (State->Type == 8)
377 State->LineWidth = State->Header.width * 1;
378 else if (State->Type == 4)
379 State->LineWidth = (State->Header.width+1)/2;
380 else if (State->Type == 1) {
381 State->LineWidth = State->Header.width / 8;
382 if ((State->Header.width & 7) != 0)
387 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
388 _("Unsupported icon type"));
392 /* Pad to a 32 bit boundary */
393 if (((State->LineWidth % 4) > 0))
394 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
397 if (State->LineBuf == NULL) {
398 State->LineBuf = g_try_malloc(State->LineWidth);
399 if (!State->LineBuf) {
402 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
403 _("Not enough memory to load icon"));
408 g_assert(State->LineBuf != NULL);
411 if (State->pixbuf == NULL) {
413 if (State->size_func) {
414 gint width = State->Header.width;
415 gint height = State->Header.height;
417 (*State->size_func) (&width, &height, State->user_data);
418 if (width == 0 || height == 0) {
419 State->LineWidth = 0;
426 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
428 State->Header.height);
429 if (!State->pixbuf) {
432 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
433 _("Not enough memory to load icon"));
438 g_snprintf (hot, 10, "%d", State->x_hot);
439 gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
440 g_snprintf (hot, 10, "%d", State->y_hot);
441 gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
444 if (State->prepared_func != NULL)
445 /* Notify the client that we are ready to go */
446 (*State->prepared_func) (State->pixbuf,
455 * func - called when we have pixmap created (but no image data)
456 * user_data - passed as arg 1 to func
457 * return context (opaque to user)
461 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
462 GdkPixbufModulePreparedFunc prepared_func,
463 GdkPixbufModuleUpdatedFunc updated_func,
467 struct ico_progressive_state *context;
469 context = g_new0(struct ico_progressive_state, 1);
470 context->size_func = size_func;
471 context->prepared_func = prepared_func;
472 context->updated_func = updated_func;
473 context->user_data = user_data;
475 context->HeaderSize = 54;
476 context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
477 if (!context->HeaderBuf) {
481 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
482 _("Not enough memory to load ICO file"));
485 /* 4*256 for the colormap */
486 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
487 context->HeaderDone = 0;
489 context->LineWidth = 0;
490 context->LineBuf = NULL;
491 context->LineDone = 0;
496 memset(&context->Header, 0, sizeof(struct headerpair));
499 context->pixbuf = NULL;
502 return (gpointer) context;
506 * context - returned from image_begin_load
508 * free context, unref gdk_pixbuf
511 gdk_pixbuf__ico_image_stop_load(gpointer data,
514 struct ico_progressive_state *context =
515 (struct ico_progressive_state *) data;
517 /* FIXME this thing needs to report errors if
518 * we have unused image data
521 g_return_val_if_fail(context != NULL, TRUE);
523 context_free (context);
528 OneLine32 (struct ico_progressive_state *context)
534 if (context->Header.Negative == 0)
535 Pixels = (context->pixbuf->pixels +
536 context->pixbuf->rowstride *
537 (context->Header.height - context->Lines - 1));
539 Pixels = (context->pixbuf->pixels +
540 context->pixbuf->rowstride *
542 while (X < context->Header.width) {
543 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
544 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
545 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
546 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
551 static void OneLine24(struct ico_progressive_state *context)
557 if (context->Header.Negative == 0)
558 Pixels = (context->pixbuf->pixels +
559 context->pixbuf->rowstride *
560 (context->Header.height - context->Lines - 1));
562 Pixels = (context->pixbuf->pixels +
563 context->pixbuf->rowstride *
565 while (X < context->Header.width) {
566 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
567 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
568 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
575 OneLine16 (struct ico_progressive_state *context)
581 if (context->Header.Negative == 0)
582 pixels = (context->pixbuf->pixels +
583 context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
585 pixels = (context->pixbuf->pixels +
586 context->pixbuf->rowstride * context->Lines);
588 src = context->LineBuf;
590 for (i = 0; i < context->Header.width; i++) {
593 v = (int) src[0] | ((int) src[1] << 8);
596 /* Extract 5-bit RGB values */
598 r = (v >> 10) & 0x1f;
602 /* Fill the rightmost bits to form 8-bit values */
604 *pixels++ = (r << 3) | (r >> 2);
605 *pixels++ = (g << 3) | (g >> 2);
606 *pixels++ = (b << 3) | (b >> 2);
607 pixels++; /* skip alpha channel */
612 static void OneLine8(struct ico_progressive_state *context)
618 if (context->Header.Negative == 0)
619 Pixels = (context->pixbuf->pixels +
620 context->pixbuf->rowstride *
621 (context->Header.height - context->Lines - 1));
623 Pixels = (context->pixbuf->pixels +
624 context->pixbuf->rowstride *
626 while (X < context->Header.width) {
627 /* The joys of having a BGR byteorder */
629 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
631 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
633 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
637 static void OneLine4(struct ico_progressive_state *context)
643 if (context->Header.Negative == 0)
644 Pixels = (context->pixbuf->pixels +
645 context->pixbuf->rowstride *
646 (context->Header.height - context->Lines - 1));
648 Pixels = (context->pixbuf->pixels +
649 context->pixbuf->rowstride *
652 while (X < context->Header.width) {
655 Pix = context->LineBuf[X/2];
658 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
660 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
662 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
664 if (X<context->Header.width) {
665 /* Handle the other 4 bit pixel only when there is one */
667 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
669 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
671 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
678 static void OneLine1(struct ico_progressive_state *context)
684 if (context->Header.Negative == 0)
685 Pixels = (context->pixbuf->pixels +
686 context->pixbuf->rowstride *
687 (context->Header.height - context->Lines - 1));
689 Pixels = (context->pixbuf->pixels +
690 context->pixbuf->rowstride *
692 while (X < context->Header.width) {
695 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
697 /* The joys of having a BGR byteorder */
698 Pixels[X * 4 + 0] = Bit*255;
699 Pixels[X * 4 + 1] = Bit*255;
700 Pixels[X * 4 + 2] = Bit*255;
705 static void OneLineTransp(struct ico_progressive_state *context)
710 /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
711 if (context->Header.depth == 32)
715 if (context->Header.Negative == 0)
716 Pixels = (context->pixbuf->pixels +
717 context->pixbuf->rowstride *
718 (2*context->Header.height - context->Lines - 1));
720 Pixels = (context->pixbuf->pixels +
721 context->pixbuf->rowstride *
722 (context->Lines-context->Header.height));
723 while (X < context->Header.width) {
726 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
728 /* The joys of having a BGR byteorder */
729 Pixels[X * 4 + 3] = 255-Bit*255;
744 static void OneLine(struct ico_progressive_state *context)
746 context->LineDone = 0;
748 if (context->Lines >= context->Header.height*2) {
752 if (context->Lines <context->Header.height) {
753 if (context->Type == 32)
755 else if (context->Type == 24)
757 else if (context->Type == 16)
759 else if (context->Type == 8)
761 else if (context->Type == 4)
763 else if (context->Type == 1)
766 g_assert_not_reached ();
768 OneLineTransp(context);
771 if (context->Lines>=context->Header.height) {
773 context->LineWidth = context->Header.width / 8;
774 if ((context->Header.width & 7) != 0)
775 context->LineWidth++;
776 /* Pad to a 32 bit boundary */
777 if (((context->LineWidth % 4) > 0))
778 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
783 if (context->updated_func != NULL) {
784 (*context->updated_func) (context->pixbuf,
786 context->Lines % context->Header.height,
787 context->Header.width,
795 * context - from image_begin_load
796 * buf - new image data
797 * size - length of new image data
799 * append image data onto inrecrementally built output image
802 gdk_pixbuf__ico_image_load_increment(gpointer data,
807 struct ico_progressive_state *context =
808 (struct ico_progressive_state *) data;
813 g_assert(context->LineDone >= 0);
814 if (context->HeaderDone < context->HeaderSize) { /* We still
815 have headerbytes to do */
817 context->HeaderSize - context->HeaderDone;
818 if (BytesToCopy > size)
821 memmove(context->HeaderBuf + context->HeaderDone,
826 context->HeaderDone += BytesToCopy;
830 context->LineWidth - context->LineDone;
831 if (BytesToCopy > size)
834 if (BytesToCopy > 0) {
835 memmove(context->LineBuf +
836 context->LineDone, buf,
841 context->LineDone += BytesToCopy;
843 if ((context->LineDone >= context->LineWidth) &&
844 (context->LineWidth > 0))
850 if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
851 GError *decode_err = NULL;
852 DecodeHeader(context->HeaderBuf,
853 context->HeaderDone, context, &decode_err);
854 if (context->LineBuf != NULL && context->LineWidth == 0)
858 g_propagate_error (error, decode_err);
880 bytes = fwrite ((char*) data, sizeof (char), count, f);
898 for (i = 0; i < count; i++)
899 data[i] = GUINT16_TO_LE (data[i]);
901 return write8 (f, (guint8*) data, count * 2);
911 for (i = 0; i < count; i++)
912 data[i] = GUINT32_TO_LE (data[i]);
914 return write8 (f, (guint8*) data, count * 4);
917 typedef struct _IconEntry IconEntry;
934 fill_entry (IconEntry *icon,
940 guchar *p, *pixels, *and, *xor;
941 gint n_channels, v, x, y;
943 if (icon->width > 255 || icon->height > 255) {
946 GDK_PIXBUF_ERROR_BAD_OPTION,
947 _("Image too large to be saved as ICO"));
951 if (hot_x > -1 && hot_y > -1) {
954 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
957 GDK_PIXBUF_ERROR_BAD_OPTION,
958 _("Cursor hotspot outside image"));
967 switch (icon->depth) {
969 icon->xor_rowstride = icon->width * 4;
972 icon->xor_rowstride = icon->width * 3;
975 icon->xor_rowstride = icon->width * 2;
980 GDK_PIXBUF_ERROR_BAD_OPTION,
981 _("Unsupported depth for ICO file: %d"), icon->depth);
985 if ((icon->xor_rowstride % 4) != 0)
986 icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
987 icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
989 icon->and_rowstride = icon->width / 8;
990 if ((icon->and_rowstride % 4) != 0)
991 icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
992 icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
994 pixels = gdk_pixbuf_get_pixels (pixbuf);
995 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
996 for (y = 0; y < icon->height; y++) {
997 p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
998 and = icon->and + icon->and_rowstride * y;
999 xor = icon->xor + icon->xor_rowstride * y;
1000 for (x = 0; x < icon->width; x++) {
1001 switch (icon->depth) {
1007 if (n_channels == 4) {
1010 *and |= 1 << (7 - x % 8);
1018 if (n_channels == 4 && p[3] < 0x80)
1019 *and |= 1 << (7 - x % 8);
1023 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1026 if (n_channels == 4 && p[3] < 0x80)
1027 *and |= 1 << (7 - x % 8);
1042 free_entry (IconEntry *icon)
1044 g_free (icon->colors);
1051 write_icon (FILE *f, GSList *entries)
1063 if (((IconEntry *)entries->data)->hot_x > -1)
1067 n_entries = g_slist_length (entries);
1072 words[2] = n_entries;
1073 write16 (f, words, 3);
1075 offset = 6 + 16 * n_entries;
1077 for (entry = entries; entry; entry = entry->next) {
1078 icon = (IconEntry *)entry->data;
1079 size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1081 /* directory entry */
1082 bytes[0] = icon->width;
1083 bytes[1] = icon->height;
1084 bytes[2] = icon->n_colors;
1086 write8 (f, bytes, 4);
1089 words[1] = icon->depth;
1092 words[0] = icon->hot_x;
1093 words[1] = icon->hot_y;
1095 write16 (f, words, 2);
1098 write32 (f, dwords, 2);
1103 for (entry = entries; entry; entry = entry->next) {
1104 icon = (IconEntry *)entry->data;
1108 dwords[1] = icon->width;
1109 dwords[2] = icon->height * 2;
1110 write32 (f, dwords, 3);
1112 words[1] = icon->depth;
1113 write16 (f, words, 2);
1120 write32 (f, dwords, 6);
1123 write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1124 write8 (f, icon->and, icon->and_rowstride * icon->height);
1129 gdk_pixbuf__ico_image_save (FILE *f,
1137 GSList *entries = NULL;
1139 /* support only single-image ICOs for now */
1140 icon = g_new0 (IconEntry, 1);
1141 icon->width = gdk_pixbuf_get_width (pixbuf);
1142 icon->height = gdk_pixbuf_get_height (pixbuf);
1143 icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1148 if (keys && *keys) {
1152 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1154 if (strcmp (*kiter, "depth") == 0) {
1155 sscanf (*viter, "%d", &icon->depth);
1157 else if (strcmp (*kiter, "x_hot") == 0) {
1158 hot_x = strtol (*viter, &endptr, 10);
1160 else if (strcmp (*kiter, "y_hot") == 0) {
1161 hot_y = strtol (*viter, &endptr, 10);
1167 if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1172 entries = g_slist_append (entries, icon);
1173 write_icon (f, entries);
1175 g_slist_foreach (entries, (GFunc)free_entry, NULL);
1176 g_slist_free (entries);
1182 MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
1184 module->begin_load = gdk_pixbuf__ico_image_begin_load;
1185 module->stop_load = gdk_pixbuf__ico_image_stop_load;
1186 module->load_increment = gdk_pixbuf__ico_image_load_increment;
1187 module->save = gdk_pixbuf__ico_image_save;
1191 MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
1193 static GdkPixbufModulePattern signature[] = {
1194 { " \x1 ", "zz znz", 100 },
1195 { " \x2 ", "zz znz", 100 },
1198 static gchar * mime_types[] = {
1202 static gchar * extensions[] = {
1209 info->signature = signature;
1210 info->description = N_("The ICO image format");
1211 info->mime_types = mime_types;
1212 info->extensions = extensions;
1213 info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1214 info->license = "LGPL";