summaryrefslogtreecommitdiff
path: root/gdk-pixbuf/io-xpm.c
diff options
context:
space:
mode:
authorMark Crichton <crichton@src.gnome.org>1999-06-30 15:28:43 +0000
committerMark Crichton <crichton@src.gnome.org>1999-06-30 15:28:43 +0000
commitfeb9789e643e419247bf3a6d0e5876e188ec5a11 (patch)
tree143c18978ac90c6a05e42b8e23aceeef3d96a946 /gdk-pixbuf/io-xpm.c
parenta0b1a437cc85c638100517aa6037786d3c0e65e7 (diff)
downloadgdk-pixbuf-feb9789e643e419247bf3a6d0e5876e188ec5a11.tar.gz
io-gif.c, io-png.c: Actually put the licencing terms in the code now...
io-gif.c, io-png.c: Actually put the licencing terms in the code now... io-xpm.c: XPM parser baed off of gdk's, but this does something with the transparent colors.
Diffstat (limited to 'gdk-pixbuf/io-xpm.c')
-rw-r--r--gdk-pixbuf/io-xpm.c436
1 files changed, 436 insertions, 0 deletions
diff --git a/gdk-pixbuf/io-xpm.c b/gdk-pixbuf/io-xpm.c
new file mode 100644
index 000000000..4a9f045ff
--- /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 <crichton@gimp.org>
+ *
+ * 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 <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+/* We need gdk.h since we might need to parse X color names */
+#include <gdk/gdk.h>
+
+#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;
+}