822 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			822 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 | ||
| /* GdkPixbuf library - XPM image loader
 | ||
|  *
 | ||
|  * Copyright (C) 1999 Mark Crichton
 | ||
|  * Copyright (C) 1999 The Free Software Foundation
 | ||
|  *
 | ||
|  * Authors: Mark Crichton <crichton@gimp.org>
 | ||
|  *          Federico Mena-Quintero <federico@gimp.org>
 | ||
|  *
 | ||
|  * This library is free software; you can redistribute it and/or
 | ||
|  * modify it under the terms of the GNU Lesser General Public
 | ||
|  * License as published by the Free Software Foundation; either
 | ||
|  * version 2 of the License, or (at your option) any later version.
 | ||
|  *
 | ||
|  * This library is distributed in the hope that it will be useful,
 | ||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | ||
|  * Lesser General Public License for more details.
 | ||
|  *
 | ||
|  * You should have received a copy of the GNU Lesser General Public
 | ||
|  * License along with this library; if not, write to the
 | ||
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 | ||
|  * Boston, MA 02111-1307, USA.
 | ||
|  */
 | ||
| 
 | ||
| #include "config.h"
 | ||
| #include <stdio.h>
 | ||
| #include <stdlib.h>
 | ||
| #include <string.h>
 | ||
| #include <glib.h>
 | ||
| #ifdef HAVE_UNISTD_H
 | ||
| #include <unistd.h> /* for unlink */
 | ||
| #endif
 | ||
| #include <errno.h>
 | ||
| #include "gdk-pixbuf-private.h"
 | ||
| #include "gdk-pixbuf-io.h"
 | ||
| #include <glib/gstdio.h>
 | ||
| 
 | ||
| 
 | ||
| /* I have must have done something to deserve this.
 | ||
|  * XPM is such a crappy format to handle.
 | ||
|  * This code is an ugly hybred from gdkpixmap.c
 | ||
|  * modified to respect transparent colors.
 | ||
|  * It's still a mess, though.
 | ||
|  */
 | ||
| 
 | ||
| enum buf_op {
 | ||
| 	op_header,
 | ||
| 	op_cmap,
 | ||
| 	op_body
 | ||
| };
 | ||
| 
 | ||
| typedef struct {
 | ||
| 	gchar *color_string;
 | ||
| 	guint16 red;
 | ||
| 	guint16 green;
 | ||
| 	guint16 blue;
 | ||
| 	gint transparent;
 | ||
| } XPMColor;
 | ||
| 
 | ||
| struct file_handle {
 | ||
| 	FILE *infile;
 | ||
| 	gchar *buffer;
 | ||
| 	guint buffer_size;
 | ||
| };
 | ||
| 
 | ||
| struct mem_handle {
 | ||
| 	const gchar **data;
 | ||
| 	int offset;
 | ||
| };
 | ||
| 
 | ||
| /* The following 2 routines (parse_color, find_color) come from Tk, via the Win32
 | ||
|  * port of GDK. The licensing terms on these (longer than the functions) is:
 | ||
|  *
 | ||
|  * This software is copyrighted by the Regents of the University of
 | ||
|  * California, Sun Microsystems, Inc., and other parties.  The following
 | ||
|  * terms apply to all files associated with the software unless explicitly
 | ||
|  * disclaimed in individual files.
 | ||
|  * 
 | ||
|  * The authors hereby grant permission to use, copy, modify, distribute,
 | ||
|  * and license this software and its documentation for any purpose, provided
 | ||
|  * that existing copyright notices are retained in all copies and that this
 | ||
|  * notice is included verbatim in any distributions. No written agreement,
 | ||
|  * license, or royalty fee is required for any of the authorized uses.
 | ||
|  * Modifications to this software may be copyrighted by their authors
 | ||
|  * and need not follow the licensing terms described here, provided that
 | ||
|  * the new terms are clearly indicated on the first page of each file where
 | ||
|  * they apply.
 | ||
|  * 
 | ||
|  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
 | ||
|  * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 | ||
|  * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
 | ||
|  * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
 | ||
|  * POSSIBILITY OF SUCH DAMAGE.
 | ||
|  * 
 | ||
|  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 | ||
|  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 | ||
|  * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
 | ||
|  * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
 | ||
|  * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 | ||
|  * MODIFICATIONS.
 | ||
|  * 
 | ||
|  * GOVERNMENT USE: If you are acquiring this software on behalf of the
 | ||
|  * U.S. government, the Government shall have only "Restricted Rights"
 | ||
|  * in the software and related documentation as defined in the Federal 
 | ||
|  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 | ||
|  * are acquiring the software on behalf of the Department of Defense, the
 | ||
|  * software shall be classified as "Commercial Computer Software" and the
 | ||
|  * Government shall have only "Restricted Rights" as defined in Clause
 | ||
|  * 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
 | ||
|  * authors grant the U.S. Government and others acting in its behalf
 | ||
|  * permission to use and distribute the software in accordance with the
 | ||
|  * terms specified in this license.
 | ||
|  */
 | ||
| 
 | ||
| #include "xpm-color-table.h"
 | ||
|  
 | ||
| /*
 | ||
|  *----------------------------------------------------------------------
 | ||
|  *
 | ||
|  * find_color --
 | ||
|  *
 | ||
|  *	This routine finds the color entry that corresponds to the
 | ||
|  *	specified color.
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *	Returns non-zero on success.  The RGB values of the XColor
 | ||
|  *	will be initialized to the proper values on success.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *	None.
 | ||
|  *
 | ||
|  *----------------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static int
 | ||
| compare_xcolor_entries (const void *a, const void *b)
 | ||
| {
 | ||
|   return g_ascii_strcasecmp ((const char *) a, 
 | ||
| 			     color_names + ((const XPMColorEntry *)b)->name_offset);
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| find_color(const char *name,
 | ||
| 	   XPMColor   *colorPtr)
 | ||
| {
 | ||
| 	XPMColorEntry *found;
 | ||
| 
 | ||
| 	found = bsearch (name, xColors, G_N_ELEMENTS (xColors), sizeof (XPMColorEntry),
 | ||
| 			 compare_xcolor_entries);
 | ||
| 	if (found == NULL)
 | ||
| 	  return FALSE;
 | ||
| 	
 | ||
| 	colorPtr->red = (found->red * 65535) / 255;
 | ||
| 	colorPtr->green = (found->green * 65535) / 255;
 | ||
| 	colorPtr->blue = (found->blue * 65535) / 255;
 | ||
| 	
 | ||
| 	return TRUE;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  *----------------------------------------------------------------------
 | ||
|  *
 | ||
|  * parse_color --
 | ||
|  *
 | ||
|  *	Partial implementation of X color name parsing interface.
 | ||
|  *
 | ||
|  * Results:
 | ||
|  *	Returns TRUE on success.
 | ||
|  *
 | ||
|  * Side effects:
 | ||
|  *	None.
 | ||
|  *
 | ||
|  *----------------------------------------------------------------------
 | ||
|  */
 | ||
| 
 | ||
| static gboolean
 | ||
| parse_color (const char *spec,
 | ||
| 	     XPMColor   *colorPtr)
 | ||
| {
 | ||
| 	if (spec[0] == '#') {
 | ||
| 		char fmt[16];
 | ||
| 		int i, red, green, blue;
 | ||
| 
 | ||
| 		if ((i = strlen (spec + 1)) % 3) {
 | ||
| 			return FALSE;
 | ||
| 		}
 | ||
| 		i /= 3;
 | ||
| 
 | ||
| 		g_snprintf (fmt, 16, "%%%dx%%%dx%%%dx", i, i, i);
 | ||
| 
 | ||
| 		if (sscanf (spec + 1, fmt, &red, &green, &blue) != 3) {
 | ||
| 			return FALSE;
 | ||
| 		}
 | ||
| 		if (i == 4) {
 | ||
| 			colorPtr->red = red;
 | ||
| 			colorPtr->green = green;
 | ||
| 			colorPtr->blue = blue;
 | ||
| 		} else if (i == 1) {
 | ||
| 			colorPtr->red = (red * 65535) / 15;
 | ||
| 			colorPtr->green = (green * 65535) / 15;
 | ||
| 			colorPtr->blue = (blue * 65535) / 15;
 | ||
| 		} else if (i == 2)
 | ||
| 		{
 | ||
| 			colorPtr->red = (red * 65535) / 255;
 | ||
| 			colorPtr->green = (green * 65535) / 255;
 | ||
| 			colorPtr->blue = (blue * 65535) / 255;
 | ||
| 		} else /* if (i == 3) */ {
 | ||
| 			colorPtr->red = (red * 65535) / 4095;
 | ||
| 			colorPtr->green = (green * 65535) / 4095;
 | ||
| 			colorPtr->blue = (blue * 65535) / 4095;
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		if (!find_color(spec, colorPtr))
 | ||
| 			return FALSE;
 | ||
| 	}
 | ||
| 	return TRUE;
 | ||
| }
 | ||
| 
 | ||
| static gint
 | ||
| xpm_seek_string (FILE *infile, const gchar *str)
 | ||
| {
 | ||
| 	char instr[1024];
 | ||
| 
 | ||
| 	while (!feof (infile)) {
 | ||
| 		if (fscanf (infile, "%1023s", instr) < 0)
 | ||
|                         return FALSE;
 | ||
| 		if (strcmp (instr, str) == 0)
 | ||
| 			return TRUE;
 | ||
| 	}
 | ||
| 
 | ||
| 	return FALSE;
 | ||
| }
 | ||
| 
 | ||
| static gint
 | ||
| xpm_seek_char (FILE *infile, gchar c)
 | ||
| {
 | ||
| 	gint b, oldb;
 | ||
| 
 | ||
| 	while ((b = getc (infile)) != EOF) {
 | ||
| 		if (c != b && b == '/') {
 | ||
| 			b = getc (infile);
 | ||
| 			if (b == EOF)
 | ||
| 				return FALSE;
 | ||
| 
 | ||
| 			else if (b == '*') {	/* we have a comment */
 | ||
| 				b = -1;
 | ||
| 				do {
 | ||
| 					oldb = b;
 | ||
| 					b = getc (infile);
 | ||
| 					if (b == EOF)
 | ||
| 						return FALSE;
 | ||
| 				} while (!(oldb == '*' && b == '/'));
 | ||
| 			}
 | ||
| 		} else if (c == b)
 | ||
| 			return TRUE;
 | ||
| 	}
 | ||
| 
 | ||
| 	return FALSE;
 | ||
| }
 | ||
| 
 | ||
| static gint
 | ||
| xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
 | ||
| {
 | ||
| 	gint c;
 | ||
| 	guint cnt = 0, bufsiz, ret = FALSE;
 | ||
| 	gchar *buf;
 | ||
| 
 | ||
| 	buf = *buffer;
 | ||
| 	bufsiz = *buffer_size;
 | ||
| 	if (buf == NULL) {
 | ||
| 		bufsiz = 10 * sizeof (gchar);
 | ||
| 		buf = g_new (gchar, bufsiz);
 | ||
| 	}
 | ||
| 
 | ||
| 	do {
 | ||
| 		c = getc (infile);
 | ||
| 	} while (c != EOF && c != '"');
 | ||
| 
 | ||
| 	if (c != '"')
 | ||
| 		goto out;
 | ||
| 
 | ||
| 	while ((c = getc (infile)) != EOF) {
 | ||
| 		if (cnt == bufsiz) {
 | ||
| 			guint new_size = bufsiz * 2;
 | ||
| 
 | ||
| 			if (new_size > bufsiz)
 | ||
| 				bufsiz = new_size;
 | ||
| 			else
 | ||
| 				goto out;
 | ||
| 
 | ||
| 			buf = g_realloc (buf, bufsiz);
 | ||
| 			buf[bufsiz - 1] = '\0';
 | ||
| 		}
 | ||
| 
 | ||
| 		if (c != '"')
 | ||
| 			buf[cnt++] = c;
 | ||
| 		else {
 | ||
| 			buf[cnt] = 0;
 | ||
| 			ret = TRUE;
 | ||
| 			break;
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
|  out:
 | ||
| 	buf[bufsiz - 1] = '\0';	/* ensure null termination for errors */
 | ||
| 	*buffer = buf;
 | ||
| 	*buffer_size = bufsiz;
 | ||
| 	return ret;
 | ||
| }
 | ||
| 
 | ||
| static gchar *
 | ||
| xpm_extract_color (const gchar *buffer)
 | ||
| {
 | ||
| 	const gchar *p = &buffer[0];
 | ||
| 	gint new_key = 0;
 | ||
| 	gint key = 0;
 | ||
| 	gint current_key = 1;
 | ||
| 	gint space = 128;
 | ||
| 	gchar word[129], color[129], current_color[129];
 | ||
| 	gchar *r; 
 | ||
| 	
 | ||
| 	word[0] = '\0';
 | ||
| 	color[0] = '\0';
 | ||
| 	current_color[0] = '\0';
 | ||
|         while (1) {
 | ||
| 		/* skip whitespace */
 | ||
| 		for (; *p != '\0' && g_ascii_isspace (*p); p++) {
 | ||
| 		} 
 | ||
| 		/* copy word */
 | ||
| 		for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) {
 | ||
| 			*r = *p;
 | ||
| 		}
 | ||
| 		*r = '\0';
 | ||
| 		if (*word == '\0') {
 | ||
| 			if (color[0] == '\0')  /* incomplete colormap entry */
 | ||
| 				return NULL; 				
 | ||
| 			else  /* end of entry, still store the last color */
 | ||
| 				new_key = 1;
 | ||
| 		} 
 | ||
| 		else if (key > 0 && color[0] == '\0')  /* next word must be a color name part */
 | ||
| 			new_key = 0;
 | ||
| 		else {
 | ||
| 			if (strcmp (word, "c") == 0)
 | ||
| 				new_key = 5;
 | ||
| 			else if (strcmp (word, "g") == 0)
 | ||
| 				new_key = 4;
 | ||
| 			else if (strcmp (word, "g4") == 0)
 | ||
| 				new_key = 3;
 | ||
| 			else if (strcmp (word, "m") == 0)
 | ||
| 				new_key = 2;
 | ||
| 			else if (strcmp (word, "s") == 0)
 | ||
| 				new_key = 1;
 | ||
| 			else 
 | ||
| 				new_key = 0;
 | ||
| 		}
 | ||
| 		if (new_key == 0) {  /* word is a color name part */
 | ||
| 			if (key == 0)  /* key expected */
 | ||
| 				return NULL;
 | ||
| 			/* accumulate color name */
 | ||
| 			if (color[0] != '\0') {
 | ||
| 				strncat (color, " ", space);
 | ||
| 				space -= MIN (space, 1);
 | ||
| 			}
 | ||
| 			strncat (color, word, space);
 | ||
| 			space -= MIN (space, strlen (word));
 | ||
| 		}
 | ||
| 		else {  /* word is a key */
 | ||
| 			if (key > current_key) {
 | ||
| 				current_key = key;
 | ||
| 				strcpy (current_color, color);
 | ||
| 			}
 | ||
| 			space = 128;
 | ||
| 			color[0] = '\0';
 | ||
| 			key = new_key;
 | ||
| 			if (*p == '\0') break;
 | ||
| 		}
 | ||
| 		
 | ||
| 	}
 | ||
| 	if (current_key > 1)
 | ||
| 		return g_strdup (current_color);
 | ||
| 	else
 | ||
| 		return NULL; 
 | ||
| }
 | ||
| 
 | ||
| /* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */
 | ||
| 
 | ||
| static const gchar *
 | ||
| file_buffer (enum buf_op op, gpointer handle)
 | ||
| {
 | ||
| 	struct file_handle *h = handle;
 | ||
| 
 | ||
| 	switch (op) {
 | ||
| 	case op_header:
 | ||
| 		if (xpm_seek_string (h->infile, "XPM") != TRUE)
 | ||
| 			break;
 | ||
| 
 | ||
| 		if (xpm_seek_char (h->infile, '{') != TRUE)
 | ||
| 			break;
 | ||
| 		/* Fall through to the next xpm_seek_char. */
 | ||
| 
 | ||
| 	case op_cmap:
 | ||
| 		xpm_seek_char (h->infile, '"');
 | ||
| 		fseek (h->infile, -1, SEEK_CUR);
 | ||
| 		/* Fall through to the xpm_read_string. */
 | ||
| 
 | ||
| 	case op_body:
 | ||
| 		if(!xpm_read_string (h->infile, &h->buffer, &h->buffer_size))
 | ||
| 			return NULL;
 | ||
| 		return h->buffer;
 | ||
| 
 | ||
| 	default:
 | ||
| 		g_assert_not_reached ();
 | ||
| 	}
 | ||
| 
 | ||
| 	return NULL;
 | ||
| }
 | ||
| 
 | ||
| /* This reads from memory */
 | ||
| static const gchar *
 | ||
| mem_buffer (enum buf_op op, gpointer handle)
 | ||
| {
 | ||
| 	struct mem_handle *h = handle;
 | ||
| 	switch (op) {
 | ||
| 	case op_header:
 | ||
| 	case op_cmap:
 | ||
| 	case op_body:
 | ||
|                 if (h->data[h->offset]) {
 | ||
|                         const gchar* retval;
 | ||
| 
 | ||
|                         retval = h->data[h->offset];
 | ||
|                         h->offset += 1;
 | ||
|                         return retval;
 | ||
|                 }
 | ||
|                 break;
 | ||
| 
 | ||
| 	default:
 | ||
| 		g_assert_not_reached ();
 | ||
|                 break;
 | ||
| 	}
 | ||
| 
 | ||
| 	return NULL;
 | ||
| }
 | ||
| 
 | ||
| /* This function does all the work. */
 | ||
| static GdkPixbuf *
 | ||
| pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle,
 | ||
|                         GError **error)
 | ||
| {
 | ||
| 	gint w, h, n_col, cpp, x_hot, y_hot, items;
 | ||
| 	gint cnt, xcnt, ycnt, wbytes, n;
 | ||
| 	gint is_trans = FALSE;
 | ||
| 	const gchar *buffer;
 | ||
|         gchar *name_buf;
 | ||
| 	gchar pixel_str[32];
 | ||
| 	GHashTable *color_hash;
 | ||
| 	XPMColor *colors, *color, *fallbackcolor;
 | ||
| 	guchar *pixtmp;
 | ||
| 	GdkPixbuf *pixbuf;
 | ||
| 
 | ||
| 	fallbackcolor = NULL;
 | ||
| 
 | ||
| 	buffer = (*get_buf) (op_header, handle);
 | ||
| 	if (!buffer) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("No XPM header found"));
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 	items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot);
 | ||
| 
 | ||
| 	if (items != 4 && items != 6) {
 | ||
| 		g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("Invalid XPM header"));
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (w <= 0) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("XPM file has image width <= 0"));
 | ||
| 		return NULL;
 | ||
| 
 | ||
| 	}
 | ||
| 	if (h <= 0) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("XPM file has image height <= 0"));
 | ||
| 		return NULL;
 | ||
| 
 | ||
| 	}
 | ||
| 	if (cpp <= 0 || cpp >= 32) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("XPM has invalid number of chars per pixel"));
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 	if (n_col <= 0 || 
 | ||
| 	    n_col >= G_MAXINT / (cpp + 1) || 
 | ||
| 	    n_col >= G_MAXINT / sizeof (XPMColor)) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                      _("XPM file has invalid number of colors"));
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 
 | ||
| 	/* The hash is used for fast lookups of color from chars */
 | ||
| 	color_hash = g_hash_table_new (g_str_hash, g_str_equal);
 | ||
| 
 | ||
| 	name_buf = g_try_malloc (n_col * (cpp + 1));
 | ||
| 	if (!name_buf) {
 | ||
| 		g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
 | ||
|                                      _("Cannot allocate memory for loading XPM image"));
 | ||
| 		g_hash_table_destroy (color_hash);
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 	colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col);
 | ||
| 	if (!colors) {
 | ||
| 		g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
 | ||
|                                      _("Cannot allocate memory for loading XPM image"));
 | ||
| 		g_hash_table_destroy (color_hash);
 | ||
| 		g_free (name_buf);
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 
 | ||
| 	for (cnt = 0; cnt < n_col; cnt++) {
 | ||
| 		gchar *color_name;
 | ||
| 
 | ||
| 		buffer = (*get_buf) (op_cmap, handle);
 | ||
| 		if (!buffer) {
 | ||
|                         g_set_error_literal (error,
 | ||
|                                              GDK_PIXBUF_ERROR,
 | ||
|                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
 | ||
|                                              _("Cannot read XPM colormap"));
 | ||
| 			g_hash_table_destroy (color_hash);
 | ||
| 			g_free (name_buf);
 | ||
| 			g_free (colors);
 | ||
| 			return NULL;
 | ||
| 		}
 | ||
| 
 | ||
| 		color = &colors[cnt];
 | ||
| 		color->color_string = &name_buf[cnt * (cpp + 1)];
 | ||
| 		strncpy (color->color_string, buffer, cpp);
 | ||
| 		color->color_string[cpp] = 0;
 | ||
| 		buffer += strlen (color->color_string);
 | ||
| 		color->transparent = FALSE;
 | ||
| 
 | ||
| 		color_name = xpm_extract_color (buffer);
 | ||
| 
 | ||
| 		if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0)
 | ||
| 		    || (parse_color (color_name, color) == FALSE)) {
 | ||
| 			color->transparent = TRUE;
 | ||
| 			color->red = 0;
 | ||
| 			color->green = 0;
 | ||
| 			color->blue = 0;
 | ||
| 			is_trans = TRUE;
 | ||
| 		}
 | ||
| 
 | ||
| 		g_free (color_name);
 | ||
| 		g_hash_table_insert (color_hash, color->color_string, color);
 | ||
| 
 | ||
| 		if (cnt == 0)
 | ||
| 			fallbackcolor = color;
 | ||
| 	}
 | ||
| 
 | ||
| 	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h);
 | ||
| 
 | ||
| 	if (!pixbuf) {
 | ||
|                 g_set_error_literal (error,
 | ||
|                                      GDK_PIXBUF_ERROR,
 | ||
|                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
 | ||
|                                      _("Cannot allocate memory for loading XPM image"));
 | ||
| 		g_hash_table_destroy (color_hash);
 | ||
| 		g_free (colors);
 | ||
| 		g_free (name_buf);
 | ||
| 		return NULL;
 | ||
| 	}
 | ||
| 
 | ||
| 	wbytes = w * cpp;
 | ||
| 
 | ||
| 	for (ycnt = 0; ycnt < h; ycnt++) {
 | ||
| 		pixtmp = pixbuf->pixels + ycnt * pixbuf->rowstride;
 | ||
| 
 | ||
| 		buffer = (*get_buf) (op_body, handle);
 | ||
| 		if ((!buffer) || (strlen (buffer) < wbytes))
 | ||
| 			continue;
 | ||
| 
 | ||
| 		for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
 | ||
| 			strncpy (pixel_str, &buffer[n], cpp);
 | ||
| 			pixel_str[cpp] = 0;
 | ||
| 
 | ||
| 			color = g_hash_table_lookup (color_hash, pixel_str);
 | ||
| 
 | ||
| 			/* Bad XPM...punt */
 | ||
| 			if (!color)
 | ||
| 				color = fallbackcolor;
 | ||
| 
 | ||
| 			*pixtmp++ = color->red >> 8;
 | ||
| 			*pixtmp++ = color->green >> 8;
 | ||
| 			*pixtmp++ = color->blue >> 8;
 | ||
| 
 | ||
| 			if (is_trans && color->transparent)
 | ||
| 				*pixtmp++ = 0;
 | ||
| 			else if (is_trans)
 | ||
| 				*pixtmp++ = 0xFF;
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	g_hash_table_destroy (color_hash);
 | ||
| 	g_free (colors);
 | ||
| 	g_free (name_buf);
 | ||
| 
 | ||
| 	if (items == 6) {
 | ||
| 		gchar hot[10];
 | ||
| 		g_snprintf (hot, 10, "%d", x_hot);
 | ||
| 		gdk_pixbuf_set_option (pixbuf, "x_hot", hot);
 | ||
| 		g_snprintf (hot, 10, "%d", y_hot);
 | ||
| 		gdk_pixbuf_set_option (pixbuf, "y_hot", hot);
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	return pixbuf;
 | ||
| }
 | ||
| 
 | ||
| /* Shared library entry point for file loading */
 | ||
| static GdkPixbuf *
 | ||
| gdk_pixbuf__xpm_image_load (FILE *f,
 | ||
|                             GError **error)
 | ||
| {
 | ||
| 	GdkPixbuf *pixbuf;
 | ||
| 	struct file_handle h;
 | ||
| 
 | ||
| 	memset (&h, 0, sizeof (h));
 | ||
| 	h.infile = f;
 | ||
| 	pixbuf = pixbuf_create_from_xpm (file_buffer, &h, error);
 | ||
| 	g_free (h.buffer);
 | ||
| 
 | ||
| 	return pixbuf;
 | ||
| }
 | ||
| 
 | ||
| /* Shared library entry point for memory loading */
 | ||
| static GdkPixbuf *
 | ||
| gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data)
 | ||
| {
 | ||
|         GdkPixbuf *pixbuf;
 | ||
|         struct mem_handle h;
 | ||
|         GError *error = NULL;
 | ||
|         
 | ||
|         h.data = data;
 | ||
|         h.offset = 0;
 | ||
|         
 | ||
| 	pixbuf = pixbuf_create_from_xpm (mem_buffer, &h, &error);
 | ||
| 
 | ||
|         if (error) {
 | ||
|                 g_warning ("Inline XPM data is broken: %s", error->message);
 | ||
|                 g_error_free (error);
 | ||
|                 error = NULL;
 | ||
|         }
 | ||
|         
 | ||
| 	return pixbuf;
 | ||
| }
 | ||
| 
 | ||
| /* Progressive loader */
 | ||
| typedef struct _XPMContext XPMContext;
 | ||
| struct _XPMContext
 | ||
| {
 | ||
|        GdkPixbufModulePreparedFunc prepare_func;
 | ||
|        GdkPixbufModuleUpdatedFunc update_func;
 | ||
|        gpointer user_data;
 | ||
| 
 | ||
|        gchar *tempname;
 | ||
|        FILE *file;
 | ||
|        gboolean all_okay;
 | ||
| };
 | ||
| 
 | ||
| /*
 | ||
|  * FIXME xpm loading progressively is not properly implemented.
 | ||
|  * Instead we will buffer to a file then load that file when done.
 | ||
|  * This is very broken but it should be relayively simple to fix
 | ||
|  * in the future.
 | ||
|  */
 | ||
| static gpointer
 | ||
| gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
 | ||
|                                   GdkPixbufModulePreparedFunc prepare_func,
 | ||
|                                   GdkPixbufModuleUpdatedFunc update_func,
 | ||
|                                   gpointer user_data,
 | ||
|                                   GError **error)
 | ||
| {
 | ||
|        XPMContext *context;
 | ||
|        gint fd;
 | ||
| 
 | ||
|        context = g_new (XPMContext, 1);
 | ||
|        context->prepare_func = prepare_func;
 | ||
|        context->update_func = update_func;
 | ||
|        context->user_data = user_data;
 | ||
|        context->all_okay = TRUE;
 | ||
|        fd = g_file_open_tmp ("gdkpixbuf-xpm-tmp.XXXXXX", &context->tempname,
 | ||
| 			     NULL);
 | ||
|        if (fd < 0) {
 | ||
|                g_free (context);
 | ||
|                return NULL;
 | ||
|        }
 | ||
| 
 | ||
|        context->file = fdopen (fd, "w+");
 | ||
|        if (context->file == NULL) {
 | ||
|                g_free (context->tempname);
 | ||
|                g_free (context);
 | ||
|                return NULL;
 | ||
|        }
 | ||
| 
 | ||
|        return context;
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| gdk_pixbuf__xpm_image_stop_load (gpointer data,
 | ||
|                                  GError **error)
 | ||
| {
 | ||
|        XPMContext *context = (XPMContext*) data;
 | ||
|        GdkPixbuf *pixbuf;
 | ||
|        gboolean retval = FALSE;
 | ||
|        
 | ||
|        g_return_val_if_fail (data != NULL, FALSE);
 | ||
| 
 | ||
|        fflush (context->file);
 | ||
|        rewind (context->file);
 | ||
|        if (context->all_okay) {
 | ||
|                pixbuf = gdk_pixbuf__xpm_image_load (context->file, error);
 | ||
| 
 | ||
|                if (pixbuf != NULL) {
 | ||
| 		       if (context->prepare_func)
 | ||
| 			       (* context->prepare_func) (pixbuf,
 | ||
| 							  NULL,
 | ||
| 							  context->user_data);
 | ||
| 		       if (context->update_func)
 | ||
| 			       (* context->update_func) (pixbuf, 0, 0, pixbuf->width, pixbuf->height, context->user_data);
 | ||
|                        g_object_unref (pixbuf);
 | ||
| 
 | ||
|                        retval = TRUE;
 | ||
|                }
 | ||
|        }
 | ||
| 
 | ||
|        fclose (context->file);
 | ||
|        g_unlink (context->tempname);
 | ||
|        g_free (context->tempname);
 | ||
|        g_free ((XPMContext *) context);
 | ||
| 
 | ||
|        return retval;
 | ||
| }
 | ||
| 
 | ||
| static gboolean
 | ||
| gdk_pixbuf__xpm_image_load_increment (gpointer data,
 | ||
|                                       const guchar *buf,
 | ||
|                                       guint    size,
 | ||
|                                       GError **error)
 | ||
| {
 | ||
|        XPMContext *context = (XPMContext *) data;
 | ||
| 
 | ||
|        g_return_val_if_fail (data != NULL, FALSE);
 | ||
| 
 | ||
|        if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
 | ||
| 	       gint save_errno = errno;
 | ||
|                context->all_okay = FALSE;
 | ||
|                g_set_error_literal (error,
 | ||
|                                     G_FILE_ERROR,
 | ||
|                                     g_file_error_from_errno (save_errno),
 | ||
|                                     _("Failed to write to temporary file when loading XPM image"));
 | ||
|                return FALSE;
 | ||
|        }
 | ||
| 
 | ||
|        return TRUE;
 | ||
| }
 | ||
| 
 | ||
| #ifndef INCLUDE_xpm
 | ||
| #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
 | ||
| #else
 | ||
| #define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function
 | ||
| #endif
 | ||
| 
 | ||
| MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
 | ||
| {
 | ||
| 	module->load = gdk_pixbuf__xpm_image_load;
 | ||
| 	module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data;
 | ||
| 	module->begin_load = gdk_pixbuf__xpm_image_begin_load;
 | ||
| 	module->stop_load = gdk_pixbuf__xpm_image_stop_load;
 | ||
| 	module->load_increment = gdk_pixbuf__xpm_image_load_increment;
 | ||
| }
 | ||
| 
 | ||
| MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
 | ||
| {
 | ||
| 	static GdkPixbufModulePattern signature[] = {
 | ||
| 		{ "/* XPM */", NULL, 100 },
 | ||
| 		{ NULL, NULL, 0 }
 | ||
| 	};
 | ||
| 	static gchar * mime_types[] = {
 | ||
| 		"image/x-xpixmap",
 | ||
| 		NULL
 | ||
| 	};
 | ||
| 	static gchar * extensions[] = {
 | ||
| 		"xpm",
 | ||
| 		NULL
 | ||
| 	};
 | ||
| 
 | ||
| 	info->name = "xpm";
 | ||
| 	info->signature = signature;
 | ||
| 	info->description = N_("The XPM image format");
 | ||
| 	info->mime_types = mime_types;
 | ||
| 	info->extensions = extensions;
 | ||
| 	info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
 | ||
| 	info->license = "LGPL";
 | ||
| }
 | 
