1 /* GdkPixbuf library - JPEG image loader
3 * Copyright (C) 1999 Mark Crichton
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Mark Crichton <crichton@gimp.org>
7 * Federico Mena-Quintero <federico@gimp.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
30 #include "gdk-pixbuf.h"
34 /* I have must have done something to deserve this.
35 * XPM is such a crappy format to handle.
36 * This code is an ugly hybred from gdkpixmap.c
37 * modified to respect transparent colors.
38 * It's still a mess, though.
65 xpm_seek_string (FILE *infile, const gchar *str, gint skip_comments)
69 while (!feof (infile)) {
70 fscanf (infile, "%1023s", instr);
71 if (skip_comments == TRUE && strcmp (instr, "/*") == 0) {
72 fscanf (infile, "%1023s", instr);
73 while (!feof (infile) && strcmp (instr, "*/") != 0)
74 fscanf (infile, "%1023s", instr);
75 fscanf (infile, "%1023s", instr);
78 if (strcmp (instr, str) == 0)
86 xpm_seek_char (FILE *infile, gchar c)
90 while ((b = getc (infile)) != EOF) {
91 if (c != b && b == '/') {
96 else if (b == '*') { /* we have a comment */
103 } while (!(oldb == '*' && b == '/'));
113 xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
116 guint cnt = 0, bufsiz, ret = FALSE;
120 bufsiz = *buffer_size;
122 bufsiz = 10 * sizeof (gchar);
123 buf = g_new (gchar, bufsiz);
128 } while (c != EOF && c != '"');
133 while ((c = getc (infile)) != EOF) {
135 guint new_size = bufsiz * 2;
137 if (new_size > bufsiz)
142 buf = g_realloc (buf, bufsiz);
143 buf[bufsiz - 1] = '\0';
156 buf[bufsiz - 1] = '\0'; /* ensure null termination for errors */
158 *buffer_size = bufsiz;
163 xpm_skip_whitespaces (const gchar *buffer)
167 while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
170 return &buffer[index];
174 xpm_skip_string (const gchar *buffer)
178 while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
181 return &buffer[index];
184 /* Xlib crashed once at a color name lengths around 125 */
185 #define MAX_COLOR_LEN 120
188 xpm_extract_color (const gchar *buffer)
190 gint counter, numnames;
191 const gchar *ptr = NULL;
193 gchar color[MAX_COLOR_LEN], *retcol;
197 while (ptr == NULL) {
198 if (buffer[counter] == 'c') {
199 ch = buffer[counter + 1];
200 if (ch == 0x20 || ch == 0x09)
201 ptr = &buffer[counter + 1];
202 } else if (buffer[counter] == 0)
207 ptr = xpm_skip_whitespaces (ptr);
211 else if (ptr[0] == '#') {
213 while (ptr[counter] != 0 &&
214 ((ptr[counter] >= '0' && ptr[counter] <= '9') ||
215 (ptr[counter] >= 'a' && ptr[counter] <= 'f') ||
216 (ptr[counter] >= 'A' && ptr[counter] <= 'F')))
218 retcol = g_new (gchar, counter + 1);
219 strncpy (retcol, ptr, counter);
228 space = MAX_COLOR_LEN - 1;
230 sscanf (ptr, "%127s", temp);
232 if (((gint) ptr[0] == 0) ||
233 (strcmp ("s", temp) == 0) || (strcmp ("m", temp) == 0) ||
234 (strcmp ("g", temp) == 0) || (strcmp ("g4", temp) == 0))
242 strncat (color, temp, space);
243 space -= MIN (space, strlen (temp));
244 ptr = xpm_skip_string (ptr);
245 ptr = xpm_skip_whitespaces (ptr);
250 retcol = g_strdup (color);
254 /* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */
257 file_buffer (enum buf_op op, gpointer handle)
259 struct file_handle *h = handle;
263 if (xpm_seek_string (h->infile, "XPM", FALSE) != TRUE)
266 if (xpm_seek_char (h->infile, '{') != TRUE)
268 /* Fall through to the next xpm_seek_char. */
271 xpm_seek_char (h->infile, '"');
272 fseek (h->infile, -1, SEEK_CUR);
273 /* Fall through to the xpm_read_string. */
276 xpm_read_string (h->infile, &h->buffer, &h->buffer_size);
280 g_assert_not_reached ();
286 /* This reads from memory */
288 mem_buffer (enum buf_op op, gpointer handle)
290 struct mem_handle *h = handle;
295 if (h->data[h->offset]) {
298 retval = h->data[h->offset];
305 g_assert_not_reached ();
312 /* Destroy notification function for the libart pixbuf */
314 free_buffer (gpointer user_data, gpointer data)
319 /* This function does all the work. */
321 pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle)
323 gint w, h, n_col, cpp;
324 gint cnt, xcnt, ycnt, wbytes, n, ns;
325 gint is_trans = FALSE;
329 GHashTable *color_hash;
330 _XPMColor *colors, *color, *fallbackcolor;
331 guchar *pixels, *pixtmp;
333 fallbackcolor = NULL;
335 buffer = (*get_buf) (op_header, handle);
337 g_warning ("No XPM header found");
340 sscanf (buffer, "%d %d %d %d", &w, &h, &n_col, &cpp);
342 g_warning ("XPM has more than 31 chars per pixel.");
346 /* The hash is used for fast lookups of color from chars */
347 color_hash = g_hash_table_new (g_str_hash, g_str_equal);
349 name_buf = g_new (gchar, n_col * (cpp + 1));
350 colors = g_new (_XPMColor, n_col);
352 for (cnt = 0; cnt < n_col; cnt++) {
355 buffer = (*get_buf) (op_cmap, handle);
357 g_warning ("Can't load XPM colormap");
358 g_hash_table_destroy (color_hash);
364 color = &colors[cnt];
365 color->color_string = &name_buf[cnt * (cpp + 1)];
366 strncpy (color->color_string, buffer, cpp);
367 color->color_string[cpp] = 0;
368 buffer += strlen (color->color_string);
369 color->transparent = FALSE;
371 color_name = xpm_extract_color (buffer);
373 if ((color_name == NULL) || (g_strcasecmp (color_name, "None") == 0)
374 || (gdk_color_parse (color_name, &color->color) == FALSE)) {
375 color->transparent = TRUE;
380 g_hash_table_insert (color_hash, color->color_string, color);
383 fallbackcolor = color;
387 pixels = malloc (w * h * 4);
389 pixels = malloc (w * h * 3);
392 g_hash_table_destroy (color_hash);
401 for (ycnt = 0; ycnt < h; ycnt++) {
402 buffer = (*get_buf) (op_body, handle);
403 if ((!buffer) || (strlen (buffer) < wbytes))
406 for (n = 0, cnt = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
407 strncpy (pixel_str, &buffer[n], cpp);
411 color = g_hash_table_lookup (color_hash, pixel_str);
415 color = fallbackcolor;
417 *pixtmp++ = color->color.red >> 8;
418 *pixtmp++ = color->color.green >> 8;
419 *pixtmp++ = color->color.blue >> 8;
421 if (is_trans && color->transparent)
428 g_hash_table_destroy (color_hash);
432 return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, is_trans,
433 w, h, is_trans ? (w * 4) : (w * 3),
437 /* Shared library entry point for file loading */
442 struct file_handle h;
444 memset (&h, 0, sizeof (h));
446 pixbuf = pixbuf_create_from_xpm (file_buffer, &h);
452 /* Shared library entry point for memory loading */
454 image_load_xpm_data (const gchar **data)
462 pixbuf = pixbuf_create_from_xpm (mem_buffer, &h);