mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 21:51:08 +00:00
6ba75ff95b
2004-12-05 Tor Lillqvist <tml@iki.fi> * gdk-pixbuf-animation.c * gdk-pixbuf-io.c * io-xbm.c * io-xpm.c * make-inline-pixbuf.c * queryloaders.c: Use gstdio wrappers. Document that file names are in the GLib file name encoding. * gdk-pixbuf-csource.c * queryloaders.c: On Windows, convert command line arguments and environment variable values from locale encoding to UTF-8. * queryloaders.c: On Windows, use wide character API when available. * Makefile.am * gdk-pixbuf-core.h * gdk-pixbuf-io.c * gdk-pixbuf-animation.h * gdk-pixbuf-animation.c: Like in GLib, for DLL ABI stability on Windows, add binary compatibility versions of functions that take file names as arguments. They use the system codepage, not GLib file name encoding (which is UTF-8 on Windows). Use #defines to make newly compiled code use the "real" functions that use the GLib file name encoding scheme.
482 lines
11 KiB
C
482 lines
11 KiB
C
/* -*- mode: C; c-file-style: "linux" -*- */
|
||
/* GdkPixbuf library - XBM image loader
|
||
*
|
||
* Copyright (C) 1999 Mark Crichton
|
||
* Copyright (C) 1999 The Free Software Foundation
|
||
* Copyright (C) 2001 Eazel, Inc.
|
||
*
|
||
* Authors: Mark Crichton <crichton@gimp.org>
|
||
* Federico Mena-Quintero <federico@gimp.org>
|
||
* Jonathan Blandford <jrb@redhat.com>
|
||
* John Harper <jsh@eazel.com>
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Library 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
|
||
* Library General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Library 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.
|
||
*/
|
||
|
||
/* Following code adapted from io-tiff.c, which was ``(almost) blatantly
|
||
ripped from Imlib'' */
|
||
|
||
#include <config.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#ifdef HAVE_UNISTD_H
|
||
#include <unistd.h>
|
||
#endif
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
#include "gdk-pixbuf-private.h"
|
||
#include "gdk-pixbuf-io.h"
|
||
#include <glib/gstdio.h>
|
||
|
||
|
||
typedef struct _XBMData XBMData;
|
||
struct _XBMData
|
||
{
|
||
GdkPixbufModulePreparedFunc prepare_func;
|
||
GdkPixbufModuleUpdatedFunc update_func;
|
||
gpointer user_data;
|
||
|
||
gchar *tempname;
|
||
FILE *file;
|
||
gboolean all_okay;
|
||
};
|
||
|
||
|
||
/* xbm parser borrowed from xc/lib/X11/RdBitF.c */
|
||
|
||
#define MAX_SIZE 255
|
||
|
||
/* shared data for the image read/parse logic */
|
||
static short hex_table[256]; /* conversion value */
|
||
static gboolean initialized = FALSE; /* easier to fill in at run time */
|
||
|
||
|
||
/* Table index for the hex values. Initialized once, first time.
|
||
* Used for translation value or delimiter significance lookup.
|
||
*/
|
||
static void
|
||
init_hex_table (void)
|
||
{
|
||
/*
|
||
* We build the table at run time for several reasons:
|
||
*
|
||
* 1. portable to non-ASCII machines.
|
||
* 2. still reentrant since we set the init flag after setting table.
|
||
* 3. easier to extend.
|
||
* 4. less prone to bugs.
|
||
*/
|
||
hex_table['0'] = 0;
|
||
hex_table['1'] = 1;
|
||
hex_table['2'] = 2;
|
||
hex_table['3'] = 3;
|
||
hex_table['4'] = 4;
|
||
hex_table['5'] = 5;
|
||
hex_table['6'] = 6;
|
||
hex_table['7'] = 7;
|
||
hex_table['8'] = 8;
|
||
hex_table['9'] = 9;
|
||
hex_table['A'] = 10;
|
||
hex_table['B'] = 11;
|
||
hex_table['C'] = 12;
|
||
hex_table['D'] = 13;
|
||
hex_table['E'] = 14;
|
||
hex_table['F'] = 15;
|
||
hex_table['a'] = 10;
|
||
hex_table['b'] = 11;
|
||
hex_table['c'] = 12;
|
||
hex_table['d'] = 13;
|
||
hex_table['e'] = 14;
|
||
hex_table['f'] = 15;
|
||
|
||
/* delimiters of significance are flagged w/ negative value */
|
||
hex_table[' '] = -1;
|
||
hex_table[','] = -1;
|
||
hex_table['}'] = -1;
|
||
hex_table['\n'] = -1;
|
||
hex_table['\t'] = -1;
|
||
|
||
initialized = TRUE;
|
||
}
|
||
|
||
/* Read next hex value in the input stream, return -1 if EOF */
|
||
static int
|
||
next_int (FILE *fstream)
|
||
{
|
||
int ch;
|
||
int value = 0;
|
||
int gotone = 0;
|
||
int done = 0;
|
||
|
||
/* loop, accumulate hex value until find delimiter
|
||
skip any initial delimiters found in read stream */
|
||
|
||
while (!done) {
|
||
ch = getc (fstream);
|
||
if (ch == EOF) {
|
||
value = -1;
|
||
done++;
|
||
} else {
|
||
/* trim high bits, check type and accumulate */
|
||
ch &= 0xff;
|
||
if (g_ascii_isxdigit (ch)) {
|
||
value = (value << 4) + g_ascii_xdigit_value (ch);
|
||
gotone++;
|
||
} else if ((hex_table[ch]) < 0 && gotone) {
|
||
done++;
|
||
}
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
|
||
static gboolean
|
||
read_bitmap_file_data (FILE *fstream,
|
||
guint *width, guint *height,
|
||
guchar **data,
|
||
int *x_hot, int *y_hot)
|
||
{
|
||
guchar *bits = NULL; /* working variable */
|
||
char line[MAX_SIZE]; /* input line from file */
|
||
int size; /* number of bytes of data */
|
||
char name_and_type[MAX_SIZE]; /* an input line */
|
||
char *type; /* for parsing */
|
||
int value; /* from an input line */
|
||
int version10p; /* boolean, old format */
|
||
int padding; /* to handle alignment */
|
||
int bytes_per_line; /* per scanline of data */
|
||
guint ww = 0; /* width */
|
||
guint hh = 0; /* height */
|
||
int hx = -1; /* x hotspot */
|
||
int hy = -1; /* y hotspot */
|
||
|
||
/* first time initialization */
|
||
if (!initialized) {
|
||
init_hex_table ();
|
||
}
|
||
|
||
/* error cleanup and return macro */
|
||
#define RETURN(code) { g_free (bits); return code; }
|
||
|
||
while (fgets (line, MAX_SIZE, fstream)) {
|
||
if (strlen (line) == MAX_SIZE-1)
|
||
RETURN (FALSE);
|
||
if (sscanf (line,"#define %s %d",name_and_type,&value) == 2) {
|
||
if (!(type = strrchr (name_and_type, '_')))
|
||
type = name_and_type;
|
||
else {
|
||
type++;
|
||
}
|
||
|
||
if (!strcmp ("width", type))
|
||
ww = (unsigned int) value;
|
||
if (!strcmp ("height", type))
|
||
hh = (unsigned int) value;
|
||
if (!strcmp ("hot", type)) {
|
||
if (type-- == name_and_type
|
||
|| type-- == name_and_type)
|
||
continue;
|
||
if (!strcmp ("x_hot", type))
|
||
hx = value;
|
||
if (!strcmp ("y_hot", type))
|
||
hy = value;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (sscanf (line, "static short %s = {", name_and_type) == 1)
|
||
version10p = 1;
|
||
else if (sscanf (line,"static unsigned char %s = {",name_and_type) == 1)
|
||
version10p = 0;
|
||
else if (sscanf (line, "static char %s = {", name_and_type) == 1)
|
||
version10p = 0;
|
||
else
|
||
continue;
|
||
|
||
if (!(type = strrchr (name_and_type, '_')))
|
||
type = name_and_type;
|
||
else
|
||
type++;
|
||
|
||
if (strcmp ("bits[]", type))
|
||
continue;
|
||
|
||
if (!ww || !hh)
|
||
RETURN (FALSE);
|
||
|
||
if ((ww % 16) && ((ww % 16) < 9) && version10p)
|
||
padding = 1;
|
||
else
|
||
padding = 0;
|
||
|
||
bytes_per_line = (ww+7)/8 + padding;
|
||
|
||
size = bytes_per_line * hh;
|
||
bits = g_malloc (size);
|
||
|
||
if (version10p) {
|
||
unsigned char *ptr;
|
||
int bytes;
|
||
|
||
for (bytes = 0, ptr = bits; bytes < size; (bytes += 2)) {
|
||
if ((value = next_int (fstream)) < 0)
|
||
RETURN (FALSE);
|
||
*(ptr++) = value;
|
||
if (!padding || ((bytes+2) % bytes_per_line))
|
||
*(ptr++) = value >> 8;
|
||
}
|
||
} else {
|
||
unsigned char *ptr;
|
||
int bytes;
|
||
|
||
for (bytes = 0, ptr = bits; bytes < size; bytes++, ptr++) {
|
||
if ((value = next_int (fstream)) < 0)
|
||
RETURN (FALSE);
|
||
*ptr=value;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (!bits)
|
||
RETURN (FALSE);
|
||
|
||
*data = bits;
|
||
*width = ww;
|
||
*height = hh;
|
||
if (x_hot)
|
||
*x_hot = hx;
|
||
if (y_hot)
|
||
*y_hot = hy;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
static GdkPixbuf *
|
||
gdk_pixbuf__xbm_image_load_real (FILE *f, XBMData *context, GError **error)
|
||
{
|
||
guint w, h;
|
||
int x_hot, y_hot;
|
||
guchar *data, *ptr;
|
||
guchar *pixels;
|
||
guint row_stride;
|
||
int x, y;
|
||
int reg = 0; /* Quiet compiler */
|
||
int bits;
|
||
|
||
GdkPixbuf *pixbuf;
|
||
|
||
if (!read_bitmap_file_data (f, &w, &h, &data, &x_hot, &y_hot)) {
|
||
g_set_error (error,
|
||
GDK_PIXBUF_ERROR,
|
||
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
|
||
_("Invalid XBM file"));
|
||
return NULL;
|
||
}
|
||
|
||
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h);
|
||
|
||
if (pixbuf == NULL) {
|
||
g_set_error (error,
|
||
GDK_PIXBUF_ERROR,
|
||
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
|
||
_("Insufficient memory to load XBM image file"));
|
||
return NULL;
|
||
}
|
||
|
||
if (x_hot != -1 && y_hot != -1) {
|
||
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);
|
||
}
|
||
|
||
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
||
row_stride = gdk_pixbuf_get_rowstride (pixbuf);
|
||
|
||
if (context)
|
||
(* context->prepare_func) (pixbuf, NULL, context->user_data);
|
||
|
||
|
||
/* Initialize PIXBUF */
|
||
|
||
ptr = data;
|
||
for (y = 0; y < h; y++) {
|
||
bits = 0;
|
||
for (x = 0; x < w; x++) {
|
||
guchar channel;
|
||
if (bits == 0) {
|
||
reg = *ptr++;
|
||
bits = 8;
|
||
}
|
||
|
||
channel = (reg & 1) ? 0 : 255;
|
||
reg >>= 1;
|
||
bits--;
|
||
|
||
pixels[x*3+0] = channel;
|
||
pixels[x*3+1] = channel;
|
||
pixels[x*3+2] = channel;
|
||
}
|
||
pixels += row_stride;
|
||
}
|
||
g_free (data);
|
||
|
||
if (context) {
|
||
(* context->update_func) (pixbuf, 0, 0, w, h, context->user_data);
|
||
g_object_unref (pixbuf);
|
||
pixbuf = NULL;
|
||
}
|
||
|
||
return pixbuf;
|
||
}
|
||
|
||
|
||
/* Static loader */
|
||
|
||
static GdkPixbuf *
|
||
gdk_pixbuf__xbm_image_load (FILE *f, GError **error)
|
||
{
|
||
return gdk_pixbuf__xbm_image_load_real (f, NULL, error);
|
||
}
|
||
|
||
|
||
/* Progressive loader */
|
||
|
||
/*
|
||
* Proper XBM progressive loading isn't implemented. Instead we write
|
||
* it to a file, then load the file when it's done. It's not pretty.
|
||
*/
|
||
|
||
static gpointer
|
||
gdk_pixbuf__xbm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
|
||
GdkPixbufModulePreparedFunc prepare_func,
|
||
GdkPixbufModuleUpdatedFunc update_func,
|
||
gpointer user_data,
|
||
GError **error)
|
||
{
|
||
XBMData *context;
|
||
gint fd;
|
||
|
||
context = g_new (XBMData, 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-xbm-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__xbm_image_stop_load (gpointer data,
|
||
GError **error)
|
||
{
|
||
XBMData *context = (XBMData*) data;
|
||
gboolean retval = TRUE;
|
||
|
||
g_return_val_if_fail (data != NULL, TRUE);
|
||
|
||
fflush (context->file);
|
||
rewind (context->file);
|
||
if (context->all_okay) {
|
||
GdkPixbuf *pixbuf;
|
||
pixbuf = gdk_pixbuf__xbm_image_load_real (context->file, context,
|
||
error);
|
||
if (pixbuf == NULL)
|
||
retval = FALSE;
|
||
}
|
||
|
||
fclose (context->file);
|
||
g_unlink (context->tempname);
|
||
g_free (context->tempname);
|
||
g_free ((XBMData *) context);
|
||
|
||
return retval;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_pixbuf__xbm_image_load_increment (gpointer data,
|
||
const guchar *buf,
|
||
guint size,
|
||
GError **error)
|
||
{
|
||
XBMData *context = (XBMData *) data;
|
||
|
||
g_return_val_if_fail (data != NULL, FALSE);
|
||
|
||
if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
|
||
context->all_okay = FALSE;
|
||
g_set_error (error,
|
||
G_FILE_ERROR,
|
||
g_file_error_from_errno (errno),
|
||
_("Failed to write to temporary file when loading XBM image"));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void
|
||
MODULE_ENTRY (xbm, fill_vtable) (GdkPixbufModule *module)
|
||
{
|
||
module->load = gdk_pixbuf__xbm_image_load;
|
||
module->begin_load = gdk_pixbuf__xbm_image_begin_load;
|
||
module->stop_load = gdk_pixbuf__xbm_image_stop_load;
|
||
module->load_increment = gdk_pixbuf__xbm_image_load_increment;
|
||
}
|
||
|
||
void
|
||
MODULE_ENTRY (xbm, fill_info) (GdkPixbufFormat *info)
|
||
{
|
||
static GdkPixbufModulePattern signature[] = {
|
||
{ "#define ", NULL, 100 },
|
||
{ "/*", NULL, 50 },
|
||
{ NULL, NULL, 0 }
|
||
};
|
||
static gchar * mime_types[] = {
|
||
"image/x-xbitmap",
|
||
NULL
|
||
};
|
||
static gchar * extensions[] = {
|
||
"xbm",
|
||
NULL
|
||
};
|
||
|
||
info->name = "xbm";
|
||
info->signature = signature;
|
||
info->description = N_("The XBM image format");
|
||
info->mime_types = mime_types;
|
||
info->extensions = extensions;
|
||
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
|
||
info->license = "LGPL";
|
||
}
|