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(ModulePreparedNotifyFunc prepared_func,
162 ModuleUpdatedNotifyFunc updated_func,
165 static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
166 static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
167 const guchar * buf, guint size,
171 context_free (struct ico_progressive_state *context)
173 if (context->LineBuf != NULL)
174 g_free (context->LineBuf);
175 context->LineBuf = NULL;
176 if (context->HeaderBuf != NULL)
177 g_free (context->HeaderBuf);
180 g_object_unref (context->pixbuf);
185 /* Shared library entry point --> Can go when generic_image_load
186 enters gdk-pixbuf-io */
188 gdk_pixbuf__ico_image_load(FILE * f, GError **error)
190 guchar membuf [4096];
192 struct ico_progressive_state *State;
196 State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, error);
202 length = fread(membuf, 1, 4096, f);
206 g_file_error_from_errno (errno),
207 _("Failure reading ICO: %s"), g_strerror (errno));
208 context_free (State);
212 if (!gdk_pixbuf__ico_image_load_increment(State, membuf, length,
214 context_free (State);
218 if (State->pixbuf != NULL)
219 g_object_ref (State->pixbuf);
223 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
224 _("ICO file was missing some data (perhaps it was truncated somehow?)"));
225 context_free (State);
231 gdk_pixbuf__ico_image_stop_load(State, NULL);
235 static void DecodeHeader(guchar *Data, gint Bytes,
236 struct ico_progressive_state *State,
239 /* For ICO's we have to be very clever. There are multiple images possible
240 in an .ICO. For now, we select (in order of priority):
241 1) The one with the highest number of colors
245 gint IconCount = 0; /* The number of icon-versions in the file */
246 guchar *BIH; /* The DIB for the used icon */
250 /* Step 1: The ICO header */
252 IconCount = (Data[5] << 8) + (Data[4]);
254 State->HeaderSize = 6 + IconCount*16;
256 if (State->HeaderSize>State->BytesInHeaderBuf) {
257 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
258 if (!State->HeaderBuf) {
261 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
262 _("Not enough memory to load icon"));
265 State->BytesInHeaderBuf = State->HeaderSize;
267 if (Bytes < State->HeaderSize)
270 /* We now have all the "short-specs" of the versions
271 So we iterate through them and select the best one */
273 State->ImageScore = 0;
274 State->DIBoffset = 0;
276 for (I=0;I<IconCount;I++) {
277 int ThisWidth, ThisHeight,ThisColors;
282 ThisColors = (Ptr[2]);
284 ThisColors=256; /* Yes, this is in the spec, ugh */
286 ThisScore = ThisColors*1024+ThisWidth*ThisHeight;
288 if (ThisScore>State->ImageScore) {
289 State->ImageScore = ThisScore;
290 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
291 (Ptr[13]<<8) + (Ptr[12]);
299 if (State->DIBoffset < 0) {
302 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
303 _("Invalid header in icon"));
307 /* We now have a winner, pointed to in State->DIBoffset,
308 so we know how many bytes are in the "header" part. */
310 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
312 if (State->HeaderSize>State->BytesInHeaderBuf) {
313 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
314 if (!State->HeaderBuf) {
317 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
318 _("Not enough memory to load icon"));
321 State->BytesInHeaderBuf = State->HeaderSize;
323 if (Bytes<State->HeaderSize)
326 BIH = Data+State->DIBoffset;
332 /* Add the palette to the headersize */
334 State->Header.width =
335 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
336 if (State->Header.width == 0) {
339 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
340 _("Icon has zero width"));
343 State->Header.height =
344 (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8])/2;
345 /* /2 because the BIH height includes the transparency mask */
346 if (State->Header.height == 0) {
349 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
350 _("Icon has zero height"));
353 State->Header.depth = (BIH[15] << 8) + (BIH[14]);;
355 State->Type = State->Header.depth;
356 if (State->Lines>=State->Header.height)
357 State->Type = 1; /* The transparency mask is 1 bpp */
361 /* Determine the palette size. If the header indicates 0, it
362 is actually the maximum for the bpp. You have to love the
363 guys who made the spec. */
364 I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
366 if ((I==0)&&(State->Type==1))
368 if ((I==0)&&(State->Type==4))
370 if ((I==0)&&(State->Type==8))
373 State->HeaderSize+=I;
375 if (State->HeaderSize>State->BytesInHeaderBuf) {
376 State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
377 if (!State->HeaderBuf) {
380 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
381 _("Not enough memory to load icon"));
384 State->BytesInHeaderBuf = State->HeaderSize;
386 if (Bytes < State->HeaderSize)
389 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
391 /* FIXME: is this the correct message? */
394 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
395 _("Compressed icons are not supported"));
399 /* Negative heights mean top-down pixel-order */
400 if (State->Header.height < 0) {
401 State->Header.height = -State->Header.height;
402 State->Header.Negative = 1;
404 if (State->Header.width < 0) {
405 State->Header.width = -State->Header.width;
407 g_assert (State->Header.width > 0);
408 g_assert (State->Header.height > 0);
410 if (State->Type == 32)
411 State->LineWidth = State->Header.width * 4;
412 else if (State->Type == 24)
413 State->LineWidth = State->Header.width * 3;
414 else if (State->Type == 16)
415 State->LineWidth = State->Header.height * 2;
416 else if (State->Type == 8)
417 State->LineWidth = State->Header.width * 1;
418 else if (State->Type == 4)
419 State->LineWidth = (State->Header.width+1)/2;
420 else if (State->Type == 1) {
421 State->LineWidth = State->Header.width / 8;
422 if ((State->Header.width & 7) != 0)
427 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
428 _("Unsupported icon type"));
432 /* Pad to a 32 bit boundary */
433 if (((State->LineWidth % 4) > 0))
434 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
437 if (State->LineBuf == NULL) {
438 State->LineBuf = g_try_malloc(State->LineWidth);
439 if (!State->LineBuf) {
442 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
443 _("Not enough memory to load icon"));
448 g_assert(State->LineBuf != NULL);
451 if (State->pixbuf == NULL) {
453 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
455 State->Header.height);
456 if (!State->pixbuf) {
459 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
460 _("Not enough memory to load icon"));
464 if (State->prepared_func != NULL)
465 /* Notify the client that we are ready to go */
466 (*State->prepared_func) (State->pixbuf,
475 * func - called when we have pixmap created (but no image data)
476 * user_data - passed as arg 1 to func
477 * return context (opaque to user)
481 gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
482 ModuleUpdatedNotifyFunc updated_func,
486 struct ico_progressive_state *context;
488 context = g_new0(struct ico_progressive_state, 1);
489 context->prepared_func = prepared_func;
490 context->updated_func = updated_func;
491 context->user_data = user_data;
493 context->HeaderSize = 54;
494 context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
495 if (!context->HeaderBuf) {
498 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
499 _("Not enough memory to load ICO file"));
502 /* 4*256 for the colormap */
503 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
504 context->HeaderDone = 0;
506 context->LineWidth = 0;
507 context->LineBuf = NULL;
508 context->LineDone = 0;
513 memset(&context->Header, 0, sizeof(struct headerpair));
516 context->pixbuf = NULL;
519 return (gpointer) context;
523 * context - returned from image_begin_load
525 * free context, unref gdk_pixbuf
527 gboolean gdk_pixbuf__ico_image_stop_load(gpointer data,
530 struct ico_progressive_state *context =
531 (struct ico_progressive_state *) data;
533 /* FIXME this thing needs to report errors if
534 * we have unused image data
537 g_return_val_if_fail(context != NULL, TRUE);
539 context_free (context);
544 OneLine32 (struct ico_progressive_state *context)
550 if (context->Header.Negative == 0)
551 Pixels = (context->pixbuf->pixels +
552 context->pixbuf->rowstride *
553 (context->Header.height - context->Lines - 1));
555 Pixels = (context->pixbuf->pixels +
556 context->pixbuf->rowstride *
558 while (X < context->Header.width) {
559 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
560 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
561 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
562 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
567 static void OneLine24(struct ico_progressive_state *context)
573 if (context->Header.Negative == 0)
574 Pixels = (context->pixbuf->pixels +
575 context->pixbuf->rowstride *
576 (context->Header.height - context->Lines - 1));
578 Pixels = (context->pixbuf->pixels +
579 context->pixbuf->rowstride *
581 while (X < context->Header.width) {
582 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
583 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
584 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
591 OneLine16 (struct ico_progressive_state *context)
597 if (context->Header.Negative == 0)
598 pixels = (context->pixbuf->pixels +
599 context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
601 pixels = (context->pixbuf->pixels +
602 context->pixbuf->rowstride * context->Lines);
604 src = context->LineBuf;
606 for (i = 0; i < context->Header.width; i++) {
609 v = (int) src[0] | ((int) src[1] << 8);
612 /* Extract 5-bit RGB values */
614 r = (v >> 10) & 0x1f;
618 /* Fill the rightmost bits to form 8-bit values */
620 *pixels++ = (r << 3) | (r >> 2);
621 *pixels++ = (g << 3) | (g >> 2);
622 *pixels++ = (b << 3) | (b >> 2);
623 pixels++; /* skip alpha channel */
628 static void OneLine8(struct ico_progressive_state *context)
634 if (context->Header.Negative == 0)
635 Pixels = (context->pixbuf->pixels +
636 context->pixbuf->rowstride *
637 (context->Header.height - context->Lines - 1));
639 Pixels = (context->pixbuf->pixels +
640 context->pixbuf->rowstride *
642 while (X < context->Header.width) {
643 /* The joys of having a BGR byteorder */
645 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
647 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
649 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
653 static void OneLine4(struct ico_progressive_state *context)
659 if (context->Header.Negative == 0)
660 Pixels = (context->pixbuf->pixels +
661 context->pixbuf->rowstride *
662 (context->Header.height - context->Lines - 1));
664 Pixels = (context->pixbuf->pixels +
665 context->pixbuf->rowstride *
668 while (X < context->Header.width) {
671 Pix = context->LineBuf[X/2];
674 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
676 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
678 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
680 if (X<context->Header.width) {
681 /* Handle the other 4 bit pixel only when there is one */
683 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
685 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
687 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
694 static void OneLine1(struct ico_progressive_state *context)
700 if (context->Header.Negative == 0)
701 Pixels = (context->pixbuf->pixels +
702 context->pixbuf->rowstride *
703 (context->Header.height - context->Lines - 1));
705 Pixels = (context->pixbuf->pixels +
706 context->pixbuf->rowstride *
708 while (X < context->Header.width) {
711 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
713 /* The joys of having a BGR byteorder */
714 Pixels[X * 4 + 0] = Bit*255;
715 Pixels[X * 4 + 1] = Bit*255;
716 Pixels[X * 4 + 2] = Bit*255;
721 static void OneLineTransp(struct ico_progressive_state *context)
727 if (context->Header.Negative == 0)
728 Pixels = (context->pixbuf->pixels +
729 context->pixbuf->rowstride *
730 (2*context->Header.height - context->Lines - 1));
732 Pixels = (context->pixbuf->pixels +
733 context->pixbuf->rowstride *
734 (context->Lines-context->Header.height));
735 while (X < context->Header.width) {
738 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
740 /* The joys of having a BGR byteorder */
741 Pixels[X * 4 + 3] = 255-Bit*255;
756 static void OneLine(struct ico_progressive_state *context)
758 context->LineDone = 0;
760 if (context->Lines >= context->Header.height*2) {
764 if (context->Lines <context->Header.height) {
765 if (context->Type == 32)
767 else if (context->Type == 24)
769 else if (context->Type == 16)
771 else if (context->Type == 8)
773 else if (context->Type == 4)
775 else if (context->Type == 1)
778 g_assert_not_reached ();
780 OneLineTransp(context);
783 if (context->Lines>=context->Header.height) {
785 context->LineWidth = context->Header.width / 8;
786 if ((context->Header.width & 7) != 0)
787 context->LineWidth++;
788 /* Pad to a 32 bit boundary */
789 if (((context->LineWidth % 4) > 0))
790 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
795 if (context->updated_func != NULL) {
796 (*context->updated_func) (context->pixbuf,
799 context->Header.width,
800 context->Header.height,
807 * context - from image_begin_load
808 * buf - new image data
809 * size - length of new image data
811 * append image data onto inrecrementally built output image
814 gdk_pixbuf__ico_image_load_increment(gpointer data,
819 struct ico_progressive_state *context =
820 (struct ico_progressive_state *) data;
825 g_assert(context->LineDone >= 0);
826 if (context->HeaderDone < context->HeaderSize) { /* We still
827 have headerbytes to do */
829 context->HeaderSize - context->HeaderDone;
830 if (BytesToCopy > size)
833 memmove(context->HeaderBuf + context->HeaderDone,
838 context->HeaderDone += BytesToCopy;
842 context->LineWidth - context->LineDone;
843 if (BytesToCopy > size)
846 if (BytesToCopy > 0) {
847 memmove(context->LineBuf +
848 context->LineDone, buf,
853 context->LineDone += BytesToCopy;
855 if ((context->LineDone >= context->LineWidth) &&
856 (context->LineWidth > 0))
862 if (context->HeaderDone >= 6) {
863 GError *decode_err = NULL;
864 DecodeHeader(context->HeaderBuf,
865 context->HeaderDone, context, &decode_err);
867 g_propagate_error (error, decode_err);
877 gdk_pixbuf__ico_fill_vtable (GdkPixbufModule *module)
879 module->load = gdk_pixbuf__ico_image_load;
880 module->begin_load = gdk_pixbuf__ico_image_begin_load;
881 module->stop_load = gdk_pixbuf__ico_image_stop_load;
882 module->load_increment = gdk_pixbuf__ico_image_load_increment;