1 /* GdkPixbuf library - Windows Icon/Cursor image loader
3 * Copyright (C) 1999 The Free Software Foundation
5 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
6 * Federico Mena-Quintero <federico@gimp.org>
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.
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.
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.
29 Icons are just like BMP's, except for the header.
32 * bi-tonal files aren't tested
42 #include "gdk-pixbuf-private.h"
43 #include "gdk-pixbuf-io.h"
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
56 struct BitmapFileHeader {
63 struct BitmapInfoHeader {
71 guint biXPelsPerMeter;
72 guint biYPelsPerMeter;
80 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
84 static void DumpBIH(unsigned char *BIH)
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) +
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) +
98 printf("biSizeImage = %i \n",
99 (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
101 printf("biXPels = %i \n",
102 (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
104 printf("biYPels = %i \n",
105 (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
107 printf("biClrUsed = %i \n",
108 (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
110 printf("biClrImprtnt= %i \n",
111 (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
116 /* Progressive loading */
121 guint Negative; /* Negative = 1 -> top down BMP,
122 Negative = 0 -> bottom up BMP */
125 struct ico_progressive_state {
126 ModulePreparedNotifyFunc prepared_func;
127 ModuleUpdatedNotifyFunc updated_func;
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 */
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 */
142 8 = 8 bit colormapped
147 struct headerpair Header; /* Decoded (BE->CPU) header */
153 GdkPixbuf *pixbuf; /* Our "target" */
157 gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
158 ModuleUpdatedNotifyFunc updated_func,
159 ModuleFrameDoneNotifyFunc frame_done_func,
160 ModuleAnimationDoneNotifyFunc anim_done_func,
163 void gdk_pixbuf__ico_image_stop_load(gpointer data);
164 gboolean gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size,
169 /* Shared library entry point --> Can go when generic_image_load
170 enters gdk-pixbuf-io */
172 gdk_pixbuf__ico_image_load(FILE * f, GError **error)
176 struct ico_progressive_state *State;
180 State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL,
185 membuf = g_malloc(4096);
187 g_assert(membuf != NULL);
189 while (feof(f) == 0) {
190 length = fread(membuf, 1, 4096, f);
192 if (!gdk_pixbuf__ico_image_load_increment(State, membuf, length,
194 gdk_pixbuf__ico_image_stop_load (State);
199 if (State->pixbuf != NULL)
200 gdk_pixbuf_ref(State->pixbuf);
204 gdk_pixbuf__ico_image_stop_load(State);
208 static void DecodeHeader(guchar *Data, gint Bytes,
209 struct ico_progressive_state *State)
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
217 gint IconCount = 0; /* The number of icon-versions in the file */
218 guchar *BIH; /* The DIB for the used icon */
222 /* Step 1: The ICO header */
224 IconCount = (Data[5] << 8) + (Data[4]);
226 State->HeaderSize = 6 + IconCount*16;
228 if (State->HeaderSize>State->BytesInHeaderBuf) {
229 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
230 State->BytesInHeaderBuf = State->HeaderSize;
232 if (Bytes < State->HeaderSize)
235 /* We now have all the "short-specs" of the versions
236 So we iterate through them and select the best one */
238 State->ImageScore = 0;
239 State->DIBoffset = 0;
241 for (I=0;I<IconCount;I++) {
242 int ThisWidth, ThisHeight,ThisColors;
247 ThisColors = (Ptr[2]);
249 ThisColors=256; /* Yes, this is in the spec, ugh */
251 ThisScore = ThisColors*1024+ThisWidth*ThisHeight;
253 if (ThisScore>State->ImageScore) {
254 State->ImageScore = ThisScore;
255 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
256 (Ptr[13]<<8) + (Ptr[12]);
264 /* We now have a winner, pointed to in State->DIBoffset,
265 so we know how many bytes are in the "header" part. */
267 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
269 if (State->HeaderSize>State->BytesInHeaderBuf) {
270 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
271 State->BytesInHeaderBuf = State->HeaderSize;
273 if (Bytes<State->HeaderSize)
276 BIH = Data+State->DIBoffset;
282 /* Add the palette to the headersize */
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]);;
291 State->Type = State->Header.depth;
292 if (State->Lines>=State->Header.height)
293 State->Type = 1; /* The transparency mask is 1 bpp */
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]);
302 if ((I==0)&&(State->Type==1))
304 if ((I==0)&&(State->Type==4))
306 if ((I==0)&&(State->Type==8))
309 State->HeaderSize+=I;
311 if (State->HeaderSize>State->BytesInHeaderBuf) {
312 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
313 State->BytesInHeaderBuf = State->HeaderSize;
315 if (Bytes < State->HeaderSize)
318 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
320 g_assert(0); /* Compressed icons aren't allowed */
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;
328 if (State->Header.width < 0) {
329 State->Header.width = -State->Header.width;
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;
339 if (State->Type == 1) {
340 State->LineWidth = State->Header.width / 8;
341 if ((State->Header.width & 7) != 0)
345 /* Pad to a 32 bit boundary */
346 if (((State->LineWidth % 4) > 0))
347 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
350 if (State->LineBuf == NULL)
351 State->LineBuf = g_malloc(State->LineWidth);
353 g_assert(State->LineBuf != NULL);
356 if (State->pixbuf == NULL) {
358 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
359 (gint) State->Header.width,
360 (gint) State->Header.height);
362 if (State->prepared_func != NULL)
363 /* Notify the client that we are ready to go */
364 (*State->prepared_func) (State->pixbuf,
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)
378 gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
379 ModuleUpdatedNotifyFunc updated_func,
380 ModuleFrameDoneNotifyFunc frame_done_func,
381 ModuleAnimationDoneNotifyFunc anim_done_func,
385 struct ico_progressive_state *context;
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;
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;
398 context->LineWidth = 0;
399 context->LineBuf = NULL;
400 context->LineDone = 0;
405 memset(&context->Header, 0, sizeof(struct headerpair));
408 context->pixbuf = NULL;
411 return (gpointer) context;
415 * context - returned from image_begin_load
417 * free context, unref gdk_pixbuf
419 void gdk_pixbuf__ico_image_stop_load(gpointer data)
421 struct ico_progressive_state *context =
422 (struct ico_progressive_state *) data;
425 g_return_if_fail(context != NULL);
427 if (context->LineBuf != NULL)
428 g_free(context->LineBuf);
429 context->LineBuf = NULL;
430 if (context->HeaderBuf != NULL)
431 g_free(context->HeaderBuf);
434 gdk_pixbuf_unref(context->pixbuf);
440 static void OneLine24(struct ico_progressive_state *context)
446 if (context->Header.Negative == 0)
447 Pixels = (context->pixbuf->pixels +
448 context->pixbuf->rowstride *
449 (context->Header.height - context->Lines - 1));
451 Pixels = (context->pixbuf->pixels +
452 context->pixbuf->rowstride *
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];
463 static void OneLine8(struct ico_progressive_state *context)
469 if (context->Header.Negative == 0)
470 Pixels = (context->pixbuf->pixels +
471 context->pixbuf->rowstride *
472 (context->Header.height - context->Lines - 1));
474 Pixels = (context->pixbuf->pixels +
475 context->pixbuf->rowstride *
477 while (X < context->Header.width) {
478 /* The joys of having a BGR byteorder */
480 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
482 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
484 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
488 static void OneLine4(struct ico_progressive_state *context)
494 if (context->Header.Negative == 0)
495 Pixels = (context->pixbuf->pixels +
496 context->pixbuf->rowstride *
497 (context->Header.height - context->Lines - 1));
499 Pixels = (context->pixbuf->pixels +
500 context->pixbuf->rowstride *
503 while (X < context->Header.width) {
506 Pix = context->LineBuf[X/2];
509 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
511 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
513 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
515 if (X<context->Header.width) {
516 /* Handle the other 4 bit pixel only when there is one */
518 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
520 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
522 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
529 static void OneLine1(struct ico_progressive_state *context)
535 if (context->Header.Negative == 0)
536 Pixels = (context->pixbuf->pixels +
537 context->pixbuf->rowstride *
538 (context->Header.height - context->Lines - 1));
540 Pixels = (context->pixbuf->pixels +
541 context->pixbuf->rowstride *
543 while (X < context->Header.width) {
546 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
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;
556 static void OneLineTransp(struct ico_progressive_state *context)
562 if (context->Header.Negative == 0)
563 Pixels = (context->pixbuf->pixels +
564 context->pixbuf->rowstride *
565 (2*context->Header.height - context->Lines - 1));
567 Pixels = (context->pixbuf->pixels +
568 context->pixbuf->rowstride *
569 (context->Lines-context->Header.height));
570 while (X < context->Header.width) {
573 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
575 /* The joys of having a BGR byteorder */
576 Pixels[X * 4 + 3] = 255-Bit*255;
591 static void OneLine(struct ico_progressive_state *context)
593 context->LineDone = 0;
595 if (context->Lines >= context->Header.height*2) {
599 if (context->Lines <context->Header.height) {
601 if (context->Type == 24)
603 if (context->Type == 8)
605 if (context->Type == 4)
607 if (context->Type == 1)
611 OneLineTransp(context);
615 if (context->Lines>=context->Header.height) {
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;
627 if (context->updated_func != NULL) {
628 (*context->updated_func) (context->pixbuf,
631 context->Header.width,
632 context->Header.height,
639 * context - from image_begin_load
640 * buf - new image data
641 * size - length of new image data
643 * append image data onto inrecrementally built output image
646 gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size,
649 struct ico_progressive_state *context =
650 (struct ico_progressive_state *) data;
655 g_assert(context->LineDone >= 0);
656 if (context->HeaderDone < context->HeaderSize) { /* We still
657 have headerbytes to do */
659 context->HeaderSize - context->HeaderDone;
660 if (BytesToCopy > size)
663 memmove(context->HeaderBuf + context->HeaderDone,
668 context->HeaderDone += BytesToCopy;
673 context->LineWidth - context->LineDone;
674 if (BytesToCopy > size)
677 if (BytesToCopy > 0) {
678 memmove(context->LineBuf +
679 context->LineDone, buf,
684 context->LineDone += BytesToCopy;
686 if ((context->LineDone >= context->LineWidth) &&
687 (context->LineWidth > 0))
693 if (context->HeaderDone >= 6)
694 DecodeHeader(context->HeaderBuf,
695 context->HeaderDone, context);