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"
50 These structures are actually dummies. These are according to
51 the "Windows API reference guide volume II" as written by
52 Borland International, but GCC fiddles with the alignment of
57 struct BitmapFileHeader {
64 struct BitmapInfoHeader {
72 guint biXPelsPerMeter;
73 guint biYPelsPerMeter;
81 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
85 static void DumpBIH(unsigned char *BIH)
87 printf("biSize = %i \n",
88 (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
89 printf("biWidth = %i \n",
90 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
91 printf("biHeight = %i \n",
92 (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
94 printf("biPlanes = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
95 printf("biBitCount = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
96 printf("biCompress = %i \n",
97 (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
99 printf("biSizeImage = %i \n",
100 (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
102 printf("biXPels = %i \n",
103 (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
105 printf("biYPels = %i \n",
106 (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
108 printf("biClrUsed = %i \n",
109 (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
111 printf("biClrImprtnt= %i \n",
112 (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
117 /* Progressive loading */
122 guint Negative; /* Negative = 1 -> top down BMP,
123 Negative = 0 -> bottom up BMP */
126 struct ico_progressive_state {
127 ModulePreparedNotifyFunc prepared_func;
128 ModuleUpdatedNotifyFunc updated_func;
131 gint HeaderSize; /* The size of the header-part (incl colormap) */
132 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
133 gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */
134 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
136 gint LineWidth; /* The width of a line in bytes */
137 guchar *LineBuf; /* Buffer for 1 line */
138 gint LineDone; /* # of bytes in LineBuf */
139 gint Lines; /* # of finished lines */
145 8 = 8 bit colormapped
146 4 = 4 bpp colormapped
151 struct headerpair Header; /* Decoded (BE->CPU) header */
157 GdkPixbuf *pixbuf; /* Our "target" */
161 gdk_pixbuf__ico_image_begin_load(ModuleSizeFunc size_func,
162 ModulePreparedNotifyFunc prepared_func,
163 ModuleUpdatedNotifyFunc updated_func,
166 static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
167 static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
168 const guchar * buf, guint size,
172 context_free (struct ico_progressive_state *context)
174 if (context->LineBuf != NULL)
175 g_free (context->LineBuf);
176 context->LineBuf = NULL;
177 if (context->HeaderBuf != NULL)
178 g_free (context->HeaderBuf);
181 g_object_unref (context->pixbuf);
186 /* Shared library entry point --> Can go when generic_image_load
187 enters gdk-pixbuf-io */
189 gdk_pixbuf__ico_image_load(FILE * f, GError **error)
191 guchar membuf [4096];
193 struct ico_progressive_state *State;
197 State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, NULL, error);
203 length = fread(membuf, 1, 4096, f);
207 g_file_error_from_errno (errno),
208 _("Failure reading ICO: %s"), g_strerror (errno));
209 context_free (State);
213 if (!gdk_pixbuf__ico_image_load_increment(State, membuf, length,
215 context_free (State);
219 if (State->pixbuf != NULL)
220 g_object_ref (State->pixbuf);
224 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
225 _("ICO file was missing some data (perhaps it was truncated somehow?)"));
226 context_free (State);
232 gdk_pixbuf__ico_image_stop_load(State, NULL);
236 static void DecodeHeader(guchar *Data, gint Bytes,
237 struct ico_progressive_state *State,
240 /* For ICO's we have to be very clever. There are multiple images possible
241 in an .ICO. For now, we select (in order of priority):
242 1) The one with the highest number of colors
246 gint IconCount = 0; /* The number of icon-versions in the file */
247 guchar *BIH; /* The DIB for the used icon */
251 /* Step 1: The ICO header */
253 IconCount = (Data[5] << 8) + (Data[4]);
255 State->HeaderSize = 6 + IconCount*16;
257 if (State->HeaderSize>State->BytesInHeaderBuf) {
258 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
259 if (!State->HeaderBuf) {
262 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
263 _("Not enough memory to load icon"));
266 State->BytesInHeaderBuf = State->HeaderSize;
268 if (Bytes < State->HeaderSize)
271 /* We now have all the "short-specs" of the versions
272 So we iterate through them and select the best one */
274 State->ImageScore = 0;
275 State->DIBoffset = 0;
277 for (I=0;I<IconCount;I++) {
278 int ThisWidth, ThisHeight,ThisColors;
283 ThisColors = (Ptr[2]);
285 ThisColors=256; /* Yes, this is in the spec, ugh */
287 ThisScore = ThisColors*1024+ThisWidth*ThisHeight;
289 if (ThisScore>State->ImageScore) {
290 State->ImageScore = ThisScore;
291 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
292 (Ptr[13]<<8) + (Ptr[12]);
300 if (State->DIBoffset < 0) {
303 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
304 _("Invalid header in icon"));
308 /* We now have a winner, pointed to in State->DIBoffset,
309 so we know how many bytes are in the "header" part. */
311 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
313 if (State->HeaderSize>State->BytesInHeaderBuf) {
314 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
315 if (!State->HeaderBuf) {
318 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
319 _("Not enough memory to load icon"));
322 State->BytesInHeaderBuf = State->HeaderSize;
324 if (Bytes<State->HeaderSize)
327 BIH = Data+State->DIBoffset;
333 /* Add the palette to the headersize */
335 State->Header.width =
336 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
337 if (State->Header.width == 0) {
340 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
341 _("Icon has zero width"));
344 State->Header.height =
345 (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
346 /* /2 because the BIH height includes the transparency mask */
347 if (State->Header.height == 0) {
350 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
351 _("Icon has zero height"));
354 State->Header.depth = (BIH[15] << 8) + (BIH[14]);;
356 State->Type = State->Header.depth;
357 if (State->Lines>=State->Header.height)
358 State->Type = 1; /* The transparency mask is 1 bpp */
362 /* Determine the palette size. If the header indicates 0, it
363 is actually the maximum for the bpp. You have to love the
364 guys who made the spec. */
365 I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
367 if ((I==0)&&(State->Type==1))
369 if ((I==0)&&(State->Type==4))
371 if ((I==0)&&(State->Type==8))
374 State->HeaderSize+=I;
376 if (State->HeaderSize>State->BytesInHeaderBuf) {
377 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
378 if (!State->HeaderBuf) {
381 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
382 _("Not enough memory to load icon"));
385 State->BytesInHeaderBuf = State->HeaderSize;
387 if (Bytes < State->HeaderSize)
390 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
392 /* FIXME: is this the correct message? */
395 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
396 _("Compressed icons are not supported"));
400 /* Negative heights mean top-down pixel-order */
401 if (State->Header.height < 0) {
402 State->Header.height = -State->Header.height;
403 State->Header.Negative = 1;
405 if (State->Header.width < 0) {
406 State->Header.width = -State->Header.width;
408 g_assert (State->Header.width > 0);
409 g_assert (State->Header.height > 0);
411 if (State->Type == 32)
412 State->LineWidth = State->Header.width * 4;
413 else if (State->Type == 24)
414 State->LineWidth = State->Header.width * 3;
415 else if (State->Type == 16)
416 State->LineWidth = State->Header.height * 2;
417 else if (State->Type == 8)
418 State->LineWidth = State->Header.width * 1;
419 else if (State->Type == 4)
420 State->LineWidth = (State->Header.width+1)/2;
421 else if (State->Type == 1) {
422 State->LineWidth = State->Header.width / 8;
423 if ((State->Header.width & 7) != 0)
428 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
429 _("Unsupported icon type"));
433 /* Pad to a 32 bit boundary */
434 if (((State->LineWidth % 4) > 0))
435 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
438 if (State->LineBuf == NULL) {
439 State->LineBuf = g_try_malloc(State->LineWidth);
440 if (!State->LineBuf) {
443 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
444 _("Not enough memory to load icon"));
449 g_assert(State->LineBuf != NULL);
452 if (State->pixbuf == NULL) {
454 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
456 State->Header.height);
457 if (!State->pixbuf) {
460 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
461 _("Not enough memory to load icon"));
465 if (State->prepared_func != NULL)
466 /* Notify the client that we are ready to go */
467 (*State->prepared_func) (State->pixbuf,
476 * func - called when we have pixmap created (but no image data)
477 * user_data - passed as arg 1 to func
478 * return context (opaque to user)
482 gdk_pixbuf__ico_image_begin_load(ModuleSizeFunc size_func,
483 ModulePreparedNotifyFunc prepared_func,
484 ModuleUpdatedNotifyFunc updated_func,
488 struct ico_progressive_state *context;
490 context = g_new0(struct ico_progressive_state, 1);
491 context->prepared_func = prepared_func;
492 context->updated_func = updated_func;
493 context->user_data = user_data;
495 context->HeaderSize = 54;
496 context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
497 if (!context->HeaderBuf) {
500 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
501 _("Not enough memory to load ICO file"));
504 /* 4*256 for the colormap */
505 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
506 context->HeaderDone = 0;
508 context->LineWidth = 0;
509 context->LineBuf = NULL;
510 context->LineDone = 0;
515 memset(&context->Header, 0, sizeof(struct headerpair));
518 context->pixbuf = NULL;
521 return (gpointer) context;
525 * context - returned from image_begin_load
527 * free context, unref gdk_pixbuf
529 gboolean gdk_pixbuf__ico_image_stop_load(gpointer data,
532 struct ico_progressive_state *context =
533 (struct ico_progressive_state *) data;
535 /* FIXME this thing needs to report errors if
536 * we have unused image data
539 g_return_val_if_fail(context != NULL, TRUE);
541 context_free (context);
546 OneLine32 (struct ico_progressive_state *context)
552 if (context->Header.Negative == 0)
553 Pixels = (context->pixbuf->pixels +
554 context->pixbuf->rowstride *
555 (context->Header.height - context->Lines - 1));
557 Pixels = (context->pixbuf->pixels +
558 context->pixbuf->rowstride *
560 while (X < context->Header.width) {
561 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
562 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
563 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
564 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
569 static void OneLine24(struct ico_progressive_state *context)
575 if (context->Header.Negative == 0)
576 Pixels = (context->pixbuf->pixels +
577 context->pixbuf->rowstride *
578 (context->Header.height - context->Lines - 1));
580 Pixels = (context->pixbuf->pixels +
581 context->pixbuf->rowstride *
583 while (X < context->Header.width) {
584 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
585 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
586 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
593 OneLine16 (struct ico_progressive_state *context)
599 if (context->Header.Negative == 0)
600 pixels = (context->pixbuf->pixels +
601 context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
603 pixels = (context->pixbuf->pixels +
604 context->pixbuf->rowstride * context->Lines);
606 src = context->LineBuf;
608 for (i = 0; i < context->Header.width; i++) {
611 v = (int) src[0] | ((int) src[1] << 8);
614 /* Extract 5-bit RGB values */
616 r = (v >> 10) & 0x1f;
620 /* Fill the rightmost bits to form 8-bit values */
622 *pixels++ = (r << 3) | (r >> 2);
623 *pixels++ = (g << 3) | (g >> 2);
624 *pixels++ = (b << 3) | (b >> 2);
625 pixels++; /* skip alpha channel */
630 static void OneLine8(struct ico_progressive_state *context)
636 if (context->Header.Negative == 0)
637 Pixels = (context->pixbuf->pixels +
638 context->pixbuf->rowstride *
639 (context->Header.height - context->Lines - 1));
641 Pixels = (context->pixbuf->pixels +
642 context->pixbuf->rowstride *
644 while (X < context->Header.width) {
645 /* The joys of having a BGR byteorder */
647 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
649 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
651 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
655 static void OneLine4(struct ico_progressive_state *context)
661 if (context->Header.Negative == 0)
662 Pixels = (context->pixbuf->pixels +
663 context->pixbuf->rowstride *
664 (context->Header.height - context->Lines - 1));
666 Pixels = (context->pixbuf->pixels +
667 context->pixbuf->rowstride *
670 while (X < context->Header.width) {
673 Pix = context->LineBuf[X/2];
676 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
678 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
680 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
682 if (X<context->Header.width) {
683 /* Handle the other 4 bit pixel only when there is one */
685 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
687 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
689 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
696 static void OneLine1(struct ico_progressive_state *context)
702 if (context->Header.Negative == 0)
703 Pixels = (context->pixbuf->pixels +
704 context->pixbuf->rowstride *
705 (context->Header.height - context->Lines - 1));
707 Pixels = (context->pixbuf->pixels +
708 context->pixbuf->rowstride *
710 while (X < context->Header.width) {
713 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
715 /* The joys of having a BGR byteorder */
716 Pixels[X * 4 + 0] = Bit*255;
717 Pixels[X * 4 + 1] = Bit*255;
718 Pixels[X * 4 + 2] = Bit*255;
723 static void OneLineTransp(struct ico_progressive_state *context)
729 if (context->Header.Negative == 0)
730 Pixels = (context->pixbuf->pixels +
731 context->pixbuf->rowstride *
732 (2*context->Header.height - context->Lines - 1));
734 Pixels = (context->pixbuf->pixels +
735 context->pixbuf->rowstride *
736 (context->Lines-context->Header.height));
737 while (X < context->Header.width) {
740 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
742 /* The joys of having a BGR byteorder */
743 Pixels[X * 4 + 3] = 255-Bit*255;
758 static void OneLine(struct ico_progressive_state *context)
760 context->LineDone = 0;
762 if (context->Lines >= context->Header.height*2) {
766 if (context->Lines <context->Header.height) {
767 if (context->Type == 32)
769 else if (context->Type == 24)
771 else if (context->Type == 16)
773 else if (context->Type == 8)
775 else if (context->Type == 4)
777 else if (context->Type == 1)
780 g_assert_not_reached ();
782 OneLineTransp(context);
785 if (context->Lines>=context->Header.height) {
787 context->LineWidth = context->Header.width / 8;
788 if ((context->Header.width & 7) != 0)
789 context->LineWidth++;
790 /* Pad to a 32 bit boundary */
791 if (((context->LineWidth % 4) > 0))
792 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
797 if (context->updated_func != NULL) {
798 (*context->updated_func) (context->pixbuf,
800 context->Lines % context->Header.height,
801 context->Header.width,
809 * context - from image_begin_load
810 * buf - new image data
811 * size - length of new image data
813 * append image data onto inrecrementally built output image
816 gdk_pixbuf__ico_image_load_increment(gpointer data,
821 struct ico_progressive_state *context =
822 (struct ico_progressive_state *) data;
827 g_assert(context->LineDone >= 0);
828 if (context->HeaderDone < context->HeaderSize) { /* We still
829 have headerbytes to do */
831 context->HeaderSize - context->HeaderDone;
832 if (BytesToCopy > size)
835 memmove(context->HeaderBuf + context->HeaderDone,
840 context->HeaderDone += BytesToCopy;
844 context->LineWidth - context->LineDone;
845 if (BytesToCopy > size)
848 if (BytesToCopy > 0) {
849 memmove(context->LineBuf +
850 context->LineDone, buf,
855 context->LineDone += BytesToCopy;
857 if ((context->LineDone >= context->LineWidth) &&
858 (context->LineWidth > 0))
864 if (context->HeaderDone >= 6) {
865 GError *decode_err = NULL;
866 DecodeHeader(context->HeaderBuf,
867 context->HeaderDone, context, &decode_err);
869 g_propagate_error (error, decode_err);
879 gdk_pixbuf__ico_fill_vtable (GdkPixbufModule *module)
881 module->load = gdk_pixbuf__ico_image_load;
882 module->begin_load = gdk_pixbuf__ico_image_begin_load;
883 module->stop_load = gdk_pixbuf__ico_image_stop_load;
884 module->load_increment = gdk_pixbuf__ico_image_load_increment;