]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-ico.c
Some updates
[~andy/gtk] / gdk-pixbuf / io-ico.c
1 /* GdkPixbuf library - Windows Icon/Cursor image loader
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
6  *          Federico Mena-Quintero <federico@gimp.org>
7  *
8  * Based on io-bmp.c
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #undef DUMPBIH
27 /*
28
29 Icons are just like BMP's, except for the header.
30  
31 Known bugs:
32         * bi-tonal files aren't tested 
33
34 */
35
36 #include <config.h>
37 #include <stdio.h>
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include <string.h>
42 #include "gdk-pixbuf-private.h"
43 #include "gdk-pixbuf-io.h"
44
45 \f
46
47 /* 
48
49 These structures are actually dummies. These are according to
50 the "Windows API reference guide volume II" as written by 
51 Borland International, but GCC fiddles with the alignment of
52 the internal members.
53
54 */
55
56 struct BitmapFileHeader {
57         gushort bfType;
58         guint bfSize;
59         guint reserverd;
60         guint bfOffbits;
61 };
62
63 struct BitmapInfoHeader {
64         guint biSize;
65         guint biWidth;
66         guint biHeight;
67         gushort biPlanes;
68         gushort biBitCount;
69         guint biCompression;
70         guint biSizeImage;
71         guint biXPelsPerMeter;
72         guint biYPelsPerMeter;
73         guint biClrUsed;
74         guint biClrImportant;
75 };
76
77 #ifdef DUMPBIH
78 /* 
79
80 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
81 debugging purposes.
82
83 */
84 static void DumpBIH(unsigned char *BIH)
85 {                               
86         printf("biSize      = %i \n",
87                (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
88         printf("biWidth     = %i \n",
89                (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
90         printf("biHeight    = %i \n",
91                (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
92                (BIH[8]));
93         printf("biPlanes    = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
94         printf("biBitCount  = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
95         printf("biCompress  = %i \n",
96                (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
97                (BIH[16]));
98         printf("biSizeImage = %i \n",
99                (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
100                (BIH[20]));
101         printf("biXPels     = %i \n",
102                (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
103                (BIH[24]));
104         printf("biYPels     = %i \n",
105                (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
106                (BIH[28]));
107         printf("biClrUsed   = %i \n",
108                (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
109                (BIH[32]));
110         printf("biClrImprtnt= %i \n",
111                (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
112                (BIH[36]));
113 }
114 #endif
115
116 /* Progressive loading */
117 struct headerpair {
118         guint width;
119         guint height;
120         guint depth;
121         guint Negative;         /* Negative = 1 -> top down BMP,  
122                                    Negative = 0 -> bottom up BMP */
123 };
124
125 struct ico_progressive_state {
126         ModulePreparedNotifyFunc prepared_func;
127         ModuleUpdatedNotifyFunc updated_func;
128         gpointer user_data;
129
130         gint HeaderSize;        /* The size of the header-part (incl colormap) */
131         guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
132         gint BytesInHeaderBuf;  /* The size of the allocated HeaderBuf */
133         gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
134
135         gint LineWidth;         /* The width of a line in bytes */
136         guchar *LineBuf;        /* Buffer for 1 line */
137         gint LineDone;          /* # of bytes in LineBuf */
138         gint Lines;             /* # of finished lines */
139
140         gint Type;              /*  
141                                    24 = RGB
142                                    8 = 8 bit colormapped
143                                    1  = 1 bit bitonal 
144                                  */
145
146
147         struct headerpair Header;       /* Decoded (BE->CPU) header */
148         
149         gint                    DIBoffset;
150         gint                    ImageScore;
151
152
153         GdkPixbuf *pixbuf;      /* Our "target" */
154 };
155
156 gpointer
157 gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
158                                  ModuleUpdatedNotifyFunc updated_func,
159                                  ModuleFrameDoneNotifyFunc frame_done_func,
160                                  ModuleAnimationDoneNotifyFunc anim_done_func,
161                                  gpointer user_data,
162                                  GError **error);
163 void gdk_pixbuf__ico_image_stop_load(gpointer data);
164 gboolean gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size,
165                                               GError **error);
166
167
168
169 /* Shared library entry point --> Can go when generic_image_load
170    enters gdk-pixbuf-io */
171 GdkPixbuf *
172 gdk_pixbuf__ico_image_load(FILE * f, GError **error)
173 {
174         guchar *membuf;
175         size_t length;
176         struct ico_progressive_state *State;
177
178         GdkPixbuf *pb;
179
180         State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL,
181                                                  NULL, NULL, error);
182         if (State == NULL)
183           return NULL;
184         
185         membuf = g_malloc(4096);
186
187         g_assert(membuf != NULL);
188
189         while (feof(f) == 0) {
190                 length = fread(membuf, 1, 4096, f);
191                 if (length > 0)
192                         if (!gdk_pixbuf__ico_image_load_increment(State, membuf, length,
193                                                                   error)) {
194                                 gdk_pixbuf__ico_image_stop_load (State);
195                                 return NULL;
196                         }
197         }
198         g_free(membuf);
199         if (State->pixbuf != NULL)
200                 gdk_pixbuf_ref(State->pixbuf);
201
202         pb = State->pixbuf;
203
204         gdk_pixbuf__ico_image_stop_load(State);
205         return pb;
206 }
207
208 static void DecodeHeader(guchar *Data, gint Bytes,
209                          struct ico_progressive_state *State)
210 {
211 /* For ICO's we have to be very clever. There are multiple images possible
212    in an .ICO. For now, we select (in order of priority):
213      1) The one with the highest number of colors
214      2) The largest one
215  */   
216  
217         gint IconCount = 0; /* The number of icon-versions in the file */
218         guchar *BIH; /* The DIB for the used icon */
219         guchar *Ptr;
220         gint I;
221  
222         /* Step 1: The ICO header */
223  
224         IconCount = (Data[5] << 8) + (Data[4]);
225  
226         State->HeaderSize = 6 + IconCount*16;
227         
228         if (State->HeaderSize>State->BytesInHeaderBuf) {
229                 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
230                 State->BytesInHeaderBuf = State->HeaderSize;
231         }
232         if (Bytes < State->HeaderSize)
233                 return;
234         
235         /* We now have all the "short-specs" of the versions 
236            So we iterate through them and select the best one */
237            
238         State->ImageScore = 0;
239         State->DIBoffset  = 0;
240         Ptr = Data + 6;
241         for (I=0;I<IconCount;I++) {
242                 int ThisWidth, ThisHeight,ThisColors;
243                 int ThisScore;
244                 
245                 ThisWidth = Ptr[0];
246                 ThisHeight = Ptr[1];
247                 ThisColors = (Ptr[2]);
248                 if (ThisColors==0) 
249                         ThisColors=256; /* Yes, this is in the spec, ugh */
250                 
251                 ThisScore = ThisColors*1024+ThisWidth*ThisHeight; 
252
253                 if (ThisScore>State->ImageScore) {
254                         State->ImageScore = ThisScore;
255                         State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
256                                            (Ptr[13]<<8) + (Ptr[12]);
257                                                                  
258                 }
259                 
260                 
261                 Ptr += 16;      
262         } 
263                 
264         /* We now have a winner, pointed to in State->DIBoffset,
265            so we know how many bytes are in the "header" part. */
266               
267         State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
268         
269         if (State->HeaderSize>State->BytesInHeaderBuf) {
270                 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
271                 State->BytesInHeaderBuf = State->HeaderSize;
272         }
273         if (Bytes<State->HeaderSize) 
274                 return;   
275         
276         BIH = Data+State->DIBoffset;
277
278 #ifdef DUMPBIH
279         DumpBIH(BIH);
280 #endif  
281         
282         /* Add the palette to the headersize */
283                 
284         State->Header.width =
285             (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
286         State->Header.height =
287             (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8])/2;
288             /* /2 because the BIH height includes the transparency mask */
289         State->Header.depth = (BIH[15] << 8) + (BIH[14]);;
290
291         State->Type = State->Header.depth;      
292         if (State->Lines>=State->Header.height)
293                 State->Type = 1; /* The transparency mask is 1 bpp */
294         
295         
296         
297         /* Determine the  palette size. If the header indicates 0, it
298            is actually the maximum for the bpp. You have to love the
299            guys who made the spec. */
300         I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
301         I = I*4;
302         if ((I==0)&&(State->Type==1))
303                 I = 2*4;
304         if ((I==0)&&(State->Type==4))
305                 I = 16*4;
306         if ((I==0)&&(State->Type==8))
307                 I = 256*4;
308         
309         State->HeaderSize+=I;
310         
311         if (State->HeaderSize>State->BytesInHeaderBuf) {
312                 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
313                 State->BytesInHeaderBuf = State->HeaderSize;
314         }
315         if (Bytes < State->HeaderSize)
316                 return;
317
318         if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
319             || (BIH[19] != 0)) {
320                 g_assert(0); /* Compressed icons aren't allowed */
321         }
322
323         /* Negative heights mean top-down pixel-order */
324         if (State->Header.height < 0) {
325                 State->Header.height = -State->Header.height;
326                 State->Header.Negative = 1;
327         }
328         if (State->Header.width < 0) {
329                 State->Header.width = -State->Header.width;
330         }
331
332         if (State->Type == 24)
333                 State->LineWidth = State->Header.width * 3;
334         if (State->Type == 8)
335                 State->LineWidth = State->Header.width * 1;
336         if (State->Type == 4) {
337                 State->LineWidth = (State->Header.width+1)/2;
338         }
339         if (State->Type == 1) {
340                 State->LineWidth = State->Header.width / 8;
341                 if ((State->Header.width & 7) != 0)
342                         State->LineWidth++;
343         }
344
345         /* Pad to a 32 bit boundary */
346         if (((State->LineWidth % 4) > 0))
347                 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
348
349
350         if (State->LineBuf == NULL)
351                 State->LineBuf = g_malloc(State->LineWidth);
352
353         g_assert(State->LineBuf != NULL);
354
355
356         if (State->pixbuf == NULL) {
357                 State->pixbuf =
358                     gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
359                                    (gint) State->Header.width,
360                                    (gint) State->Header.height);
361
362                 if (State->prepared_func != NULL)
363                         /* Notify the client that we are ready to go */
364                         (*State->prepared_func) (State->pixbuf,
365                                                  State->user_data);
366
367         }
368
369 }
370
371 /* 
372  * func - called when we have pixmap created (but no image data)
373  * user_data - passed as arg 1 to func
374  * return context (opaque to user)
375  */
376
377 gpointer
378 gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
379                                  ModuleUpdatedNotifyFunc updated_func,
380                                  ModuleFrameDoneNotifyFunc frame_done_func,
381                                  ModuleAnimationDoneNotifyFunc anim_done_func,
382                                  gpointer user_data,
383                                  GError **error)
384 {
385         struct ico_progressive_state *context;
386
387         context = g_new0(struct ico_progressive_state, 1);
388         context->prepared_func = prepared_func;
389         context->updated_func = updated_func;
390         context->user_data = user_data;
391
392         context->HeaderSize = 54;
393         context->HeaderBuf = g_malloc(14 + 40 + 4*256 + 512);
394         /* 4*256 for the colormap */
395         context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
396         context->HeaderDone = 0;
397
398         context->LineWidth = 0;
399         context->LineBuf = NULL;
400         context->LineDone = 0;
401         context->Lines = 0;
402
403         context->Type = 0;
404
405         memset(&context->Header, 0, sizeof(struct headerpair));
406
407
408         context->pixbuf = NULL;
409
410
411         return (gpointer) context;
412 }
413
414 /*
415  * context - returned from image_begin_load
416  *
417  * free context, unref gdk_pixbuf
418  */
419 void gdk_pixbuf__ico_image_stop_load(gpointer data)
420 {
421         struct ico_progressive_state *context =
422             (struct ico_progressive_state *) data;
423
424
425         g_return_if_fail(context != NULL);
426
427         if (context->LineBuf != NULL)
428                 g_free(context->LineBuf);
429         context->LineBuf = NULL;
430         if (context->HeaderBuf != NULL)
431                 g_free(context->HeaderBuf);
432
433         if (context->pixbuf)
434                 gdk_pixbuf_unref(context->pixbuf);
435
436         g_free(context);
437 }
438
439
440 static void OneLine24(struct ico_progressive_state *context)
441 {
442         gint X;
443         guchar *Pixels;
444
445         X = 0;
446         if (context->Header.Negative == 0)
447                 Pixels = (context->pixbuf->pixels +
448                           context->pixbuf->rowstride *
449                           (context->Header.height - context->Lines - 1));
450         else
451                 Pixels = (context->pixbuf->pixels +
452                           context->pixbuf->rowstride *
453                           context->Lines);
454         while (X < context->Header.width) {
455                 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
456                 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
457                 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
458                 X++;
459         }
460
461 }
462
463 static void OneLine8(struct ico_progressive_state *context)
464 {
465         gint X;
466         guchar *Pixels;
467
468         X = 0;
469         if (context->Header.Negative == 0)
470                 Pixels = (context->pixbuf->pixels +
471                           context->pixbuf->rowstride *
472                           (context->Header.height - context->Lines - 1));
473         else
474                 Pixels = (context->pixbuf->pixels +
475                           context->pixbuf->rowstride *
476                           context->Lines);
477         while (X < context->Header.width) {
478                 /* The joys of having a BGR byteorder */
479                 Pixels[X * 4 + 0] =
480                     context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
481                 Pixels[X * 4 + 1] =
482                     context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
483                 Pixels[X * 4 + 2] =
484                     context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
485                 X++;
486         }
487 }
488 static void OneLine4(struct ico_progressive_state *context)
489 {
490         gint X;
491         guchar *Pixels;
492
493         X = 0;
494         if (context->Header.Negative == 0)
495                 Pixels = (context->pixbuf->pixels +
496                           context->pixbuf->rowstride *
497                           (context->Header.height - context->Lines - 1));
498         else
499                 Pixels = (context->pixbuf->pixels +
500                           context->pixbuf->rowstride *
501                           context->Lines);
502         
503         while (X < context->Header.width) {
504                 guchar Pix;
505                 
506                 Pix = context->LineBuf[X/2];
507
508                 Pixels[X * 4 + 0] =
509                     context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
510                 Pixels[X * 4 + 1] =
511                     context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
512                 Pixels[X * 4 + 2] =
513                     context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
514                 X++;
515                 if (X<context->Header.width) { 
516                         /* Handle the other 4 bit pixel only when there is one */
517                         Pixels[X * 4 + 0] =
518                             context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
519                         Pixels[X * 4 + 1] =
520                             context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
521                         Pixels[X * 4 + 2] =
522                             context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
523                         X++;
524                 }
525         }
526         
527 }
528
529 static void OneLine1(struct ico_progressive_state *context)
530 {
531         gint X;
532         guchar *Pixels;
533
534         X = 0;
535         if (context->Header.Negative == 0)
536                 Pixels = (context->pixbuf->pixels +
537                           context->pixbuf->rowstride *
538                           (context->Header.height - context->Lines - 1));
539         else
540                 Pixels = (context->pixbuf->pixels +
541                           context->pixbuf->rowstride *
542                           context->Lines);
543         while (X < context->Header.width) {
544                 int Bit;
545
546                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
547                 Bit = Bit & 1;
548                 /* The joys of having a BGR byteorder */
549                 Pixels[X * 4 + 0] = Bit*255;
550                 Pixels[X * 4 + 1] = Bit*255;
551                 Pixels[X * 4 + 2] = Bit*255;
552                 X++;
553         }
554 }
555
556 static void OneLineTransp(struct ico_progressive_state *context)
557 {
558         gint X;
559         guchar *Pixels;
560
561         X = 0;
562         if (context->Header.Negative == 0)
563                 Pixels = (context->pixbuf->pixels +
564                           context->pixbuf->rowstride *
565                           (2*context->Header.height - context->Lines - 1));
566         else
567                 Pixels = (context->pixbuf->pixels +
568                           context->pixbuf->rowstride *
569                           (context->Lines-context->Header.height));
570         while (X < context->Header.width) {
571                 int Bit;
572
573                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
574                 Bit = Bit & 1;
575                 /* The joys of having a BGR byteorder */
576                 Pixels[X * 4 + 3] = 255-Bit*255;
577 #if 0           
578                 if (Bit){
579                   Pixels[X*4+0] = 255;
580                   Pixels[X*4+1] = 255;
581                 } else {
582                   Pixels[X*4+0] = 0;
583                   Pixels[X*4+1] = 0;
584                 }
585 #endif          
586                 X++;
587         }
588 }
589
590
591 static void OneLine(struct ico_progressive_state *context)
592 {
593         context->LineDone = 0;
594         
595         if (context->Lines >= context->Header.height*2) {
596                 return;
597         }
598                 
599         if (context->Lines <context->Header.height) {           
600
601                 if (context->Type == 24)
602                         OneLine24(context);
603                 if (context->Type == 8)
604                         OneLine8(context);
605                 if (context->Type == 4)
606                         OneLine4(context);
607                 if (context->Type == 1)
608                         OneLine1(context);
609         } else
610         {
611                 OneLineTransp(context);
612         }
613         
614         context->Lines++;
615         if (context->Lines>=context->Header.height) {
616                 context->Type = 1;
617                 context->LineWidth = context->Header.width / 8;
618                 if ((context->Header.width & 7) != 0)
619                         context->LineWidth++;
620                 /* Pad to a 32 bit boundary */
621                 if (((context->LineWidth % 4) > 0))
622                         context->LineWidth = (context->LineWidth / 4) * 4 + 4;
623                         
624         }
625           
626
627         if (context->updated_func != NULL) {
628                 (*context->updated_func) (context->pixbuf,
629                                           0,
630                                           context->Lines,
631                                           context->Header.width,
632                                           context->Header.height,
633                                           context->user_data);
634
635         }
636 }
637
638 /*
639  * context - from image_begin_load
640  * buf - new image data
641  * size - length of new image data
642  *
643  * append image data onto inrecrementally built output image
644  */
645 gboolean
646 gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size,
647                                      GError **error)
648 {
649         struct ico_progressive_state *context =
650             (struct ico_progressive_state *) data;
651
652         gint BytesToCopy;
653
654         while (size > 0) {
655                 g_assert(context->LineDone >= 0);
656                 if (context->HeaderDone < context->HeaderSize) {        /* We still 
657                                                                            have headerbytes to do */
658                         BytesToCopy =
659                             context->HeaderSize - context->HeaderDone;
660                         if (BytesToCopy > size)
661                                 BytesToCopy = size;
662
663                         memmove(context->HeaderBuf + context->HeaderDone,
664                                buf, BytesToCopy);
665
666                         size -= BytesToCopy;
667                         buf += BytesToCopy;
668                         context->HeaderDone += BytesToCopy;
669
670                 }  else
671                 {  
672                         BytesToCopy =
673                             context->LineWidth - context->LineDone;
674                         if (BytesToCopy > size)
675                                 BytesToCopy = size;
676
677                         if (BytesToCopy > 0) {
678                                 memmove(context->LineBuf +
679                                        context->LineDone, buf,
680                                        BytesToCopy);
681
682                                 size -= BytesToCopy;
683                                 buf += BytesToCopy;
684                                 context->LineDone += BytesToCopy;
685                         }
686                         if ((context->LineDone >= context->LineWidth) &&
687                             (context->LineWidth > 0))
688                                 OneLine(context);
689
690
691                 }
692
693                 if (context->HeaderDone >= 6)
694                         DecodeHeader(context->HeaderBuf,
695                                      context->HeaderDone, context);
696
697
698         }
699
700         return TRUE;
701 }