/src/gdk-pixbuf/gdk-pixbuf/io-xpm.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
2 | | /* GdkPixbuf library - XPM image loader |
3 | | * |
4 | | * Copyright (C) 1999 Mark Crichton |
5 | | * Copyright (C) 1999 The Free Software Foundation |
6 | | * |
7 | | * Authors: Mark Crichton <crichton@gimp.org> |
8 | | * Federico Mena-Quintero <federico@gimp.org> |
9 | | * |
10 | | * This library is free software; you can redistribute it and/or |
11 | | * modify it under the terms of the GNU Lesser General Public |
12 | | * License as published by the Free Software Foundation; either |
13 | | * version 2 of the License, or (at your option) any later version. |
14 | | * |
15 | | * This library is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | | * Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | #include <stdio.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | #include <glib.h> |
29 | | #ifdef HAVE_UNISTD_H |
30 | | #include <unistd.h> /* for unlink */ |
31 | | #endif |
32 | | #include <errno.h> |
33 | | #include <glib/gstdio.h> |
34 | | #include <glib/gi18n-lib.h> |
35 | | #include "gdk-pixbuf-core.h" |
36 | | #include "gdk-pixbuf-io.h" |
37 | | |
38 | | |
39 | | |
40 | | |
41 | | /* I have must have done something to deserve this. |
42 | | * XPM is such a crappy format to handle. |
43 | | * This code is an ugly hybrid from gdkpixmap.c |
44 | | * modified to respect transparent colors. |
45 | | * It's still a mess, though. |
46 | | */ |
47 | | |
48 | | enum buf_op { |
49 | | op_header, |
50 | | op_cmap, |
51 | | op_body |
52 | | }; |
53 | | |
54 | | typedef struct { |
55 | | gchar *color_string; |
56 | | guint16 red; |
57 | | guint16 green; |
58 | | guint16 blue; |
59 | | gint transparent; |
60 | | } XPMColor; |
61 | | |
62 | | struct file_handle { |
63 | | FILE *infile; |
64 | | gchar *buffer; |
65 | | guint buffer_size; |
66 | | }; |
67 | | |
68 | | struct mem_handle { |
69 | | const gchar **data; |
70 | | int offset; |
71 | | }; |
72 | | |
73 | | /* The following 2 routines (parse_color, find_color) come from Tk, via the Win32 |
74 | | * port of GDK. The licensing terms on these (longer than the functions) is: |
75 | | * |
76 | | * This software is copyrighted by the Regents of the University of |
77 | | * California, Sun Microsystems, Inc., and other parties. The following |
78 | | * terms apply to all files associated with the software unless explicitly |
79 | | * disclaimed in individual files. |
80 | | * |
81 | | * The authors hereby grant permission to use, copy, modify, distribute, |
82 | | * and license this software and its documentation for any purpose, provided |
83 | | * that existing copyright notices are retained in all copies and that this |
84 | | * notice is included verbatim in any distributions. No written agreement, |
85 | | * license, or royalty fee is required for any of the authorized uses. |
86 | | * Modifications to this software may be copyrighted by their authors |
87 | | * and need not follow the licensing terms described here, provided that |
88 | | * the new terms are clearly indicated on the first page of each file where |
89 | | * they apply. |
90 | | * |
91 | | * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY |
92 | | * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
93 | | * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY |
94 | | * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE |
95 | | * POSSIBILITY OF SUCH DAMAGE. |
96 | | * |
97 | | * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, |
98 | | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, |
99 | | * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE |
100 | | * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE |
101 | | * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR |
102 | | * MODIFICATIONS. |
103 | | * |
104 | | * GOVERNMENT USE: If you are acquiring this software on behalf of the |
105 | | * U.S. government, the Government shall have only "Restricted Rights" |
106 | | * in the software and related documentation as defined in the Federal |
107 | | * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you |
108 | | * are acquiring the software on behalf of the Department of Defense, the |
109 | | * software shall be classified as "Commercial Computer Software" and the |
110 | | * Government shall have only "Restricted Rights" as defined in Clause |
111 | | * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the |
112 | | * authors grant the U.S. Government and others acting in its behalf |
113 | | * permission to use and distribute the software in accordance with the |
114 | | * terms specified in this license. |
115 | | */ |
116 | | |
117 | | #include "xpm-color-table.h" |
118 | | |
119 | | /* |
120 | | *---------------------------------------------------------------------- |
121 | | * |
122 | | * find_color -- |
123 | | * |
124 | | * This routine finds the color entry that corresponds to the |
125 | | * specified color. |
126 | | * |
127 | | * Results: |
128 | | * Returns non-zero on success. The RGB values of the XColor |
129 | | * will be initialized to the proper values on success. |
130 | | * |
131 | | * Side effects: |
132 | | * None. |
133 | | * |
134 | | *---------------------------------------------------------------------- |
135 | | */ |
136 | | |
137 | | static int |
138 | | compare_xcolor_entries (const void *a, const void *b) |
139 | 15.6k | { |
140 | 15.6k | return g_ascii_strcasecmp ((const char *) a, |
141 | 15.6k | color_names + ((const XPMColorEntry *)b)->name_offset); |
142 | 15.6k | } |
143 | | |
144 | | static gboolean |
145 | | find_color(const char *name, |
146 | | XPMColor *colorPtr) |
147 | 1.57k | { |
148 | 1.57k | XPMColorEntry *found; |
149 | | |
150 | 1.57k | found = bsearch (name, xColors, G_N_ELEMENTS (xColors), sizeof (XPMColorEntry), |
151 | 1.57k | compare_xcolor_entries); |
152 | 1.57k | if (found == NULL) |
153 | 1.57k | return FALSE; |
154 | | |
155 | 0 | colorPtr->red = (found->red * 65535) / 255; |
156 | 0 | colorPtr->green = (found->green * 65535) / 255; |
157 | 0 | colorPtr->blue = (found->blue * 65535) / 255; |
158 | | |
159 | 0 | return TRUE; |
160 | 1.57k | } |
161 | | |
162 | | /* |
163 | | *---------------------------------------------------------------------- |
164 | | * |
165 | | * parse_color -- |
166 | | * |
167 | | * Partial implementation of X color name parsing interface. |
168 | | * |
169 | | * Results: |
170 | | * Returns TRUE on success. |
171 | | * |
172 | | * Side effects: |
173 | | * None. |
174 | | * |
175 | | *---------------------------------------------------------------------- |
176 | | */ |
177 | | |
178 | | static gboolean |
179 | | parse_color (const char *spec, |
180 | | XPMColor *colorPtr) |
181 | 8.89k | { |
182 | 8.89k | if (spec[0] == '#') { |
183 | 7.32k | int i, red, green, blue; |
184 | | |
185 | 7.32k | if ((i = strlen (spec + 1)) % 3) { |
186 | 2.18k | return FALSE; |
187 | 2.18k | } |
188 | 5.14k | i /= 3; |
189 | | |
190 | 5.14k | if (i == 4) { |
191 | 469 | if (sscanf (spec + 1, "%4x%4x%4x", &red, &green, &blue) != 3) |
192 | 345 | return FALSE; |
193 | 124 | colorPtr->red = red; |
194 | 124 | colorPtr->green = green; |
195 | 124 | colorPtr->blue = blue; |
196 | 4.67k | } else if (i == 1) { |
197 | 914 | if (sscanf (spec + 1, "%1x%1x%1x", &red, &green, &blue) != 3) |
198 | 669 | return FALSE; |
199 | 245 | colorPtr->red = (red * 65535) / 15; |
200 | 245 | colorPtr->green = (green * 65535) / 15; |
201 | 245 | colorPtr->blue = (blue * 65535) / 15; |
202 | 3.76k | } else if (i == 2) { |
203 | 2.87k | if (sscanf (spec + 1, "%2x%2x%2x", &red, &green, &blue) != 3) |
204 | 472 | return FALSE; |
205 | 2.40k | colorPtr->red = (red * 65535) / 255; |
206 | 2.40k | colorPtr->green = (green * 65535) / 255; |
207 | 2.40k | colorPtr->blue = (blue * 65535) / 255; |
208 | 2.40k | } else /* if (i == 3) */ { |
209 | 882 | if (sscanf (spec + 1, "%3x%3x%3x", &red, &green, &blue) != 3) |
210 | 589 | return FALSE; |
211 | 293 | colorPtr->red = (red * 65535) / 4095; |
212 | 293 | colorPtr->green = (green * 65535) / 4095; |
213 | 293 | colorPtr->blue = (blue * 65535) / 4095; |
214 | 293 | } |
215 | 5.14k | } else { |
216 | 1.57k | if (!find_color(spec, colorPtr)) |
217 | 1.57k | return FALSE; |
218 | 1.57k | } |
219 | 3.06k | return TRUE; |
220 | 8.89k | } |
221 | | |
222 | | static gint |
223 | | xpm_seek_string (FILE *infile, const gchar *str) |
224 | 89 | { |
225 | 89 | char instr[1024]; |
226 | | |
227 | 328 | while (!feof (infile)) { |
228 | 328 | if (fscanf (infile, "%1023s", instr) < 0) |
229 | 1 | return FALSE; |
230 | 327 | if (strcmp (instr, str) == 0) |
231 | 88 | return TRUE; |
232 | 327 | } |
233 | | |
234 | 0 | return FALSE; |
235 | 89 | } |
236 | | |
237 | | static gint |
238 | | xpm_seek_char (FILE *infile, gchar c) |
239 | 23.4k | { |
240 | 23.4k | gint b, oldb; |
241 | | |
242 | 575k | while ((b = getc (infile)) != EOF) { |
243 | 574k | if (c != b && b == '/') { |
244 | 2.53k | b = getc (infile); |
245 | 2.53k | if (b == EOF) |
246 | 1 | return FALSE; |
247 | | |
248 | 2.53k | else if (b == '*') { /* we have a comment */ |
249 | 513 | b = -1; |
250 | 93.1k | do { |
251 | 93.1k | oldb = b; |
252 | 93.1k | b = getc (infile); |
253 | 93.1k | if (b == EOF) |
254 | 3 | return FALSE; |
255 | 93.1k | } while (!(oldb == '*' && b == '/')); |
256 | 513 | } |
257 | 572k | } else if (c == b) |
258 | 23.4k | return TRUE; |
259 | 574k | } |
260 | | |
261 | 32 | return FALSE; |
262 | 23.4k | } |
263 | | |
264 | | static gint |
265 | | xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size) |
266 | 24.0k | { |
267 | 24.0k | gint c; |
268 | 24.0k | guint cnt = 0, bufsiz, ret = FALSE; |
269 | 24.0k | gchar *buf; |
270 | | |
271 | 24.0k | buf = *buffer; |
272 | 24.0k | bufsiz = *buffer_size; |
273 | 24.0k | if (buf == NULL) { |
274 | 85 | bufsiz = 10 * sizeof (gchar); |
275 | 85 | buf = g_new (gchar, bufsiz); |
276 | 85 | } |
277 | | |
278 | 40.2k | do { |
279 | 40.2k | c = getc (infile); |
280 | 40.2k | } while (c != EOF && c != '"'); |
281 | | |
282 | 24.0k | if (c != '"') |
283 | 37 | goto out; |
284 | | |
285 | 3.74M | while ((c = getc (infile)) != EOF) { |
286 | 3.74M | if (cnt == bufsiz) { |
287 | 473 | guint new_size = bufsiz * 2; |
288 | | |
289 | 473 | if (new_size > bufsiz) |
290 | 473 | bufsiz = new_size; |
291 | 0 | else |
292 | 0 | goto out; |
293 | | |
294 | 473 | buf = g_realloc (buf, bufsiz); |
295 | 473 | buf[bufsiz - 1] = '\0'; |
296 | 473 | } |
297 | | |
298 | 3.74M | if (c != '"') |
299 | 3.72M | buf[cnt++] = c; |
300 | 23.9k | else { |
301 | 23.9k | buf[cnt] = 0; |
302 | 23.9k | ret = TRUE; |
303 | 23.9k | break; |
304 | 23.9k | } |
305 | 3.74M | } |
306 | | |
307 | 24.0k | out: |
308 | 24.0k | buf[bufsiz - 1] = '\0'; /* ensure null termination for errors */ |
309 | 24.0k | *buffer = buf; |
310 | 24.0k | *buffer_size = bufsiz; |
311 | 24.0k | return ret; |
312 | 23.9k | } |
313 | | |
314 | | static gchar * |
315 | | xpm_extract_color (const gchar *buffer) |
316 | 23.2k | { |
317 | 23.2k | const gchar *p = &buffer[0]; |
318 | 23.2k | gint new_key = 0; |
319 | 23.2k | gint key = 0; |
320 | 23.2k | gint current_key = 1; |
321 | 23.2k | gint space = 128; |
322 | 23.2k | gchar word[129], color[129], current_color[129]; |
323 | 23.2k | gchar *r; |
324 | | |
325 | 23.2k | word[0] = '\0'; |
326 | 23.2k | color[0] = '\0'; |
327 | 23.2k | current_color[0] = '\0'; |
328 | 49.5k | while (1) { |
329 | | /* skip whitespace */ |
330 | 80.8k | for (; *p != '\0' && g_ascii_isspace (*p); p++) { |
331 | 31.2k | } |
332 | | /* copy word */ |
333 | 274k | for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) { |
334 | 224k | *r = *p; |
335 | 224k | } |
336 | 49.5k | *r = '\0'; |
337 | 49.5k | if (*word == '\0') { |
338 | 17.9k | if (color[0] == '\0') /* incomplete colormap entry */ |
339 | 8.34k | return NULL; |
340 | 9.65k | else /* end of entry, still store the last color */ |
341 | 9.65k | new_key = 1; |
342 | 17.9k | } |
343 | 31.5k | else if (key > 0 && color[0] == '\0') /* next word must be a color name part */ |
344 | 10.4k | new_key = 0; |
345 | 21.0k | else { |
346 | 21.0k | if (strcmp (word, "c") == 0) |
347 | 9.38k | new_key = 5; |
348 | 11.6k | else if (strcmp (word, "g") == 0) |
349 | 832 | new_key = 4; |
350 | 10.8k | else if (strcmp (word, "g4") == 0) |
351 | 0 | new_key = 3; |
352 | 10.8k | else if (strcmp (word, "m") == 0) |
353 | 830 | new_key = 2; |
354 | 10.0k | else if (strcmp (word, "s") == 0) |
355 | 0 | new_key = 1; |
356 | 10.0k | else |
357 | 10.0k | new_key = 0; |
358 | 21.0k | } |
359 | 41.2k | if (new_key == 0) { /* word is a color name part */ |
360 | 20.5k | if (key == 0) /* key expected */ |
361 | 5.16k | return NULL; |
362 | | /* accumulate color name */ |
363 | 15.3k | if (color[0] != '\0') { |
364 | 4.87k | strncat (color, " ", space); |
365 | 4.87k | space -= MIN (space, 1); |
366 | 4.87k | } |
367 | 15.3k | strncat (color, word, space); |
368 | 15.3k | space -= MIN (space, strlen (word)); |
369 | 15.3k | } |
370 | 20.7k | else { /* word is a key */ |
371 | 20.7k | if (key > current_key) { |
372 | 9.66k | current_key = key; |
373 | 9.66k | strcpy (current_color, color); |
374 | 9.66k | } |
375 | 20.7k | space = 128; |
376 | 20.7k | color[0] = '\0'; |
377 | 20.7k | key = new_key; |
378 | 20.7k | if (*p == '\0') break; |
379 | 20.7k | } |
380 | | |
381 | 41.2k | } |
382 | 9.71k | if (current_key > 1) |
383 | 9.65k | return g_strdup (current_color); |
384 | 57 | else |
385 | 57 | return NULL; |
386 | 9.71k | } |
387 | | |
388 | | /* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */ |
389 | | |
390 | | static const gchar * |
391 | | file_buffer (enum buf_op op, gpointer handle) |
392 | 24.0k | { |
393 | 24.0k | struct file_handle *h = handle; |
394 | | |
395 | 24.0k | switch (op) { |
396 | 89 | case op_header: |
397 | 89 | if (xpm_seek_string (h->infile, "XPM") != TRUE) |
398 | 1 | break; |
399 | | |
400 | 88 | if (xpm_seek_char (h->infile, '{') != TRUE) |
401 | 3 | break; |
402 | | /* Fall through to the next xpm_seek_char. */ |
403 | | |
404 | 23.3k | case op_cmap: |
405 | 23.3k | xpm_seek_char (h->infile, '"'); |
406 | 23.3k | if (fseek (h->infile, -1, SEEK_CUR) != 0) |
407 | 0 | return NULL; |
408 | | /* Fall through to the xpm_read_string. */ |
409 | | |
410 | 24.0k | case op_body: |
411 | 24.0k | if(!xpm_read_string (h->infile, &h->buffer, &h->buffer_size)) |
412 | 74 | return NULL; |
413 | 23.9k | return h->buffer; |
414 | | |
415 | 0 | default: |
416 | 0 | g_assert_not_reached (); |
417 | 24.0k | } |
418 | | |
419 | 4 | return NULL; |
420 | 24.0k | } |
421 | | |
422 | | /* This reads from memory */ |
423 | | static const gchar * |
424 | | mem_buffer (enum buf_op op, gpointer handle) |
425 | 0 | { |
426 | 0 | struct mem_handle *h = handle; |
427 | 0 | switch (op) { |
428 | 0 | case op_header: |
429 | 0 | case op_cmap: |
430 | 0 | case op_body: |
431 | 0 | if (h->data[h->offset]) { |
432 | 0 | const gchar* retval; |
433 | |
|
434 | 0 | retval = h->data[h->offset]; |
435 | 0 | h->offset += 1; |
436 | 0 | return retval; |
437 | 0 | } |
438 | 0 | break; |
439 | | |
440 | 0 | default: |
441 | 0 | g_assert_not_reached (); |
442 | 0 | break; |
443 | 0 | } |
444 | | |
445 | 0 | return NULL; |
446 | 0 | } |
447 | | |
448 | | /* This function does all the work. */ |
449 | | static GdkPixbuf * |
450 | | pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle, |
451 | | GError **error) |
452 | 89 | { |
453 | 89 | gint w, h, n_col, cpp, x_hot, y_hot, items; |
454 | 89 | gint cnt, xcnt, ycnt, wbytes, n; |
455 | 89 | gint is_trans = FALSE; |
456 | 89 | const gchar *buffer; |
457 | 89 | gchar *name_buf; |
458 | 89 | gchar pixel_str[32]; |
459 | 89 | GHashTable *color_hash; |
460 | 89 | XPMColor *colors, *color, *fallbackcolor; |
461 | 89 | guchar *pixtmp; |
462 | 89 | GdkPixbuf *pixbuf = NULL; |
463 | 89 | gint rowstride; |
464 | | |
465 | 89 | fallbackcolor = NULL; |
466 | | |
467 | 89 | buffer = (*get_buf) (op_header, handle); |
468 | 89 | if (!buffer) { |
469 | 5 | g_set_error_literal (error, |
470 | 5 | GDK_PIXBUF_ERROR, |
471 | 5 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
472 | 5 | _("No XPM header found")); |
473 | 5 | return NULL; |
474 | 5 | } |
475 | 84 | items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot); |
476 | | |
477 | 84 | if (items != 4 && items != 6) { |
478 | 2 | g_set_error_literal (error, |
479 | 2 | GDK_PIXBUF_ERROR, |
480 | 2 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
481 | 2 | _("Invalid XPM header")); |
482 | 2 | return NULL; |
483 | 2 | } |
484 | | |
485 | 82 | if (w <= 0) { |
486 | 0 | g_set_error_literal (error, |
487 | 0 | GDK_PIXBUF_ERROR, |
488 | 0 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
489 | 0 | _("XPM file has image width <= 0")); |
490 | 0 | return NULL; |
491 | |
|
492 | 0 | } |
493 | 82 | if (h <= 0) { |
494 | 0 | g_set_error_literal (error, |
495 | 0 | GDK_PIXBUF_ERROR, |
496 | 0 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
497 | 0 | _("XPM file has image height <= 0")); |
498 | 0 | return NULL; |
499 | |
|
500 | 0 | } |
501 | | /* Check from libXpm's ParsePixels() */ |
502 | 82 | if ((h > 0 && w >= UINT_MAX / h) || |
503 | 82 | w * h >= UINT_MAX / sizeof(unsigned int)) { |
504 | 0 | g_set_error_literal (error, |
505 | 0 | GDK_PIXBUF_ERROR, |
506 | 0 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
507 | 0 | _("Invalid XPM header")); |
508 | 0 | return NULL; |
509 | 0 | } |
510 | 82 | if (cpp <= 0 || cpp >= 32 || w >= G_MAXINT / cpp) { |
511 | 0 | g_set_error_literal (error, |
512 | 0 | GDK_PIXBUF_ERROR, |
513 | 0 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
514 | 0 | _("XPM has invalid number of chars per pixel")); |
515 | 0 | return NULL; |
516 | 0 | } |
517 | 82 | if (n_col <= 0 || |
518 | 82 | n_col >= G_MAXINT / (cpp + 1) || |
519 | 82 | n_col >= G_MAXINT / sizeof (XPMColor)) { |
520 | 0 | g_set_error_literal (error, |
521 | 0 | GDK_PIXBUF_ERROR, |
522 | 0 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
523 | 0 | _("XPM file has invalid number of colors")); |
524 | 0 | return NULL; |
525 | 0 | } |
526 | | |
527 | | /* The hash is used for fast lookups of color from chars */ |
528 | 82 | color_hash = g_hash_table_new (g_str_hash, g_str_equal); |
529 | | |
530 | 82 | name_buf = g_try_malloc (n_col * (cpp + 1)); |
531 | 82 | if (!name_buf) { |
532 | 0 | g_set_error_literal (error, |
533 | 0 | GDK_PIXBUF_ERROR, |
534 | 0 | GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
535 | 0 | _("Cannot allocate memory for loading XPM image")); |
536 | 0 | g_hash_table_destroy (color_hash); |
537 | 0 | return NULL; |
538 | 0 | } |
539 | 82 | colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col); |
540 | 82 | if (!colors) { |
541 | 0 | g_set_error_literal (error, |
542 | 0 | GDK_PIXBUF_ERROR, |
543 | 0 | GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
544 | 0 | _("Cannot allocate memory for loading XPM image")); |
545 | 0 | g_hash_table_destroy (color_hash); |
546 | 0 | g_free (name_buf); |
547 | 0 | return NULL; |
548 | 0 | } |
549 | | |
550 | 23.2k | for (cnt = 0; cnt < n_col; cnt++) { |
551 | 23.2k | gchar *color_name; |
552 | | |
553 | 23.2k | buffer = (*get_buf) (op_cmap, handle); |
554 | 23.2k | if (!buffer) { |
555 | 67 | g_set_error_literal (error, |
556 | 67 | GDK_PIXBUF_ERROR, |
557 | 67 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
558 | 67 | _("Cannot read XPM colormap")); |
559 | 67 | goto out; |
560 | 67 | } |
561 | | |
562 | 23.2k | color = &colors[cnt]; |
563 | 23.2k | color->color_string = &name_buf[cnt * (cpp + 1)]; |
564 | 23.2k | strncpy (color->color_string, buffer, cpp); |
565 | 23.2k | color->color_string[cpp] = 0; |
566 | 23.2k | buffer += strlen (color->color_string); |
567 | 23.2k | color->transparent = FALSE; |
568 | | |
569 | 23.2k | color_name = xpm_extract_color (buffer); |
570 | | |
571 | 23.2k | if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0) |
572 | 20.1k | || (parse_color (color_name, color) == FALSE)) { |
573 | 20.1k | color->transparent = TRUE; |
574 | 20.1k | color->red = 0; |
575 | 20.1k | color->green = 0; |
576 | 20.1k | color->blue = 0; |
577 | 20.1k | is_trans = TRUE; |
578 | 20.1k | } |
579 | | |
580 | 23.2k | g_free (color_name); |
581 | 23.2k | g_hash_table_insert (color_hash, color->color_string, color); |
582 | | |
583 | 23.2k | if (cnt == 0) |
584 | 82 | fallbackcolor = color; |
585 | 23.2k | } |
586 | | |
587 | 15 | pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h); |
588 | | |
589 | 15 | if (!pixbuf) { |
590 | 0 | g_set_error_literal (error, |
591 | 0 | GDK_PIXBUF_ERROR, |
592 | 0 | GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
593 | 0 | _("Cannot allocate memory for loading XPM image")); |
594 | 0 | goto out; |
595 | 0 | } |
596 | | |
597 | 15 | rowstride = gdk_pixbuf_get_rowstride (pixbuf); |
598 | | |
599 | 15 | wbytes = w * cpp; |
600 | | |
601 | 640 | for (ycnt = 0; ycnt < h; ycnt++) { |
602 | 639 | pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * rowstride; |
603 | | |
604 | 639 | buffer = (*get_buf) (op_body, handle); |
605 | 639 | if ((!buffer) || (strlen (buffer) < wbytes)) { |
606 | | /* Advertised width doesn't match pixels */ |
607 | 14 | g_set_error_literal (error, |
608 | 14 | GDK_PIXBUF_ERROR, |
609 | 14 | GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
610 | 14 | _("Dimensions do not match data")); |
611 | 14 | goto out; |
612 | 14 | } |
613 | | |
614 | 2.42k | for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { |
615 | 1.80k | strncpy (pixel_str, &buffer[n], cpp); |
616 | 1.80k | pixel_str[cpp] = 0; |
617 | | |
618 | 1.80k | color = g_hash_table_lookup (color_hash, pixel_str); |
619 | | |
620 | | /* Bad XPM...punt */ |
621 | 1.80k | if (!color) |
622 | 1.23k | color = fallbackcolor; |
623 | | |
624 | 1.80k | *pixtmp++ = color->red >> 8; |
625 | 1.80k | *pixtmp++ = color->green >> 8; |
626 | 1.80k | *pixtmp++ = color->blue >> 8; |
627 | | |
628 | 1.80k | if (is_trans && color->transparent) |
629 | 1.77k | *pixtmp++ = 0; |
630 | 26 | else if (is_trans) |
631 | 0 | *pixtmp++ = 0xFF; |
632 | 1.80k | } |
633 | 625 | } |
634 | | |
635 | 1 | g_hash_table_destroy (color_hash); |
636 | 1 | g_free (colors); |
637 | 1 | g_free (name_buf); |
638 | | |
639 | 1 | if (items == 6) { |
640 | 1 | gchar hot[10]; |
641 | 1 | g_snprintf (hot, 10, "%d", x_hot); |
642 | 1 | gdk_pixbuf_set_option (pixbuf, "x_hot", hot); |
643 | 1 | g_snprintf (hot, 10, "%d", y_hot); |
644 | 1 | gdk_pixbuf_set_option (pixbuf, "y_hot", hot); |
645 | | |
646 | 1 | } |
647 | | |
648 | 1 | return pixbuf; |
649 | | |
650 | 81 | out: |
651 | 81 | g_hash_table_destroy (color_hash); |
652 | 81 | g_free (colors); |
653 | 81 | g_free (name_buf); |
654 | | |
655 | 81 | g_clear_object (&pixbuf); |
656 | 81 | return NULL; |
657 | 15 | } |
658 | | |
659 | | /* Shared library entry point for file loading */ |
660 | | static GdkPixbuf * |
661 | | gdk_pixbuf__xpm_image_load (FILE *f, |
662 | | GError **error) |
663 | 89 | { |
664 | 89 | GdkPixbuf *pixbuf; |
665 | 89 | struct file_handle h; |
666 | | |
667 | 89 | memset (&h, 0, sizeof (h)); |
668 | 89 | h.infile = f; |
669 | 89 | pixbuf = pixbuf_create_from_xpm (file_buffer, &h, error); |
670 | 89 | g_free (h.buffer); |
671 | | |
672 | 89 | return pixbuf; |
673 | 89 | } |
674 | | |
675 | | /* Shared library entry point for memory loading */ |
676 | | static GdkPixbuf * |
677 | | gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data) |
678 | 0 | { |
679 | 0 | GdkPixbuf *pixbuf; |
680 | 0 | struct mem_handle h; |
681 | 0 | GError *error = NULL; |
682 | | |
683 | 0 | h.data = data; |
684 | 0 | h.offset = 0; |
685 | | |
686 | 0 | pixbuf = pixbuf_create_from_xpm (mem_buffer, &h, &error); |
687 | |
|
688 | 0 | if (error) { |
689 | 0 | g_warning ("Inline XPM data is broken: %s", error->message); |
690 | 0 | g_error_free (error); |
691 | 0 | error = NULL; |
692 | 0 | } |
693 | | |
694 | 0 | return pixbuf; |
695 | 0 | } |
696 | | |
697 | | /* Progressive loader */ |
698 | | typedef struct _XPMContext XPMContext; |
699 | | struct _XPMContext |
700 | | { |
701 | | GdkPixbufModulePreparedFunc prepared_func; |
702 | | GdkPixbufModuleUpdatedFunc updated_func; |
703 | | gpointer user_data; |
704 | | |
705 | | gchar *tempname; |
706 | | FILE *file; |
707 | | gboolean all_okay; |
708 | | }; |
709 | | |
710 | | /* |
711 | | * FIXME xpm loading progressively is not properly implemented. |
712 | | * Instead we will buffer to a file then load that file when done. |
713 | | * This is very broken but it should be relatively simple to fix |
714 | | * in the future. |
715 | | */ |
716 | | static gpointer |
717 | | gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func, |
718 | | GdkPixbufModulePreparedFunc prepared_func, |
719 | | GdkPixbufModuleUpdatedFunc updated_func, |
720 | | gpointer user_data, |
721 | | GError **error) |
722 | 89 | { |
723 | 89 | XPMContext *context; |
724 | 89 | gint fd; |
725 | | |
726 | 89 | g_assert (size_func != NULL); |
727 | 89 | g_assert (prepared_func != NULL); |
728 | 89 | g_assert (updated_func != NULL); |
729 | | |
730 | 89 | context = g_new (XPMContext, 1); |
731 | 89 | context->prepared_func = prepared_func; |
732 | 89 | context->updated_func = updated_func; |
733 | 89 | context->user_data = user_data; |
734 | 89 | context->all_okay = TRUE; |
735 | 89 | fd = g_file_open_tmp ("gdkpixbuf-xpm-tmp.XXXXXX", &context->tempname, |
736 | 89 | NULL); |
737 | 89 | if (fd < 0) { |
738 | 0 | g_free (context); |
739 | 0 | return NULL; |
740 | 0 | } |
741 | | |
742 | 89 | context->file = fdopen (fd, "w+"); |
743 | 89 | if (context->file == NULL) { |
744 | 0 | g_free (context->tempname); |
745 | 0 | g_free (context); |
746 | 0 | return NULL; |
747 | 0 | } |
748 | | |
749 | 89 | return context; |
750 | 89 | } |
751 | | |
752 | | static gboolean |
753 | | gdk_pixbuf__xpm_image_stop_load (gpointer data, |
754 | | GError **error) |
755 | 89 | { |
756 | 89 | XPMContext *context = (XPMContext*) data; |
757 | 89 | GdkPixbuf *pixbuf; |
758 | 89 | gboolean retval = FALSE; |
759 | | |
760 | 89 | g_return_val_if_fail (data != NULL, FALSE); |
761 | | |
762 | 89 | fflush (context->file); |
763 | 89 | rewind (context->file); |
764 | 89 | if (context->all_okay) { |
765 | 89 | pixbuf = gdk_pixbuf__xpm_image_load (context->file, error); |
766 | | |
767 | 89 | if (pixbuf != NULL) { |
768 | 1 | (* context->prepared_func) (pixbuf, |
769 | 1 | NULL, |
770 | 1 | context->user_data); |
771 | 1 | (* context->updated_func) (pixbuf, |
772 | 1 | 0, 0, |
773 | 1 | gdk_pixbuf_get_width (pixbuf), |
774 | 1 | gdk_pixbuf_get_height (pixbuf), |
775 | 1 | context->user_data); |
776 | 1 | g_object_unref (pixbuf); |
777 | | |
778 | 1 | retval = TRUE; |
779 | 1 | } |
780 | 89 | } |
781 | | |
782 | 89 | fclose (context->file); |
783 | 89 | g_unlink (context->tempname); |
784 | 89 | g_free (context->tempname); |
785 | 89 | g_free ((XPMContext *) context); |
786 | | |
787 | 89 | return retval; |
788 | 89 | } |
789 | | |
790 | | static gboolean |
791 | | gdk_pixbuf__xpm_image_load_increment (gpointer data, |
792 | | const guchar *buf, |
793 | | guint size, |
794 | | GError **error) |
795 | 302 | { |
796 | 302 | XPMContext *context = (XPMContext *) data; |
797 | | |
798 | 302 | g_return_val_if_fail (data != NULL, FALSE); |
799 | | |
800 | 302 | if (fwrite (buf, sizeof (guchar), size, context->file) != size) { |
801 | 0 | gint save_errno = errno; |
802 | 0 | context->all_okay = FALSE; |
803 | 0 | g_set_error_literal (error, |
804 | 0 | G_FILE_ERROR, |
805 | 0 | g_file_error_from_errno (save_errno), |
806 | 0 | _("Failed to write to temporary file when loading XPM image")); |
807 | 0 | return FALSE; |
808 | 0 | } |
809 | | |
810 | 302 | return TRUE; |
811 | 302 | } |
812 | | |
813 | | #ifndef INCLUDE_xpm |
814 | | #define MODULE_ENTRY(function) G_MODULE_EXPORT void function |
815 | | #else |
816 | | #define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function |
817 | | #endif |
818 | | |
819 | | MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module) |
820 | 3 | { |
821 | 3 | module->load = gdk_pixbuf__xpm_image_load; |
822 | 3 | module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data; |
823 | 3 | module->begin_load = gdk_pixbuf__xpm_image_begin_load; |
824 | 3 | module->stop_load = gdk_pixbuf__xpm_image_stop_load; |
825 | 3 | module->load_increment = gdk_pixbuf__xpm_image_load_increment; |
826 | 3 | } |
827 | | |
828 | | MODULE_ENTRY (fill_info) (GdkPixbufFormat *info) |
829 | 3 | { |
830 | 3 | static const GdkPixbufModulePattern signature[] = { |
831 | 3 | { "/* XPM */", NULL, 100 }, |
832 | 3 | { NULL, NULL, 0 } |
833 | 3 | }; |
834 | 3 | static const gchar *mime_types[] = { |
835 | 3 | "image/x-xpixmap", |
836 | 3 | NULL |
837 | 3 | }; |
838 | 3 | static const gchar *extensions[] = { |
839 | 3 | "xpm", |
840 | 3 | NULL |
841 | 3 | }; |
842 | | |
843 | 3 | info->name = "xpm"; |
844 | 3 | info->signature = (GdkPixbufModulePattern *) signature; |
845 | 3 | info->description = NC_("image format", "XPM"); |
846 | 3 | info->mime_types = (gchar **) mime_types; |
847 | 3 | info->extensions = (gchar **) extensions; |
848 | 3 | info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; |
849 | 3 | info->license = "LGPL"; |
850 | 3 | } |