]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-ico.c
Emit the closed signal when closing the loader, pointed out by David
[~andy/gtk] / gdk-pixbuf / io-ico.c
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, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 #undef DUMPBIH
28 /*
29
30 Icons are just like BMP's, except for the header.
31  
32 Known bugs:
33         * bi-tonal files aren't tested 
34
35 */
36
37 #include <config.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <string.h>
44 #include "gdk-pixbuf-private.h"
45 #include "gdk-pixbuf-io.h"
46 #include <errno.h>
47
48 \f
49
50 /* 
51
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
55 the internal members.
56
57 */
58
59 struct BitmapFileHeader {
60         gushort bfType;
61         guint bfSize;
62         guint reserverd;
63         guint bfOffbits;
64 };
65
66 struct BitmapInfoHeader {
67         guint biSize;
68         guint biWidth;
69         guint biHeight;
70         gushort biPlanes;
71         gushort biBitCount;
72         guint biCompression;
73         guint biSizeImage;
74         guint biXPelsPerMeter;
75         guint biYPelsPerMeter;
76         guint biClrUsed;
77         guint biClrImportant;
78 };
79
80 #ifdef DUMPBIH
81 /* 
82
83 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
84 debugging purposes.
85
86 */
87 static void DumpBIH(unsigned char *BIH)
88 {                               
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) +
95                (BIH[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) +
100                (BIH[16]));
101         printf("biSizeImage = %i \n",
102                (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
103                (BIH[20]));
104         printf("biXPels     = %i \n",
105                (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
106                (BIH[24]));
107         printf("biYPels     = %i \n",
108                (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
109                (BIH[28]));
110         printf("biClrUsed   = %i \n",
111                (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
112                (BIH[32]));
113         printf("biClrImprtnt= %i \n",
114                (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
115                (BIH[36]));
116 }
117 #endif
118
119 /* Progressive loading */
120 struct headerpair {
121         gint width;
122         gint height;
123         guint depth;
124         guint Negative;         /* Negative = 1 -> top down BMP,  
125                                    Negative = 0 -> bottom up BMP */
126 };
127
128 struct ico_progressive_state {
129         GdkPixbufModuleSizeFunc size_func;
130         GdkPixbufModulePreparedFunc prepared_func;
131         GdkPixbufModuleUpdatedFunc updated_func;
132         gpointer user_data;
133
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 */
138
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 */
143
144         gint Type;              /*  
145                                    32 = RGBA
146                                    24 = RGB
147                                    16 = 555 RGB
148                                    8 = 8 bit colormapped
149                                    4 = 4 bpp colormapped
150                                    1  = 1 bit bitonal 
151                                  */
152         gboolean cursor;
153         gint x_hot;
154         gint y_hot;
155
156         struct headerpair Header;       /* Decoded (BE->CPU) header */
157         
158         gint                    DIBoffset;
159         gint                    ImageScore;
160
161
162         GdkPixbuf *pixbuf;      /* Our "target" */
163 };
164
165 static gpointer
166 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
167                                  GdkPixbufModulePreparedFunc prepared_func,
168                                  GdkPixbufModuleUpdatedFunc updated_func,
169                                  gpointer user_data,
170                                  GError **error);
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,
174                                                      GError **error);
175
176 static void 
177 context_free (struct ico_progressive_state *context)
178 {
179         if (context->LineBuf != NULL)
180                 g_free (context->LineBuf);
181         context->LineBuf = NULL;
182         if (context->HeaderBuf != NULL)
183                 g_free (context->HeaderBuf);
184
185         if (context->pixbuf)
186                 g_object_unref (context->pixbuf);
187
188         g_free (context);
189 }
190
191 static void DecodeHeader(guchar *Data, gint Bytes,
192                          struct ico_progressive_state *State,
193                          GError **error)
194 {
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.
198  */   
199  
200         gint IconCount = 0; /* The number of icon-versions in the file */
201         guchar *BIH; /* The DIB for the used icon */
202         guchar *Ptr;
203         gint I;
204  
205         /* Step 1: The ICO header */
206
207         State->cursor = ((Data[3] << 8) + Data[2] == 2) ? TRUE : FALSE;
208
209         IconCount = (Data[5] << 8) + (Data[4]);
210         
211         State->HeaderSize = 6 + IconCount*16;
212
213         if (State->HeaderSize>State->BytesInHeaderBuf) {
214                 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
215                 if (!tmp) {
216                         g_set_error (error,
217                                      GDK_PIXBUF_ERROR,
218                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
219                                      _("Not enough memory to load icon"));
220                         return;
221                 }
222                 State->HeaderBuf = tmp;
223                 State->BytesInHeaderBuf = State->HeaderSize;
224         }
225         if (Bytes < State->HeaderSize)
226                 return;
227         
228         /* We now have all the "short-specs" of the versions 
229            So we iterate through them and select the best one */
230            
231         State->ImageScore = 0;
232         State->DIBoffset  = 0;
233         Ptr = Data + 6;
234         for (I=0;I<IconCount;I++) {
235                 int ThisScore;
236                 
237                 ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
238
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]);
245                                                                  
246                 }
247                 
248                 
249                 Ptr += 16;      
250         } 
251
252         if (State->DIBoffset < 0) {
253                 g_set_error (error,
254                              GDK_PIXBUF_ERROR,
255                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
256                              _("Invalid header in icon"));
257                 return;
258         }
259
260         /* We now have a winner, pointed to in State->DIBoffset,
261            so we know how many bytes are in the "header" part. */
262               
263         State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
264
265         if (State->HeaderSize < 0) {
266                 g_set_error (error,
267                              GDK_PIXBUF_ERROR,
268                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
269                              _("Invalid header in icon"));
270                 return;
271         }
272
273         if (State->HeaderSize>State->BytesInHeaderBuf) {
274                 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
275                 if (!tmp) {
276                         g_set_error (error,
277                                      GDK_PIXBUF_ERROR,
278                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
279                                      _("Not enough memory to load icon"));
280                         return;
281                 }
282                 State->HeaderBuf = tmp;
283                 State->BytesInHeaderBuf = State->HeaderSize;
284         }
285         if (Bytes<State->HeaderSize) 
286                 return;   
287         
288         BIH = Data+State->DIBoffset;
289
290 #ifdef DUMPBIH
291         DumpBIH(BIH);
292 #endif  
293         /* Add the palette to the headersize */
294                 
295         State->Header.width =
296             (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
297         if (State->Header.width == 0) {
298                 g_set_error (error,
299                              GDK_PIXBUF_ERROR,
300                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
301                              _("Icon has zero width"));
302                 return;
303         }
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) {
308                 g_set_error (error,
309                              GDK_PIXBUF_ERROR,
310                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
311                              _("Icon has zero height"));
312                 return;
313         }
314         State->Header.depth = (BIH[15] << 8) + (BIH[14]);
315
316         State->Type = State->Header.depth;      
317         if (State->Lines>=State->Header.height)
318                 State->Type = 1; /* The transparency mask is 1 bpp */
319         
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]);
324         I = I*4;
325         if ((I==0)&&(State->Type==1))
326                 I = 2*4;
327         if ((I==0)&&(State->Type==4))
328                 I = 16*4;
329         if ((I==0)&&(State->Type==8))
330                 I = 256*4;
331         
332         State->HeaderSize+=I;
333         
334         if (State->HeaderSize < 0) {
335                 g_set_error (error,
336                              GDK_PIXBUF_ERROR,
337                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
338                              _("Invalid header in icon"));
339                 return;
340         }
341
342         if (State->HeaderSize>State->BytesInHeaderBuf) {
343                 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
344                 if (!tmp) {
345                         g_set_error (error,
346                                      GDK_PIXBUF_ERROR,
347                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
348                                      _("Not enough memory to load icon"));
349                         return;
350                 }
351                 State->HeaderBuf = tmp;
352                 State->BytesInHeaderBuf = State->HeaderSize;
353         }
354         if (Bytes < State->HeaderSize)
355                 return;
356
357         if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
358             || (BIH[19] != 0)) {
359                 /* FIXME: is this the correct message? */
360                 g_set_error (error,
361                              GDK_PIXBUF_ERROR,
362                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
363                              _("Compressed icons are not supported"));
364                 return;
365         }
366
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;
371         }
372         if (State->Header.width < 0) {
373                 State->Header.width = -State->Header.width;
374         }
375         g_assert (State->Header.width > 0);
376         g_assert (State->Header.height > 0);
377
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)
391                         State->LineWidth++;
392         } else {
393           g_set_error (error,
394                        GDK_PIXBUF_ERROR,
395                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
396                        _("Unsupported icon type"));
397           return;
398         }
399
400         /* Pad to a 32 bit boundary */
401         if (((State->LineWidth % 4) > 0))
402                 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
403
404
405         if (State->LineBuf == NULL) {
406                 State->LineBuf = g_try_malloc(State->LineWidth);
407                 if (!State->LineBuf) {
408                         g_set_error (error,
409                                      GDK_PIXBUF_ERROR,
410                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
411                                      _("Not enough memory to load icon"));
412                         return;
413                 }
414         }
415
416         g_assert(State->LineBuf != NULL);
417
418
419         if (State->pixbuf == NULL) {
420 #if 1
421                 if (State->size_func) {
422                         gint width = State->Header.width;
423                         gint height = State->Header.height;
424
425                         (*State->size_func) (&width, &height, State->user_data);
426                         if (width == 0 || height == 0) {
427                                 State->LineWidth = 0;
428                                 return;
429                         }
430                 }
431 #endif
432
433                 State->pixbuf =
434                     gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
435                                    State->Header.width,
436                                    State->Header.height);
437                 if (!State->pixbuf) {
438                         g_set_error (error,
439                                      GDK_PIXBUF_ERROR,
440                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
441                                      _("Not enough memory to load icon"));
442                         return;
443                 }
444                 if (State->cursor) {
445                         gchar hot[10];
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);
450                 }
451
452                 if (State->prepared_func != NULL)
453                         /* Notify the client that we are ready to go */
454                         (*State->prepared_func) (State->pixbuf,
455                                                  NULL,
456                                                  State->user_data);
457
458         }
459
460 }
461
462 /* 
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)
466  */
467
468 static gpointer
469 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
470                                  GdkPixbufModulePreparedFunc prepared_func,
471                                  GdkPixbufModuleUpdatedFunc updated_func,
472                                  gpointer user_data,
473                                  GError **error)
474 {
475         struct ico_progressive_state *context;
476
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;
482
483         context->HeaderSize = 54;
484         context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
485         if (!context->HeaderBuf) {
486                 g_free (context);
487                 g_set_error (error,
488                              GDK_PIXBUF_ERROR,
489                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
490                              _("Not enough memory to load ICO file"));
491                 return NULL;
492         }
493         /* 4*256 for the colormap */
494         context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
495         context->HeaderDone = 0;
496
497         context->LineWidth = 0;
498         context->LineBuf = NULL;
499         context->LineDone = 0;
500         context->Lines = 0;
501
502         context->Type = 0;
503
504         memset(&context->Header, 0, sizeof(struct headerpair));
505
506
507         context->pixbuf = NULL;
508
509
510         return (gpointer) context;
511 }
512
513 /*
514  * context - returned from image_begin_load
515  *
516  * free context, unref gdk_pixbuf
517  */
518 static gboolean 
519 gdk_pixbuf__ico_image_stop_load(gpointer data,
520                                 GError **error)
521 {
522         struct ico_progressive_state *context =
523             (struct ico_progressive_state *) data;
524
525         /* FIXME this thing needs to report errors if
526          * we have unused image data
527          */
528
529         g_return_val_if_fail(context != NULL, TRUE);
530
531         context_free (context);
532         return TRUE;
533 }
534
535 static void
536 OneLine32 (struct ico_progressive_state *context)
537 {
538         gint X;
539         guchar *Pixels;
540
541         X = 0;
542         if (context->Header.Negative == 0)
543                 Pixels = (context->pixbuf->pixels +
544                           context->pixbuf->rowstride *
545                           (context->Header.height - context->Lines - 1));
546         else
547                 Pixels = (context->pixbuf->pixels +
548                           context->pixbuf->rowstride *
549                           context->Lines);
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];
555                 X++;
556         }
557 }
558
559 static void OneLine24(struct ico_progressive_state *context)
560 {
561         gint X;
562         guchar *Pixels;
563
564         X = 0;
565         if (context->Header.Negative == 0)
566                 Pixels = (context->pixbuf->pixels +
567                           context->pixbuf->rowstride *
568                           (context->Header.height - context->Lines - 1));
569         else
570                 Pixels = (context->pixbuf->pixels +
571                           context->pixbuf->rowstride *
572                           context->Lines);
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];
577                 X++;
578         }
579
580 }
581
582 static void
583 OneLine16 (struct ico_progressive_state *context)
584 {
585         int i;
586         guchar *pixels;
587         guchar *src;
588
589         if (context->Header.Negative == 0)
590                 pixels = (context->pixbuf->pixels +
591                           context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
592         else
593                 pixels = (context->pixbuf->pixels +
594                           context->pixbuf->rowstride * context->Lines);
595
596         src = context->LineBuf;
597
598         for (i = 0; i < context->Header.width; i++) {
599                 int v, r, g, b;
600
601                 v = (int) src[0] | ((int) src[1] << 8);
602                 src += 2;
603
604                 /* Extract 5-bit RGB values */
605
606                 r = (v >> 10) & 0x1f;
607                 g = (v >> 5) & 0x1f;
608                 b = v & 0x1f;
609
610                 /* Fill the rightmost bits to form 8-bit values */
611
612                 *pixels++ = (r << 3) | (r >> 2);
613                 *pixels++ = (g << 3) | (g >> 2);
614                 *pixels++ = (b << 3) | (b >> 2);
615                 pixels++; /* skip alpha channel */
616         }
617 }
618
619
620 static void OneLine8(struct ico_progressive_state *context)
621 {
622         gint X;
623         guchar *Pixels;
624
625         X = 0;
626         if (context->Header.Negative == 0)
627                 Pixels = (context->pixbuf->pixels +
628                           context->pixbuf->rowstride *
629                           (context->Header.height - context->Lines - 1));
630         else
631                 Pixels = (context->pixbuf->pixels +
632                           context->pixbuf->rowstride *
633                           context->Lines);
634         while (X < context->Header.width) {
635                 /* The joys of having a BGR byteorder */
636                 Pixels[X * 4 + 0] =
637                     context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
638                 Pixels[X * 4 + 1] =
639                     context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
640                 Pixels[X * 4 + 2] =
641                     context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
642                 X++;
643         }
644 }
645 static void OneLine4(struct ico_progressive_state *context)
646 {
647         gint X;
648         guchar *Pixels;
649
650         X = 0;
651         if (context->Header.Negative == 0)
652                 Pixels = (context->pixbuf->pixels +
653                           context->pixbuf->rowstride *
654                           (context->Header.height - context->Lines - 1));
655         else
656                 Pixels = (context->pixbuf->pixels +
657                           context->pixbuf->rowstride *
658                           context->Lines);
659         
660         while (X < context->Header.width) {
661                 guchar Pix;
662                 
663                 Pix = context->LineBuf[X/2];
664
665                 Pixels[X * 4 + 0] =
666                     context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
667                 Pixels[X * 4 + 1] =
668                     context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
669                 Pixels[X * 4 + 2] =
670                     context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
671                 X++;
672                 if (X<context->Header.width) { 
673                         /* Handle the other 4 bit pixel only when there is one */
674                         Pixels[X * 4 + 0] =
675                             context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
676                         Pixels[X * 4 + 1] =
677                             context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
678                         Pixels[X * 4 + 2] =
679                             context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
680                         X++;
681                 }
682         }
683         
684 }
685
686 static void OneLine1(struct ico_progressive_state *context)
687 {
688         gint X;
689         guchar *Pixels;
690
691         X = 0;
692         if (context->Header.Negative == 0)
693                 Pixels = (context->pixbuf->pixels +
694                           context->pixbuf->rowstride *
695                           (context->Header.height - context->Lines - 1));
696         else
697                 Pixels = (context->pixbuf->pixels +
698                           context->pixbuf->rowstride *
699                           context->Lines);
700         while (X < context->Header.width) {
701                 int Bit;
702
703                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
704                 Bit = Bit & 1;
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;
709                 X++;
710         }
711 }
712
713 static void OneLineTransp(struct ico_progressive_state *context)
714 {
715         gint X;
716         guchar *Pixels;
717
718         /* Ignore the XOR mask for XP style 32-bpp icons with alpha */ 
719         if (context->Header.depth == 32)
720                 return;
721
722         X = 0;
723         if (context->Header.Negative == 0)
724                 Pixels = (context->pixbuf->pixels +
725                           context->pixbuf->rowstride *
726                           (2*context->Header.height - context->Lines - 1));
727         else
728                 Pixels = (context->pixbuf->pixels +
729                           context->pixbuf->rowstride *
730                           (context->Lines-context->Header.height));
731         while (X < context->Header.width) {
732                 int Bit;
733
734                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
735                 Bit = Bit & 1;
736                 /* The joys of having a BGR byteorder */
737                 Pixels[X * 4 + 3] = 255-Bit*255;
738 #if 0
739                 if (Bit){
740                   Pixels[X*4+0] = 255;
741                   Pixels[X*4+1] = 255;
742                 } else {
743                   Pixels[X*4+0] = 0;
744                   Pixels[X*4+1] = 0;
745                 }
746 #endif          
747                 X++;
748         }
749 }
750
751
752 static void OneLine(struct ico_progressive_state *context)
753 {
754         context->LineDone = 0;
755         
756         if (context->Lines >= context->Header.height*2) {
757                 return;
758         }
759                 
760         if (context->Lines <context->Header.height) {           
761                 if (context->Type == 32)
762                         OneLine32 (context);
763                 else if (context->Type == 24)
764                         OneLine24(context);
765                 else if (context->Type == 16)
766                         OneLine16 (context);
767                 else if (context->Type == 8)
768                         OneLine8(context);
769                 else if (context->Type == 4)
770                         OneLine4(context);
771                 else if (context->Type == 1)
772                         OneLine1(context);
773                 else 
774                         g_assert_not_reached ();
775         } else
776                 OneLineTransp(context);
777         
778         context->Lines++;
779         if (context->Lines>=context->Header.height) {
780                 context->Type = 1;
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;
787                         
788         }
789           
790
791         if (context->updated_func != NULL) {
792                 (*context->updated_func) (context->pixbuf,
793                                           0,
794                                           context->Lines % context->Header.height,
795                                           context->Header.width,
796                                           1,
797                                           context->user_data);
798
799         }
800 }
801
802 /*
803  * context - from image_begin_load
804  * buf - new image data
805  * size - length of new image data
806  *
807  * append image data onto inrecrementally built output image
808  */
809 static gboolean
810 gdk_pixbuf__ico_image_load_increment(gpointer data,
811                                      const guchar * buf,
812                                      guint size,
813                                      GError **error)
814 {
815         struct ico_progressive_state *context =
816             (struct ico_progressive_state *) data;
817
818         gint BytesToCopy;
819
820         while (size > 0) {
821                 g_assert(context->LineDone >= 0);
822                 if (context->HeaderDone < context->HeaderSize) {        /* We still 
823                                                                            have headerbytes to do */
824                         BytesToCopy =
825                             context->HeaderSize - context->HeaderDone;
826                         if (BytesToCopy > size)
827                                 BytesToCopy = size;
828
829                         memmove(context->HeaderBuf + context->HeaderDone,
830                                buf, BytesToCopy);
831
832                         size -= BytesToCopy;
833                         buf += BytesToCopy;
834                         context->HeaderDone += BytesToCopy;
835                 } 
836                 else {
837                         BytesToCopy =
838                             context->LineWidth - context->LineDone;
839                         if (BytesToCopy > size)
840                                 BytesToCopy = size;
841
842                         if (BytesToCopy > 0) {
843                                 memmove(context->LineBuf +
844                                        context->LineDone, buf,
845                                        BytesToCopy);
846
847                                 size -= BytesToCopy;
848                                 buf += BytesToCopy;
849                                 context->LineDone += BytesToCopy;
850                         }
851                         if ((context->LineDone >= context->LineWidth) &&
852                             (context->LineWidth > 0))
853                                 OneLine(context);
854
855
856                 }
857
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)
863                                 return TRUE;
864
865                         if (decode_err) {
866                                 g_propagate_error (error, decode_err);
867                                 return FALSE;
868                         }
869                 }
870         }
871
872         return TRUE;
873 }
874
875 /* saving ICOs */ 
876
877 static gint
878 write8 (FILE     *f,
879         guint8   *data,
880         gint      count)
881 {
882   gint bytes;
883   gint written;
884
885   written = 0;
886   while (count > 0)
887     {
888       bytes = fwrite ((char*) data, sizeof (char), count, f);
889       if (bytes <= 0)
890         break;
891       count -= bytes;
892       data += bytes;
893       written += bytes;
894     }
895
896   return written;
897 }
898
899 static gint
900 write16 (FILE     *f,
901          guint16  *data,
902          gint      count)
903 {
904   gint i;
905
906   for (i = 0; i < count; i++)
907           data[i] = GUINT16_TO_LE (data[i]);
908
909   return write8 (f, (guint8*) data, count * 2);
910 }
911
912 static gint
913 write32 (FILE     *f,
914          guint32  *data,
915          gint      count)
916 {
917   gint i;
918
919   for (i = 0; i < count; i++)
920           data[i] = GUINT32_TO_LE (data[i]);
921   
922   return write8 (f, (guint8*) data, count * 4);
923 }
924
925 typedef struct _IconEntry IconEntry;
926 struct _IconEntry {
927         gint width;
928         gint height;
929         gint depth;
930         gint hot_x;
931         gint hot_y;
932
933         guint8 n_colors;
934         guint32 *colors;
935         guint xor_rowstride;
936         guint8 *xor;
937         guint and_rowstride;
938         guint8 *and;
939 };
940
941 static gboolean
942 fill_entry (IconEntry *icon, 
943             GdkPixbuf *pixbuf, 
944             gint       hot_x, 
945             gint       hot_y, 
946             GError   **error) 
947  {
948         guchar *p, *pixels, *and, *xor;
949         gint n_channels, v, x, y;
950
951         if (icon->width > 255 || icon->height > 255) {
952                 g_set_error (error,
953                              GDK_PIXBUF_ERROR,
954                              GDK_PIXBUF_ERROR_BAD_OPTION,
955                              _("Image too large to be saved as ICO"));
956                 return FALSE;
957         } 
958         
959         if (hot_x > -1 && hot_y > -1) {
960                 icon->hot_x = hot_x;
961                 icon->hot_y = hot_y;
962                 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
963                         g_set_error (error,
964                                      GDK_PIXBUF_ERROR,
965                                      GDK_PIXBUF_ERROR_BAD_OPTION,
966                                      _("Cursor hotspot outside image"));
967                         return FALSE;
968                 }
969         }
970         else {
971                 icon->hot_x = -1;
972                 icon->hot_y = -1;
973         }
974         
975         switch (icon->depth) {
976         case 32:
977                 icon->xor_rowstride = icon->width * 4;
978                 break;
979         case 24:
980                 icon->xor_rowstride = icon->width * 3;
981                 break;
982         case 16:
983                 icon->xor_rowstride = icon->width * 2;
984                 break;
985         default:
986                 g_set_error (error,
987                              GDK_PIXBUF_ERROR,
988                              GDK_PIXBUF_ERROR_BAD_OPTION,
989                              _("Unsupported depth for ICO file: %d"), icon->depth);
990                 return FALSE;
991         }
992
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);
996
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);
1001
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) {
1010                         case 32:
1011                                 xor[0] = p[2];
1012                                 xor[1] = p[1];
1013                                 xor[2] = p[0];
1014                                 xor[3] = 0xff;
1015                                 if (n_channels == 4) {
1016                                         xor[3] = p[3];
1017                                         if (p[3] < 0x80)
1018                                                 *and |= 1 << (7 - x % 8);
1019                                 }
1020                                 xor += 4;
1021                                 break;
1022                         case 24:
1023                                 xor[0] = p[2];
1024                                 xor[1] = p[1];
1025                                 xor[2] = p[0];
1026                                 if (n_channels == 4 && p[3] < 0x80)
1027                                         *and |= 1 << (7 - x % 8);
1028                                 xor += 3;
1029                                 break;
1030                         case 16:
1031                                 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1032                                 xor[0] = v & 0xff;
1033                                 xor[1] = v >> 8;
1034                                 if (n_channels == 4 && p[3] < 0x80)
1035                                         *and |= 1 << (7 - x % 8);
1036                                 xor += 2;
1037                                 break;
1038                         }
1039                         
1040                         p += n_channels;
1041                         if (x % 8 == 7) 
1042                                 and++;
1043                 }
1044         }
1045
1046         return TRUE;
1047 }
1048
1049 static void
1050 free_entry (IconEntry *icon)
1051 {
1052         g_free (icon->colors);
1053         g_free (icon->and);
1054         g_free (icon->xor);
1055         g_free (icon);
1056 }
1057
1058 static void
1059 write_icon (FILE *f, GSList *entries)
1060 {
1061         IconEntry *icon;
1062         GSList *entry;
1063         guint8 bytes[4];
1064         guint16 words[4];
1065         guint32 dwords[6];
1066         gint type;
1067         gint n_entries;
1068         gint offset;
1069         gint size;
1070
1071         if (((IconEntry *)entries->data)->hot_x > -1)
1072                 type = 2;
1073         else 
1074                 type = 1;
1075         n_entries = g_slist_length (entries);
1076
1077         /* header */
1078         words[0] = 0;
1079         words[1] = type;
1080         words[2] = n_entries;
1081         write16 (f, words, 3);
1082         
1083         offset = 6 + 16 * n_entries;
1084
1085         for (entry = entries; entry; entry = entry->next) {
1086                 icon = (IconEntry *)entry->data;
1087                 size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1088                 
1089                 /* directory entry */
1090                 bytes[0] = icon->width;
1091                 bytes[1] = icon->height;
1092                 bytes[2] = icon->n_colors;
1093                 bytes[3] = 0;
1094                 write8 (f, bytes, 4);
1095                 if (type == 1) {
1096                         words[0] = 1;
1097                         words[1] = icon->depth;
1098                 }
1099                 else {
1100                         words[0] = icon->hot_x;
1101                         words[1] = icon->hot_y;
1102                 }
1103                 write16 (f, words, 2);
1104                 dwords[0] = size;
1105                 dwords[1] = offset;
1106                 write32 (f, dwords, 2);
1107
1108                 offset += size;
1109         }
1110
1111         for (entry = entries; entry; entry = entry->next) {
1112                 icon = (IconEntry *)entry->data;
1113
1114                 /* bitmap header */
1115                 dwords[0] = 40;
1116                 dwords[1] = icon->width;
1117                 dwords[2] = icon->height * 2;
1118                 write32 (f, dwords, 3);
1119                 words[0] = 1;
1120                 words[1] = icon->depth;
1121                 write16 (f, words, 2);
1122                 dwords[0] = 0;
1123                 dwords[1] = 0;
1124                 dwords[2] = 0;
1125                 dwords[3] = 0;
1126                 dwords[4] = 0;
1127                 dwords[5] = 0;
1128                 write32 (f, dwords, 6);
1129
1130                 /* image data */
1131                 write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1132                 write8 (f, icon->and, icon->and_rowstride * icon->height);
1133         }
1134 }
1135
1136 static gboolean
1137 gdk_pixbuf__ico_image_save (FILE          *f, 
1138                             GdkPixbuf     *pixbuf, 
1139                             gchar        **keys,
1140                             gchar        **values,
1141                             GError       **error)
1142 {
1143         gint hot_x, hot_y;
1144         IconEntry *icon;
1145         GSList *entries = NULL;
1146
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;
1152         hot_x = -1;
1153         hot_y = -1;
1154         
1155         /* parse options */
1156         if (keys && *keys) {
1157                 gchar **kiter;
1158                 gchar **viter;
1159                 
1160                 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1161                         char *endptr;
1162                         if (strcmp (*kiter, "depth") == 0) {
1163                                 sscanf (*viter, "%d", &icon->depth);
1164                         }
1165                         else if (strcmp (*kiter, "x_hot") == 0) {
1166                                 hot_x = strtol (*viter, &endptr, 10);
1167                         }
1168                         else if (strcmp (*kiter, "y_hot") == 0) {
1169                                 hot_y = strtol (*viter, &endptr, 10);
1170                         }
1171
1172                 }
1173         }
1174
1175         if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1176                 free_entry (icon);
1177                 return FALSE;
1178         }
1179
1180         entries = g_slist_append (entries, icon); 
1181         write_icon (f, entries);
1182
1183         g_slist_foreach (entries, (GFunc)free_entry, NULL);
1184         g_slist_free (entries);
1185
1186         return TRUE;
1187 }
1188
1189 #ifndef INCLUDE_ico
1190 #define MODULE_ENTRY(type,function) function
1191 #else
1192 #define MODULE_ENTRY(type,function) _gdk_pixbuf__ ## type ## _ ## function
1193 #endif
1194
1195 void
1196 MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
1197 {
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;
1202 }
1203
1204 void
1205 MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
1206 {
1207         static GdkPixbufModulePattern signature[] = {
1208                 { "  \x1   ", "zz znz", 100 }, 
1209                 { "  \x2   ", "zz znz", 100 },
1210                 { NULL, NULL, 0 }
1211         };
1212         static gchar * mime_types[] = {
1213                 "image/x-icon",
1214                 NULL
1215         };
1216         static gchar * extensions[] = {
1217                 "ico",
1218                 "cur",
1219                 NULL
1220         };
1221
1222         info->name = "ico";
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";
1229 }
1230
1231
1232
1233