#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 */
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
*/
GdkPixbuf *pixbuf; /* Our "target" */
};
-gpointer
+static 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);
-
+ 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)
+{
+ if (context->LineBuf != NULL)
+ g_free (context->LineBuf);
+ context->LineBuf = NULL;
+ if (context->HeaderBuf != NULL)
+ g_free (context->HeaderBuf);
+ if (context->pixbuf)
+ g_object_unref (context->pixbuf);
+ g_free (context);
+}
+
/* Shared library entry point --> Can go when generic_image_load
enters gdk-pixbuf-io */
-GdkPixbuf *
-gdk_pixbuf__ico_image_load(FILE * f)
+static GdkPixbuf *
+gdk_pixbuf__ico_image_load(FILE * f, GError **error)
{
- guchar *membuf;
+ guchar membuf [4096];
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);
+ State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, error);
- g_assert(membuf != NULL);
-
- while (feof(f) == 0) {
+ if (State == NULL)
+ return NULL;
+
+ while (!feof(f)) {
length = fread(membuf, 1, 4096, f);
+ if (ferror (f)) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Failure reading ICO: %s"), g_strerror (errno));
+ context_free (State);
+ return NULL;
+ }
if (length > 0)
- gdk_pixbuf__ico_image_load_increment(State, membuf, length);
-
+ if (!gdk_pixbuf__ico_image_load_increment(State, membuf, length,
+ error)) {
+ context_free (State);
+ return NULL;
+ }
}
- g_free(membuf);
if (State->pixbuf != NULL)
- gdk_pixbuf_ref(State->pixbuf);
+ g_object_ref (State->pixbuf);
+ else {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("ICO file was missing some data (perhaps it was truncated somehow?)"));
+ context_free (State);
+ return NULL;
+ }
pb = State->pixbuf;
- gdk_pixbuf__ico_image_stop_load(State);
+ gdk_pixbuf__ico_image_stop_load(State, NULL);
return pb;
}
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):
/* Step 1: The ICO header */
IconCount = (Data[5] << 8) + (Data[4]);
-
+
State->HeaderSize = 6 + IconCount*16;
-
+
if (State->HeaderSize>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!State->HeaderBuf) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
State->BytesInHeaderBuf = State->HeaderSize;
}
if (Bytes < State->HeaderSize)
Ptr += 16;
}
-
+
+ if (State->DIBoffset < 0) {
+ g_set_error (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>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!State->HeaderBuf) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
State->BytesInHeaderBuf = State->HeaderSize;
}
if (Bytes<State->HeaderSize)
State->Header.width =
(int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
+ if (State->Header.width == 0) {
+ g_set_error (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;
/* /2 because the BIH height includes the transparency mask */
+ if (State->Header.height == 0) {
+ g_set_error (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;
State->HeaderSize+=I;
if (State->HeaderSize>State->BytesInHeaderBuf) {
- State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
+ State->HeaderBuf=g_try_realloc(State->HeaderBuf,State->HeaderSize);
+ if (!State->HeaderBuf) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
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 (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.height * 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 (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 (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) {
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 (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Not enough memory to load icon"));
+ return;
+ }
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
+static gpointer
gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func,
ModuleUpdatedNotifyFunc updated_func,
- ModuleFrameDoneNotifyFunc frame_done_func,
- ModuleAnimationDoneNotifyFunc anim_done_func,
- gpointer user_data)
+ gpointer user_data,
+ GError **error)
{
struct ico_progressive_state *context;
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_set_error (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)
+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_free(context);
+ g_return_val_if_fail(context != NULL, TRUE);
+
+ 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;
}
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) {
*
* 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) {
+ GError *decode_err = NULL;
DecodeHeader(context->HeaderBuf,
- context->HeaderDone, context);
-
-
+ context->HeaderDone, context, &decode_err);
+ if (decode_err) {
+ g_propagate_error (error, decode_err);
+ return FALSE;
+ }
+ }
}
return TRUE;
}
+
+void
+gdk_pixbuf__ico_fill_vtable (GdkPixbufModule *module)
+{
+ module->load = gdk_pixbuf__ico_image_load;
+ 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;
+}