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