+/* -*- mode: C; c-file-style: "linux" -*- */
/* GdkPixbuf library - Windows Icon/Cursor image loader
*
* Copyright (C) 1999 The Free Software Foundation
*/
-#include <config.h>
+#include "config.h"
#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include <string.h>
#include "gdk-pixbuf-private.h"
#include "gdk-pixbuf-io.h"
+#include <errno.h>
\f
/* Progressive loading */
struct headerpair {
- guint width;
- guint height;
+ gint width;
+ gint height;
guint depth;
guint Negative; /* Negative = 1 -> top down BMP,
Negative = 0 -> bottom up BMP */
};
struct ico_progressive_state {
- ModulePreparedNotifyFunc prepared_func;
- ModuleUpdatedNotifyFunc updated_func;
+ GdkPixbufModuleSizeFunc size_func;
+ GdkPixbufModulePreparedFunc prepared_func;
+ GdkPixbufModuleUpdatedFunc updated_func;
gpointer user_data;
gint HeaderSize; /* The size of the header-part (incl colormap) */
gint Lines; /* # of finished lines */
gint Type; /*
+ 32 = RGBA
24 = RGB
+ 16 = 555 RGB
8 = 8 bit colormapped
+ 4 = 4 bpp colormapped
1 = 1 bit bitonal
*/
-
+ gboolean cursor;
+ gint x_hot;
+ gint y_hot;
struct headerpair Header; /* Decoded (BE->CPU) header */
GdkPixbuf *pixbuf; /* Our "target" */
};
-gpointer
-gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
- ModuleUpdatedNotifyFunc updated_func,
- ModuleFrameDoneNotifyFunc frame_done_func,
- ModuleAnimationDoneNotifyFunc anim_done_func,
- gpointer user_data);
-void gdk_pixbuf__ico_image_stop_load(gpointer data);
-gboolean gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size);
-
-
-
-/* Shared library entry point --> Can go when generic_image_load
- enters gdk-pixbuf-io */
-GdkPixbuf *
-gdk_pixbuf__ico_image_load(FILE * f)
+static gpointer
+gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
+ GdkPixbufModulePreparedFunc prepared_func,
+ GdkPixbufModuleUpdatedFunc updated_func,
+ gpointer user_data,
+ GError **error);
+static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
+static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
+ const guchar * buf, guint size,
+ GError **error);
+
+static void
+context_free (struct ico_progressive_state *context)
{
- guchar *membuf;
- size_t length;
- struct ico_progressive_state *State;
-
- GdkPixbuf *pb;
-
- State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, NULL, NULL);
- membuf = g_malloc(4096);
-
- g_assert(membuf != NULL);
-
- while (feof(f) == 0) {
- length = fread(membuf, 1, 4096, f);
- if (length > 0)
- gdk_pixbuf__ico_image_load_increment(State, membuf, length);
-
- }
- g_free(membuf);
- if (State->pixbuf != NULL)
- gdk_pixbuf_ref(State->pixbuf);
+ g_free (context->LineBuf);
+ context->LineBuf = NULL;
+ g_free (context->HeaderBuf);
- pb = State->pixbuf;
+ if (context->pixbuf)
+ g_object_unref (context->pixbuf);
- gdk_pixbuf__ico_image_stop_load(State);
- return pb;
+ g_free (context);
}
static void DecodeHeader(guchar *Data, gint Bytes,
- struct ico_progressive_state *State)
+ struct ico_progressive_state *State,
+ GError **error)
{
/* For ICO's we have to be very clever. There are multiple images possible
- in an .ICO. For now, we select (in order of priority):
- 1) The one with the highest number of colors
- 2) The largest one
+ in an .ICO. As a simple heuristic, we select the image which occupies the
+ largest number of bytes.
*/
gint IconCount = 0; /* The number of icon-versions in the file */
guchar *BIH; /* The DIB for the used icon */
guchar *Ptr;
gint I;
+ guint16 imgtype; /* 1 = icon, 2 = cursor */
/* Step 1: The ICO header */
-
+
+ /* First word should be 0 according to specs */
+ if (((Data[1] << 8) + Data[0]) != 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+
+ }
+
+ imgtype = (Data[3] << 8) + Data[2];
+
+ State->cursor = (imgtype == 2) ? TRUE : FALSE;
+
+ /* If it is not a cursor make sure it is actually an icon */
+ if (!State->cursor && imgtype != 1) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+ }
+
+
IconCount = (Data[5] << 8) + (Data[4]);
-
+
State->HeaderSize = 6 + IconCount*16;
-
+
if (State->HeaderSize>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!tmp) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ State->HeaderBuf = tmp;
State->BytesInHeaderBuf = State->HeaderSize;
}
if (Bytes < State->HeaderSize)
State->DIBoffset = 0;
Ptr = Data + 6;
for (I=0;I<IconCount;I++) {
- int ThisWidth, ThisHeight,ThisColors;
int ThisScore;
- ThisWidth = Ptr[0];
- ThisHeight = Ptr[1];
- ThisColors = (Ptr[2]);
- if (ThisColors==0)
- ThisColors=256; /* Yes, this is in the spec, ugh */
-
- ThisScore = ThisColors*1024+ThisWidth*ThisHeight;
+ ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
- if (ThisScore>State->ImageScore) {
+ if (ThisScore>=State->ImageScore) {
State->ImageScore = ThisScore;
+ State->x_hot = (Ptr[5] << 8) + Ptr[4];
+ State->y_hot = (Ptr[7] << 8) + Ptr[6];
State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
(Ptr[13]<<8) + (Ptr[12]);
Ptr += 16;
}
-
+
+ if (State->DIBoffset < 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+ }
+
/* We now have a winner, pointed to in State->DIBoffset,
so we know how many bytes are in the "header" part. */
State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
-
+
+ if (State->HeaderSize < 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+ }
+
if (State->HeaderSize>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!tmp) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ State->HeaderBuf = tmp;
State->BytesInHeaderBuf = State->HeaderSize;
}
if (Bytes<State->HeaderSize)
#ifdef DUMPBIH
DumpBIH(BIH);
#endif
-
/* Add the palette to the headersize */
State->Header.width =
(int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
+ if (State->Header.width == 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Icon has zero width"));
+ return;
+ }
State->Header.height =
- (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8])/2;
+ (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
/* /2 because the BIH height includes the transparency mask */
- State->Header.depth = (BIH[15] << 8) + (BIH[14]);;
+ if (State->Header.height == 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Icon has zero height"));
+ return;
+ }
+ State->Header.depth = (BIH[15] << 8) + (BIH[14]);
State->Type = State->Header.depth;
if (State->Lines>=State->Header.height)
State->Type = 1; /* The transparency mask is 1 bpp */
-
-
/* Determine the palette size. If the header indicates 0, it
is actually the maximum for the bpp. You have to love the
guys who made the spec. */
State->HeaderSize+=I;
+ if (State->HeaderSize < 0) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Invalid header in icon"));
+ return;
+ }
+
if (State->HeaderSize>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!tmp) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ State->HeaderBuf = tmp;
State->BytesInHeaderBuf = State->HeaderSize;
}
if (Bytes < State->HeaderSize)
if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
|| (BIH[19] != 0)) {
- g_assert(0); /* Compressed icons aren't allowed */
+ /* FIXME: is this the correct message? */
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Compressed icons are not supported"));
+ return;
}
/* Negative heights mean top-down pixel-order */
if (State->Header.width < 0) {
State->Header.width = -State->Header.width;
}
+ g_assert (State->Header.width > 0);
+ g_assert (State->Header.height > 0);
- if (State->Type == 24)
+ if (State->Type == 32)
+ State->LineWidth = State->Header.width * 4;
+ else if (State->Type == 24)
State->LineWidth = State->Header.width * 3;
- if (State->Type == 8)
+ else if (State->Type == 16)
+ State->LineWidth = State->Header.width * 2;
+ else if (State->Type == 8)
State->LineWidth = State->Header.width * 1;
- if (State->Type == 4) {
+ else if (State->Type == 4)
State->LineWidth = (State->Header.width+1)/2;
- }
- if (State->Type == 1) {
+ else if (State->Type == 1) {
State->LineWidth = State->Header.width / 8;
if ((State->Header.width & 7) != 0)
State->LineWidth++;
+ } else {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Unsupported icon type"));
+ return;
}
/* Pad to a 32 bit boundary */
State->LineWidth = (State->LineWidth / 4) * 4 + 4;
- if (State->LineBuf == NULL)
- State->LineBuf = g_malloc(State->LineWidth);
+ if (State->LineBuf == NULL) {
+ State->LineBuf = g_try_malloc(State->LineWidth);
+ if (!State->LineBuf) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ }
g_assert(State->LineBuf != NULL);
if (State->pixbuf == NULL) {
+#if 1
+ if (State->size_func) {
+ gint width = State->Header.width;
+ gint height = State->Header.height;
+
+ (*State->size_func) (&width, &height, State->user_data);
+ if (width == 0 || height == 0) {
+ State->LineWidth = 0;
+ return;
+ }
+ }
+#endif
+
State->pixbuf =
gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
- (gint) State->Header.width,
- (gint) State->Header.height);
+ State->Header.width,
+ State->Header.height);
+ if (!State->pixbuf) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
+ if (State->cursor) {
+ gchar hot[10];
+ g_snprintf (hot, 10, "%d", State->x_hot);
+ gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
+ g_snprintf (hot, 10, "%d", State->y_hot);
+ gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
+ }
if (State->prepared_func != NULL)
/* Notify the client that we are ready to go */
(*State->prepared_func) (State->pixbuf,
+ NULL,
State->user_data);
}
* return context (opaque to user)
*/
-gpointer
-gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
- ModuleUpdatedNotifyFunc updated_func,
- ModuleFrameDoneNotifyFunc frame_done_func,
- ModuleAnimationDoneNotifyFunc anim_done_func,
- gpointer user_data)
+static gpointer
+gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
+ GdkPixbufModulePreparedFunc prepared_func,
+ GdkPixbufModuleUpdatedFunc updated_func,
+ gpointer user_data,
+ GError **error)
{
struct ico_progressive_state *context;
context = g_new0(struct ico_progressive_state, 1);
+ context->size_func = size_func;
context->prepared_func = prepared_func;
context->updated_func = updated_func;
context->user_data = user_data;
context->HeaderSize = 54;
- context->HeaderBuf = g_malloc(14 + 40 + 4*256 + 512);
+ context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
+ if (!context->HeaderBuf) {
+ g_free (context);
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load ICO file"));
+ return NULL;
+ }
/* 4*256 for the colormap */
context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
context->HeaderDone = 0;
*
* free context, unref gdk_pixbuf
*/
-void gdk_pixbuf__ico_image_stop_load(gpointer data)
+static gboolean
+gdk_pixbuf__ico_image_stop_load(gpointer data,
+ GError **error)
{
struct ico_progressive_state *context =
(struct ico_progressive_state *) data;
+ /* FIXME this thing needs to report errors if
+ * we have unused image data
+ */
- g_return_if_fail(context != NULL);
-
- if (context->LineBuf != NULL)
- g_free(context->LineBuf);
- context->LineBuf = NULL;
- if (context->HeaderBuf != NULL)
- g_free(context->HeaderBuf);
-
- if (context->pixbuf)
- gdk_pixbuf_unref(context->pixbuf);
+ g_return_val_if_fail(context != NULL, TRUE);
- g_free(context);
+ context_free (context);
+ return TRUE;
}
+static void
+OneLine32 (struct ico_progressive_state *context)
+{
+ gint X;
+ guchar *Pixels;
+
+ X = 0;
+ if (context->Header.Negative == 0)
+ Pixels = (context->pixbuf->pixels +
+ context->pixbuf->rowstride *
+ (context->Header.height - context->Lines - 1));
+ else
+ Pixels = (context->pixbuf->pixels +
+ context->pixbuf->rowstride *
+ context->Lines);
+ while (X < context->Header.width) {
+ Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
+ Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
+ Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
+ Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
+ X++;
+ }
+}
static void OneLine24(struct ico_progressive_state *context)
{
}
+static void
+OneLine16 (struct ico_progressive_state *context)
+{
+ int i;
+ guchar *pixels;
+ guchar *src;
+
+ if (context->Header.Negative == 0)
+ pixels = (context->pixbuf->pixels +
+ context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
+ else
+ pixels = (context->pixbuf->pixels +
+ context->pixbuf->rowstride * context->Lines);
+
+ src = context->LineBuf;
+
+ for (i = 0; i < context->Header.width; i++) {
+ int v, r, g, b;
+
+ v = (int) src[0] | ((int) src[1] << 8);
+ src += 2;
+
+ /* Extract 5-bit RGB values */
+
+ r = (v >> 10) & 0x1f;
+ g = (v >> 5) & 0x1f;
+ b = v & 0x1f;
+
+ /* Fill the rightmost bits to form 8-bit values */
+
+ *pixels++ = (r << 3) | (r >> 2);
+ *pixels++ = (g << 3) | (g >> 2);
+ *pixels++ = (b << 3) | (b >> 2);
+ pixels++; /* skip alpha channel */
+ }
+}
+
+
static void OneLine8(struct ico_progressive_state *context)
{
gint X;
gint X;
guchar *Pixels;
+ /* Ignore the XOR mask for XP style 32-bpp icons with alpha */
+ if (context->Header.depth == 32)
+ return;
+
X = 0;
if (context->Header.Negative == 0)
Pixels = (context->pixbuf->pixels +
Bit = Bit & 1;
/* The joys of having a BGR byteorder */
Pixels[X * 4 + 3] = 255-Bit*255;
-#if 0
+#if 0
if (Bit){
Pixels[X*4+0] = 255;
Pixels[X*4+1] = 255;
}
if (context->Lines <context->Header.height) {
-
- if (context->Type == 24)
+ if (context->Type == 32)
+ OneLine32 (context);
+ else if (context->Type == 24)
OneLine24(context);
- if (context->Type == 8)
+ else if (context->Type == 16)
+ OneLine16 (context);
+ else if (context->Type == 8)
OneLine8(context);
- if (context->Type == 4)
+ else if (context->Type == 4)
OneLine4(context);
- if (context->Type == 1)
+ else if (context->Type == 1)
OneLine1(context);
+ else
+ g_assert_not_reached ();
} else
- {
OneLineTransp(context);
- }
context->Lines++;
if (context->Lines>=context->Header.height) {
if (context->updated_func != NULL) {
(*context->updated_func) (context->pixbuf,
0,
- context->Lines,
+ context->Lines % context->Header.height,
context->Header.width,
- context->Header.height,
+ 1,
context->user_data);
}
*
* append image data onto inrecrementally built output image
*/
-gboolean
-gdk_pixbuf__ico_image_load_increment(gpointer data, guchar * buf, guint size)
+static gboolean
+gdk_pixbuf__ico_image_load_increment(gpointer data,
+ const guchar * buf,
+ guint size,
+ GError **error)
{
struct ico_progressive_state *context =
(struct ico_progressive_state *) data;
size -= BytesToCopy;
buf += BytesToCopy;
context->HeaderDone += BytesToCopy;
-
- } else
- {
+ }
+ else {
BytesToCopy =
context->LineWidth - context->LineDone;
if (BytesToCopy > size)
}
- if (context->HeaderDone >= 6)
+ if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
+ GError *decode_err = NULL;
DecodeHeader(context->HeaderBuf,
- context->HeaderDone, context);
+ context->HeaderDone, context, &decode_err);
+ if (context->LineBuf != NULL && context->LineWidth == 0)
+ return TRUE;
+ if (decode_err) {
+ g_propagate_error (error, decode_err);
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+/* saving ICOs */
+
+static gint
+write8 (FILE *f,
+ guint8 *data,
+ gint count)
+{
+ gint bytes;
+ gint written;
+
+ written = 0;
+ while (count > 0)
+ {
+ bytes = fwrite ((char*) data, sizeof (char), count, f);
+ if (bytes <= 0)
+ break;
+ count -= bytes;
+ data += bytes;
+ written += bytes;
+ }
+
+ return written;
+}
+
+static gint
+write16 (FILE *f,
+ guint16 *data,
+ gint count)
+{
+ gint i;
+
+ for (i = 0; i < count; i++)
+ data[i] = GUINT16_TO_LE (data[i]);
+
+ return write8 (f, (guint8*) data, count * 2);
+}
+
+static gint
+write32 (FILE *f,
+ guint32 *data,
+ gint count)
+{
+ gint i;
+
+ for (i = 0; i < count; i++)
+ data[i] = GUINT32_TO_LE (data[i]);
+
+ return write8 (f, (guint8*) data, count * 4);
+}
+
+typedef struct _IconEntry IconEntry;
+struct _IconEntry {
+ gint width;
+ gint height;
+ gint depth;
+ gint hot_x;
+ gint hot_y;
+
+ guint8 n_colors;
+ guint32 *colors;
+ guint xor_rowstride;
+ guint8 *xor;
+ guint and_rowstride;
+ guint8 *and;
+};
+
+static gboolean
+fill_entry (IconEntry *icon,
+ GdkPixbuf *pixbuf,
+ gint hot_x,
+ gint hot_y,
+ GError **error)
+ {
+ guchar *p, *pixels, *and, *xor;
+ gint n_channels, v, x, y;
+
+ if (icon->width > 255 || icon->height > 255) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Image too large to be saved as ICO"));
+ return FALSE;
+ }
+
+ if (hot_x > -1 && hot_y > -1) {
+ icon->hot_x = hot_x;
+ icon->hot_y = hot_y;
+ if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Cursor hotspot outside image"));
+ return FALSE;
+ }
+ }
+ else {
+ icon->hot_x = -1;
+ icon->hot_y = -1;
+ }
+
+ switch (icon->depth) {
+ case 32:
+ icon->xor_rowstride = icon->width * 4;
+ break;
+ case 24:
+ icon->xor_rowstride = icon->width * 3;
+ break;
+ case 16:
+ icon->xor_rowstride = icon->width * 2;
+ break;
+ default:
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Unsupported depth for ICO file: %d"), icon->depth);
+ return FALSE;
+ }
+
+ if ((icon->xor_rowstride % 4) != 0)
+ icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
+ icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
+
+ icon->and_rowstride = icon->width / 8;
+ if ((icon->and_rowstride % 4) != 0)
+ icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
+ icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
+
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+ for (y = 0; y < icon->height; y++) {
+ p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
+ and = icon->and + icon->and_rowstride * y;
+ xor = icon->xor + icon->xor_rowstride * y;
+ for (x = 0; x < icon->width; x++) {
+ switch (icon->depth) {
+ case 32:
+ xor[0] = p[2];
+ xor[1] = p[1];
+ xor[2] = p[0];
+ xor[3] = 0xff;
+ if (n_channels == 4) {
+ xor[3] = p[3];
+ if (p[3] < 0x80)
+ *and |= 1 << (7 - x % 8);
+ }
+ xor += 4;
+ break;
+ case 24:
+ xor[0] = p[2];
+ xor[1] = p[1];
+ xor[2] = p[0];
+ if (n_channels == 4 && p[3] < 0x80)
+ *and |= 1 << (7 - x % 8);
+ xor += 3;
+ break;
+ case 16:
+ v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
+ xor[0] = v & 0xff;
+ xor[1] = v >> 8;
+ if (n_channels == 4 && p[3] < 0x80)
+ *and |= 1 << (7 - x % 8);
+ xor += 2;
+ break;
+ }
+
+ p += n_channels;
+ if (x % 8 == 7)
+ and++;
+ }
}
return TRUE;
}
+
+static void
+free_entry (IconEntry *icon)
+{
+ g_free (icon->colors);
+ g_free (icon->and);
+ g_free (icon->xor);
+ g_free (icon);
+}
+
+static void
+write_icon (FILE *f, GSList *entries)
+{
+ IconEntry *icon;
+ GSList *entry;
+ guint8 bytes[4];
+ guint16 words[4];
+ guint32 dwords[6];
+ gint type;
+ gint n_entries;
+ gint offset;
+ gint size;
+
+ if (((IconEntry *)entries->data)->hot_x > -1)
+ type = 2;
+ else
+ type = 1;
+ n_entries = g_slist_length (entries);
+
+ /* header */
+ words[0] = 0;
+ words[1] = type;
+ words[2] = n_entries;
+ write16 (f, words, 3);
+
+ offset = 6 + 16 * n_entries;
+
+ for (entry = entries; entry; entry = entry->next) {
+ icon = (IconEntry *)entry->data;
+ size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
+
+ /* directory entry */
+ bytes[0] = icon->width;
+ bytes[1] = icon->height;
+ bytes[2] = icon->n_colors;
+ bytes[3] = 0;
+ write8 (f, bytes, 4);
+ if (type == 1) {
+ words[0] = 1;
+ words[1] = icon->depth;
+ }
+ else {
+ words[0] = icon->hot_x;
+ words[1] = icon->hot_y;
+ }
+ write16 (f, words, 2);
+ dwords[0] = size;
+ dwords[1] = offset;
+ write32 (f, dwords, 2);
+
+ offset += size;
+ }
+
+ for (entry = entries; entry; entry = entry->next) {
+ icon = (IconEntry *)entry->data;
+
+ /* bitmap header */
+ dwords[0] = 40;
+ dwords[1] = icon->width;
+ dwords[2] = icon->height * 2;
+ write32 (f, dwords, 3);
+ words[0] = 1;
+ words[1] = icon->depth;
+ write16 (f, words, 2);
+ dwords[0] = 0;
+ dwords[1] = 0;
+ dwords[2] = 0;
+ dwords[3] = 0;
+ dwords[4] = 0;
+ dwords[5] = 0;
+ write32 (f, dwords, 6);
+
+ /* image data */
+ write8 (f, icon->xor, icon->xor_rowstride * icon->height);
+ write8 (f, icon->and, icon->and_rowstride * icon->height);
+ }
+}
+
+static gboolean
+gdk_pixbuf__ico_image_save (FILE *f,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ gint hot_x, hot_y;
+ IconEntry *icon;
+ GSList *entries = NULL;
+
+ /* support only single-image ICOs for now */
+ icon = g_new0 (IconEntry, 1);
+ icon->width = gdk_pixbuf_get_width (pixbuf);
+ icon->height = gdk_pixbuf_get_height (pixbuf);
+ icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
+ hot_x = -1;
+ hot_y = -1;
+
+ /* parse options */
+ if (keys && *keys) {
+ gchar **kiter;
+ gchar **viter;
+
+ for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
+ char *endptr;
+ if (strcmp (*kiter, "depth") == 0) {
+ sscanf (*viter, "%d", &icon->depth);
+ }
+ else if (strcmp (*kiter, "x_hot") == 0) {
+ hot_x = strtol (*viter, &endptr, 10);
+ }
+ else if (strcmp (*kiter, "y_hot") == 0) {
+ hot_y = strtol (*viter, &endptr, 10);
+ }
+
+ }
+ }
+
+ if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
+ free_entry (icon);
+ return FALSE;
+ }
+
+ entries = g_slist_append (entries, icon);
+ write_icon (f, entries);
+
+ g_slist_foreach (entries, (GFunc)free_entry, NULL);
+ g_slist_free (entries);
+
+ return TRUE;
+}
+
+#ifndef INCLUDE_ico
+#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
+#else
+#define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
+#endif
+
+MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
+{
+ module->begin_load = gdk_pixbuf__ico_image_begin_load;
+ module->stop_load = gdk_pixbuf__ico_image_stop_load;
+ module->load_increment = gdk_pixbuf__ico_image_load_increment;
+ module->save = gdk_pixbuf__ico_image_save;
+}
+
+MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
+{
+ static GdkPixbufModulePattern signature[] = {
+ { " \x1 ", "zz znz", 100 },
+ { " \x2 ", "zz znz", 100 },
+ { NULL, NULL, 0 }
+ };
+ static gchar * mime_types[] = {
+ "image/x-icon",
+ "image/x-ico",
+ "image/x-win-bitmap",
+ NULL
+ };
+ static gchar * extensions[] = {
+ "ico",
+ "cur",
+ NULL
+ };
+
+ info->name = "ico";
+ info->signature = signature;
+ info->description = N_("The ICO image format");
+ info->mime_types = mime_types;
+ info->extensions = extensions;
+ info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
+ info->license = "LGPL";
+}
+
+
+
+