]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-xbm.c
Clarify misleading explanation of rowstride. (#119000)
[~andy/gtk] / gdk-pixbuf / io-xbm.c
1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - XBM image loader
3  *
4  * Copyright (C) 1999 Mark Crichton
5  * Copyright (C) 1999 The Free Software Foundation
6  * Copyright (C) 2001 Eazel, Inc.
7  *
8  * Authors: Mark Crichton <crichton@gimp.org>
9  *          Federico Mena-Quintero <federico@gimp.org>
10  *          Jonathan Blandford <jrb@redhat.com>
11  *          John Harper <jsh@eazel.com>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public
24  * License along with this library; if not, write to the
25  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26  * Boston, MA 02111-1307, USA.
27  */
28
29 /* Following code adapted from io-tiff.c, which was ``(almost) blatantly
30    ripped from Imlib'' */
31
32 #include <config.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include "gdk-pixbuf-private.h"
39 #include "gdk-pixbuf-io.h"
40
41 \f
42
43 typedef struct _XBMData XBMData;
44 struct _XBMData
45 {
46         GdkPixbufModulePreparedFunc prepare_func;
47         GdkPixbufModuleUpdatedFunc update_func;
48         gpointer user_data;
49
50         gchar *tempname;
51         FILE *file;
52         gboolean all_okay;
53 };
54
55 \f
56 /* xbm parser borrowed from xc/lib/X11/RdBitF.c */
57
58 #define MAX_SIZE 255
59
60 /* shared data for the image read/parse logic */
61 static short hex_table[256];            /* conversion value */
62 static gboolean initialized = FALSE;    /* easier to fill in at run time */
63
64
65 /* Table index for the hex values. Initialized once, first time.
66  * Used for translation value or delimiter significance lookup.
67  */
68 static void
69 init_hex_table (void)
70 {
71         /*
72          * We build the table at run time for several reasons:
73          *
74          * 1. portable to non-ASCII machines.
75          * 2. still reentrant since we set the init flag after setting table.
76          * 3. easier to extend.
77          * 4. less prone to bugs.
78          */
79         hex_table['0'] = 0;
80         hex_table['1'] = 1;
81         hex_table['2'] = 2;
82         hex_table['3'] = 3;
83         hex_table['4'] = 4;
84         hex_table['5'] = 5;
85         hex_table['6'] = 6;
86         hex_table['7'] = 7;
87         hex_table['8'] = 8;
88         hex_table['9'] = 9;
89         hex_table['A'] = 10;
90         hex_table['B'] = 11;
91         hex_table['C'] = 12;
92         hex_table['D'] = 13;
93         hex_table['E'] = 14;
94         hex_table['F'] = 15;
95         hex_table['a'] = 10;
96         hex_table['b'] = 11;
97         hex_table['c'] = 12;
98         hex_table['d'] = 13;
99         hex_table['e'] = 14;
100         hex_table['f'] = 15;
101
102         /* delimiters of significance are flagged w/ negative value */
103         hex_table[' '] = -1;
104         hex_table[','] = -1;
105         hex_table['}'] = -1;
106         hex_table['\n'] = -1;
107         hex_table['\t'] = -1;
108
109         initialized = TRUE;
110 }
111
112 /* Read next hex value in the input stream, return -1 if EOF */
113 static int
114 next_int (FILE *fstream)
115 {
116         int ch;
117         int value = 0;
118         int gotone = 0;
119         int done = 0;
120     
121         /* loop, accumulate hex value until find delimiter 
122            skip any initial delimiters found in read stream */
123
124         while (!done) {
125                 ch = getc (fstream);
126                 if (ch == EOF) {
127                         value = -1;
128                         done++;
129                 } else {
130                         /* trim high bits, check type and accumulate */
131                         ch &= 0xff;
132                         if (g_ascii_isxdigit (ch)) {
133                                 value = (value << 4) + g_ascii_xdigit_value (ch);
134                                 gotone++;
135                         } else if ((hex_table[ch]) < 0 && gotone) {
136                                 done++;
137                         }
138                 }
139         }
140         return value;
141 }
142
143 static gboolean
144 read_bitmap_file_data (FILE *fstream,
145                        guint *width, guint *height,
146                        guchar **data,
147                        int *x_hot, int *y_hot)
148 {
149         guchar *bits = NULL;            /* working variable */
150         char line[MAX_SIZE];            /* input line from file */
151         int size;                       /* number of bytes of data */
152         char name_and_type[MAX_SIZE];   /* an input line */
153         char *type;                     /* for parsing */
154         int value;                      /* from an input line */
155         int version10p;                 /* boolean, old format */
156         int padding;                    /* to handle alignment */
157         int bytes_per_line;             /* per scanline of data */
158         guint ww = 0;                   /* width */
159         guint hh = 0;                   /* height */
160         int hx = -1;                    /* x hotspot */
161         int hy = -1;                    /* y hotspot */
162
163         /* first time initialization */
164         if (!initialized) {
165                 init_hex_table ();
166         }
167
168         /* error cleanup and return macro */
169 #define RETURN(code) { g_free (bits); return code; }
170
171         while (fgets (line, MAX_SIZE, fstream)) {
172                 if (strlen (line) == MAX_SIZE-1)
173                         RETURN (FALSE);
174                 if (sscanf (line,"#define %s %d",name_and_type,&value) == 2) {
175                         if (!(type = strrchr (name_and_type, '_')))
176                                 type = name_and_type;
177                         else {
178                                 type++;
179                         }
180
181                         if (!strcmp ("width", type))
182                                 ww = (unsigned int) value;
183                         if (!strcmp ("height", type))
184                                 hh = (unsigned int) value;
185                         if (!strcmp ("hot", type)) {
186                                 if (type-- == name_and_type
187                                     || type-- == name_and_type)
188                                         continue;
189                                 if (!strcmp ("x_hot", type))
190                                         hx = value;
191                                 if (!strcmp ("y_hot", type))
192                                         hy = value;
193                         }
194                         continue;
195                 }
196     
197                 if (sscanf (line, "static short %s = {", name_and_type) == 1)
198                         version10p = 1;
199                 else if (sscanf (line,"static unsigned char %s = {",name_and_type) == 1)
200                         version10p = 0;
201                 else if (sscanf (line, "static char %s = {", name_and_type) == 1)
202                         version10p = 0;
203                 else
204                         continue;
205
206                 if (!(type = strrchr (name_and_type, '_')))
207                         type = name_and_type;
208                 else
209                         type++;
210
211                 if (strcmp ("bits[]", type))
212                         continue;
213     
214                 if (!ww || !hh)
215                         RETURN (FALSE);
216
217                 if ((ww % 16) && ((ww % 16) < 9) && version10p)
218                         padding = 1;
219                 else
220                         padding = 0;
221
222                 bytes_per_line = (ww+7)/8 + padding;
223
224                 size = bytes_per_line * hh;
225                 bits = g_malloc (size);
226
227                 if (version10p) {
228                         unsigned char *ptr;
229                         int bytes;
230
231                         for (bytes = 0, ptr = bits; bytes < size; (bytes += 2)) {
232                                 if ((value = next_int (fstream)) < 0)
233                                         RETURN (FALSE);
234                                 *(ptr++) = value;
235                                 if (!padding || ((bytes+2) % bytes_per_line))
236                                         *(ptr++) = value >> 8;
237                         }
238                 } else {
239                         unsigned char *ptr;
240                         int bytes;
241
242                         for (bytes = 0, ptr = bits; bytes < size; bytes++, ptr++) {
243                                 if ((value = next_int (fstream)) < 0) 
244                                         RETURN (FALSE);
245                                 *ptr=value;
246                         }
247                 }
248                 break;
249         }
250
251         if (!bits)
252                 RETURN (FALSE);
253
254         *data = bits;
255         *width = ww;
256         *height = hh;
257         if (x_hot)
258                 *x_hot = hx;
259         if (y_hot)
260                 *y_hot = hy;
261
262         return TRUE;
263 }
264
265 \f
266
267 static GdkPixbuf *
268 gdk_pixbuf__xbm_image_load_real (FILE *f, XBMData *context, GError **error)
269 {
270         guint w, h;
271         int x_hot, y_hot;
272         guchar *data, *ptr;
273         guchar *pixels;
274         guint row_stride;
275         int x, y;
276         int reg = 0; /* Quiet compiler */
277         int bits;
278
279         GdkPixbuf *pixbuf;
280
281         if (!read_bitmap_file_data (f, &w, &h, &data, &x_hot, &y_hot)) {
282                 g_set_error (error,
283                              GDK_PIXBUF_ERROR,
284                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
285                              _("Invalid XBM file"));
286                 return NULL;
287         }
288
289         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h);
290
291         if (pixbuf == NULL) {
292                 g_set_error (error,
293                              GDK_PIXBUF_ERROR,
294                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
295                              _("Insufficient memory to load XBM image file"));
296                 return NULL;
297         }
298         
299         if (x_hot != -1 && y_hot != -1) {
300                 gchar hot[10];
301                 g_snprintf (hot, 10, "%d", x_hot);
302                 gdk_pixbuf_set_option (pixbuf, "x_hot", hot);
303                 g_snprintf (hot, 10, "%d", y_hot);
304                 gdk_pixbuf_set_option (pixbuf, "y_hot", hot);
305         }
306
307         pixels = gdk_pixbuf_get_pixels (pixbuf);
308         row_stride = gdk_pixbuf_get_rowstride (pixbuf);
309
310         if (context)
311                 (* context->prepare_func) (pixbuf, NULL, context->user_data);
312
313
314         /* Initialize PIXBUF */
315
316         ptr = data;
317         for (y = 0; y < h; y++) {
318                 bits = 0;
319                 for (x = 0; x < w; x++) {
320                         guchar channel;
321                         if (bits == 0) {
322                                 reg = *ptr++;
323                                 bits = 8;
324                         }
325
326                         channel = (reg & 1) ? 0 : 255;
327                         reg >>= 1;
328                         bits--;
329
330                         pixels[x*3+0] = channel;
331                         pixels[x*3+1] = channel;
332                         pixels[x*3+2] = channel;
333                 }
334                 pixels += row_stride;
335         }
336         g_free (data);
337
338         if (context) {
339                 (* context->update_func) (pixbuf, 0, 0, w, h, context->user_data);
340                 g_object_unref (pixbuf);
341                 pixbuf = NULL;
342         }
343
344         return pixbuf;
345 }
346
347 \f
348 /* Static loader */
349
350 static GdkPixbuf *
351 gdk_pixbuf__xbm_image_load (FILE *f, GError **error)
352 {
353         return gdk_pixbuf__xbm_image_load_real (f, NULL, error);
354 }
355
356 \f
357 /* Progressive loader */
358
359 /*
360  * Proper XBM progressive loading isn't implemented.  Instead we write
361  * it to a file, then load the file when it's done.  It's not pretty.
362  */
363
364 static gpointer
365 gdk_pixbuf__xbm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
366                                   GdkPixbufModulePreparedFunc prepare_func,
367                                   GdkPixbufModuleUpdatedFunc update_func,
368                                   gpointer user_data,
369                                   GError **error)
370 {
371         XBMData *context;
372         gint fd;
373
374         context = g_new (XBMData, 1);
375         context->prepare_func = prepare_func;
376         context->update_func = update_func;
377         context->user_data = user_data;
378         context->all_okay = TRUE;
379         fd = g_file_open_tmp ("gdkpixbuf-xbm-tmp.XXXXXX",
380                               &context->tempname,
381                               NULL);
382         if (fd < 0) {
383                 g_free (context);
384                 return NULL;
385         }
386
387         context->file = fdopen (fd, "w+");
388         if (context->file == NULL) {
389                 g_free (context->tempname);
390                 g_free (context);
391                 return NULL;
392         }
393
394         return context;
395 }
396
397 static gboolean
398 gdk_pixbuf__xbm_image_stop_load (gpointer data,
399                                  GError **error)
400 {
401         XBMData *context = (XBMData*) data;
402         gboolean retval = TRUE;
403
404         g_return_val_if_fail (data != NULL, TRUE);
405
406         fflush (context->file);
407         rewind (context->file);
408         if (context->all_okay) {
409                 GdkPixbuf *pixbuf;
410                 pixbuf = gdk_pixbuf__xbm_image_load_real (context->file, context,
411                                                           error);
412                 if (pixbuf == NULL)
413                         retval = FALSE;
414         }
415
416         fclose (context->file);
417         unlink (context->tempname);
418         g_free (context->tempname);
419         g_free ((XBMData *) context);
420
421         return retval;
422 }
423
424 static gboolean
425 gdk_pixbuf__xbm_image_load_increment (gpointer data,
426                                       const guchar  *buf,
427                                       guint    size,
428                                       GError **error)
429 {
430         XBMData *context = (XBMData *) data;
431
432         g_return_val_if_fail (data != NULL, FALSE);
433
434         if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
435                 context->all_okay = FALSE;
436                 g_set_error (error,
437                              G_FILE_ERROR,
438                              g_file_error_from_errno (errno),
439                              _("Failed to write to temporary file when loading XBM image"));
440                 return FALSE;
441         }
442
443         return TRUE;
444 }
445
446 void
447 MODULE_ENTRY (xbm, fill_vtable) (GdkPixbufModule *module)
448 {
449         module->load = gdk_pixbuf__xbm_image_load;
450         module->begin_load = gdk_pixbuf__xbm_image_begin_load;
451         module->stop_load = gdk_pixbuf__xbm_image_stop_load;
452         module->load_increment = gdk_pixbuf__xbm_image_load_increment;
453 }
454
455 void
456 MODULE_ENTRY (xbm, fill_info) (GdkPixbufFormat *info)
457 {
458         static GdkPixbufModulePattern signature[] = {
459                 { "#define ", NULL, 100 },
460                 { "/*", NULL, 50 },
461                 { NULL, NULL, 0 }
462         };
463         static gchar * mime_types[] = {
464                 "image/x-xbitmap",
465                 NULL
466         };
467         static gchar * extensions[] = {
468                 "xbm",
469                 NULL
470         };
471
472         info->name = "xbm";
473         info->signature = signature;
474         info->description = N_("The XBM image format");
475         info->mime_types = mime_types;
476         info->extensions = extensions;
477         info->flags = 0;
478 }