Add powerbox hook
[gtk-with-powerbox.git] / gdk-pixbuf / io-ico.c
blob4a6bad8703e1eb17e1b63fdcdf435fbc78db3244
1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - Windows Icon/Cursor image loader
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
7 * Federico Mena-Quintero <federico@gimp.org>
9 * Based on io-bmp.c
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
27 #undef DUMPBIH
30 Icons are just like BMP's, except for the header.
32 Known bugs:
33 * bi-tonal files aren't tested
37 #include "config.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <string.h>
44 #include "gdk-pixbuf-private.h"
45 #include "gdk-pixbuf-io.h"
46 #include <errno.h>
50 /*
52 These structures are actually dummies. These are according to
53 the "Windows API reference guide volume II" as written by
54 Borland International, but GCC fiddles with the alignment of
55 the internal members.
59 struct BitmapFileHeader {
60 gushort bfType;
61 guint bfSize;
62 guint reserverd;
63 guint bfOffbits;
66 struct BitmapInfoHeader {
67 guint biSize;
68 guint biWidth;
69 guint biHeight;
70 gushort biPlanes;
71 gushort biBitCount;
72 guint biCompression;
73 guint biSizeImage;
74 guint biXPelsPerMeter;
75 guint biYPelsPerMeter;
76 guint biClrUsed;
77 guint biClrImportant;
80 #ifdef DUMPBIH
81 /*
83 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
84 debugging purposes.
87 static void DumpBIH(unsigned char *BIH)
89 printf("biSize = %i \n",
90 (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
91 printf("biWidth = %i \n",
92 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
93 printf("biHeight = %i \n",
94 (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
95 (BIH[8]));
96 printf("biPlanes = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
97 printf("biBitCount = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
98 printf("biCompress = %i \n",
99 (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
100 (BIH[16]));
101 printf("biSizeImage = %i \n",
102 (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
103 (BIH[20]));
104 printf("biXPels = %i \n",
105 (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
106 (BIH[24]));
107 printf("biYPels = %i \n",
108 (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
109 (BIH[28]));
110 printf("biClrUsed = %i \n",
111 (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
112 (BIH[32]));
113 printf("biClrImprtnt= %i \n",
114 (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
115 (BIH[36]));
117 #endif
119 /* Progressive loading */
120 struct headerpair {
121 gint width;
122 gint height;
123 guint depth;
124 guint Negative; /* Negative = 1 -> top down BMP,
125 Negative = 0 -> bottom up BMP */
128 struct ico_progressive_state {
129 GdkPixbufModuleSizeFunc size_func;
130 GdkPixbufModulePreparedFunc prepared_func;
131 GdkPixbufModuleUpdatedFunc updated_func;
132 gpointer user_data;
134 gint HeaderSize; /* The size of the header-part (incl colormap) */
135 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
136 gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */
137 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
139 gint LineWidth; /* The width of a line in bytes */
140 guchar *LineBuf; /* Buffer for 1 line */
141 gint LineDone; /* # of bytes in LineBuf */
142 gint Lines; /* # of finished lines */
144 gint Type; /*
145 32 = RGBA
146 24 = RGB
147 16 = 555 RGB
148 8 = 8 bit colormapped
149 4 = 4 bpp colormapped
150 1 = 1 bit bitonal
152 gboolean cursor;
153 gint x_hot;
154 gint y_hot;
156 struct headerpair Header; /* Decoded (BE->CPU) header */
158 gint DIBoffset;
159 gint ImageScore;
162 GdkPixbuf *pixbuf; /* Our "target" */
165 static gpointer
166 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
167 GdkPixbufModulePreparedFunc prepared_func,
168 GdkPixbufModuleUpdatedFunc updated_func,
169 gpointer user_data,
170 GError **error);
171 static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
172 static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
173 const guchar * buf, guint size,
174 GError **error);
176 static void
177 context_free (struct ico_progressive_state *context)
179 g_free (context->LineBuf);
180 context->LineBuf = NULL;
181 g_free (context->HeaderBuf);
183 if (context->pixbuf)
184 g_object_unref (context->pixbuf);
186 g_free (context);
189 static void DecodeHeader(guchar *Data, gint Bytes,
190 struct ico_progressive_state *State,
191 GError **error)
193 /* For ICO's we have to be very clever. There are multiple images possible
194 in an .ICO. As a simple heuristic, we select the image which occupies the
195 largest number of bytes.
198 gint IconCount = 0; /* The number of icon-versions in the file */
199 guchar *BIH; /* The DIB for the used icon */
200 guchar *Ptr;
201 gint I;
202 guint16 imgtype; /* 1 = icon, 2 = cursor */
204 /* Step 1: The ICO header */
206 /* First word should be 0 according to specs */
207 if (((Data[1] << 8) + Data[0]) != 0) {
208 g_set_error_literal (error,
209 GDK_PIXBUF_ERROR,
210 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
211 _("Invalid header in icon"));
212 return;
216 imgtype = (Data[3] << 8) + Data[2];
218 State->cursor = (imgtype == 2) ? TRUE : FALSE;
220 /* If it is not a cursor make sure it is actually an icon */
221 if (!State->cursor && imgtype != 1) {
222 g_set_error_literal (error,
223 GDK_PIXBUF_ERROR,
224 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
225 _("Invalid header in icon"));
226 return;
230 IconCount = (Data[5] << 8) + (Data[4]);
232 State->HeaderSize = 6 + IconCount*16;
234 if (State->HeaderSize>State->BytesInHeaderBuf) {
235 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
236 if (!tmp) {
237 g_set_error_literal (error,
238 GDK_PIXBUF_ERROR,
239 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
240 _("Not enough memory to load icon"));
241 return;
243 State->HeaderBuf = tmp;
244 State->BytesInHeaderBuf = State->HeaderSize;
246 if (Bytes < State->HeaderSize)
247 return;
249 /* We now have all the "short-specs" of the versions
250 So we iterate through them and select the best one */
252 State->ImageScore = 0;
253 State->DIBoffset = 0;
254 Ptr = Data + 6;
255 for (I=0;I<IconCount;I++) {
256 int ThisScore;
258 ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
260 if (ThisScore>=State->ImageScore) {
261 State->ImageScore = ThisScore;
262 State->x_hot = (Ptr[5] << 8) + Ptr[4];
263 State->y_hot = (Ptr[7] << 8) + Ptr[6];
264 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
265 (Ptr[13]<<8) + (Ptr[12]);
270 Ptr += 16;
273 if (State->DIBoffset < 0) {
274 g_set_error_literal (error,
275 GDK_PIXBUF_ERROR,
276 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
277 _("Invalid header in icon"));
278 return;
281 /* We now have a winner, pointed to in State->DIBoffset,
282 so we know how many bytes are in the "header" part. */
284 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
286 if (State->HeaderSize < 0) {
287 g_set_error_literal (error,
288 GDK_PIXBUF_ERROR,
289 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
290 _("Invalid header in icon"));
291 return;
294 if (State->HeaderSize>State->BytesInHeaderBuf) {
295 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
296 if (!tmp) {
297 g_set_error_literal (error,
298 GDK_PIXBUF_ERROR,
299 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
300 _("Not enough memory to load icon"));
301 return;
303 State->HeaderBuf = tmp;
304 State->BytesInHeaderBuf = State->HeaderSize;
306 if (Bytes<State->HeaderSize)
307 return;
309 BIH = Data+State->DIBoffset;
311 #ifdef DUMPBIH
312 DumpBIH(BIH);
313 #endif
314 /* Add the palette to the headersize */
316 State->Header.width =
317 (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
318 if (State->Header.width == 0) {
319 g_set_error_literal (error,
320 GDK_PIXBUF_ERROR,
321 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
322 _("Icon has zero width"));
323 return;
325 State->Header.height =
326 (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
327 /* /2 because the BIH height includes the transparency mask */
328 if (State->Header.height == 0) {
329 g_set_error_literal (error,
330 GDK_PIXBUF_ERROR,
331 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
332 _("Icon has zero height"));
333 return;
335 State->Header.depth = (BIH[15] << 8) + (BIH[14]);
337 State->Type = State->Header.depth;
338 if (State->Lines>=State->Header.height)
339 State->Type = 1; /* The transparency mask is 1 bpp */
341 /* Determine the palette size. If the header indicates 0, it
342 is actually the maximum for the bpp. You have to love the
343 guys who made the spec. */
344 I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
345 I = I*4;
346 if ((I==0)&&(State->Type==1))
347 I = 2*4;
348 if ((I==0)&&(State->Type==4))
349 I = 16*4;
350 if ((I==0)&&(State->Type==8))
351 I = 256*4;
353 State->HeaderSize+=I;
355 if (State->HeaderSize < 0) {
356 g_set_error_literal (error,
357 GDK_PIXBUF_ERROR,
358 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
359 _("Invalid header in icon"));
360 return;
363 if (State->HeaderSize>State->BytesInHeaderBuf) {
364 guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
365 if (!tmp) {
366 g_set_error_literal (error,
367 GDK_PIXBUF_ERROR,
368 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
369 _("Not enough memory to load icon"));
370 return;
372 State->HeaderBuf = tmp;
373 State->BytesInHeaderBuf = State->HeaderSize;
375 if (Bytes < State->HeaderSize)
376 return;
378 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
379 || (BIH[19] != 0)) {
380 /* FIXME: is this the correct message? */
381 g_set_error_literal (error,
382 GDK_PIXBUF_ERROR,
383 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
384 _("Compressed icons are not supported"));
385 return;
388 /* Negative heights mean top-down pixel-order */
389 if (State->Header.height < 0) {
390 State->Header.height = -State->Header.height;
391 State->Header.Negative = 1;
393 if (State->Header.width < 0) {
394 State->Header.width = -State->Header.width;
396 g_assert (State->Header.width > 0);
397 g_assert (State->Header.height > 0);
399 if (State->Type == 32)
400 State->LineWidth = State->Header.width * 4;
401 else if (State->Type == 24)
402 State->LineWidth = State->Header.width * 3;
403 else if (State->Type == 16)
404 State->LineWidth = State->Header.width * 2;
405 else if (State->Type == 8)
406 State->LineWidth = State->Header.width * 1;
407 else if (State->Type == 4)
408 State->LineWidth = (State->Header.width+1)/2;
409 else if (State->Type == 1) {
410 State->LineWidth = State->Header.width / 8;
411 if ((State->Header.width & 7) != 0)
412 State->LineWidth++;
413 } else {
414 g_set_error_literal (error,
415 GDK_PIXBUF_ERROR,
416 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
417 _("Unsupported icon type"));
418 return;
421 /* Pad to a 32 bit boundary */
422 if (((State->LineWidth % 4) > 0))
423 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
426 if (State->LineBuf == NULL) {
427 State->LineBuf = g_try_malloc(State->LineWidth);
428 if (!State->LineBuf) {
429 g_set_error_literal (error,
430 GDK_PIXBUF_ERROR,
431 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
432 _("Not enough memory to load icon"));
433 return;
437 g_assert(State->LineBuf != NULL);
440 if (State->pixbuf == NULL) {
441 #if 1
442 if (State->size_func) {
443 gint width = State->Header.width;
444 gint height = State->Header.height;
446 (*State->size_func) (&width, &height, State->user_data);
447 if (width == 0 || height == 0) {
448 State->LineWidth = 0;
449 return;
452 #endif
454 State->pixbuf =
455 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
456 State->Header.width,
457 State->Header.height);
458 if (!State->pixbuf) {
459 g_set_error_literal (error,
460 GDK_PIXBUF_ERROR,
461 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
462 _("Not enough memory to load icon"));
463 return;
465 if (State->cursor) {
466 gchar hot[10];
467 g_snprintf (hot, 10, "%d", State->x_hot);
468 gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
469 g_snprintf (hot, 10, "%d", State->y_hot);
470 gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
473 if (State->prepared_func != NULL)
474 /* Notify the client that we are ready to go */
475 (*State->prepared_func) (State->pixbuf,
476 NULL,
477 State->user_data);
484 * func - called when we have pixmap created (but no image data)
485 * user_data - passed as arg 1 to func
486 * return context (opaque to user)
489 static gpointer
490 gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
491 GdkPixbufModulePreparedFunc prepared_func,
492 GdkPixbufModuleUpdatedFunc updated_func,
493 gpointer user_data,
494 GError **error)
496 struct ico_progressive_state *context;
498 context = g_new0(struct ico_progressive_state, 1);
499 context->size_func = size_func;
500 context->prepared_func = prepared_func;
501 context->updated_func = updated_func;
502 context->user_data = user_data;
504 context->HeaderSize = 54;
505 context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
506 if (!context->HeaderBuf) {
507 g_free (context);
508 g_set_error_literal (error,
509 GDK_PIXBUF_ERROR,
510 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
511 _("Not enough memory to load ICO file"));
512 return NULL;
514 /* 4*256 for the colormap */
515 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
516 context->HeaderDone = 0;
518 context->LineWidth = 0;
519 context->LineBuf = NULL;
520 context->LineDone = 0;
521 context->Lines = 0;
523 context->Type = 0;
525 memset(&context->Header, 0, sizeof(struct headerpair));
528 context->pixbuf = NULL;
531 return (gpointer) context;
535 * context - returned from image_begin_load
537 * free context, unref gdk_pixbuf
539 static gboolean
540 gdk_pixbuf__ico_image_stop_load(gpointer data,
541 GError **error)
543 struct ico_progressive_state *context =
544 (struct ico_progressive_state *) data;
546 /* FIXME this thing needs to report errors if
547 * we have unused image data
550 g_return_val_if_fail(context != NULL, TRUE);
552 context_free (context);
553 return TRUE;
556 static void
557 OneLine32 (struct ico_progressive_state *context)
559 gint X;
560 guchar *Pixels;
562 X = 0;
563 if (context->Header.Negative == 0)
564 Pixels = (context->pixbuf->pixels +
565 context->pixbuf->rowstride *
566 (context->Header.height - context->Lines - 1));
567 else
568 Pixels = (context->pixbuf->pixels +
569 context->pixbuf->rowstride *
570 context->Lines);
571 while (X < context->Header.width) {
572 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
573 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
574 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
575 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
576 X++;
580 static void OneLine24(struct ico_progressive_state *context)
582 gint X;
583 guchar *Pixels;
585 X = 0;
586 if (context->Header.Negative == 0)
587 Pixels = (context->pixbuf->pixels +
588 context->pixbuf->rowstride *
589 (context->Header.height - context->Lines - 1));
590 else
591 Pixels = (context->pixbuf->pixels +
592 context->pixbuf->rowstride *
593 context->Lines);
594 while (X < context->Header.width) {
595 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
596 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
597 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
598 X++;
603 static void
604 OneLine16 (struct ico_progressive_state *context)
606 int i;
607 guchar *pixels;
608 guchar *src;
610 if (context->Header.Negative == 0)
611 pixels = (context->pixbuf->pixels +
612 context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
613 else
614 pixels = (context->pixbuf->pixels +
615 context->pixbuf->rowstride * context->Lines);
617 src = context->LineBuf;
619 for (i = 0; i < context->Header.width; i++) {
620 int v, r, g, b;
622 v = (int) src[0] | ((int) src[1] << 8);
623 src += 2;
625 /* Extract 5-bit RGB values */
627 r = (v >> 10) & 0x1f;
628 g = (v >> 5) & 0x1f;
629 b = v & 0x1f;
631 /* Fill the rightmost bits to form 8-bit values */
633 *pixels++ = (r << 3) | (r >> 2);
634 *pixels++ = (g << 3) | (g >> 2);
635 *pixels++ = (b << 3) | (b >> 2);
636 pixels++; /* skip alpha channel */
641 static void OneLine8(struct ico_progressive_state *context)
643 gint X;
644 guchar *Pixels;
646 X = 0;
647 if (context->Header.Negative == 0)
648 Pixels = (context->pixbuf->pixels +
649 context->pixbuf->rowstride *
650 (context->Header.height - context->Lines - 1));
651 else
652 Pixels = (context->pixbuf->pixels +
653 context->pixbuf->rowstride *
654 context->Lines);
655 while (X < context->Header.width) {
656 /* The joys of having a BGR byteorder */
657 Pixels[X * 4 + 0] =
658 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
659 Pixels[X * 4 + 1] =
660 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
661 Pixels[X * 4 + 2] =
662 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
663 X++;
666 static void OneLine4(struct ico_progressive_state *context)
668 gint X;
669 guchar *Pixels;
671 X = 0;
672 if (context->Header.Negative == 0)
673 Pixels = (context->pixbuf->pixels +
674 context->pixbuf->rowstride *
675 (context->Header.height - context->Lines - 1));
676 else
677 Pixels = (context->pixbuf->pixels +
678 context->pixbuf->rowstride *
679 context->Lines);
681 while (X < context->Header.width) {
682 guchar Pix;
684 Pix = context->LineBuf[X/2];
686 Pixels[X * 4 + 0] =
687 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
688 Pixels[X * 4 + 1] =
689 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
690 Pixels[X * 4 + 2] =
691 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
692 X++;
693 if (X<context->Header.width) {
694 /* Handle the other 4 bit pixel only when there is one */
695 Pixels[X * 4 + 0] =
696 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
697 Pixels[X * 4 + 1] =
698 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
699 Pixels[X * 4 + 2] =
700 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
701 X++;
707 static void OneLine1(struct ico_progressive_state *context)
709 gint X;
710 guchar *Pixels;
712 X = 0;
713 if (context->Header.Negative == 0)
714 Pixels = (context->pixbuf->pixels +
715 context->pixbuf->rowstride *
716 (context->Header.height - context->Lines - 1));
717 else
718 Pixels = (context->pixbuf->pixels +
719 context->pixbuf->rowstride *
720 context->Lines);
721 while (X < context->Header.width) {
722 int Bit;
724 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
725 Bit = Bit & 1;
726 /* The joys of having a BGR byteorder */
727 Pixels[X * 4 + 0] = Bit*255;
728 Pixels[X * 4 + 1] = Bit*255;
729 Pixels[X * 4 + 2] = Bit*255;
730 X++;
734 static void OneLineTransp(struct ico_progressive_state *context)
736 gint X;
737 guchar *Pixels;
739 /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
740 if (context->Header.depth == 32)
741 return;
743 X = 0;
744 if (context->Header.Negative == 0)
745 Pixels = (context->pixbuf->pixels +
746 context->pixbuf->rowstride *
747 (2*context->Header.height - context->Lines - 1));
748 else
749 Pixels = (context->pixbuf->pixels +
750 context->pixbuf->rowstride *
751 (context->Lines-context->Header.height));
752 while (X < context->Header.width) {
753 int Bit;
755 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
756 Bit = Bit & 1;
757 /* The joys of having a BGR byteorder */
758 Pixels[X * 4 + 3] = 255-Bit*255;
759 #if 0
760 if (Bit){
761 Pixels[X*4+0] = 255;
762 Pixels[X*4+1] = 255;
763 } else {
764 Pixels[X*4+0] = 0;
765 Pixels[X*4+1] = 0;
767 #endif
768 X++;
773 static void OneLine(struct ico_progressive_state *context)
775 context->LineDone = 0;
777 if (context->Lines >= context->Header.height*2) {
778 return;
781 if (context->Lines <context->Header.height) {
782 if (context->Type == 32)
783 OneLine32 (context);
784 else if (context->Type == 24)
785 OneLine24(context);
786 else if (context->Type == 16)
787 OneLine16 (context);
788 else if (context->Type == 8)
789 OneLine8(context);
790 else if (context->Type == 4)
791 OneLine4(context);
792 else if (context->Type == 1)
793 OneLine1(context);
794 else
795 g_assert_not_reached ();
796 } else
797 OneLineTransp(context);
799 context->Lines++;
800 if (context->Lines>=context->Header.height) {
801 context->Type = 1;
802 context->LineWidth = context->Header.width / 8;
803 if ((context->Header.width & 7) != 0)
804 context->LineWidth++;
805 /* Pad to a 32 bit boundary */
806 if (((context->LineWidth % 4) > 0))
807 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
812 if (context->updated_func != NULL) {
813 (*context->updated_func) (context->pixbuf,
815 context->Lines % context->Header.height,
816 context->Header.width,
818 context->user_data);
824 * context - from image_begin_load
825 * buf - new image data
826 * size - length of new image data
828 * append image data onto inrecrementally built output image
830 static gboolean
831 gdk_pixbuf__ico_image_load_increment(gpointer data,
832 const guchar * buf,
833 guint size,
834 GError **error)
836 struct ico_progressive_state *context =
837 (struct ico_progressive_state *) data;
839 gint BytesToCopy;
841 while (size > 0) {
842 g_assert(context->LineDone >= 0);
843 if (context->HeaderDone < context->HeaderSize) { /* We still
844 have headerbytes to do */
845 BytesToCopy =
846 context->HeaderSize - context->HeaderDone;
847 if (BytesToCopy > size)
848 BytesToCopy = size;
850 memmove(context->HeaderBuf + context->HeaderDone,
851 buf, BytesToCopy);
853 size -= BytesToCopy;
854 buf += BytesToCopy;
855 context->HeaderDone += BytesToCopy;
857 else {
858 BytesToCopy =
859 context->LineWidth - context->LineDone;
860 if (BytesToCopy > size)
861 BytesToCopy = size;
863 if (BytesToCopy > 0) {
864 memmove(context->LineBuf +
865 context->LineDone, buf,
866 BytesToCopy);
868 size -= BytesToCopy;
869 buf += BytesToCopy;
870 context->LineDone += BytesToCopy;
872 if ((context->LineDone >= context->LineWidth) &&
873 (context->LineWidth > 0))
874 OneLine(context);
879 if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
880 GError *decode_err = NULL;
881 DecodeHeader(context->HeaderBuf,
882 context->HeaderDone, context, &decode_err);
883 if (context->LineBuf != NULL && context->LineWidth == 0)
884 return TRUE;
886 if (decode_err) {
887 g_propagate_error (error, decode_err);
888 return FALSE;
893 return TRUE;
896 /* saving ICOs */
898 static gint
899 write8 (FILE *f,
900 guint8 *data,
901 gint count)
903 gint bytes;
904 gint written;
906 written = 0;
907 while (count > 0)
909 bytes = fwrite ((char*) data, sizeof (char), count, f);
910 if (bytes <= 0)
911 break;
912 count -= bytes;
913 data += bytes;
914 written += bytes;
917 return written;
920 static gint
921 write16 (FILE *f,
922 guint16 *data,
923 gint count)
925 gint i;
927 for (i = 0; i < count; i++)
928 data[i] = GUINT16_TO_LE (data[i]);
930 return write8 (f, (guint8*) data, count * 2);
933 static gint
934 write32 (FILE *f,
935 guint32 *data,
936 gint count)
938 gint i;
940 for (i = 0; i < count; i++)
941 data[i] = GUINT32_TO_LE (data[i]);
943 return write8 (f, (guint8*) data, count * 4);
946 typedef struct _IconEntry IconEntry;
947 struct _IconEntry {
948 gint width;
949 gint height;
950 gint depth;
951 gint hot_x;
952 gint hot_y;
954 guint8 n_colors;
955 guint32 *colors;
956 guint xor_rowstride;
957 guint8 *xor;
958 guint and_rowstride;
959 guint8 *and;
962 static gboolean
963 fill_entry (IconEntry *icon,
964 GdkPixbuf *pixbuf,
965 gint hot_x,
966 gint hot_y,
967 GError **error)
969 guchar *p, *pixels, *and, *xor;
970 gint n_channels, v, x, y;
972 if (icon->width > 255 || icon->height > 255) {
973 g_set_error_literal (error,
974 GDK_PIXBUF_ERROR,
975 GDK_PIXBUF_ERROR_BAD_OPTION,
976 _("Image too large to be saved as ICO"));
977 return FALSE;
980 if (hot_x > -1 && hot_y > -1) {
981 icon->hot_x = hot_x;
982 icon->hot_y = hot_y;
983 if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
984 g_set_error_literal (error,
985 GDK_PIXBUF_ERROR,
986 GDK_PIXBUF_ERROR_BAD_OPTION,
987 _("Cursor hotspot outside image"));
988 return FALSE;
991 else {
992 icon->hot_x = -1;
993 icon->hot_y = -1;
996 switch (icon->depth) {
997 case 32:
998 icon->xor_rowstride = icon->width * 4;
999 break;
1000 case 24:
1001 icon->xor_rowstride = icon->width * 3;
1002 break;
1003 case 16:
1004 icon->xor_rowstride = icon->width * 2;
1005 break;
1006 default:
1007 g_set_error (error,
1008 GDK_PIXBUF_ERROR,
1009 GDK_PIXBUF_ERROR_BAD_OPTION,
1010 _("Unsupported depth for ICO file: %d"), icon->depth);
1011 return FALSE;
1014 if ((icon->xor_rowstride % 4) != 0)
1015 icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
1016 icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
1018 icon->and_rowstride = icon->width / 8;
1019 if ((icon->and_rowstride % 4) != 0)
1020 icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
1021 icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
1023 pixels = gdk_pixbuf_get_pixels (pixbuf);
1024 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1025 for (y = 0; y < icon->height; y++) {
1026 p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
1027 and = icon->and + icon->and_rowstride * y;
1028 xor = icon->xor + icon->xor_rowstride * y;
1029 for (x = 0; x < icon->width; x++) {
1030 switch (icon->depth) {
1031 case 32:
1032 xor[0] = p[2];
1033 xor[1] = p[1];
1034 xor[2] = p[0];
1035 xor[3] = 0xff;
1036 if (n_channels == 4) {
1037 xor[3] = p[3];
1038 if (p[3] < 0x80)
1039 *and |= 1 << (7 - x % 8);
1041 xor += 4;
1042 break;
1043 case 24:
1044 xor[0] = p[2];
1045 xor[1] = p[1];
1046 xor[2] = p[0];
1047 if (n_channels == 4 && p[3] < 0x80)
1048 *and |= 1 << (7 - x % 8);
1049 xor += 3;
1050 break;
1051 case 16:
1052 v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1053 xor[0] = v & 0xff;
1054 xor[1] = v >> 8;
1055 if (n_channels == 4 && p[3] < 0x80)
1056 *and |= 1 << (7 - x % 8);
1057 xor += 2;
1058 break;
1061 p += n_channels;
1062 if (x % 8 == 7)
1063 and++;
1067 return TRUE;
1070 static void
1071 free_entry (IconEntry *icon)
1073 g_free (icon->colors);
1074 g_free (icon->and);
1075 g_free (icon->xor);
1076 g_free (icon);
1079 static void
1080 write_icon (FILE *f, GSList *entries)
1082 IconEntry *icon;
1083 GSList *entry;
1084 guint8 bytes[4];
1085 guint16 words[4];
1086 guint32 dwords[6];
1087 gint type;
1088 gint n_entries;
1089 gint offset;
1090 gint size;
1092 if (((IconEntry *)entries->data)->hot_x > -1)
1093 type = 2;
1094 else
1095 type = 1;
1096 n_entries = g_slist_length (entries);
1098 /* header */
1099 words[0] = 0;
1100 words[1] = type;
1101 words[2] = n_entries;
1102 write16 (f, words, 3);
1104 offset = 6 + 16 * n_entries;
1106 for (entry = entries; entry; entry = entry->next) {
1107 icon = (IconEntry *)entry->data;
1108 size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1110 /* directory entry */
1111 bytes[0] = icon->width;
1112 bytes[1] = icon->height;
1113 bytes[2] = icon->n_colors;
1114 bytes[3] = 0;
1115 write8 (f, bytes, 4);
1116 if (type == 1) {
1117 words[0] = 1;
1118 words[1] = icon->depth;
1120 else {
1121 words[0] = icon->hot_x;
1122 words[1] = icon->hot_y;
1124 write16 (f, words, 2);
1125 dwords[0] = size;
1126 dwords[1] = offset;
1127 write32 (f, dwords, 2);
1129 offset += size;
1132 for (entry = entries; entry; entry = entry->next) {
1133 icon = (IconEntry *)entry->data;
1135 /* bitmap header */
1136 dwords[0] = 40;
1137 dwords[1] = icon->width;
1138 dwords[2] = icon->height * 2;
1139 write32 (f, dwords, 3);
1140 words[0] = 1;
1141 words[1] = icon->depth;
1142 write16 (f, words, 2);
1143 dwords[0] = 0;
1144 dwords[1] = 0;
1145 dwords[2] = 0;
1146 dwords[3] = 0;
1147 dwords[4] = 0;
1148 dwords[5] = 0;
1149 write32 (f, dwords, 6);
1151 /* image data */
1152 write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1153 write8 (f, icon->and, icon->and_rowstride * icon->height);
1157 static gboolean
1158 gdk_pixbuf__ico_image_save (FILE *f,
1159 GdkPixbuf *pixbuf,
1160 gchar **keys,
1161 gchar **values,
1162 GError **error)
1164 gint hot_x, hot_y;
1165 IconEntry *icon;
1166 GSList *entries = NULL;
1168 /* support only single-image ICOs for now */
1169 icon = g_new0 (IconEntry, 1);
1170 icon->width = gdk_pixbuf_get_width (pixbuf);
1171 icon->height = gdk_pixbuf_get_height (pixbuf);
1172 icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1173 hot_x = -1;
1174 hot_y = -1;
1176 /* parse options */
1177 if (keys && *keys) {
1178 gchar **kiter;
1179 gchar **viter;
1181 for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1182 char *endptr;
1183 if (strcmp (*kiter, "depth") == 0) {
1184 sscanf (*viter, "%d", &icon->depth);
1186 else if (strcmp (*kiter, "x_hot") == 0) {
1187 hot_x = strtol (*viter, &endptr, 10);
1189 else if (strcmp (*kiter, "y_hot") == 0) {
1190 hot_y = strtol (*viter, &endptr, 10);
1196 if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1197 free_entry (icon);
1198 return FALSE;
1201 entries = g_slist_append (entries, icon);
1202 write_icon (f, entries);
1204 g_slist_foreach (entries, (GFunc)free_entry, NULL);
1205 g_slist_free (entries);
1207 return TRUE;
1210 #ifndef INCLUDE_ico
1211 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1212 #else
1213 #define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
1214 #endif
1216 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1218 module->begin_load = gdk_pixbuf__ico_image_begin_load;
1219 module->stop_load = gdk_pixbuf__ico_image_stop_load;
1220 module->load_increment = gdk_pixbuf__ico_image_load_increment;
1221 module->save = gdk_pixbuf__ico_image_save;
1224 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1226 static GdkPixbufModulePattern signature[] = {
1227 { " \x1 ", "zz znz", 100 },
1228 { " \x2 ", "zz znz", 100 },
1229 { NULL, NULL, 0 }
1231 static gchar * mime_types[] = {
1232 "image/x-icon",
1233 "image/x-ico",
1234 "image/x-win-bitmap",
1235 NULL
1237 static gchar * extensions[] = {
1238 "ico",
1239 "cur",
1240 NULL
1243 info->name = "ico";
1244 info->signature = signature;
1245 info->description = N_("The ICO image format");
1246 info->mime_types = mime_types;
1247 info->extensions = extensions;
1248 info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1249 info->license = "LGPL";