diff --git a/gdk-pixbuf/io-gif.c b/gdk-pixbuf/io-gif.c index c3252db3c6..3c6a047716 100644 --- a/gdk-pixbuf/io-gif.c +++ b/gdk-pixbuf/io-gif.c @@ -1,11 +1,26 @@ /* - * io-png.c: GdkPixBuf I/O for GIF files. + * io-gif.c: GdkPixBuf I/O for GIF files. * ...second verse, same as the first... * - * Author: - * Mark Crichton + * Copyright (C) 1999 Mark Crichton + * Author: Mark Crichton + * + * 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, Cambridge, MA 02139, USA. * */ + #include #include #include @@ -16,141 +31,143 @@ /* Shared library entry point */ GdkPixBuf *image_load(FILE * f) { - gint fn, is_trans, done; - gint t_color = -1; - gint w, h, i, j; - art_u8 *pixels, *tmpptr; - GifFileType *gif; - GifRowType *rows; - GifRecordType rec; - ColorMapObject *cmap; - int intoffset[] = {0, 4, 2, 1}; - int intjump[] = {8, 8, 4, 2}; + gint fn, is_trans, done; + gint t_color = -1; + gint w, h, i, j; + art_u8 *pixels, *tmpptr; + GifFileType *gif; + GifRowType *rows; + GifRecordType rec; + ColorMapObject *cmap; + int intoffset[] = + {0, 4, 2, 1}; + int intjump[] = + {8, 8, 4, 2}; - GdkPixBuf *pixbuf; + GdkPixBuf *pixbuf; - g_return_val_if_fail(f != NULL, NULL); + g_return_val_if_fail(f != NULL, NULL); - fn = fileno(f); - gif = DGifOpenFileHandle(fn); + fn = fileno(f); + gif = DGifOpenFileHandle(fn); - if (!gif) { - g_error("DGifOpenFilehandle FAILED"); + if (!gif) { + g_error("DGifOpenFilehandle FAILED"); + PrintGifError(); + return NULL; + } + /* Now we do the ungodly mess of loading a GIF image + * I used to remember when I liked this file format... + * of course, I still coded in assembler then. + * This comes from gdk_imlib, with some cleanups. + */ + + do { + if (DGifGetRecordType(gif, &rec) == GIF_ERROR) { + PrintGifError(); + rec = TERMINATE_RECORD_TYPE; + } + if ((rec == IMAGE_DESC_RECORD_TYPE) && (!done)) { + if (DGifGetImageDesc(gif) == GIF_ERROR) { PrintGifError(); + rec = TERMINATE_RECORD_TYPE; + } + w = gif->Image.Width; + h = gif->Image.Height; + rows = g_malloc(h * sizeof(GifRowType *)); + if (!rows) { + DGifCloseFile(gif); return NULL; - } - /* Now we do the ungodly mess of loading a GIF image - * I used to remember when I liked this file format... - * of course, I still coded in assembler then. - * This comes from gdk_imlib, with some cleanups. - */ - - do { - if (DGifGetRecordType(gif, &rec) == GIF_ERROR) { - PrintGifError(); - rec = TERMINATE_RECORD_TYPE; + } + for (i = 0; i < h; i++) { + rows[i] = g_malloc(w * sizeof(GifPixelType)); + if (!rows[i]) { + DGifCloseFile(gif); + for (i = 0; i < h; i++) + if (rows[i]) + g_free(rows[i]); + free(rows); + return NULL; } - if ((rec == IMAGE_DESC_RECORD_TYPE) && (!done)) { - if (DGifGetImageDesc(gif) == GIF_ERROR) { - PrintGifError(); - rec = TERMINATE_RECORD_TYPE; - } - w = gif->Image.Width; - h = gif->Image.Height; - rows = g_malloc(h * sizeof(GifRowType *)); - if (!rows) { - DGifCloseFile(gif); - return NULL; - } - for (i = 0; i < h; i++) { - rows[i] = g_malloc(w * sizeof(GifPixelType)); - if (!rows[i]) { - DGifCloseFile(gif); - for (i = 0; i < h; i++) - if (rows[i]) - g_free(rows[i]); - free(rows); - return NULL; - } - } - if (gif->Image.Interlace) { - for (i = 0; i < 4; i++) { - for (j = intoffset[i]; j < h; j += intjump[i]) - DGifGetLine(gif, rows[j], w); - } - } else { - for (i = 0; i < h; i++) - DGifGetLine(gif, rows[i], w); - } - done = TRUE; - } else if (rec == EXTENSION_RECORD_TYPE) { - gint ext_code; - GifByteType *ext; - - DGifGetExtension(gif, &ext_code, &ext); - while (ext) { - if ((ext_code == GRAPHICS_EXT_FUNC_CODE) && - (ext[1] & 1) && (t_color < 0)) { - is_trans = TRUE; - t_color = (gint) ext[4]; - } - ext = NULL; - DGifGetExtensionNext(gif, &ext); - } + } + if (gif->Image.Interlace) { + for (i = 0; i < 4; i++) { + for (j = intoffset[i]; j < h; j += intjump[i]) + DGifGetLine(gif, rows[j], w); } - } - while (rec != TERMINATE_RECORD_TYPE); + } else { + for (i = 0; i < h; i++) + DGifGetLine(gif, rows[i], w); + } + done = TRUE; + } else if (rec == EXTENSION_RECORD_TYPE) { + gint ext_code; + GifByteType *ext; - /* Ok, we're loaded, now to convert from indexed -> RGB - * with alpha if necessary - */ - - if (is_trans) - pixels = art_alloc(h * w * 4); - else - pixels = art_alloc(h * w * 3); - tmpptr = pixels; - - if (!pixels) - return NULL; - - /* The meat of the transformation */ - /* Get the right palette */ - cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap); - - /* Unindex the data, and pack it in RGB(A) order. - * Note for transparent GIFs, the alpha is set to 0 - * for the transparent color, and 0xFF for everything else. - * I think that's right... - */ - - for (i = 0; i < h; i++) { - for (j = 0; j < w; j++) { - tmpptr[0] = cmap->Colors[rows[i][j]].Red; - tmpptr[1] = cmap->Colors[rows[i][j]].Green; - tmpptr[2] = cmap->Colors[rows[i][j]].Blue; - if (is_trans && (rows[i][j] == t_color)) - tmpptr[3] = 0; - else - tmpptr[3] = 0xFF; - tmpptr += (is_trans ? 3 : 4); + DGifGetExtension(gif, &ext_code, &ext); + while (ext) { + if ((ext_code == GRAPHICS_EXT_FUNC_CODE) && + (ext[1] & 1) && (t_color < 0)) { + is_trans = TRUE; + t_color = (gint) ext[4]; } + ext = NULL; + DGifGetExtensionNext(gif, &ext); + } } + } + while (rec != TERMINATE_RECORD_TYPE); - /* Ok, now stuff the GdkPixBuf with goodies */ + /* Ok, we're loaded, now to convert from indexed -> RGB + * with alpha if necessary + */ - pixbuf = g_new(GdkPixBuf, 1); + if (is_trans) + pixels = art_alloc(h * w * 4); + else + pixels = art_alloc(h * w * 3); + tmpptr = pixels; - if (is_trans) - pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4)); - else - pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3)); + if (!pixels) + return NULL; - /* Ok, I'm anal...shoot me */ - if (!(pixbuf->art_pixbuf)) - return NULL; - pixbuf->ref_count = 0; - pixbuf->unref_func = NULL; + /* The meat of the transformation */ + /* Get the right palette */ + cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap); - return pixbuf; + /* Unindex the data, and pack it in RGB(A) order. + * Note for transparent GIFs, the alpha is set to 0 + * for the transparent color, and 0xFF for everything else. + * I think that's right... + */ + + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + tmpptr[0] = cmap->Colors[rows[i][j]].Red; + tmpptr[1] = cmap->Colors[rows[i][j]].Green; + tmpptr[2] = cmap->Colors[rows[i][j]].Blue; + if (is_trans && (rows[i][j] == t_color)) + tmpptr[3] = 0; + else + tmpptr[3] = 0xFF; + tmpptr += (is_trans ? 3 : 4); + } + } + + /* Ok, now stuff the GdkPixBuf with goodies */ + + pixbuf = g_new(GdkPixBuf, 1); + + if (is_trans) + pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4)); + else + pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3)); + + /* Ok, I'm anal...shoot me */ + if (!(pixbuf->art_pixbuf)) + return NULL; + pixbuf->ref_count = 0; + pixbuf->unref_func = NULL; + + return pixbuf; } diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c index 789a086454..443010cdcc 100644 --- a/gdk-pixbuf/io-png.c +++ b/gdk-pixbuf/io-png.c @@ -1,8 +1,21 @@ /* * io-png.c: GdkPixBuf I/O for PNG files. + * Copyright (C) 1999 Mark Crichton + * Author: Mark Crichton * - * Author: - * Mark Crichton + * 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, Cambridge, MA 02139, USA. * */ #include @@ -15,137 +28,133 @@ /* Shared library entry point */ GdkPixBuf *image_load(FILE * f) { - png_structp png_ptr; - png_infop info_ptr, end_info; - gint i, depth, ctype, inttype, passes, bpp; /* bpp = BYTES/pixel */ - png_uint_32 w, h, x, y; - png_bytepp rows; - art_u8 *pixels, *temp, *rowdata; - GdkPixBuf *pixbuf; + png_structp png_ptr; + png_infop info_ptr, end_info; + gint i, depth, ctype, inttype, passes, bpp; /* bpp = BYTES/pixel */ + png_uint_32 w, h, x, y; + png_bytepp rows; + art_u8 *pixels, *temp, *rowdata; + GdkPixBuf *pixbuf; - g_return_val_if_fail (f != NULL, NULL); + g_return_val_if_fail(f != NULL, NULL); - png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, - NULL); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, + NULL); - info_ptr = png_create_info_struct (png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - return NULL; - } - - end_info = png_create_info_struct (png_ptr); - if (!end_info) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - return NULL; - } - - if (setjmp(png_ptr->jmpbuf)) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return NULL; - } - - png_init_io(png_ptr, f); - png_read_info(png_ptr, info_ptr); - - png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype, - NULL, NULL); - - /* Ok, we want to work with 24 bit images. - * However, PNG can vary depth per channel. - * So, we use the png_set_expand function to expand - * everything into a format libart expects. - * We also use png_set_strip_16 to reduce down to 8 bit/chan. - */ - - if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8) - png_set_expand(png_ptr); - - if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8) - png_set_expand(png_ptr); - - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_expand(png_ptr); - - if (depth == 16) - png_set_strip_16(png_ptr); - - /* We also have png "packing" bits into bytes if < 8 */ - if (depth < 8) - png_set_packing(png_ptr); - - /* Lastly, if the PNG is greyscale, convert to RGB */ - if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png_ptr); - - /* ...and if we're interlaced... */ - passes = png_set_interlace_handling(png_ptr); - - /* Update our info structs */ - png_read_update_info(png_ptr, info_ptr); - - /* Allocate some memory and set up row array */ - /* This "inhales vigirously"... */ - if (ctype & PNG_COLOR_MASK_ALPHA) - bpp = 4; - else - bpp = 3; - - pixels = art_alloc(w*h*bpp); - rows = g_malloc(h*sizeof(png_bytep)); - - if ((!pixels) || (!rows)) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return NULL; - } - - /* Icky code, but it has to be done... */ - for (i = 0; i < h; i++) { - if ((rows[i] = g_malloc(w*sizeof(art_u8)*bpp)) == NULL) { - int n; - for (n = 0; n < i; n++) - g_free(rows[i]); - g_free(rows); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - return NULL; - } - } - - /* And we FINALLY get here... */ - png_read_image(png_ptr, rows); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return NULL; + } + end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return NULL; + } + if (setjmp(png_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - - /* Now stuff the bytes into pixels & free rows[y] */ - - temp = pixels; + return NULL; + } + png_init_io(png_ptr, f); + png_read_info(png_ptr, info_ptr); - for (y = 0; y < h; y++) { - (png_bytep)rowdata = rows[y]; - for (x = 0; x < w; x++) { - temp[0] = rowdata[(x*bpp)]; - temp[1] = rowdata[(x*bpp)+1]; - temp[2] = rowdata[(x*bpp)+2]; - if (bpp == 4) - temp[3] = rowdata[(x*bpp)+3]; - temp += bpp; - } - g_free(rows[y]); + png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype, + NULL, NULL); + + /* Ok, we want to work with 24 bit images. + * However, PNG can vary depth per channel. + * So, we use the png_set_expand function to expand + * everything into a format libart expects. + * We also use png_set_strip_16 to reduce down to 8 bit/chan. + */ + + if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8) + png_set_expand(png_ptr); + + if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8) + png_set_expand(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + + if (depth == 16) + png_set_strip_16(png_ptr); + + /* We also have png "packing" bits into bytes if < 8 */ + if (depth < 8) + png_set_packing(png_ptr); + + /* Lastly, if the PNG is greyscale, convert to RGB */ + if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + /* ...and if we're interlaced... */ + passes = png_set_interlace_handling(png_ptr); + + /* Update our info structs */ + png_read_update_info(png_ptr, info_ptr); + + /* Allocate some memory and set up row array */ + /* This "inhales vigirously"... */ + if (ctype & PNG_COLOR_MASK_ALPHA) + bpp = 4; + else + bpp = 3; + + pixels = art_alloc(w * h * bpp); + rows = g_malloc(h * sizeof(png_bytep)); + + if ((!pixels) || (!rows)) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return NULL; + } + /* Icky code, but it has to be done... */ + for (i = 0; i < h; i++) { + if ((rows[i] = g_malloc(w * sizeof(art_u8) * bpp)) == NULL) { + int n; + for (n = 0; n < i; n++) + g_free(rows[i]); + g_free(rows); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return NULL; } - g_free(rows); + } - /* Return the GdkPixBuf */ - pixbuf = g_new(GdkPixBuf, 1); + /* And we FINALLY get here... */ + png_read_image(png_ptr, rows); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - if (ctype & PNG_COLOR_MASK_ALPHA) - pixbuf->art_pixbuf = art_pixbuf_new_rgba (pixels, w, h, (w*4)); - else - pixbuf->art_pixbuf = art_pixbuf_new_rgb (pixels, w, h, (w*3)); + /* Now stuff the bytes into pixels & free rows[y] */ - /* Ok, I'm anal...shoot me */ - if (!(pixbuf->art_pixbuf)) - return NULL; - pixbuf->ref_count = 0; - pixbuf->unref_func = NULL; + temp = pixels; - return pixbuf; + for (y = 0; y < h; y++) { + (png_bytep) rowdata = rows[y]; + for (x = 0; x < w; x++) { + temp[0] = rowdata[(x * bpp)]; + temp[1] = rowdata[(x * bpp) + 1]; + temp[2] = rowdata[(x * bpp) + 2]; + if (bpp == 4) + temp[3] = rowdata[(x * bpp) + 3]; + temp += bpp; + } + g_free(rows[y]); + } + g_free(rows); + + /* Return the GdkPixBuf */ + pixbuf = g_new(GdkPixBuf, 1); + + if (ctype & PNG_COLOR_MASK_ALPHA) + pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4)); + else + pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3)); + + /* Ok, I'm anal...shoot me */ + if (!(pixbuf->art_pixbuf)) + return NULL; + pixbuf->ref_count = 0; + pixbuf->unref_func = NULL; + + return pixbuf; } diff --git a/gdk-pixbuf/io-xpm.c b/gdk-pixbuf/io-xpm.c new file mode 100644 index 0000000000..4a9f045ff4 --- /dev/null +++ b/gdk-pixbuf/io-xpm.c @@ -0,0 +1,436 @@ +/* + * io-xpm.c: GdkPixBuf I/O for XPM files. + * Copyright (C) 1999 Mark Crichton + * Author: Mark Crichton + * + * 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, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include + +/* We need gdk.h since we might need to parse X color names */ +#include + +#include "gdk-pixbuf.h" +/*#include "gdk-pixbuf-io.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; + GdkColor color; + gint transparent; +} _XPMColor; + +struct file_handle { + FILE *infile; + gchar *buffer; + guint buffer_size; +}; + +struct mem_handle { + gchar **data; + int offset; +}; + +static gint + xpm_seek_string(FILE * infile, + const gchar * str, + gint skip_comments) +{ + char instr[1024]; + + while (!feof(infile)) { + fscanf(infile, "%1023s", instr); + if (skip_comments == TRUE && strcmp(instr, "/*") == 0) { + fscanf(infile, "%1023s", instr); + while (!feof(infile) && strcmp(instr, "*/") != 0) + fscanf(infile, "%1023s", instr); + fscanf(infile, "%1023s", instr); + } + 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 = (gchar *) 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_skip_whitespaces(gchar * buffer) +{ + gint32 index = 0; + + while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09)) + index++; + + return &buffer[index]; +} + +static gchar * + xpm_skip_string(gchar * buffer) +{ + gint32 index = 0; + + while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09) + index++; + + return &buffer[index]; +} + +/* Xlib crashed once at a color name lengths around 125 */ +#define MAX_COLOR_LEN 120 + +static gchar * + xpm_extract_color(gchar * buffer) +{ + gint counter, numnames; + gchar *ptr = NULL, ch, temp[128]; + gchar color[MAX_COLOR_LEN], *retcol; + gint space; + counter = 0; + while (ptr == NULL) { + if (buffer[counter] == 'c') { + ch = buffer[counter + 1]; + if (ch == 0x20 || ch == 0x09) + ptr = &buffer[counter + 1]; + } else if (buffer[counter] == 0) + return NULL; + + counter++; + } + ptr = xpm_skip_whitespaces(ptr); + + if (ptr[0] == 0) + return NULL; + else if (ptr[0] == '#') { + counter = 1; + while (ptr[counter] != 0 && + ((ptr[counter] >= '0' && ptr[counter] <= '9') || + (ptr[counter] >= 'a' && ptr[counter] <= 'f') || + (ptr[counter] >= 'A' && ptr[counter] <= 'F'))) + counter++; + retcol = g_new(gchar, counter + 1); + strncpy(retcol, ptr, counter); + + retcol[counter] = 0; + + return retcol; + } + color[0] = 0; + numnames = 0; + + space = MAX_COLOR_LEN - 1; + while (space > 0) { + sscanf(ptr, "%127s", temp); + + if (((gint) ptr[0] == 0) || + (strcmp("s", temp) == 0) || (strcmp("m", temp) == 0) || + (strcmp("g", temp) == 0) || (strcmp("g4", temp) == 0)) { + break; + } else { + if (numnames > 0) { + space -= 1; + strcat(color, " "); + } + strncat(color, temp, space); + space -= MIN(space, strlen(temp)); + ptr = xpm_skip_string(ptr); + ptr = xpm_skip_whitespaces(ptr); + numnames++; + } + } + + retcol = g_strdup(color); + return retcol; +} + + +/* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */ + +static 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", FALSE) != 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: + xpm_read_string(h->infile, &h->buffer, &h->buffer_size); + return h->buffer; + } + return NULL; +} + +/* This reads from memory */ +static 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]) + return h->data[h->offset++]; + } + return NULL; +} + +/* This function does all the work. */ + +static GdkPixBuf * + _pixbuf_create_from_xpm(gchar * (*get_buf) (enum buf_op op, gpointer handle), + gpointer handle) +{ + gint w, h, n_col, cpp; + gint cnt, xcnt, ycnt, wbytes, n, ns; + gint is_trans = FALSE; + gchar *buffer, *name_buf; + gchar pixel_str[32]; + GHashTable *color_hash; + _XPMColor *colors, *color, *fallbackcolor; + art_u8 *pixels, *pixtmp; + GdkPixBuf *pixbuf; + + buffer = (*get_buf) (op_header, handle); + if (!buffer) { + g_warning("No XPM header found"); + return NULL; + } + sscanf(buffer, "%d %d %d %d", &w, &h, &n_col, &cpp); + if (cpp >= 32) { + g_warning("XPM has more than 31 chars per pixel."); + 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_new(gchar, n_col * (cpp + 1)); + colors = g_new(_XPMColor, n_col); + + for (cnt = 0; cnt < n_col; cnt++) { + gchar *color_name; + + buffer = (*get_buf) (op_cmap, handle); + if (!buffer) { + g_warning("Can't load XPM colormap"); + 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) || (g_strcasecmp(color_name, "None") == 0) + || (gdk_color_parse(color_name, &color->color) == FALSE)) { + color->transparent = TRUE; + is_trans = TRUE; + } + g_free(color_name); + g_hash_table_insert(color_hash, color->color_string, color); + + if (cnt == 0) + fallbackcolor = color; + } + + if (is_trans) + pixels = art_alloc(w * h * 4); + else + pixels = art_alloc(w * h * 3); + + if (!pixels) { + g_warning("XPM: Cannot alloc ArtBuf"); + g_hash_table_destroy(color_hash); + g_free(colors); + g_free(name_buf); + return NULL; + } + wbytes = w * cpp; + pixtmp = pixels; + + for (ycnt = 0; ycnt < h; ycnt++) { + buffer = (*get_buf) (op_body, handle); + if ((!buffer) || (strlen(buffer) < wbytes)) + continue; + for (n = 0, cnt = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { + strncpy(pixel_str, &buffer[n], cpp); + pixel_str[cpp] = 0; + ns = 0; + + color = g_hash_table_lookup(color_hash, pixel_str); + + /* Bad XPM...punt */ + if (!color) + color = fallbackcolor; + + pixtmp[0] = color->color.red; + pixtmp[1] = color->color.green; + pixtmp[2] = color->color.blue; + if ((is_trans) && (color->transparent)) { + pixtmp[3] = 0; + pixtmp++; + } else if (is_trans) { + pixtmp[3] = 0xFF; + pixtmp++; + } + pixtmp += 3; + } + } + /* Ok, now stuff the GdkPixBuf with goodies */ + + pixbuf = g_new(GdkPixBuf, 1); + + if (is_trans) + pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4)); + else + pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3)); + + /* Ok, I'm anal...shoot me */ + if (!(pixbuf->art_pixbuf)) + return NULL; + pixbuf->ref_count = 0; + pixbuf->unref_func = NULL; + + g_hash_table_destroy(color_hash); + g_free(colors); + g_free(name_buf); + + return pixbuf; +} + +/* Shared library entry point for file loading */ +GdkPixBuf *image_load(FILE * f) +{ + GdkPixBuf *pixbuf; + struct file_handler h; + + g_return_val_if_fail(f != NULL, NULL); + + h = g_new(file_handler, 1); + h.infile = f; + pixbuf = _pixbuf_create_from_xpm(file_buffer, &h); + g_free(h); + + return pixbuf; +}