Blame gdk-pixbuf/io-ico.c

Packit a4058c
/* -*- mode: C; c-file-style: "linux" -*- */
Packit a4058c
/* GdkPixbuf library - Windows Icon/Cursor image loader
Packit a4058c
 *
Packit a4058c
 * Copyright (C) 1999 The Free Software Foundation
Packit a4058c
 *
Packit a4058c
 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
Packit a4058c
 *          Federico Mena-Quintero <federico@gimp.org>
Packit a4058c
 *
Packit a4058c
 * Based on io-bmp.c
Packit a4058c
 *
Packit a4058c
 * This library is free software; you can redistribute it and/or
Packit a4058c
 * modify it under the terms of the GNU Lesser General Public
Packit a4058c
 * License as published by the Free Software Foundation; either
Packit a4058c
 * version 2 of the License, or (at your option) any later version.
Packit a4058c
 *
Packit a4058c
 * This library is distributed in the hope that it will be useful,
Packit a4058c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4058c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4058c
 * Lesser General Public License for more details.
Packit a4058c
 *
Packit a4058c
 * You should have received a copy of the GNU Lesser General Public
Packit a4058c
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit a4058c
 */
Packit a4058c
Packit a4058c
#undef DUMPBIH
Packit a4058c
#define DEBUG(s)
Packit a4058c
Packit a4058c
#define INFOHEADER_SIZE 40
Packit a4058c
Packit a4058c
/*
Packit a4058c
Packit a4058c
Icons are just like BMP's, except for the header.
Packit a4058c
 
Packit a4058c
Known bugs:
Packit a4058c
	* bi-tonal files aren't tested 
Packit a4058c
Packit a4058c
*/
Packit a4058c
Packit a4058c
#include "config.h"
Packit a4058c
#include <stdio.h>
Packit a4058c
#include <stdlib.h>
Packit a4058c
#ifdef HAVE_UNISTD_H
Packit a4058c
#include <unistd.h>
Packit a4058c
#endif
Packit a4058c
#include <string.h>
Packit a4058c
#include <errno.h>
Packit a4058c
#include "gdk-pixbuf-private.h"
Packit a4058c
Packit a4058c

Packit a4058c
Packit a4058c
/* 
Packit a4058c
Packit a4058c
These structures are actually dummies. These are according to
Packit a4058c
the "Windows API reference guide volume II" as written by 
Packit a4058c
Borland International, but GCC fiddles with the alignment of
Packit a4058c
the internal members.
Packit a4058c
Packit a4058c
*/
Packit a4058c
Packit a4058c
struct BitmapFileHeader {
Packit a4058c
	gushort bfType;
Packit a4058c
	guint bfSize;
Packit a4058c
	guint reserverd;
Packit a4058c
	guint bfOffbits;
Packit a4058c
};
Packit a4058c
Packit a4058c
struct BitmapInfoHeader {
Packit a4058c
	guint biSize;
Packit a4058c
	guint biWidth;
Packit a4058c
	guint biHeight;
Packit a4058c
	gushort biPlanes;
Packit a4058c
	gushort biBitCount;
Packit a4058c
	guint biCompression;
Packit a4058c
	guint biSizeImage;
Packit a4058c
	guint biXPelsPerMeter;
Packit a4058c
	guint biYPelsPerMeter;
Packit a4058c
	guint biClrUsed;
Packit a4058c
	guint biClrImportant;
Packit a4058c
};
Packit a4058c
Packit a4058c
#ifdef DUMPBIH
Packit a4058c
/*
Packit a4058c
Packit a4058c
DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
Packit a4058c
debugging purposes.
Packit a4058c
Packit a4058c
*/
Packit a4058c
static void DumpBIH(unsigned char *BIH)
Packit a4058c
{
Packit a4058c
	printf("biSize      = %i \n",
Packit a4058c
	       (int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
Packit a4058c
	printf("biWidth     = %i \n",
Packit a4058c
	       (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
Packit a4058c
	printf("biHeight    = %i \n",
Packit a4058c
	       (int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
Packit a4058c
	       (BIH[8]));
Packit a4058c
	printf("biPlanes    = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
Packit a4058c
	printf("biBitCount  = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
Packit a4058c
	printf("biCompress  = %i \n",
Packit a4058c
	       (int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
Packit a4058c
	       (BIH[16]));
Packit a4058c
	printf("biSizeImage = %i \n",
Packit a4058c
	       (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
Packit a4058c
	       (BIH[20]));
Packit a4058c
	printf("biXPels     = %i \n",
Packit a4058c
	       (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
Packit a4058c
	       (BIH[24]));
Packit a4058c
	printf("biYPels     = %i \n",
Packit a4058c
	       (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
Packit a4058c
	       (BIH[28]));
Packit a4058c
	printf("biClrUsed   = %i \n",
Packit a4058c
	       (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
Packit a4058c
	       (BIH[32]));
Packit a4058c
	printf("biClrImprtnt= %i \n",
Packit a4058c
	       (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
Packit a4058c
	       (BIH[36]));
Packit a4058c
}
Packit a4058c
#endif
Packit a4058c
Packit a4058c
/* Progressive loading */
Packit a4058c
struct headerpair {
Packit a4058c
	gint width;
Packit a4058c
	gint height;
Packit a4058c
	guint depth;
Packit a4058c
	guint Negative;		/* Negative = 1 -> top down BMP,  
Packit a4058c
				   Negative = 0 -> bottom up BMP */
Packit a4058c
};
Packit a4058c
Packit a4058c
/* Score the various parts of the icon */
Packit a4058c
struct ico_direntry_data {
Packit a4058c
	gint ImageScore;
Packit a4058c
        gint width;
Packit a4058c
        gint height;
Packit a4058c
	guint DIBoffset;
Packit a4058c
	gint x_hot;
Packit a4058c
	gint y_hot;
Packit a4058c
};
Packit a4058c
Packit a4058c
struct ico_progressive_state {
Packit a4058c
	GdkPixbufModuleSizeFunc size_func;
Packit a4058c
	GdkPixbufModulePreparedFunc prepared_func;
Packit a4058c
	GdkPixbufModuleUpdatedFunc updated_func;
Packit a4058c
	gpointer user_data;
Packit a4058c
Packit a4058c
	gint HeaderSize;	/* The size of the header-part (incl colormap) */
Packit a4058c
	guchar *HeaderBuf;	/* The buffer for the header (incl colormap) */
Packit a4058c
	gint BytesInHeaderBuf;  /* The size of the allocated HeaderBuf */
Packit a4058c
	gint HeaderDone;	/* The nr of bytes actually in HeaderBuf */
Packit a4058c
Packit a4058c
	gint LineWidth;		/* The width of a line in bytes */
Packit a4058c
	guchar *LineBuf;	/* Buffer for 1 line */
Packit a4058c
	gint LineDone;		/* # of bytes in LineBuf */
Packit a4058c
	gint Lines;		/* # of finished lines */
Packit a4058c
Packit a4058c
	gint Type;		/*  
Packit a4058c
				   32 = RGBA
Packit a4058c
				   24 = RGB
Packit a4058c
				   16 = 555 RGB
Packit a4058c
				   8 = 8 bit colormapped
Packit a4058c
				   4 = 4 bpp colormapped
Packit a4058c
				   1  = 1 bit bitonal 
Packit a4058c
				 */
Packit a4058c
        gboolean cursor;
Packit a4058c
        gint x_hot;
Packit a4058c
        gint y_hot;
Packit a4058c
Packit a4058c
	struct headerpair Header;	/* Decoded (BE->CPU) header */
Packit a4058c
	GList *entries;
Packit a4058c
	guint			DIBoffset;
Packit a4058c
Packit a4058c
	GdkPixbuf *pixbuf;	/* Our "target" */
Packit a4058c
};
Packit a4058c
Packit a4058c
static gpointer
Packit a4058c
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
Packit a4058c
                                 GdkPixbufModulePreparedFunc prepared_func,
Packit a4058c
				 GdkPixbufModuleUpdatedFunc updated_func,
Packit a4058c
				 gpointer user_data,
Packit a4058c
                                 GError **error);
Packit a4058c
static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
Packit a4058c
static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
Packit a4058c
                                                     const guchar * buf, guint size,
Packit a4058c
                                                     GError **error);
Packit a4058c
Packit a4058c
static void 
Packit a4058c
context_free (struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	g_free (context->LineBuf);
Packit a4058c
	context->LineBuf = NULL;
Packit a4058c
	g_free (context->HeaderBuf);
Packit a4058c
	g_list_free_full (context->entries, g_free);
Packit a4058c
	if (context->pixbuf)
Packit a4058c
		g_object_unref (context->pixbuf);
Packit a4058c
Packit a4058c
	g_free (context);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gint
Packit a4058c
compare_direntry_scores (gconstpointer a,
Packit a4058c
                         gconstpointer b)
Packit a4058c
{
Packit a4058c
	const struct ico_direntry_data *ia = a;
Packit a4058c
	const struct ico_direntry_data *ib = b;
Packit a4058c
Packit a4058c
	/* Backwards, so largest first */
Packit a4058c
	if (ib->ImageScore < ia->ImageScore)
Packit a4058c
		return -1;
Packit a4058c
	else if (ib->ImageScore > ia->ImageScore)
Packit a4058c
		return 1;
Packit a4058c
	return 0;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void DecodeHeader(guchar *Data, gint Bytes,
Packit a4058c
			 struct ico_progressive_state *State,
Packit a4058c
			 GError **error)
Packit a4058c
{
Packit a4058c
/* For ICO's we have to be very clever. There are multiple images possible
Packit a4058c
   in an .ICO. As a simple heuristic, we select the image which is the largest
Packit a4058c
   in pixels.
Packit a4058c
 */
Packit a4058c
	struct ico_direntry_data *entry;
Packit a4058c
	gint IconCount = 0; /* The number of icon-versions in the file */
Packit a4058c
	guchar *BIH; /* The DIB for the used icon */
Packit a4058c
 	guchar *Ptr;
Packit a4058c
 	gint I;
Packit a4058c
	guint16 imgtype; /* 1 = icon, 2 = cursor */
Packit a4058c
	GList *l;
Packit a4058c
	gboolean got_broken_header = FALSE;
Packit a4058c
Packit a4058c
 	/* Step 1: The ICO header */
Packit a4058c
Packit a4058c
	/* First word should be 0 according to specs */
Packit a4058c
	if (((Data[1] << 8) + Data[0]) != 0) {
Packit a4058c
		g_set_error (error,
Packit a4058c
			     GDK_PIXBUF_ERROR,
Packit a4058c
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
			     _("Invalid header in icon (%s)"), "first word");
Packit a4058c
		return;
Packit a4058c
Packit a4058c
	}
Packit a4058c
Packit a4058c
	imgtype = (Data[3] << 8) + Data[2];
Packit a4058c
Packit a4058c
	State->cursor = (imgtype == 2) ? TRUE : FALSE;
Packit a4058c
Packit a4058c
	/* If it is not a cursor make sure it is actually an icon */
Packit a4058c
	if (!State->cursor && imgtype != 1) {
Packit a4058c
		g_set_error (error,
Packit a4058c
			     GDK_PIXBUF_ERROR,
Packit a4058c
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
			     _("Invalid header in icon (%s)"), "image type");
Packit a4058c
		return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
 	IconCount = (Data[5] << 8) + (Data[4]);
Packit a4058c
	
Packit a4058c
 	State->HeaderSize = 6 + IconCount*16;
Packit a4058c
Packit a4058c
        DEBUG(g_print ("Image type: %d (%s)\nImage count: %d\n", imgtype, imgtype == 2 ? "cursor" : "icon", IconCount));
Packit a4058c
Packit a4058c
 	if (State->HeaderSize>State->BytesInHeaderBuf) {
Packit a4058c
 		guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
Packit a4058c
		if (!tmp) {
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load icon"));
Packit a4058c
			return;
Packit a4058c
		}
Packit a4058c
		State->HeaderBuf = tmp;
Packit a4058c
 		State->BytesInHeaderBuf = State->HeaderSize;
Packit a4058c
 	}
Packit a4058c
 	if (Bytes < State->HeaderSize) {
Packit a4058c
 		return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* Now iterate through the ICONDIRENTRY structures, and sort them by
Packit a4058c
	 * which one we think is "best" (essentially the largest) */
Packit a4058c
	g_list_free_full (State->entries, g_free);
Packit a4058c
	State->entries = 0;
Packit a4058c
	Ptr = Data + 6;
Packit a4058c
	for (I=0;I
Packit a4058c
                int width;
Packit a4058c
                int height;
Packit a4058c
                int depth;
Packit a4058c
                int x_hot;
Packit a4058c
                int y_hot;
Packit a4058c
                guint data_size G_GNUC_UNUSED;
Packit a4058c
                guint data_offset;
Packit a4058c
Packit a4058c
                width = Ptr[0];
Packit a4058c
                height = Ptr[1];
Packit a4058c
                depth = Ptr[2];
Packit a4058c
		x_hot = (Ptr[5] << 8) + Ptr[4];
Packit a4058c
		y_hot = (Ptr[7] << 8) + Ptr[6];
Packit a4058c
                data_size = ((guint) (Ptr[11]) << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
Packit a4058c
		data_offset = ((guint) (Ptr[15]) << 24) + (Ptr[14] << 16) + (Ptr[13] << 8) + (Ptr[12]);
Packit a4058c
                DEBUG(g_print ("Image %d: %d x %d\n\tDepth: %d\n", I, width, height, depth);
Packit a4058c
                if (imgtype == 2)
Packit a4058c
                  g_print ("\tHotspot: %d x %d\n", x_hot, y_hot);
Packit a4058c
                else
Packit a4058c
                  g_print ("\tColor planes: %d\n\tBits per pixel: %d\n", x_hot, y_hot);
Packit a4058c
                g_print ("\tSize: %d\n\tOffset: %d\n", data_size, data_offset);)
Packit a4058c
Packit a4058c
                if (depth == 0)
Packit a4058c
                        depth = 32;
Packit a4058c
                else if (depth <= 2)
Packit a4058c
                        depth = 1;
Packit a4058c
                else if (depth <= 16)
Packit a4058c
                        depth = 4;
Packit a4058c
                else if (depth <= 256)
Packit a4058c
                        depth = 8;
Packit a4058c
Packit a4058c
		/* We check whether the HeaderSize (int) would overflow */
Packit a4058c
                if (data_offset > INT_MAX - INFOHEADER_SIZE) {
Packit a4058c
			got_broken_header = TRUE;
Packit a4058c
			continue;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		entry = g_new0 (struct ico_direntry_data, 1);
Packit a4058c
                entry->width = width ? width : 256;
Packit a4058c
                entry->height = height ? height : 256;
Packit a4058c
                entry->ImageScore = entry->width * entry->height * depth;
Packit a4058c
		entry->x_hot = x_hot;
Packit a4058c
		entry->y_hot = y_hot;
Packit a4058c
		entry->DIBoffset = data_offset;
Packit a4058c
		State->entries = g_list_insert_sorted (State->entries, entry, compare_direntry_scores);
Packit a4058c
		Ptr += 16;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* Now go through and find one we can parse */
Packit a4058c
	entry = NULL;
Packit a4058c
	for (l = State->entries; l != NULL; l = g_list_next (l)) {
Packit a4058c
		entry = l->data;
Packit a4058c
Packit a4058c
		/* We know how many bytes are in the "header" part. */
Packit a4058c
		State->HeaderSize = entry->DIBoffset + INFOHEADER_SIZE;
Packit a4058c
Packit a4058c
		if (State->HeaderSize < 0) {
Packit a4058c
			g_set_error (error,
Packit a4058c
			             GDK_PIXBUF_ERROR,
Packit a4058c
			             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
			             _("Invalid header in icon (%s)"), "header size");
Packit a4058c
			return;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		if (State->HeaderSize>State->BytesInHeaderBuf) {
Packit a4058c
			guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
Packit a4058c
			if (!tmp) {
Packit a4058c
				g_set_error_literal (error,
Packit a4058c
				                     GDK_PIXBUF_ERROR,
Packit a4058c
				                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
				                     _("Not enough memory to load icon"));
Packit a4058c
				return;
Packit a4058c
			}
Packit a4058c
			State->HeaderBuf = tmp;
Packit a4058c
			State->BytesInHeaderBuf = State->HeaderSize;
Packit a4058c
		}
Packit a4058c
		if (Bytes<State->HeaderSize)
Packit a4058c
			return;
Packit a4058c
Packit a4058c
		BIH = Data+entry->DIBoffset;
Packit a4058c
Packit a4058c
		/* A compressed icon, try the next one */
Packit a4058c
		if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
Packit a4058c
		    || (BIH[19] != 0)) {
Packit a4058c
			DEBUG(g_print("Skipping icon with score %d, as it is compressed\n", entry->ImageScore));
Packit a4058c
			continue;
Packit a4058c
		}
Packit a4058c
Packit a4058c
		DEBUG(g_print("Selecting icon with score %d\n", entry->ImageScore));
Packit a4058c
Packit a4058c
		/* If we made it to here then we have selected a BIH structure
Packit a4058c
		 * in a format that we can parse */
Packit a4058c
		break;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* No valid icon found, because all are compressed? */
Packit a4058c
	if (l == NULL) {
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
				     GDK_PIXBUF_ERROR,
Packit a4058c
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
				     got_broken_header ?
Packit a4058c
					_("Invalid header in icon") :
Packit a4058c
					_("Compressed icons are not supported"));
Packit a4058c
		return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* This is the one we're going with */
Packit a4058c
	State->DIBoffset = entry->DIBoffset;
Packit a4058c
	State->x_hot = entry->x_hot;
Packit a4058c
	State->y_hot = entry->y_hot;
Packit a4058c
Packit a4058c
#ifdef DUMPBIH
Packit a4058c
	DumpBIH(BIH);
Packit a4058c
#endif
Packit a4058c
	/* Add the palette to the headersize */
Packit a4058c
Packit a4058c
	State->Header.width =
Packit a4058c
	    (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
Packit a4058c
	if (State->Header.width == 0)
Packit a4058c
		State->Header.width = 256;
Packit a4058c
Packit a4058c
	State->Header.height =
Packit a4058c
	    (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
Packit a4058c
	    /* /2 because the BIH height includes the transparency mask */
Packit a4058c
	if (State->Header.height == 0)
Packit a4058c
		State->Header.height = 256;
Packit a4058c
Packit a4058c
	/* Negative heights mean top-down pixel-order */
Packit a4058c
	if (State->Header.height < 0) {
Packit a4058c
		State->Header.height = -State->Header.height;
Packit a4058c
		State->Header.Negative = 1;
Packit a4058c
	}
Packit a4058c
	if (State->Header.width < 0) {
Packit a4058c
		State->Header.width = -State->Header.width;
Packit a4058c
	}
Packit a4058c
Packit a4058c
        if (State->Header.width != entry->width ||
Packit a4058c
            State->Header.height != entry->height) {
Packit a4058c
		g_set_error (error,
Packit a4058c
                             GDK_PIXBUF_ERROR,
Packit a4058c
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                             _("Invalid header in icon (%s)"), "image size");
Packit a4058c
		return;
Packit a4058c
        }
Packit a4058c
Packit a4058c
	State->Header.depth = (BIH[15] << 8) + (BIH[14]);
Packit a4058c
	State->Type = State->Header.depth;
Packit a4058c
Packit a4058c
	/* Determine the  palette size. If the header indicates 0, it
Packit a4058c
	   is actually the maximum for the bpp. You have to love the
Packit a4058c
	   guys who made the spec. */
Packit a4058c
	I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
Packit a4058c
	I = I*4;
Packit a4058c
	if ((I==0)&&(State->Type==1))
Packit a4058c
		I = 2*4;
Packit a4058c
	if ((I==0)&&(State->Type==4))
Packit a4058c
		I = 16*4;
Packit a4058c
	if ((I==0)&&(State->Type==8))
Packit a4058c
		I = 256*4;
Packit a4058c
	
Packit a4058c
	State->HeaderSize+=I;
Packit a4058c
	
Packit a4058c
	if (State->HeaderSize < 0) {
Packit a4058c
		g_set_error (error,
Packit a4058c
                             GDK_PIXBUF_ERROR,
Packit a4058c
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                             _("Invalid header in icon (%s)"), "palette size");
Packit a4058c
		return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
 	if (State->HeaderSize>State->BytesInHeaderBuf) {
Packit a4058c
	        guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
Packit a4058c
		if (!tmp) {
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load icon"));
Packit a4058c
			return;
Packit a4058c
		}
Packit a4058c
		State->HeaderBuf = tmp;
Packit a4058c
 		State->BytesInHeaderBuf = State->HeaderSize;
Packit a4058c
 	}
Packit a4058c
 	if (Bytes < State->HeaderSize) {
Packit a4058c
 		return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
        if (State->Type == 32)
Packit a4058c
                State->LineWidth = State->Header.width * 4;
Packit a4058c
        else if (State->Type == 24)
Packit a4058c
		State->LineWidth = State->Header.width * 3;
Packit a4058c
        else if (State->Type == 16)
Packit a4058c
                State->LineWidth = State->Header.width * 2;
Packit a4058c
        else if (State->Type == 8)
Packit a4058c
		State->LineWidth = State->Header.width * 1;
Packit a4058c
        else if (State->Type == 4)
Packit a4058c
		State->LineWidth = (State->Header.width+1)/2;
Packit a4058c
        else if (State->Type == 1) {
Packit a4058c
		State->LineWidth = State->Header.width / 8;
Packit a4058c
		if ((State->Header.width & 7) != 0)
Packit a4058c
			State->LineWidth++;
Packit a4058c
        } else {
Packit a4058c
          g_set_error_literal (error,
Packit a4058c
                               GDK_PIXBUF_ERROR,
Packit a4058c
                               GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                               _("Unsupported icon type"));
Packit a4058c
          return;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	/* Pad to a 32 bit boundary */
Packit a4058c
	if (((State->LineWidth % 4) > 0))
Packit a4058c
		State->LineWidth = (State->LineWidth / 4) * 4 + 4;
Packit a4058c
Packit a4058c
Packit a4058c
	if (State->LineBuf == NULL) {
Packit a4058c
		State->LineBuf = g_try_malloc(State->LineWidth);
Packit a4058c
		if (!State->LineBuf) {
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load icon"));
Packit a4058c
			return;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
	g_assert(State->LineBuf != NULL);
Packit a4058c
Packit a4058c
Packit a4058c
	if (State->pixbuf == NULL) {
Packit a4058c
		if (State->size_func) {
Packit a4058c
			gint width = State->Header.width;
Packit a4058c
			gint height = State->Header.height;
Packit a4058c
Packit a4058c
			(*State->size_func) (&width, &height, State->user_data);
Packit a4058c
			if (width == 0 || height == 0) {
Packit a4058c
				State->LineWidth = 0;
Packit a4058c
				return;
Packit a4058c
			}
Packit a4058c
		}
Packit a4058c
Packit a4058c
		State->pixbuf =
Packit a4058c
		    gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
Packit a4058c
				   State->Header.width,
Packit a4058c
				   State->Header.height);
Packit a4058c
		if (!State->pixbuf) {
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                             _("Not enough memory to load icon"));
Packit a4058c
			return;
Packit a4058c
		}
Packit a4058c
		if (State->cursor) {
Packit a4058c
			gchar hot[10];
Packit a4058c
			g_snprintf (hot, 10, "%d", State->x_hot);
Packit a4058c
			gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
Packit a4058c
			g_snprintf (hot, 10, "%d", State->y_hot);
Packit a4058c
			gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
Packit a4058c
		}
Packit a4058c
Packit a4058c
		if (State->prepared_func != NULL)
Packit a4058c
			/* Notify the client that we are ready to go */
Packit a4058c
			(*State->prepared_func) (State->pixbuf,
Packit a4058c
                                                 NULL,
Packit a4058c
						 State->user_data);
Packit a4058c
Packit a4058c
	}
Packit a4058c
Packit a4058c
}
Packit a4058c
Packit a4058c
/* 
Packit a4058c
 * func - called when we have pixmap created (but no image data)
Packit a4058c
 * user_data - passed as arg 1 to func
Packit a4058c
 * return context (opaque to user)
Packit a4058c
 */
Packit a4058c
Packit a4058c
static gpointer
Packit a4058c
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
Packit a4058c
                                 GdkPixbufModulePreparedFunc prepared_func,
Packit a4058c
				 GdkPixbufModuleUpdatedFunc updated_func,
Packit a4058c
				 gpointer user_data,
Packit a4058c
                                 GError **error)
Packit a4058c
{
Packit a4058c
	struct ico_progressive_state *context;
Packit a4058c
Packit a4058c
	context = g_new0(struct ico_progressive_state, 1);
Packit a4058c
	context->size_func = size_func;
Packit a4058c
	context->prepared_func = prepared_func;
Packit a4058c
	context->updated_func = updated_func;
Packit a4058c
	context->user_data = user_data;
Packit a4058c
Packit a4058c
	context->HeaderSize = 54;
Packit a4058c
	context->HeaderBuf = g_try_malloc(14 + INFOHEADER_SIZE + 4*256 + 512);
Packit a4058c
	if (!context->HeaderBuf) {
Packit a4058c
		g_free (context);
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                     _("Not enough memory to load ICO file"));
Packit a4058c
		return NULL;
Packit a4058c
	}
Packit a4058c
	/* 4*256 for the colormap */
Packit a4058c
	context->BytesInHeaderBuf = 14 + INFOHEADER_SIZE + 4*256 + 512 ;
Packit a4058c
	context->HeaderDone = 0;
Packit a4058c
Packit a4058c
	context->LineWidth = 0;
Packit a4058c
	context->LineBuf = NULL;
Packit a4058c
	context->LineDone = 0;
Packit a4058c
	context->Lines = 0;
Packit a4058c
Packit a4058c
	context->Type = 0;
Packit a4058c
Packit a4058c
	memset(&context->Header, 0, sizeof(struct headerpair));
Packit a4058c
Packit a4058c
Packit a4058c
	context->pixbuf = NULL;
Packit a4058c
Packit a4058c
Packit a4058c
	return (gpointer) context;
Packit a4058c
}
Packit a4058c
Packit a4058c
/*
Packit a4058c
 * context - returned from image_begin_load
Packit a4058c
 *
Packit a4058c
 * free context, unref gdk_pixbuf
Packit a4058c
 */
Packit a4058c
static gboolean 
Packit a4058c
gdk_pixbuf__ico_image_stop_load(gpointer data,
Packit a4058c
				GError **error)
Packit a4058c
{
Packit a4058c
	struct ico_progressive_state *context =
Packit a4058c
	    (struct ico_progressive_state *) data;
Packit a4058c
	gboolean ret = TRUE;
Packit a4058c
Packit a4058c
        /* FIXME this thing needs to report errors if
Packit a4058c
         * we have unused image data
Packit a4058c
         */
Packit a4058c
Packit a4058c
	g_return_val_if_fail(context != NULL, TRUE);
Packit a4058c
Packit a4058c
	if (context->HeaderDone < context->HeaderSize) {
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
				     GDK_PIXBUF_ERROR,
Packit a4058c
				     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
				     _("ICO image was truncated or incomplete."));
Packit a4058c
		ret = FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	context_free (context);
Packit a4058c
        return ret;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
OneLine32 (struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
        gint X;
Packit a4058c
        guchar *Pixels;
Packit a4058c
Packit a4058c
        X = 0;
Packit a4058c
        if (context->Header.Negative == 0)
Packit a4058c
                Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
                          (context->Header.height - context->Lines - 1));
Packit a4058c
        else
Packit a4058c
                Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
                          context->Lines);
Packit a4058c
        while (X < context->Header.width) {
Packit a4058c
                /* BGRA */
Packit a4058c
                Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
Packit a4058c
                Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
Packit a4058c
                Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
Packit a4058c
                Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
Packit a4058c
                X++;
Packit a4058c
        }
Packit a4058c
}
Packit a4058c
Packit a4058c
static void OneLine24(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	gint X;
Packit a4058c
	guchar *Pixels;
Packit a4058c
Packit a4058c
	X = 0;
Packit a4058c
	if (context->Header.Negative == 0)
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (context->Header.height - context->Lines - 1));
Packit a4058c
	else
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  context->Lines);
Packit a4058c
	while (X < context->Header.width) {
Packit a4058c
		Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
Packit a4058c
		Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
Packit a4058c
		Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
Packit a4058c
		Pixels[X * 4 + 3] = 0xff;
Packit a4058c
		X++;
Packit a4058c
	}
Packit a4058c
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
OneLine16 (struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
        int i;
Packit a4058c
        guchar *pixels;
Packit a4058c
        guchar *src;
Packit a4058c
Packit a4058c
        if (context->Header.Negative == 0)
Packit a4058c
                pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
                          (context->Header.height - context->Lines - 1));
Packit a4058c
        else
Packit a4058c
                pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
                          context->Lines);
Packit a4058c
Packit a4058c
        src = context->LineBuf;
Packit a4058c
Packit a4058c
        for (i = 0; i < context->Header.width; i++) {
Packit a4058c
                int v, r, g, b;
Packit a4058c
Packit a4058c
                v = (int) src[0] | ((int) src[1] << 8);
Packit a4058c
                src += 2;
Packit a4058c
Packit a4058c
                /* Extract 5-bit RGB values */
Packit a4058c
Packit a4058c
                r = (v >> 10) & 0x1f;
Packit a4058c
                g = (v >> 5) & 0x1f;
Packit a4058c
                b = v & 0x1f;
Packit a4058c
Packit a4058c
                /* Fill the rightmost bits to form 8-bit values */
Packit a4058c
Packit a4058c
                *pixels++ = (r << 3) | (r >> 2);
Packit a4058c
                *pixels++ = (g << 3) | (g >> 2);
Packit a4058c
                *pixels++ = (b << 3) | (b >> 2);
Packit a4058c
                *pixels++ = 0xff;
Packit a4058c
        }
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static void OneLine8(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	gint X;
Packit a4058c
	guchar *Pixels;
Packit a4058c
Packit a4058c
	X = 0;
Packit a4058c
	if (context->Header.Negative == 0)
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (context->Header.height - context->Lines - 1));
Packit a4058c
	else
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  context->Lines);
Packit a4058c
	while (X < context->Header.width) {
Packit a4058c
		/* The joys of having a BGR byteorder */
Packit a4058c
		Pixels[X * 4 + 0] =
Packit a4058c
		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 2 + context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 1] =
Packit a4058c
		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE + 1 +context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 2] =
Packit a4058c
		    context->HeaderBuf[4 * context->LineBuf[X] + INFOHEADER_SIZE +context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 3] = 0xff;
Packit a4058c
		X++;
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
static void OneLine4(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	gint X;
Packit a4058c
	guchar *Pixels;
Packit a4058c
Packit a4058c
	X = 0;
Packit a4058c
	if (context->Header.Negative == 0)
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (context->Header.height - context->Lines - 1));
Packit a4058c
	else
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  context->Lines);
Packit a4058c
	
Packit a4058c
	while (X < context->Header.width) {
Packit a4058c
		guchar Pix;
Packit a4058c
		
Packit a4058c
		Pix = context->LineBuf[X/2];
Packit a4058c
Packit a4058c
		Pixels[X * 4 + 0] =
Packit a4058c
		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 2 + context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 1] =
Packit a4058c
		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + 1 +context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 2] =
Packit a4058c
		    context->HeaderBuf[4 * (Pix>>4) + INFOHEADER_SIZE + context->DIBoffset];
Packit a4058c
		Pixels[X * 4 + 3] = 0xff;
Packit a4058c
		X++;
Packit a4058c
		if (X<context->Header.width) { 
Packit a4058c
			/* Handle the other 4 bit pixel only when there is one */
Packit a4058c
			Pixels[X * 4 + 0] =
Packit a4058c
			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 2 + context->DIBoffset];
Packit a4058c
			Pixels[X * 4 + 1] =
Packit a4058c
			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + 1 + context->DIBoffset];
Packit a4058c
			Pixels[X * 4 + 2] =
Packit a4058c
			    context->HeaderBuf[4 * (Pix&15) + INFOHEADER_SIZE + context->DIBoffset];
Packit a4058c
			Pixels[X * 4 + 3] = 0xff;
Packit a4058c
			X++;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
	
Packit a4058c
}
Packit a4058c
Packit a4058c
static void OneLine1(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	gint X;
Packit a4058c
	guchar *Pixels;
Packit a4058c
Packit a4058c
	X = 0;
Packit a4058c
	if (context->Header.Negative == 0)
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (context->Header.height - context->Lines - 1));
Packit a4058c
	else
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  context->Lines);
Packit a4058c
	while (X < context->Header.width) {
Packit a4058c
		int Bit;
Packit a4058c
Packit a4058c
		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
Packit a4058c
		Bit = Bit & 1;
Packit a4058c
		/* The joys of having a BGR byteorder */
Packit a4058c
		Pixels[X * 4 + 0] = Bit*255;
Packit a4058c
		Pixels[X * 4 + 1] = Bit*255;
Packit a4058c
		Pixels[X * 4 + 2] = Bit*255;
Packit a4058c
		Pixels[X * 4 + 3] = 0xff;
Packit a4058c
		X++;
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
static void OneLineTransp(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	gint X;
Packit a4058c
	guchar *Pixels;
Packit a4058c
Packit a4058c
	/* Ignore the XOR mask for XP style 32-bpp icons with alpha */ 
Packit a4058c
	if (context->Header.depth == 32)
Packit a4058c
		return;
Packit a4058c
Packit a4058c
	X = 0;
Packit a4058c
	if (context->Header.Negative == 0)
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (2*context->Header.height - context->Lines - 1));
Packit a4058c
	else
Packit a4058c
		Pixels = (context->pixbuf->pixels +
Packit a4058c
			  (gsize) context->pixbuf->rowstride *
Packit a4058c
			  (context->Lines-context->Header.height));
Packit a4058c
	while (X < context->Header.width) {
Packit a4058c
		int Bit;
Packit a4058c
Packit a4058c
		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
Packit a4058c
		Bit = Bit & 1;
Packit a4058c
		/* The joys of having a BGR byteorder */
Packit a4058c
		Pixels[X * 4 + 3] = 255-Bit*255;
Packit a4058c
#if 0
Packit a4058c
		if (Bit){
Packit a4058c
		  Pixels[X*4+0] = 255;
Packit a4058c
		  Pixels[X*4+1] = 255;
Packit a4058c
		} else {
Packit a4058c
		  Pixels[X*4+0] = 0;
Packit a4058c
		  Pixels[X*4+1] = 0;
Packit a4058c
		}
Packit a4058c
#endif		
Packit a4058c
		X++;
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
static void OneLine(struct ico_progressive_state *context)
Packit a4058c
{
Packit a4058c
	context->LineDone = 0;
Packit a4058c
	
Packit a4058c
	if (context->Lines >= context->Header.height*2) {
Packit a4058c
		return;
Packit a4058c
	}
Packit a4058c
		
Packit a4058c
	if (context->Lines <context->Header.height) {		
Packit a4058c
                if (context->Type == 32)
Packit a4058c
                        OneLine32 (context);
Packit a4058c
		else if (context->Type == 24)
Packit a4058c
			OneLine24(context);
Packit a4058c
                else if (context->Type == 16)
Packit a4058c
                        OneLine16 (context);
Packit a4058c
		else if (context->Type == 8)
Packit a4058c
			OneLine8(context);
Packit a4058c
		else if (context->Type == 4)
Packit a4058c
			OneLine4(context);
Packit a4058c
		else if (context->Type == 1)
Packit a4058c
			OneLine1(context);
Packit a4058c
		else 
Packit a4058c
			g_assert_not_reached ();
Packit a4058c
	} else
Packit a4058c
		OneLineTransp(context);
Packit a4058c
	
Packit a4058c
	context->Lines++;
Packit a4058c
	if (context->Lines>=context->Header.height) {
Packit a4058c
		context->Type = 1;
Packit a4058c
		context->LineWidth = context->Header.width / 8;
Packit a4058c
		if ((context->Header.width & 7) != 0)
Packit a4058c
			context->LineWidth++;
Packit a4058c
		/* Pad to a 32 bit boundary */
Packit a4058c
		if (((context->LineWidth % 4) > 0))
Packit a4058c
			context->LineWidth = (context->LineWidth / 4) * 4 + 4;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	if (context->updated_func != NULL) {
Packit a4058c
		int y;
Packit a4058c
Packit a4058c
		y = context->Lines % context->Header.height;
Packit a4058c
		if (context->Header.Negative == 0 &&
Packit a4058c
		    context->Lines < context->Header.height)
Packit a4058c
			y = context->Header.height - y;
Packit a4058c
		(*context->updated_func) (context->pixbuf,
Packit a4058c
					  0,
Packit a4058c
					  y,
Packit a4058c
					  context->Header.width,
Packit a4058c
					  1,
Packit a4058c
					  context->user_data);
Packit a4058c
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
/*
Packit a4058c
 * context - from image_begin_load
Packit a4058c
 * buf - new image data
Packit a4058c
 * size - length of new image data
Packit a4058c
 *
Packit a4058c
 * append image data onto inrecrementally built output image
Packit a4058c
 */
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__ico_image_load_increment(gpointer data,
Packit a4058c
                                     const guchar * buf,
Packit a4058c
                                     guint size,
Packit a4058c
                                     GError **error)
Packit a4058c
{
Packit a4058c
	struct ico_progressive_state *context =
Packit a4058c
	    (struct ico_progressive_state *) data;
Packit a4058c
Packit a4058c
	gint BytesToCopy;
Packit a4058c
Packit a4058c
	while (size > 0) {
Packit a4058c
		g_assert(context->LineDone >= 0);
Packit a4058c
		if (context->HeaderDone < context->HeaderSize) {	/* We still 
Packit a4058c
									   have headerbytes to do */
Packit a4058c
			BytesToCopy =
Packit a4058c
			    context->HeaderSize - context->HeaderDone;
Packit a4058c
			if (BytesToCopy > size)
Packit a4058c
				BytesToCopy = size;
Packit a4058c
Packit a4058c
			memmove(context->HeaderBuf + context->HeaderDone,
Packit a4058c
			       buf, BytesToCopy);
Packit a4058c
Packit a4058c
			size -= BytesToCopy;
Packit a4058c
			buf += BytesToCopy;
Packit a4058c
			context->HeaderDone += BytesToCopy;
Packit a4058c
		} 
Packit a4058c
		else {
Packit a4058c
			BytesToCopy =
Packit a4058c
			    context->LineWidth - context->LineDone;
Packit a4058c
			if (BytesToCopy > size)
Packit a4058c
				BytesToCopy = size;
Packit a4058c
Packit a4058c
			if (BytesToCopy > 0) {
Packit a4058c
				/* Should be non-NULL once the header is decoded, as below. */
Packit a4058c
				g_assert (context->LineBuf != NULL);
Packit a4058c
Packit a4058c
				memmove(context->LineBuf +
Packit a4058c
				       context->LineDone, buf,
Packit a4058c
				       BytesToCopy);
Packit a4058c
Packit a4058c
				size -= BytesToCopy;
Packit a4058c
				buf += BytesToCopy;
Packit a4058c
				context->LineDone += BytesToCopy;
Packit a4058c
			}
Packit a4058c
			if ((context->LineDone >= context->LineWidth) && (context->LineWidth > 0)) {
Packit a4058c
				/* By this point, DecodeHeader() will have been called, and should have returned successfully
Packit a4058c
				 * or set a #GError, as its only return-FALSE-without-setting-a-GError paths are when
Packit a4058c
				 * (context->HeaderDone < context->HeaderSize) or (context->LineWidth == 0).
Packit a4058c
				 * If it’s returned a #GError, we will have bailed already; otherwise, pixbuf will be set. */
Packit a4058c
				g_assert (context->pixbuf != NULL);
Packit a4058c
				OneLine(context);
Packit a4058c
			}
Packit a4058c
Packit a4058c
Packit a4058c
		}
Packit a4058c
Packit a4058c
		if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
Packit a4058c
			GError *decode_err = NULL;
Packit a4058c
			DecodeHeader(context->HeaderBuf,
Packit a4058c
				     context->HeaderDone, context, &decode_err);
Packit a4058c
			if (context->LineBuf != NULL && context->LineWidth == 0)
Packit a4058c
				return TRUE;
Packit a4058c
Packit a4058c
			if (decode_err) {
Packit a4058c
				g_propagate_error (error, decode_err);
Packit a4058c
				return FALSE;
Packit a4058c
			}
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
/* saving ICOs */ 
Packit a4058c
Packit a4058c
static gint
Packit a4058c
write8 (FILE     *f,
Packit a4058c
	guint8   *data,
Packit a4058c
	gint      count)
Packit a4058c
{
Packit a4058c
  gint bytes;
Packit a4058c
  gint written;
Packit a4058c
Packit a4058c
  written = 0;
Packit a4058c
  while (count > 0)
Packit a4058c
    {
Packit a4058c
      bytes = fwrite ((char*) data, sizeof (char), count, f);
Packit a4058c
      if (bytes <= 0)
Packit a4058c
        break;
Packit a4058c
      count -= bytes;
Packit a4058c
      data += bytes;
Packit a4058c
      written += bytes;
Packit a4058c
    }
Packit a4058c
Packit a4058c
  return written;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gint
Packit a4058c
write16 (FILE     *f,
Packit a4058c
	 guint16  *data,
Packit a4058c
	 gint      count)
Packit a4058c
{
Packit a4058c
  gint i;
Packit a4058c
Packit a4058c
  for (i = 0; i < count; i++)
Packit a4058c
	  data[i] = GUINT16_TO_LE (data[i]);
Packit a4058c
Packit a4058c
  return write8 (f, (guint8*) data, count * 2);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gint
Packit a4058c
write32 (FILE     *f,
Packit a4058c
	 guint32  *data,
Packit a4058c
	 gint      count)
Packit a4058c
{
Packit a4058c
  gint i;
Packit a4058c
Packit a4058c
  for (i = 0; i < count; i++)
Packit a4058c
	  data[i] = GUINT32_TO_LE (data[i]);
Packit a4058c
  
Packit a4058c
  return write8 (f, (guint8*) data, count * 4);
Packit a4058c
}
Packit a4058c
Packit a4058c
typedef struct _IconEntry IconEntry;
Packit a4058c
struct _IconEntry {
Packit a4058c
	gint width;
Packit a4058c
	gint height;
Packit a4058c
	gint depth;
Packit a4058c
	gint hot_x;
Packit a4058c
	gint hot_y;
Packit a4058c
Packit a4058c
	guint8 n_colors;
Packit a4058c
	guint32 *colors;
Packit a4058c
	guint xor_rowstride;
Packit a4058c
	guint8 *xor;
Packit a4058c
	guint and_rowstride;
Packit a4058c
	guint8 *and;
Packit a4058c
};
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
fill_entry (IconEntry *icon, 
Packit a4058c
	    GdkPixbuf *pixbuf, 
Packit a4058c
	    gint       hot_x, 
Packit a4058c
	    gint       hot_y, 
Packit a4058c
	    GError   **error) 
Packit a4058c
 {
Packit a4058c
	guchar *p, *pixels, *and, *xor;
Packit a4058c
	gint n_channels, v, x, y;
Packit a4058c
Packit a4058c
	if (icon->width > 256 || icon->height > 256) {
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
                                     GDK_PIXBUF_ERROR,
Packit a4058c
                                     GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                     _("Image too large to be saved as ICO"));
Packit a4058c
		return FALSE;
Packit a4058c
	} 
Packit a4058c
	
Packit a4058c
	if (hot_x > -1 && hot_y > -1) {
Packit a4058c
		icon->hot_x = hot_x;
Packit a4058c
		icon->hot_y = hot_y;
Packit a4058c
		if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
Packit a4058c
			g_set_error_literal (error,
Packit a4058c
                                             GDK_PIXBUF_ERROR,
Packit a4058c
                                             GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
                                             _("Cursor hotspot outside image"));
Packit a4058c
			return FALSE;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
	else {
Packit a4058c
		icon->hot_x = -1;
Packit a4058c
		icon->hot_y = -1;
Packit a4058c
	}
Packit a4058c
	
Packit a4058c
	switch (icon->depth) {
Packit a4058c
	case 32:
Packit a4058c
		icon->xor_rowstride = icon->width * 4;
Packit a4058c
		break;
Packit a4058c
	case 24:
Packit a4058c
		icon->xor_rowstride = icon->width * 3;
Packit a4058c
		break;
Packit a4058c
	case 16:
Packit a4058c
		icon->xor_rowstride = icon->width * 2;
Packit a4058c
		break;
Packit a4058c
	default:
Packit a4058c
		g_set_error (error,
Packit a4058c
			     GDK_PIXBUF_ERROR,
Packit a4058c
			     GDK_PIXBUF_ERROR_BAD_OPTION,
Packit a4058c
			     _("Unsupported depth for ICO file: %d"), icon->depth);
Packit a4058c
		return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	if ((icon->xor_rowstride % 4) != 0)
Packit a4058c
		icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
Packit a4058c
	icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
Packit a4058c
Packit a4058c
	icon->and_rowstride = (icon->width + 7) / 8;
Packit a4058c
	if ((icon->and_rowstride % 4) != 0)
Packit a4058c
		icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
Packit a4058c
	icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
Packit a4058c
Packit a4058c
	pixels = gdk_pixbuf_get_pixels (pixbuf);
Packit a4058c
	n_channels = gdk_pixbuf_get_n_channels (pixbuf);
Packit a4058c
	for (y = 0; y < icon->height; y++) {
Packit a4058c
		p = pixels + (gsize) gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
Packit a4058c
		and = icon->and + icon->and_rowstride * y;
Packit a4058c
		xor = icon->xor + icon->xor_rowstride * y;
Packit a4058c
		for (x = 0; x < icon->width; x++) {
Packit a4058c
			switch (icon->depth) {
Packit a4058c
			case 32:
Packit a4058c
				xor[0] = p[2];
Packit a4058c
				xor[1] = p[1];
Packit a4058c
				xor[2] = p[0];
Packit a4058c
				xor[3] = 0xff;
Packit a4058c
				if (n_channels == 4) {
Packit a4058c
					xor[3] = p[3];
Packit a4058c
					if (p[3] < 0x80)
Packit a4058c
						*and |= 1 << (7 - x % 8);
Packit a4058c
				}
Packit a4058c
				xor += 4;
Packit a4058c
				break;
Packit a4058c
			case 24:
Packit a4058c
				xor[0] = p[2];
Packit a4058c
				xor[1] = p[1];
Packit a4058c
				xor[2] = p[0];
Packit a4058c
				if (n_channels == 4 && p[3] < 0x80)
Packit a4058c
					*and |= 1 << (7 - x % 8);
Packit a4058c
				xor += 3;
Packit a4058c
				break;
Packit a4058c
			case 16:
Packit a4058c
				v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
Packit a4058c
				xor[0] = v & 0xff;
Packit a4058c
				xor[1] = v >> 8;
Packit a4058c
				if (n_channels == 4 && p[3] < 0x80)
Packit a4058c
					*and |= 1 << (7 - x % 8);
Packit a4058c
				xor += 2;
Packit a4058c
				break;
Packit a4058c
			}
Packit a4058c
			
Packit a4058c
			p += n_channels;
Packit a4058c
			if (x % 8 == 7) 
Packit a4058c
				and++;
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
free_entry (IconEntry *icon)
Packit a4058c
{
Packit a4058c
	g_free (icon->colors);
Packit a4058c
	g_free (icon->and);
Packit a4058c
	g_free (icon->xor);
Packit a4058c
	g_free (icon);
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
write_icon (FILE *f, GSList *entries)
Packit a4058c
{
Packit a4058c
	IconEntry *icon;
Packit a4058c
	GSList *entry;
Packit a4058c
	guint8 bytes[4];
Packit a4058c
	guint16 words[4];
Packit a4058c
	guint32 dwords[6];
Packit a4058c
	gint type;
Packit a4058c
	gint n_entries;
Packit a4058c
	gint offset;
Packit a4058c
	gint size;
Packit a4058c
Packit a4058c
	if (((IconEntry *)entries->data)->hot_x > -1)
Packit a4058c
		type = 2;
Packit a4058c
	else 
Packit a4058c
		type = 1;
Packit a4058c
	n_entries = g_slist_length (entries);
Packit a4058c
Packit a4058c
	/* header */
Packit a4058c
	words[0] = 0;
Packit a4058c
	words[1] = type;
Packit a4058c
	words[2] = n_entries;
Packit a4058c
	write16 (f, words, 3);
Packit a4058c
	
Packit a4058c
	offset = 6 + 16 * n_entries;
Packit a4058c
Packit a4058c
	for (entry = entries; entry; entry = entry->next) {
Packit a4058c
		icon = (IconEntry *)entry->data;
Packit a4058c
		size = INFOHEADER_SIZE + icon->height * (icon->and_rowstride + icon->xor_rowstride);
Packit a4058c
		
Packit a4058c
		/* directory entry */
Packit a4058c
		if (icon->width == 256)
Packit a4058c
			bytes[0] = 0;
Packit a4058c
		else
Packit a4058c
			bytes[0] = icon->width;
Packit a4058c
		if (icon->height == 256)
Packit a4058c
			bytes[1] = 0;
Packit a4058c
		else
Packit a4058c
			bytes[1] = icon->height;
Packit a4058c
		bytes[2] = icon->n_colors;
Packit a4058c
		bytes[3] = 0;
Packit a4058c
		write8 (f, bytes, 4);
Packit a4058c
		if (type == 1) {
Packit a4058c
			words[0] = 1;
Packit a4058c
			words[1] = icon->depth;
Packit a4058c
		}
Packit a4058c
		else {
Packit a4058c
			words[0] = icon->hot_x;
Packit a4058c
			words[1] = icon->hot_y;
Packit a4058c
		}
Packit a4058c
		write16 (f, words, 2);
Packit a4058c
		dwords[0] = size;
Packit a4058c
		dwords[1] = offset;
Packit a4058c
		write32 (f, dwords, 2);
Packit a4058c
Packit a4058c
		offset += size;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	for (entry = entries; entry; entry = entry->next) {
Packit a4058c
		icon = (IconEntry *)entry->data;
Packit a4058c
Packit a4058c
		/* bitmap header */
Packit a4058c
		dwords[0] = INFOHEADER_SIZE;
Packit a4058c
		dwords[1] = icon->width;
Packit a4058c
		dwords[2] = icon->height * 2;
Packit a4058c
		write32 (f, dwords, 3);
Packit a4058c
		words[0] = 1;
Packit a4058c
		words[1] = icon->depth;
Packit a4058c
		write16 (f, words, 2);
Packit a4058c
		dwords[0] = 0;
Packit a4058c
		dwords[1] = 0;
Packit a4058c
		dwords[2] = 0;
Packit a4058c
		dwords[3] = 0;
Packit a4058c
		dwords[4] = 0;
Packit a4058c
		dwords[5] = 0;
Packit a4058c
		write32 (f, dwords, 6);
Packit a4058c
Packit a4058c
		/* image data */
Packit a4058c
		write8 (f, icon->xor, icon->xor_rowstride * icon->height);
Packit a4058c
		write8 (f, icon->and, icon->and_rowstride * icon->height);
Packit a4058c
	}
Packit a4058c
}
Packit a4058c
Packit a4058c
/* Locale-independent signed integer string parser, base 10.
Packit a4058c
 * @minimum and @maximum are valid inclusively. */
Packit a4058c
static gboolean
Packit a4058c
ascii_strtoll (const gchar  *str,
Packit a4058c
               gint64        minimum,
Packit a4058c
               gint64        maximum,
Packit a4058c
               gint64       *out,
Packit a4058c
               GError      **error)
Packit a4058c
{
Packit a4058c
	gint64 retval;
Packit a4058c
	const gchar *end_ptr;
Packit a4058c
Packit a4058c
	errno = 0;
Packit a4058c
	retval = g_ascii_strtoll (str, (gchar **) &end_ptr, 10);
Packit a4058c
Packit a4058c
	if (errno != 0) {
Packit a4058c
		g_set_error_literal (error,
Packit a4058c
		                     G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
Packit a4058c
		                     g_strerror (errno));
Packit a4058c
		return FALSE;
Packit a4058c
	} else if (end_ptr == str || *end_ptr != '\0') {
Packit a4058c
		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
Packit a4058c
		             "Argument is not an integer: %s", str);
Packit a4058c
		return FALSE;
Packit a4058c
	} else if ((maximum < G_MAXINT64 && retval > maximum) ||
Packit a4058c
	           (minimum > G_MININT64 && retval < minimum)) {
Packit a4058c
		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
Packit a4058c
		             "Argument should be in range [%" G_GINT64_FORMAT
Packit a4058c
		             ", %" G_GINT64_FORMAT "]: %s",
Packit a4058c
		             minimum, maximum, str);
Packit a4058c
		return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	g_assert (retval >= minimum && retval <= maximum);
Packit a4058c
Packit a4058c
	if (out != NULL)
Packit a4058c
		*out = retval;
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__ico_image_save (FILE          *f, 
Packit a4058c
                            GdkPixbuf     *pixbuf, 
Packit a4058c
                            gchar        **keys,
Packit a4058c
                            gchar        **values,
Packit a4058c
                            GError       **error)
Packit a4058c
{
Packit a4058c
	gint hot_x, hot_y;
Packit a4058c
	IconEntry *icon;
Packit a4058c
	GSList *entries = NULL;
Packit a4058c
Packit a4058c
	/* support only single-image ICOs for now */
Packit a4058c
	icon = g_new0 (IconEntry, 1);
Packit a4058c
	icon->width = gdk_pixbuf_get_width (pixbuf);
Packit a4058c
	icon->height = gdk_pixbuf_get_height (pixbuf);
Packit a4058c
	icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
Packit a4058c
	hot_x = -1;
Packit a4058c
	hot_y = -1;
Packit a4058c
	
Packit a4058c
	/* parse options */
Packit a4058c
	if (keys && *keys) {
Packit a4058c
		gchar **kiter;
Packit a4058c
		gchar **viter;
Packit a4058c
		
Packit a4058c
		for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
Packit a4058c
			gint64 out;
Packit a4058c
			if (strcmp (*kiter, "depth") == 0) {
Packit a4058c
				if (!ascii_strtoll (*viter, 1, 32,
Packit a4058c
				                    &out, error))
Packit a4058c
					return FALSE;
Packit a4058c
				icon->depth = out;
Packit a4058c
			}
Packit a4058c
			else if (strcmp (*kiter, "x_hot") == 0) {
Packit a4058c
				if (!ascii_strtoll (*viter, G_MININT, G_MAXINT,
Packit a4058c
				                    &out, error))
Packit a4058c
					return FALSE;
Packit a4058c
				hot_x = out;
Packit a4058c
			}
Packit a4058c
			else if (strcmp (*kiter, "y_hot") == 0) {
Packit a4058c
				if (!ascii_strtoll (*viter, G_MININT, G_MAXINT,
Packit a4058c
				                    &out, error))
Packit a4058c
					return FALSE;
Packit a4058c
				hot_y = out;
Packit a4058c
			}
Packit a4058c
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
	if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
Packit a4058c
		free_entry (icon);
Packit a4058c
		return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	entries = g_slist_append (entries, icon); 
Packit a4058c
	write_icon (f, entries);
Packit a4058c
Packit a4058c
	g_slist_foreach (entries, (GFunc)free_entry, NULL);
Packit a4058c
	g_slist_free (entries);
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
gdk_pixbuf__ico_is_save_option_supported (const gchar *option_key)
Packit a4058c
{
Packit a4058c
        if (g_strcmp0 (option_key, "depth") == 0 ||
Packit a4058c
            g_strcmp0 (option_key, "x_hot") == 0 ||
Packit a4058c
            g_strcmp0 (option_key, "y_hot") == 0)
Packit a4058c
                return TRUE;
Packit a4058c
Packit a4058c
        return FALSE;
Packit a4058c
}
Packit a4058c
Packit a4058c
#ifndef INCLUDE_ico
Packit a4058c
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
Packit a4058c
#else
Packit a4058c
#define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
Packit a4058c
#endif
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
Packit a4058c
{
Packit a4058c
	module->begin_load = gdk_pixbuf__ico_image_begin_load;
Packit a4058c
	module->stop_load = gdk_pixbuf__ico_image_stop_load;
Packit a4058c
	module->load_increment = gdk_pixbuf__ico_image_load_increment;
Packit a4058c
        module->save = gdk_pixbuf__ico_image_save;
Packit a4058c
        module->is_save_option_supported = gdk_pixbuf__ico_is_save_option_supported;
Packit a4058c
}
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
Packit a4058c
{
Packit a4058c
	static const GdkPixbufModulePattern signature[] = {
Packit a4058c
		{ "  \x1   ", "zz znz", 100 }, 
Packit a4058c
		{ "  \x2   ", "zz znz", 100 },
Packit a4058c
		{ NULL, NULL, 0 }
Packit a4058c
	};
Packit a4058c
	static const gchar *mime_types[] = {
Packit a4058c
		"image/x-icon",
Packit a4058c
		"image/x-ico",
Packit a4058c
		"image/x-win-bitmap",
Packit a4058c
                "image/vnd.microsoft.icon",
Packit a4058c
                "application/ico",
Packit a4058c
                "image/ico",
Packit a4058c
                "image/icon",
Packit a4058c
                "text/ico",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
	static const gchar *extensions[] = {
Packit a4058c
		"ico",
Packit a4058c
		"cur",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
Packit a4058c
	info->name = "ico";
Packit a4058c
	info->signature = (GdkPixbufModulePattern *) signature;
Packit a4058c
	info->description = NC_("image format", "Windows icon");
Packit a4058c
	info->mime_types = (gchar **) mime_types;
Packit a4058c
	info->extensions = (gchar **) extensions;
Packit a4058c
	info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
Packit a4058c
	info->license = "LGPL";
Packit a4058c
}
Packit a4058c
Packit a4058c
Packit a4058c
Packit a4058c