]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-ico.c
Remove GLIB_DIVERT_BEFORE_HELP() on initialization of gdktarget, that was
[~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>State->BytesInHeaderBuf) {
266                 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
267                 if (!tmp) {
268                         g_set_error (error,
269                                      GDK_PIXBUF_ERROR,
270                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
271                                      _("Not enough memory to load icon"));
272                         return;
273                 }
274                 State->HeaderBuf = tmp;
275                 State->BytesInHeaderBuf = State->HeaderSize;
276         }
277         if (Bytes<State->HeaderSize) 
278                 return;   
279         
280         BIH = Data+State->DIBoffset;
281
282 #ifdef DUMPBIH
283         DumpBIH(BIH);
284 #endif  
285         /* Add the palette to the headersize */
286                 
287         State->Header.width =
288             (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
289         if (State->Header.width == 0) {
290                 g_set_error (error,
291                              GDK_PIXBUF_ERROR,
292                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
293                              _("Icon has zero width"));
294                 return;
295         }
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) {
300                 g_set_error (error,
301                              GDK_PIXBUF_ERROR,
302                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
303                              _("Icon has zero height"));
304                 return;
305         }
306         State->Header.depth = (BIH[15] << 8) + (BIH[14]);
307
308         State->Type = State->Header.depth;      
309         if (State->Lines>=State->Header.height)
310                 State->Type = 1; /* The transparency mask is 1 bpp */
311         
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]);
316         I = I*4;
317         if ((I==0)&&(State->Type==1))
318                 I = 2*4;
319         if ((I==0)&&(State->Type==4))
320                 I = 16*4;
321         if ((I==0)&&(State->Type==8))
322                 I = 256*4;
323         
324         State->HeaderSize+=I;
325         
326         if (State->HeaderSize < 0) {
327                 g_set_error (error,
328                              GDK_PIXBUF_ERROR,
329                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
330                              _("Invalid header in icon"));
331                 return;
332         }
333
334         if (State->HeaderSize>State->BytesInHeaderBuf) {
335                 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
336                 if (!tmp) {
337                         g_set_error (error,
338                                      GDK_PIXBUF_ERROR,
339                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
340                                      _("Not enough memory to load icon"));
341                         return;
342                 }
343                 State->HeaderBuf = tmp;
344                 State->BytesInHeaderBuf = State->HeaderSize;
345         }
346         if (Bytes < State->HeaderSize)
347                 return;
348
349         if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
350             || (BIH[19] != 0)) {
351                 /* FIXME: is this the correct message? */
352                 g_set_error (error,
353                              GDK_PIXBUF_ERROR,
354                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
355                              _("Compressed icons are not supported"));
356                 return;
357         }
358
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;
363         }
364         if (State->Header.width < 0) {
365                 State->Header.width = -State->Header.width;
366         }
367         g_assert (State->Header.width > 0);
368         g_assert (State->Header.height > 0);
369
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)
383                         State->LineWidth++;
384         } else {
385           g_set_error (error,
386                        GDK_PIXBUF_ERROR,
387                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
388                        _("Unsupported icon type"));
389           return;
390         }
391
392         /* Pad to a 32 bit boundary */
393         if (((State->LineWidth % 4) > 0))
394                 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
395
396
397         if (State->LineBuf == NULL) {
398                 State->LineBuf = g_try_malloc(State->LineWidth);
399                 if (!State->LineBuf) {
400                         g_set_error (error,
401                                      GDK_PIXBUF_ERROR,
402                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
403                                      _("Not enough memory to load icon"));
404                         return;
405                 }
406         }
407
408         g_assert(State->LineBuf != NULL);
409
410
411         if (State->pixbuf == NULL) {
412 #if 1
413                 if (State->size_func) {
414                         gint width = State->Header.width;
415                         gint height = State->Header.height;
416
417                         (*State->size_func) (&width, &height, State->user_data);
418                         if (width == 0 || height == 0) {
419                                 State->LineWidth = 0;
420                                 return;
421                         }
422                 }
423 #endif
424
425                 State->pixbuf =
426                     gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
427                                    State->Header.width,
428                                    State->Header.height);
429                 if (!State->pixbuf) {
430                         g_set_error (error,
431                                      GDK_PIXBUF_ERROR,
432                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
433                                      _("Not enough memory to load icon"));
434                         return;
435                 }
436                 if (State->cursor) {
437                         gchar hot[10];
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);
442                 }
443
444                 if (State->prepared_func != NULL)
445                         /* Notify the client that we are ready to go */
446                         (*State->prepared_func) (State->pixbuf,
447                                                  NULL,
448                                                  State->user_data);
449
450         }
451
452 }
453
454 /* 
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)
458  */
459
460 static gpointer
461 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
462                                  GdkPixbufModulePreparedFunc prepared_func,
463                                  GdkPixbufModuleUpdatedFunc updated_func,
464                                  gpointer user_data,
465                                  GError **error)
466 {
467         struct ico_progressive_state *context;
468
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;
474
475         context->HeaderSize = 54;
476         context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
477         if (!context->HeaderBuf) {
478                 g_free (context);
479                 g_set_error (error,
480                              GDK_PIXBUF_ERROR,
481                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
482                              _("Not enough memory to load ICO file"));
483                 return NULL;
484         }
485         /* 4*256 for the colormap */
486         context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
487         context->HeaderDone = 0;
488
489         context->LineWidth = 0;
490         context->LineBuf = NULL;
491         context->LineDone = 0;
492         context->Lines = 0;
493
494         context->Type = 0;
495
496         memset(&context->Header, 0, sizeof(struct headerpair));
497
498
499         context->pixbuf = NULL;
500
501
502         return (gpointer) context;
503 }
504
505 /*
506  * context - returned from image_begin_load
507  *
508  * free context, unref gdk_pixbuf
509  */
510 static gboolean 
511 gdk_pixbuf__ico_image_stop_load(gpointer data,
512                                 GError **error)
513 {
514         struct ico_progressive_state *context =
515             (struct ico_progressive_state *) data;
516
517         /* FIXME this thing needs to report errors if
518          * we have unused image data
519          */
520
521         g_return_val_if_fail(context != NULL, TRUE);
522
523         context_free (context);
524         return TRUE;
525 }
526
527 static void
528 OneLine32 (struct ico_progressive_state *context)
529 {
530         gint X;
531         guchar *Pixels;
532
533         X = 0;
534         if (context->Header.Negative == 0)
535                 Pixels = (context->pixbuf->pixels +
536                           context->pixbuf->rowstride *
537                           (context->Header.height - context->Lines - 1));
538         else
539                 Pixels = (context->pixbuf->pixels +
540                           context->pixbuf->rowstride *
541                           context->Lines);
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];
547                 X++;
548         }
549 }
550
551 static void OneLine24(struct ico_progressive_state *context)
552 {
553         gint X;
554         guchar *Pixels;
555
556         X = 0;
557         if (context->Header.Negative == 0)
558                 Pixels = (context->pixbuf->pixels +
559                           context->pixbuf->rowstride *
560                           (context->Header.height - context->Lines - 1));
561         else
562                 Pixels = (context->pixbuf->pixels +
563                           context->pixbuf->rowstride *
564                           context->Lines);
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];
569                 X++;
570         }
571
572 }
573
574 static void
575 OneLine16 (struct ico_progressive_state *context)
576 {
577         int i;
578         guchar *pixels;
579         guchar *src;
580
581         if (context->Header.Negative == 0)
582                 pixels = (context->pixbuf->pixels +
583                           context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
584         else
585                 pixels = (context->pixbuf->pixels +
586                           context->pixbuf->rowstride * context->Lines);
587
588         src = context->LineBuf;
589
590         for (i = 0; i < context->Header.width; i++) {
591                 int v, r, g, b;
592
593                 v = (int) src[0] | ((int) src[1] << 8);
594                 src += 2;
595
596                 /* Extract 5-bit RGB values */
597
598                 r = (v >> 10) & 0x1f;
599                 g = (v >> 5) & 0x1f;
600                 b = v & 0x1f;
601
602                 /* Fill the rightmost bits to form 8-bit values */
603
604                 *pixels++ = (r << 3) | (r >> 2);
605                 *pixels++ = (g << 3) | (g >> 2);
606                 *pixels++ = (b << 3) | (b >> 2);
607                 pixels++; /* skip alpha channel */
608         }
609 }
610
611
612 static void OneLine8(struct ico_progressive_state *context)
613 {
614         gint X;
615         guchar *Pixels;
616
617         X = 0;
618         if (context->Header.Negative == 0)
619                 Pixels = (context->pixbuf->pixels +
620                           context->pixbuf->rowstride *
621                           (context->Header.height - context->Lines - 1));
622         else
623                 Pixels = (context->pixbuf->pixels +
624                           context->pixbuf->rowstride *
625                           context->Lines);
626         while (X < context->Header.width) {
627                 /* The joys of having a BGR byteorder */
628                 Pixels[X * 4 + 0] =
629                     context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
630                 Pixels[X * 4 + 1] =
631                     context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
632                 Pixels[X * 4 + 2] =
633                     context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
634                 X++;
635         }
636 }
637 static void OneLine4(struct ico_progressive_state *context)
638 {
639         gint X;
640         guchar *Pixels;
641
642         X = 0;
643         if (context->Header.Negative == 0)
644                 Pixels = (context->pixbuf->pixels +
645                           context->pixbuf->rowstride *
646                           (context->Header.height - context->Lines - 1));
647         else
648                 Pixels = (context->pixbuf->pixels +
649                           context->pixbuf->rowstride *
650                           context->Lines);
651         
652         while (X < context->Header.width) {
653                 guchar Pix;
654                 
655                 Pix = context->LineBuf[X/2];
656
657                 Pixels[X * 4 + 0] =
658                     context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
659                 Pixels[X * 4 + 1] =
660                     context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
661                 Pixels[X * 4 + 2] =
662                     context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
663                 X++;
664                 if (X<context->Header.width) { 
665                         /* Handle the other 4 bit pixel only when there is one */
666                         Pixels[X * 4 + 0] =
667                             context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
668                         Pixels[X * 4 + 1] =
669                             context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
670                         Pixels[X * 4 + 2] =
671                             context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
672                         X++;
673                 }
674         }
675         
676 }
677
678 static void OneLine1(struct ico_progressive_state *context)
679 {
680         gint X;
681         guchar *Pixels;
682
683         X = 0;
684         if (context->Header.Negative == 0)
685                 Pixels = (context->pixbuf->pixels +
686                           context->pixbuf->rowstride *
687                           (context->Header.height - context->Lines - 1));
688         else
689                 Pixels = (context->pixbuf->pixels +
690                           context->pixbuf->rowstride *
691                           context->Lines);
692         while (X < context->Header.width) {
693                 int Bit;
694
695                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
696                 Bit = Bit & 1;
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;
701                 X++;
702         }
703 }
704
705 static void OneLineTransp(struct ico_progressive_state *context)
706 {
707         gint X;
708         guchar *Pixels;
709
710         /* Ignore the XOR mask for XP style 32-bpp icons with alpha */ 
711         if (context->Header.depth == 32)
712                 return;
713
714         X = 0;
715         if (context->Header.Negative == 0)
716                 Pixels = (context->pixbuf->pixels +
717                           context->pixbuf->rowstride *
718                           (2*context->Header.height - context->Lines - 1));
719         else
720                 Pixels = (context->pixbuf->pixels +
721                           context->pixbuf->rowstride *
722                           (context->Lines-context->Header.height));
723         while (X < context->Header.width) {
724                 int Bit;
725
726                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
727                 Bit = Bit & 1;
728                 /* The joys of having a BGR byteorder */
729                 Pixels[X * 4 + 3] = 255-Bit*255;
730 #if 0
731                 if (Bit){
732                   Pixels[X*4+0] = 255;
733                   Pixels[X*4+1] = 255;
734                 } else {
735                   Pixels[X*4+0] = 0;
736                   Pixels[X*4+1] = 0;
737                 }
738 #endif          
739                 X++;
740         }
741 }
742
743
744 static void OneLine(struct ico_progressive_state *context)
745 {
746         context->LineDone = 0;
747         
748         if (context->Lines >= context->Header.height*2) {
749                 return;
750         }
751                 
752         if (context->Lines <context->Header.height) {           
753                 if (context->Type == 32)
754                         OneLine32 (context);
755                 else if (context->Type == 24)
756                         OneLine24(context);
757                 else if (context->Type == 16)
758                         OneLine16 (context);
759                 else if (context->Type == 8)
760                         OneLine8(context);
761                 else if (context->Type == 4)
762                         OneLine4(context);
763                 else if (context->Type == 1)
764                         OneLine1(context);
765                 else 
766                         g_assert_not_reached ();
767         } else
768                 OneLineTransp(context);
769         
770         context->Lines++;
771         if (context->Lines>=context->Header.height) {
772                 context->Type = 1;
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;
779                         
780         }
781           
782
783         if (context->updated_func != NULL) {
784                 (*context->updated_func) (context->pixbuf,
785                                           0,
786                                           context->Lines % context->Header.height,
787                                           context->Header.width,
788                                           1,
789                                           context->user_data);
790
791         }
792 }
793
794 /*
795  * context - from image_begin_load
796  * buf - new image data
797  * size - length of new image data
798  *
799  * append image data onto inrecrementally built output image
800  */
801 static gboolean
802 gdk_pixbuf__ico_image_load_increment(gpointer data,
803                                      const guchar * buf,
804                                      guint size,
805                                      GError **error)
806 {
807         struct ico_progressive_state *context =
808             (struct ico_progressive_state *) data;
809
810         gint BytesToCopy;
811
812         while (size > 0) {
813                 g_assert(context->LineDone >= 0);
814                 if (context->HeaderDone < context->HeaderSize) {        /* We still 
815                                                                            have headerbytes to do */
816                         BytesToCopy =
817                             context->HeaderSize - context->HeaderDone;
818                         if (BytesToCopy > size)
819                                 BytesToCopy = size;
820
821                         memmove(context->HeaderBuf + context->HeaderDone,
822                                buf, BytesToCopy);
823
824                         size -= BytesToCopy;
825                         buf += BytesToCopy;
826                         context->HeaderDone += BytesToCopy;
827                 } 
828                 else {
829                         BytesToCopy =
830                             context->LineWidth - context->LineDone;
831                         if (BytesToCopy > size)
832                                 BytesToCopy = size;
833
834                         if (BytesToCopy > 0) {
835                                 memmove(context->LineBuf +
836                                        context->LineDone, buf,
837                                        BytesToCopy);
838
839                                 size -= BytesToCopy;
840                                 buf += BytesToCopy;
841                                 context->LineDone += BytesToCopy;
842                         }
843                         if ((context->LineDone >= context->LineWidth) &&
844                             (context->LineWidth > 0))
845                                 OneLine(context);
846
847
848                 }
849
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)
855                                 return TRUE;
856
857                         if (decode_err) {
858                                 g_propagate_error (error, decode_err);
859                                 return FALSE;
860                         }
861                 }
862         }
863
864         return TRUE;
865 }
866
867 /* saving ICOs */ 
868
869 static gint
870 write8 (FILE     *f,
871         guint8   *data,
872         gint      count)
873 {
874   gint bytes;
875   gint written;
876
877   written = 0;
878   while (count > 0)
879     {
880       bytes = fwrite ((char*) data, sizeof (char), count, f);
881       if (bytes <= 0)
882         break;
883       count -= bytes;
884       data += bytes;
885       written += bytes;
886     }
887
888   return written;
889 }
890
891 static gint
892 write16 (FILE     *f,
893          guint16  *data,
894          gint      count)
895 {
896   gint i;
897
898   for (i = 0; i < count; i++)
899           data[i] = GUINT16_TO_LE (data[i]);
900
901   return write8 (f, (guint8*) data, count * 2);
902 }
903
904 static gint
905 write32 (FILE     *f,
906          guint32  *data,
907          gint      count)
908 {
909   gint i;
910
911   for (i = 0; i < count; i++)
912           data[i] = GUINT32_TO_LE (data[i]);
913   
914   return write8 (f, (guint8*) data, count * 4);
915 }
916
917 typedef struct _IconEntry IconEntry;
918 struct _IconEntry {
919         gint width;
920         gint height;
921         gint depth;
922         gint hot_x;
923         gint hot_y;
924
925         guint8 n_colors;
926         guint32 *colors;
927         guint xor_rowstride;
928         guint8 *xor;
929         guint and_rowstride;
930         guint8 *and;
931 };
932
933 static gboolean
934 fill_entry (IconEntry *icon, 
935             GdkPixbuf *pixbuf, 
936             gint       hot_x, 
937             gint       hot_y, 
938             GError   **error) 
939  {
940         guchar *p, *pixels, *and, *xor;
941         gint n_channels, v, x, y;
942
943         if (icon->width > 255 || icon->height > 255) {
944                 g_set_error (error,
945                              GDK_PIXBUF_ERROR,
946                              GDK_PIXBUF_ERROR_BAD_OPTION,
947                              _("Image too large to be saved as ICO"));
948                 return FALSE;
949         } 
950         
951         if (hot_x > -1 && hot_y > -1) {
952                 icon->hot_x = hot_x;
953                 icon->hot_x = hot_y;
954                 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
955                         g_set_error (error,
956                                      GDK_PIXBUF_ERROR,
957                                      GDK_PIXBUF_ERROR_BAD_OPTION,
958                                      _("Cursor hotspot outside image"));
959                         return FALSE;
960                 }
961         }
962         else {
963                 icon->hot_x = -1;
964                 icon->hot_y = -1;
965         }
966         
967         switch (icon->depth) {
968         case 32:
969                 icon->xor_rowstride = icon->width * 4;
970                 break;
971         case 24:
972                 icon->xor_rowstride = icon->width * 3;
973                 break;
974         case 16:
975                 icon->xor_rowstride = icon->width * 2;
976                 break;
977         default:
978                 g_set_error (error,
979                              GDK_PIXBUF_ERROR,
980                              GDK_PIXBUF_ERROR_BAD_OPTION,
981                              _("Unsupported depth for ICO file: %d"), icon->depth);
982                 return FALSE;
983         }
984
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);
988
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);
993
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) {
1002                         case 32:
1003                                 xor[0] = p[2];
1004                                 xor[1] = p[1];
1005                                 xor[2] = p[0];
1006                                 xor[3] = 0xff;
1007                                 if (n_channels == 4) {
1008                                         xor[3] = p[3];
1009                                         if (p[3] < 0x80)
1010                                                 *and |= 1 << (7 - x % 8);
1011                                 }
1012                                 xor += 4;
1013                                 break;
1014                         case 24:
1015                                 xor[0] = p[2];
1016                                 xor[1] = p[1];
1017                                 xor[2] = p[0];
1018                                 if (n_channels == 4 && p[3] < 0x80)
1019                                         *and |= 1 << (7 - x % 8);
1020                                 xor += 3;
1021                                 break;
1022                         case 16:
1023                                 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1024                                 xor[0] = v & 0xff;
1025                                 xor[1] = v >> 8;
1026                                 if (n_channels == 4 && p[3] < 0x80)
1027                                         *and |= 1 << (7 - x % 8);
1028                                 xor += 2;
1029                                 break;
1030                         }
1031                         
1032                         p += n_channels;
1033                         if (x % 8 == 7) 
1034                                 and++;
1035                 }
1036         }
1037
1038         return TRUE;
1039 }
1040
1041 static void
1042 free_entry (IconEntry *icon)
1043 {
1044         g_free (icon->colors);
1045         g_free (icon->and);
1046         g_free (icon->xor);
1047         g_free (icon);
1048 }
1049
1050 static void
1051 write_icon (FILE *f, GSList *entries)
1052 {
1053         IconEntry *icon;
1054         GSList *entry;
1055         guint8 bytes[4];
1056         guint16 words[4];
1057         guint32 dwords[6];
1058         gint type;
1059         gint n_entries;
1060         gint offset;
1061         gint size;
1062
1063         if (((IconEntry *)entries->data)->hot_x > -1)
1064                 type = 2;
1065         else 
1066                 type = 1;
1067         n_entries = g_slist_length (entries);
1068
1069         /* header */
1070         words[0] = 0;
1071         words[1] = type;
1072         words[2] = n_entries;
1073         write16 (f, words, 3);
1074         
1075         offset = 6 + 16 * n_entries;
1076
1077         for (entry = entries; entry; entry = entry->next) {
1078                 icon = (IconEntry *)entry->data;
1079                 size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1080                 
1081                 /* directory entry */
1082                 bytes[0] = icon->width;
1083                 bytes[1] = icon->height;
1084                 bytes[2] = icon->n_colors;
1085                 bytes[3] = 0;
1086                 write8 (f, bytes, 4);
1087                 if (type == 1) {
1088                         words[0] = 1;
1089                         words[1] = icon->depth;
1090                 }
1091                 else {
1092                         words[0] = icon->hot_x;
1093                         words[1] = icon->hot_y;
1094                 }
1095                 write16 (f, words, 2);
1096                 dwords[0] = size;
1097                 dwords[1] = offset;
1098                 write32 (f, dwords, 2);
1099
1100                 offset += size;
1101         }
1102
1103         for (entry = entries; entry; entry = entry->next) {
1104                 icon = (IconEntry *)entry->data;
1105
1106                 /* bitmap header */
1107                 dwords[0] = 40;
1108                 dwords[1] = icon->width;
1109                 dwords[2] = icon->height * 2;
1110                 write32 (f, dwords, 3);
1111                 words[0] = 1;
1112                 words[1] = icon->depth;
1113                 write16 (f, words, 2);
1114                 dwords[0] = 0;
1115                 dwords[1] = 0;
1116                 dwords[2] = 0;
1117                 dwords[3] = 0;
1118                 dwords[4] = 0;
1119                 dwords[5] = 0;
1120                 write32 (f, dwords, 6);
1121
1122                 /* image data */
1123                 write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1124                 write8 (f, icon->and, icon->and_rowstride * icon->height);
1125         }
1126 }
1127
1128 static gboolean
1129 gdk_pixbuf__ico_image_save (FILE          *f, 
1130                             GdkPixbuf     *pixbuf, 
1131                             gchar        **keys,
1132                             gchar        **values,
1133                             GError       **error)
1134 {
1135         gint hot_x, hot_y;
1136         IconEntry *icon;
1137         GSList *entries = NULL;
1138
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;
1144         hot_x = -1;
1145         hot_y = -1;
1146         
1147         /* parse options */
1148         if (keys && *keys) {
1149                 gchar **kiter;
1150                 gchar **viter;
1151                 
1152                 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1153                         char *endptr;
1154                         if (strcmp (*kiter, "depth") == 0) {
1155                                 sscanf (*viter, "%d", &icon->depth);
1156                         }
1157                         else if (strcmp (*kiter, "x_hot") == 0) {
1158                                 hot_x = strtol (*viter, &endptr, 10);
1159                         }
1160                         else if (strcmp (*kiter, "y_hot") == 0) {
1161                                 hot_y = strtol (*viter, &endptr, 10);
1162                         }
1163
1164                 }
1165         }
1166
1167         if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1168                 free_entry (icon);
1169                 return FALSE;
1170         }
1171
1172         entries = g_slist_append (entries, icon); 
1173         write_icon (f, entries);
1174
1175         g_slist_foreach (entries, (GFunc)free_entry, NULL);
1176         g_slist_free (entries);
1177
1178         return TRUE;
1179 }
1180
1181 void
1182 MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
1183 {
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;
1188 }
1189
1190 void
1191 MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
1192 {
1193         static GdkPixbufModulePattern signature[] = {
1194                 { "  \x1   ", "zz znz", 100 }, 
1195                 { "  \x2   ", "zz znz", 100 },
1196                 { NULL, NULL, 0 }
1197         };
1198         static gchar * mime_types[] = {
1199                 "image/x-icon",
1200                 NULL
1201         };
1202         static gchar * extensions[] = {
1203                 "ico",
1204                 "cur",
1205                 NULL
1206         };
1207
1208         info->name = "ico";
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";
1215 }
1216
1217
1218
1219