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 g_free (context
->LineBuf
);
180 context
->LineBuf
= NULL
;
181 g_free (context
->HeaderBuf
);
184 g_object_unref (context
->pixbuf
);
189 static void DecodeHeader(guchar
*Data
, gint Bytes
,
190 struct ico_progressive_state
*State
,
193 /* For ICO's we have to be very clever. There are multiple images possible
194 in an .ICO. As a simple heuristic, we select the image which occupies the
195 largest number of bytes.
198 gint IconCount
= 0; /* The number of icon-versions in the file */
199 guchar
*BIH
; /* The DIB for the used icon */
202 guint16 imgtype
; /* 1 = icon, 2 = cursor */
204 /* Step 1: The ICO header */
206 /* First word should be 0 according to specs */
207 if (((Data
[1] << 8) + Data
[0]) != 0) {
208 g_set_error_literal (error
,
210 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
211 _("Invalid header in icon"));
216 imgtype
= (Data
[3] << 8) + Data
[2];
218 State
->cursor
= (imgtype
== 2) ? TRUE
: FALSE
;
220 /* If it is not a cursor make sure it is actually an icon */
221 if (!State
->cursor
&& imgtype
!= 1) {
222 g_set_error_literal (error
,
224 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
225 _("Invalid header in icon"));
230 IconCount
= (Data
[5] << 8) + (Data
[4]);
232 State
->HeaderSize
= 6 + IconCount
*16;
234 if (State
->HeaderSize
>State
->BytesInHeaderBuf
) {
235 guchar
*tmp
=g_try_realloc(State
->HeaderBuf
,State
->HeaderSize
);
237 g_set_error_literal (error
,
239 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
240 _("Not enough memory to load icon"));
243 State
->HeaderBuf
= tmp
;
244 State
->BytesInHeaderBuf
= State
->HeaderSize
;
246 if (Bytes
< State
->HeaderSize
)
249 /* We now have all the "short-specs" of the versions
250 So we iterate through them and select the best one */
252 State
->ImageScore
= 0;
253 State
->DIBoffset
= 0;
255 for (I
=0;I
<IconCount
;I
++) {
258 ThisScore
= (Ptr
[11] << 24) + (Ptr
[10] << 16) + (Ptr
[9] << 8) + (Ptr
[8]);
260 if (ThisScore
>=State
->ImageScore
) {
261 State
->ImageScore
= ThisScore
;
262 State
->x_hot
= (Ptr
[5] << 8) + Ptr
[4];
263 State
->y_hot
= (Ptr
[7] << 8) + Ptr
[6];
264 State
->DIBoffset
= (Ptr
[15]<<24)+(Ptr
[14]<<16)+
265 (Ptr
[13]<<8) + (Ptr
[12]);
273 if (State
->DIBoffset
< 0) {
274 g_set_error_literal (error
,
276 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
277 _("Invalid header in icon"));
281 /* We now have a winner, pointed to in State->DIBoffset,
282 so we know how many bytes are in the "header" part. */
284 State
->HeaderSize
= State
->DIBoffset
+ 40; /* 40 = sizeof(InfoHeader) */
286 if (State
->HeaderSize
< 0) {
287 g_set_error_literal (error
,
289 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
290 _("Invalid header in icon"));
294 if (State
->HeaderSize
>State
->BytesInHeaderBuf
) {
295 guchar
*tmp
=g_try_realloc(State
->HeaderBuf
,State
->HeaderSize
);
297 g_set_error_literal (error
,
299 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
300 _("Not enough memory to load icon"));
303 State
->HeaderBuf
= tmp
;
304 State
->BytesInHeaderBuf
= State
->HeaderSize
;
306 if (Bytes
<State
->HeaderSize
)
309 BIH
= Data
+State
->DIBoffset
;
314 /* Add the palette to the headersize */
316 State
->Header
.width
=
317 (int)(BIH
[7] << 24) + (BIH
[6] << 16) + (BIH
[5] << 8) + (BIH
[4]);
318 if (State
->Header
.width
== 0) {
319 g_set_error_literal (error
,
321 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
322 _("Icon has zero width"));
325 State
->Header
.height
=
326 (int)((BIH
[11] << 24) + (BIH
[10] << 16) + (BIH
[9] << 8) + (BIH
[8]))/2;
327 /* /2 because the BIH height includes the transparency mask */
328 if (State
->Header
.height
== 0) {
329 g_set_error_literal (error
,
331 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
332 _("Icon has zero height"));
335 State
->Header
.depth
= (BIH
[15] << 8) + (BIH
[14]);
337 State
->Type
= State
->Header
.depth
;
338 if (State
->Lines
>=State
->Header
.height
)
339 State
->Type
= 1; /* The transparency mask is 1 bpp */
341 /* Determine the palette size. If the header indicates 0, it
342 is actually the maximum for the bpp. You have to love the
343 guys who made the spec. */
344 I
= (int)(BIH
[35] << 24) + (BIH
[34] << 16) + (BIH
[33] << 8) + (BIH
[32]);
346 if ((I
==0)&&(State
->Type
==1))
348 if ((I
==0)&&(State
->Type
==4))
350 if ((I
==0)&&(State
->Type
==8))
353 State
->HeaderSize
+=I
;
355 if (State
->HeaderSize
< 0) {
356 g_set_error_literal (error
,
358 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
359 _("Invalid header in icon"));
363 if (State
->HeaderSize
>State
->BytesInHeaderBuf
) {
364 guchar
*tmp
=g_try_realloc(State
->HeaderBuf
,State
->HeaderSize
);
366 g_set_error_literal (error
,
368 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
369 _("Not enough memory to load icon"));
372 State
->HeaderBuf
= tmp
;
373 State
->BytesInHeaderBuf
= State
->HeaderSize
;
375 if (Bytes
< State
->HeaderSize
)
378 if ((BIH
[16] != 0) || (BIH
[17] != 0) || (BIH
[18] != 0)
380 /* FIXME: is this the correct message? */
381 g_set_error_literal (error
,
383 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
384 _("Compressed icons are not supported"));
388 /* Negative heights mean top-down pixel-order */
389 if (State
->Header
.height
< 0) {
390 State
->Header
.height
= -State
->Header
.height
;
391 State
->Header
.Negative
= 1;
393 if (State
->Header
.width
< 0) {
394 State
->Header
.width
= -State
->Header
.width
;
396 g_assert (State
->Header
.width
> 0);
397 g_assert (State
->Header
.height
> 0);
399 if (State
->Type
== 32)
400 State
->LineWidth
= State
->Header
.width
* 4;
401 else if (State
->Type
== 24)
402 State
->LineWidth
= State
->Header
.width
* 3;
403 else if (State
->Type
== 16)
404 State
->LineWidth
= State
->Header
.width
* 2;
405 else if (State
->Type
== 8)
406 State
->LineWidth
= State
->Header
.width
* 1;
407 else if (State
->Type
== 4)
408 State
->LineWidth
= (State
->Header
.width
+1)/2;
409 else if (State
->Type
== 1) {
410 State
->LineWidth
= State
->Header
.width
/ 8;
411 if ((State
->Header
.width
& 7) != 0)
414 g_set_error_literal (error
,
416 GDK_PIXBUF_ERROR_CORRUPT_IMAGE
,
417 _("Unsupported icon type"));
421 /* Pad to a 32 bit boundary */
422 if (((State
->LineWidth
% 4) > 0))
423 State
->LineWidth
= (State
->LineWidth
/ 4) * 4 + 4;
426 if (State
->LineBuf
== NULL
) {
427 State
->LineBuf
= g_try_malloc(State
->LineWidth
);
428 if (!State
->LineBuf
) {
429 g_set_error_literal (error
,
431 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
432 _("Not enough memory to load icon"));
437 g_assert(State
->LineBuf
!= NULL
);
440 if (State
->pixbuf
== NULL
) {
442 if (State
->size_func
) {
443 gint width
= State
->Header
.width
;
444 gint height
= State
->Header
.height
;
446 (*State
->size_func
) (&width
, &height
, State
->user_data
);
447 if (width
== 0 || height
== 0) {
448 State
->LineWidth
= 0;
455 gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8,
457 State
->Header
.height
);
458 if (!State
->pixbuf
) {
459 g_set_error_literal (error
,
461 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
462 _("Not enough memory to load icon"));
467 g_snprintf (hot
, 10, "%d", State
->x_hot
);
468 gdk_pixbuf_set_option (State
->pixbuf
, "x_hot", hot
);
469 g_snprintf (hot
, 10, "%d", State
->y_hot
);
470 gdk_pixbuf_set_option (State
->pixbuf
, "y_hot", hot
);
473 if (State
->prepared_func
!= NULL
)
474 /* Notify the client that we are ready to go */
475 (*State
->prepared_func
) (State
->pixbuf
,
484 * func - called when we have pixmap created (but no image data)
485 * user_data - passed as arg 1 to func
486 * return context (opaque to user)
490 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func
,
491 GdkPixbufModulePreparedFunc prepared_func
,
492 GdkPixbufModuleUpdatedFunc updated_func
,
496 struct ico_progressive_state
*context
;
498 context
= g_new0(struct ico_progressive_state
, 1);
499 context
->size_func
= size_func
;
500 context
->prepared_func
= prepared_func
;
501 context
->updated_func
= updated_func
;
502 context
->user_data
= user_data
;
504 context
->HeaderSize
= 54;
505 context
->HeaderBuf
= g_try_malloc(14 + 40 + 4*256 + 512);
506 if (!context
->HeaderBuf
) {
508 g_set_error_literal (error
,
510 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY
,
511 _("Not enough memory to load ICO file"));
514 /* 4*256 for the colormap */
515 context
->BytesInHeaderBuf
= 14 + 40 + 4*256 + 512 ;
516 context
->HeaderDone
= 0;
518 context
->LineWidth
= 0;
519 context
->LineBuf
= NULL
;
520 context
->LineDone
= 0;
525 memset(&context
->Header
, 0, sizeof(struct headerpair
));
528 context
->pixbuf
= NULL
;
531 return (gpointer
) context
;
535 * context - returned from image_begin_load
537 * free context, unref gdk_pixbuf
540 gdk_pixbuf__ico_image_stop_load(gpointer data
,
543 struct ico_progressive_state
*context
=
544 (struct ico_progressive_state
*) data
;
546 /* FIXME this thing needs to report errors if
547 * we have unused image data
550 g_return_val_if_fail(context
!= NULL
, TRUE
);
552 context_free (context
);
557 OneLine32 (struct ico_progressive_state
*context
)
563 if (context
->Header
.Negative
== 0)
564 Pixels
= (context
->pixbuf
->pixels
+
565 context
->pixbuf
->rowstride
*
566 (context
->Header
.height
- context
->Lines
- 1));
568 Pixels
= (context
->pixbuf
->pixels
+
569 context
->pixbuf
->rowstride
*
571 while (X
< context
->Header
.width
) {
572 Pixels
[X
* 4 + 0] = context
->LineBuf
[X
* 4 + 2];
573 Pixels
[X
* 4 + 1] = context
->LineBuf
[X
* 4 + 1];
574 Pixels
[X
* 4 + 2] = context
->LineBuf
[X
* 4 + 0];
575 Pixels
[X
* 4 + 3] = context
->LineBuf
[X
* 4 + 3];
580 static void OneLine24(struct ico_progressive_state
*context
)
586 if (context
->Header
.Negative
== 0)
587 Pixels
= (context
->pixbuf
->pixels
+
588 context
->pixbuf
->rowstride
*
589 (context
->Header
.height
- context
->Lines
- 1));
591 Pixels
= (context
->pixbuf
->pixels
+
592 context
->pixbuf
->rowstride
*
594 while (X
< context
->Header
.width
) {
595 Pixels
[X
* 4 + 0] = context
->LineBuf
[X
* 3 + 2];
596 Pixels
[X
* 4 + 1] = context
->LineBuf
[X
* 3 + 1];
597 Pixels
[X
* 4 + 2] = context
->LineBuf
[X
* 3 + 0];
604 OneLine16 (struct ico_progressive_state
*context
)
610 if (context
->Header
.Negative
== 0)
611 pixels
= (context
->pixbuf
->pixels
+
612 context
->pixbuf
->rowstride
* (context
->Header
.height
- context
->Lines
- 1));
614 pixels
= (context
->pixbuf
->pixels
+
615 context
->pixbuf
->rowstride
* context
->Lines
);
617 src
= context
->LineBuf
;
619 for (i
= 0; i
< context
->Header
.width
; i
++) {
622 v
= (int) src
[0] | ((int) src
[1] << 8);
625 /* Extract 5-bit RGB values */
627 r
= (v
>> 10) & 0x1f;
631 /* Fill the rightmost bits to form 8-bit values */
633 *pixels
++ = (r
<< 3) | (r
>> 2);
634 *pixels
++ = (g
<< 3) | (g
>> 2);
635 *pixels
++ = (b
<< 3) | (b
>> 2);
636 pixels
++; /* skip alpha channel */
641 static void OneLine8(struct ico_progressive_state
*context
)
647 if (context
->Header
.Negative
== 0)
648 Pixels
= (context
->pixbuf
->pixels
+
649 context
->pixbuf
->rowstride
*
650 (context
->Header
.height
- context
->Lines
- 1));
652 Pixels
= (context
->pixbuf
->pixels
+
653 context
->pixbuf
->rowstride
*
655 while (X
< context
->Header
.width
) {
656 /* The joys of having a BGR byteorder */
658 context
->HeaderBuf
[4 * context
->LineBuf
[X
] + 42+context
->DIBoffset
];
660 context
->HeaderBuf
[4 * context
->LineBuf
[X
] + 41+context
->DIBoffset
];
662 context
->HeaderBuf
[4 * context
->LineBuf
[X
] + 40+context
->DIBoffset
];
666 static void OneLine4(struct ico_progressive_state
*context
)
672 if (context
->Header
.Negative
== 0)
673 Pixels
= (context
->pixbuf
->pixels
+
674 context
->pixbuf
->rowstride
*
675 (context
->Header
.height
- context
->Lines
- 1));
677 Pixels
= (context
->pixbuf
->pixels
+
678 context
->pixbuf
->rowstride
*
681 while (X
< context
->Header
.width
) {
684 Pix
= context
->LineBuf
[X
/2];
687 context
->HeaderBuf
[4 * (Pix
>>4) + 42+context
->DIBoffset
];
689 context
->HeaderBuf
[4 * (Pix
>>4) + 41+context
->DIBoffset
];
691 context
->HeaderBuf
[4 * (Pix
>>4) + 40+context
->DIBoffset
];
693 if (X
<context
->Header
.width
) {
694 /* Handle the other 4 bit pixel only when there is one */
696 context
->HeaderBuf
[4 * (Pix
&15) + 42+context
->DIBoffset
];
698 context
->HeaderBuf
[4 * (Pix
&15) + 41+context
->DIBoffset
];
700 context
->HeaderBuf
[4 * (Pix
&15) + 40+context
->DIBoffset
];
707 static void OneLine1(struct ico_progressive_state
*context
)
713 if (context
->Header
.Negative
== 0)
714 Pixels
= (context
->pixbuf
->pixels
+
715 context
->pixbuf
->rowstride
*
716 (context
->Header
.height
- context
->Lines
- 1));
718 Pixels
= (context
->pixbuf
->pixels
+
719 context
->pixbuf
->rowstride
*
721 while (X
< context
->Header
.width
) {
724 Bit
= (context
->LineBuf
[X
/ 8]) >> (7 - (X
& 7));
726 /* The joys of having a BGR byteorder */
727 Pixels
[X
* 4 + 0] = Bit
*255;
728 Pixels
[X
* 4 + 1] = Bit
*255;
729 Pixels
[X
* 4 + 2] = Bit
*255;
734 static void OneLineTransp(struct ico_progressive_state
*context
)
739 /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
740 if (context
->Header
.depth
== 32)
744 if (context
->Header
.Negative
== 0)
745 Pixels
= (context
->pixbuf
->pixels
+
746 context
->pixbuf
->rowstride
*
747 (2*context
->Header
.height
- context
->Lines
- 1));
749 Pixels
= (context
->pixbuf
->pixels
+
750 context
->pixbuf
->rowstride
*
751 (context
->Lines
-context
->Header
.height
));
752 while (X
< context
->Header
.width
) {
755 Bit
= (context
->LineBuf
[X
/ 8]) >> (7 - (X
& 7));
757 /* The joys of having a BGR byteorder */
758 Pixels
[X
* 4 + 3] = 255-Bit
*255;
773 static void OneLine(struct ico_progressive_state
*context
)
775 context
->LineDone
= 0;
777 if (context
->Lines
>= context
->Header
.height
*2) {
781 if (context
->Lines
<context
->Header
.height
) {
782 if (context
->Type
== 32)
784 else if (context
->Type
== 24)
786 else if (context
->Type
== 16)
788 else if (context
->Type
== 8)
790 else if (context
->Type
== 4)
792 else if (context
->Type
== 1)
795 g_assert_not_reached ();
797 OneLineTransp(context
);
800 if (context
->Lines
>=context
->Header
.height
) {
802 context
->LineWidth
= context
->Header
.width
/ 8;
803 if ((context
->Header
.width
& 7) != 0)
804 context
->LineWidth
++;
805 /* Pad to a 32 bit boundary */
806 if (((context
->LineWidth
% 4) > 0))
807 context
->LineWidth
= (context
->LineWidth
/ 4) * 4 + 4;
812 if (context
->updated_func
!= NULL
) {
813 (*context
->updated_func
) (context
->pixbuf
,
815 context
->Lines
% context
->Header
.height
,
816 context
->Header
.width
,
824 * context - from image_begin_load
825 * buf - new image data
826 * size - length of new image data
828 * append image data onto inrecrementally built output image
831 gdk_pixbuf__ico_image_load_increment(gpointer data
,
836 struct ico_progressive_state
*context
=
837 (struct ico_progressive_state
*) data
;
842 g_assert(context
->LineDone
>= 0);
843 if (context
->HeaderDone
< context
->HeaderSize
) { /* We still
844 have headerbytes to do */
846 context
->HeaderSize
- context
->HeaderDone
;
847 if (BytesToCopy
> size
)
850 memmove(context
->HeaderBuf
+ context
->HeaderDone
,
855 context
->HeaderDone
+= BytesToCopy
;
859 context
->LineWidth
- context
->LineDone
;
860 if (BytesToCopy
> size
)
863 if (BytesToCopy
> 0) {
864 memmove(context
->LineBuf
+
865 context
->LineDone
, buf
,
870 context
->LineDone
+= BytesToCopy
;
872 if ((context
->LineDone
>= context
->LineWidth
) &&
873 (context
->LineWidth
> 0))
879 if (context
->HeaderDone
>= 6 && context
->pixbuf
== NULL
) {
880 GError
*decode_err
= NULL
;
881 DecodeHeader(context
->HeaderBuf
,
882 context
->HeaderDone
, context
, &decode_err
);
883 if (context
->LineBuf
!= NULL
&& context
->LineWidth
== 0)
887 g_propagate_error (error
, decode_err
);
909 bytes
= fwrite ((char*) data
, sizeof (char), count
, f
);
927 for (i
= 0; i
< count
; i
++)
928 data
[i
] = GUINT16_TO_LE (data
[i
]);
930 return write8 (f
, (guint8
*) data
, count
* 2);
940 for (i
= 0; i
< count
; i
++)
941 data
[i
] = GUINT32_TO_LE (data
[i
]);
943 return write8 (f
, (guint8
*) data
, count
* 4);
946 typedef struct _IconEntry IconEntry
;
963 fill_entry (IconEntry
*icon
,
969 guchar
*p
, *pixels
, *and, *xor;
970 gint n_channels
, v
, x
, y
;
972 if (icon
->width
> 255 || icon
->height
> 255) {
973 g_set_error_literal (error
,
975 GDK_PIXBUF_ERROR_BAD_OPTION
,
976 _("Image too large to be saved as ICO"));
980 if (hot_x
> -1 && hot_y
> -1) {
983 if (icon
->hot_x
>= icon
->width
|| icon
->hot_y
>= icon
->height
) {
984 g_set_error_literal (error
,
986 GDK_PIXBUF_ERROR_BAD_OPTION
,
987 _("Cursor hotspot outside image"));
996 switch (icon
->depth
) {
998 icon
->xor_rowstride
= icon
->width
* 4;
1001 icon
->xor_rowstride
= icon
->width
* 3;
1004 icon
->xor_rowstride
= icon
->width
* 2;
1009 GDK_PIXBUF_ERROR_BAD_OPTION
,
1010 _("Unsupported depth for ICO file: %d"), icon
->depth
);
1014 if ((icon
->xor_rowstride
% 4) != 0)
1015 icon
->xor_rowstride
= 4 * ((icon
->xor_rowstride
/ 4) + 1);
1016 icon
->xor = g_new0 (guchar
, icon
->xor_rowstride
* icon
->height
);
1018 icon
->and_rowstride
= icon
->width
/ 8;
1019 if ((icon
->and_rowstride
% 4) != 0)
1020 icon
->and_rowstride
= 4 * ((icon
->and_rowstride
/ 4) + 1);
1021 icon
->and = g_new0 (guchar
, icon
->and_rowstride
* icon
->height
);
1023 pixels
= gdk_pixbuf_get_pixels (pixbuf
);
1024 n_channels
= gdk_pixbuf_get_n_channels (pixbuf
);
1025 for (y
= 0; y
< icon
->height
; y
++) {
1026 p
= pixels
+ gdk_pixbuf_get_rowstride (pixbuf
) * (icon
->height
- 1 - y
);
1027 and = icon
->and + icon
->and_rowstride
* y
;
1028 xor = icon
->xor + icon
->xor_rowstride
* y
;
1029 for (x
= 0; x
< icon
->width
; x
++) {
1030 switch (icon
->depth
) {
1036 if (n_channels
== 4) {
1039 *and |= 1 << (7 - x
% 8);
1047 if (n_channels
== 4 && p
[3] < 0x80)
1048 *and |= 1 << (7 - x
% 8);
1052 v
= ((p
[0] >> 3) << 10) | ((p
[1] >> 3) << 5) | (p
[2] >> 3);
1055 if (n_channels
== 4 && p
[3] < 0x80)
1056 *and |= 1 << (7 - x
% 8);
1071 free_entry (IconEntry
*icon
)
1073 g_free (icon
->colors
);
1080 write_icon (FILE *f
, GSList
*entries
)
1092 if (((IconEntry
*)entries
->data
)->hot_x
> -1)
1096 n_entries
= g_slist_length (entries
);
1101 words
[2] = n_entries
;
1102 write16 (f
, words
, 3);
1104 offset
= 6 + 16 * n_entries
;
1106 for (entry
= entries
; entry
; entry
= entry
->next
) {
1107 icon
= (IconEntry
*)entry
->data
;
1108 size
= 40 + icon
->height
* (icon
->and_rowstride
+ icon
->xor_rowstride
);
1110 /* directory entry */
1111 bytes
[0] = icon
->width
;
1112 bytes
[1] = icon
->height
;
1113 bytes
[2] = icon
->n_colors
;
1115 write8 (f
, bytes
, 4);
1118 words
[1] = icon
->depth
;
1121 words
[0] = icon
->hot_x
;
1122 words
[1] = icon
->hot_y
;
1124 write16 (f
, words
, 2);
1127 write32 (f
, dwords
, 2);
1132 for (entry
= entries
; entry
; entry
= entry
->next
) {
1133 icon
= (IconEntry
*)entry
->data
;
1137 dwords
[1] = icon
->width
;
1138 dwords
[2] = icon
->height
* 2;
1139 write32 (f
, dwords
, 3);
1141 words
[1] = icon
->depth
;
1142 write16 (f
, words
, 2);
1149 write32 (f
, dwords
, 6);
1152 write8 (f
, icon
->xor, icon
->xor_rowstride
* icon
->height
);
1153 write8 (f
, icon
->and, icon
->and_rowstride
* icon
->height
);
1158 gdk_pixbuf__ico_image_save (FILE *f
,
1166 GSList
*entries
= NULL
;
1168 /* support only single-image ICOs for now */
1169 icon
= g_new0 (IconEntry
, 1);
1170 icon
->width
= gdk_pixbuf_get_width (pixbuf
);
1171 icon
->height
= gdk_pixbuf_get_height (pixbuf
);
1172 icon
->depth
= gdk_pixbuf_get_has_alpha (pixbuf
) ? 32 : 24;
1177 if (keys
&& *keys
) {
1181 for (kiter
= keys
, viter
= values
; *kiter
&& *viter
; kiter
++, viter
++) {
1183 if (strcmp (*kiter
, "depth") == 0) {
1184 sscanf (*viter
, "%d", &icon
->depth
);
1186 else if (strcmp (*kiter
, "x_hot") == 0) {
1187 hot_x
= strtol (*viter
, &endptr
, 10);
1189 else if (strcmp (*kiter
, "y_hot") == 0) {
1190 hot_y
= strtol (*viter
, &endptr
, 10);
1196 if (!fill_entry (icon
, pixbuf
, hot_x
, hot_y
, error
)) {
1201 entries
= g_slist_append (entries
, icon
);
1202 write_icon (f
, entries
);
1204 g_slist_foreach (entries
, (GFunc
)free_entry
, NULL
);
1205 g_slist_free (entries
);
1211 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1213 #define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
1216 MODULE_ENTRY (fill_vtable
) (GdkPixbufModule
*module
)
1218 module
->begin_load
= gdk_pixbuf__ico_image_begin_load
;
1219 module
->stop_load
= gdk_pixbuf__ico_image_stop_load
;
1220 module
->load_increment
= gdk_pixbuf__ico_image_load_increment
;
1221 module
->save
= gdk_pixbuf__ico_image_save
;
1224 MODULE_ENTRY (fill_info
) (GdkPixbufFormat
*info
)
1226 static GdkPixbufModulePattern signature
[] = {
1227 { " \x1 ", "zz znz", 100 },
1228 { " \x2 ", "zz znz", 100 },
1231 static gchar
* mime_types
[] = {
1234 "image/x-win-bitmap",
1237 static gchar
* extensions
[] = {
1244 info
->signature
= signature
;
1245 info
->description
= N_("The ICO image format");
1246 info
->mime_types
= mime_types
;
1247 info
->extensions
= extensions
;
1248 info
->flags
= GDK_PIXBUF_FORMAT_WRITABLE
| GDK_PIXBUF_FORMAT_THREADSAFE
;
1249 info
->license
= "LGPL";