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 | , |
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 | { |
140 | return g_ascii_strcasecmp (s1: (const char *) a, |
141 | s2: color_names + ((const XPMColorEntry *)b)->name_offset); |
142 | } |
143 | |
144 | static gboolean |
145 | find_color(const char *name, |
146 | XPMColor *colorPtr) |
147 | { |
148 | XPMColorEntry *found; |
149 | |
150 | found = bsearch (key: name, base: xColors, G_N_ELEMENTS (xColors), size: sizeof (XPMColorEntry), |
151 | compar: compare_xcolor_entries); |
152 | if (found == NULL) |
153 | return FALSE; |
154 | |
155 | colorPtr->red = (found->red * 65535) / 255; |
156 | colorPtr->green = (found->green * 65535) / 255; |
157 | colorPtr->blue = (found->blue * 65535) / 255; |
158 | |
159 | return TRUE; |
160 | } |
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 | { |
182 | if (spec[0] == '#') { |
183 | int i, red, green, blue; |
184 | |
185 | if ((i = strlen (s: spec + 1)) % 3) { |
186 | return FALSE; |
187 | } |
188 | i /= 3; |
189 | |
190 | if (i == 4) { |
191 | if (sscanf (s: spec + 1, format: "%4x%4x%4x" , &red, &green, &blue) != 3) |
192 | return FALSE; |
193 | colorPtr->red = red; |
194 | colorPtr->green = green; |
195 | colorPtr->blue = blue; |
196 | } else if (i == 1) { |
197 | if (sscanf (s: spec + 1, format: "%1x%1x%1x" , &red, &green, &blue) != 3) |
198 | return FALSE; |
199 | colorPtr->red = (red * 65535) / 15; |
200 | colorPtr->green = (green * 65535) / 15; |
201 | colorPtr->blue = (blue * 65535) / 15; |
202 | } else if (i == 2) { |
203 | if (sscanf (s: spec + 1, format: "%2x%2x%2x" , &red, &green, &blue) != 3) |
204 | return FALSE; |
205 | colorPtr->red = (red * 65535) / 255; |
206 | colorPtr->green = (green * 65535) / 255; |
207 | colorPtr->blue = (blue * 65535) / 255; |
208 | } else /* if (i == 3) */ { |
209 | if (sscanf (s: spec + 1, format: "%3x%3x%3x" , &red, &green, &blue) != 3) |
210 | return FALSE; |
211 | colorPtr->red = (red * 65535) / 4095; |
212 | colorPtr->green = (green * 65535) / 4095; |
213 | colorPtr->blue = (blue * 65535) / 4095; |
214 | } |
215 | } else { |
216 | if (!find_color(name: spec, colorPtr)) |
217 | return FALSE; |
218 | } |
219 | return TRUE; |
220 | } |
221 | |
222 | static gint |
223 | xpm_seek_string (FILE *infile, const gchar *str) |
224 | { |
225 | char instr[1024]; |
226 | |
227 | while (!feof (stream: infile)) { |
228 | if (fscanf (stream: infile, format: "%1023s" , instr) < 0) |
229 | return FALSE; |
230 | if (strcmp (s1: instr, s2: str) == 0) |
231 | return TRUE; |
232 | } |
233 | |
234 | return FALSE; |
235 | } |
236 | |
237 | static gint |
238 | xpm_seek_char (FILE *infile, gchar c) |
239 | { |
240 | gint b, oldb; |
241 | |
242 | while ((b = getc (stream: infile)) != EOF) { |
243 | if (c != b && b == '/') { |
244 | b = getc (stream: infile); |
245 | if (b == EOF) |
246 | return FALSE; |
247 | |
248 | else if (b == '*') { /* we have a comment */ |
249 | b = -1; |
250 | do { |
251 | oldb = b; |
252 | b = getc (stream: infile); |
253 | if (b == EOF) |
254 | return FALSE; |
255 | } while (!(oldb == '*' && b == '/')); |
256 | } |
257 | } else if (c == b) |
258 | return TRUE; |
259 | } |
260 | |
261 | return FALSE; |
262 | } |
263 | |
264 | static gint |
265 | xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size) |
266 | { |
267 | gint c; |
268 | guint cnt = 0, bufsiz, ret = FALSE; |
269 | gchar *buf; |
270 | |
271 | buf = *buffer; |
272 | bufsiz = *buffer_size; |
273 | if (buf == NULL) { |
274 | bufsiz = 10 * sizeof (gchar); |
275 | buf = g_new (gchar, bufsiz); |
276 | } |
277 | |
278 | do { |
279 | c = getc (stream: infile); |
280 | } while (c != EOF && c != '"'); |
281 | |
282 | if (c != '"') |
283 | goto out; |
284 | |
285 | while ((c = getc (stream: infile)) != EOF) { |
286 | if (cnt == bufsiz) { |
287 | guint new_size = bufsiz * 2; |
288 | |
289 | if (new_size > bufsiz) |
290 | bufsiz = new_size; |
291 | else |
292 | goto out; |
293 | |
294 | buf = g_realloc (mem: buf, n_bytes: bufsiz); |
295 | buf[bufsiz - 1] = '\0'; |
296 | } |
297 | |
298 | if (c != '"') |
299 | buf[cnt++] = c; |
300 | else { |
301 | buf[cnt] = 0; |
302 | ret = TRUE; |
303 | break; |
304 | } |
305 | } |
306 | |
307 | out: |
308 | buf[bufsiz - 1] = '\0'; /* ensure null termination for errors */ |
309 | *buffer = buf; |
310 | *buffer_size = bufsiz; |
311 | return ret; |
312 | } |
313 | |
314 | static gchar * |
315 | (const gchar *buffer) |
316 | { |
317 | const gchar *p = &buffer[0]; |
318 | gint new_key = 0; |
319 | gint key = 0; |
320 | gint current_key = 1; |
321 | gint space = 128; |
322 | gchar word[129], color[129], current_color[129]; |
323 | gchar *r; |
324 | |
325 | word[0] = '\0'; |
326 | color[0] = '\0'; |
327 | current_color[0] = '\0'; |
328 | while (1) { |
329 | /* skip whitespace */ |
330 | for (; *p != '\0' && g_ascii_isspace (*p); p++) { |
331 | } |
332 | /* copy word */ |
333 | for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) { |
334 | *r = *p; |
335 | } |
336 | *r = '\0'; |
337 | if (*word == '\0') { |
338 | if (color[0] == '\0') /* incomplete colormap entry */ |
339 | return NULL; |
340 | else /* end of entry, still store the last color */ |
341 | new_key = 1; |
342 | } |
343 | else if (key > 0 && color[0] == '\0') /* next word must be a color name part */ |
344 | new_key = 0; |
345 | else { |
346 | if (strcmp (s1: word, s2: "c" ) == 0) |
347 | new_key = 5; |
348 | else if (strcmp (s1: word, s2: "g" ) == 0) |
349 | new_key = 4; |
350 | else if (strcmp (s1: word, s2: "g4" ) == 0) |
351 | new_key = 3; |
352 | else if (strcmp (s1: word, s2: "m" ) == 0) |
353 | new_key = 2; |
354 | else if (strcmp (s1: word, s2: "s" ) == 0) |
355 | new_key = 1; |
356 | else |
357 | new_key = 0; |
358 | } |
359 | if (new_key == 0) { /* word is a color name part */ |
360 | if (key == 0) /* key expected */ |
361 | return NULL; |
362 | /* accumulate color name */ |
363 | if (color[0] != '\0') { |
364 | strncat (dest: color, src: " " , n: space); |
365 | space -= MIN (space, 1); |
366 | } |
367 | strncat (dest: color, src: word, n: space); |
368 | space -= MIN (space, strlen (word)); |
369 | } |
370 | else { /* word is a key */ |
371 | if (key > current_key) { |
372 | current_key = key; |
373 | strcpy (dest: current_color, src: color); |
374 | } |
375 | space = 128; |
376 | color[0] = '\0'; |
377 | key = new_key; |
378 | if (*p == '\0') break; |
379 | } |
380 | |
381 | } |
382 | if (current_key > 1) |
383 | return g_strdup (str: current_color); |
384 | else |
385 | return NULL; |
386 | } |
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 | { |
393 | struct file_handle *h = handle; |
394 | |
395 | switch (op) { |
396 | case op_header: |
397 | if (xpm_seek_string (infile: h->infile, str: "XPM" ) != TRUE) |
398 | break; |
399 | |
400 | if (xpm_seek_char (infile: h->infile, c: '{') != TRUE) |
401 | break; |
402 | /* Fall through to the next xpm_seek_char. */ |
403 | |
404 | case op_cmap: |
405 | xpm_seek_char (infile: h->infile, c: '"'); |
406 | if (fseek (stream: h->infile, off: -1, SEEK_CUR) != 0) |
407 | return NULL; |
408 | /* Fall through to the xpm_read_string. */ |
409 | |
410 | case op_body: |
411 | if(!xpm_read_string (infile: h->infile, buffer: &h->buffer, buffer_size: &h->buffer_size)) |
412 | return NULL; |
413 | return h->buffer; |
414 | |
415 | default: |
416 | g_assert_not_reached (); |
417 | } |
418 | |
419 | return NULL; |
420 | } |
421 | |
422 | /* This reads from memory */ |
423 | static const gchar * |
424 | mem_buffer (enum buf_op op, gpointer handle) |
425 | { |
426 | struct mem_handle *h = handle; |
427 | switch (op) { |
428 | case op_header: |
429 | case op_cmap: |
430 | case op_body: |
431 | if (h->data[h->offset]) { |
432 | const gchar* retval; |
433 | |
434 | retval = h->data[h->offset]; |
435 | h->offset += 1; |
436 | return retval; |
437 | } |
438 | break; |
439 | |
440 | default: |
441 | g_assert_not_reached (); |
442 | break; |
443 | } |
444 | |
445 | return NULL; |
446 | } |
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 | { |
453 | gint w, h, n_col, cpp, x_hot, y_hot, items; |
454 | gint cnt, xcnt, ycnt, wbytes, n; |
455 | gint is_trans = FALSE; |
456 | const gchar *buffer; |
457 | gchar *name_buf; |
458 | gchar pixel_str[32]; |
459 | GHashTable *color_hash; |
460 | XPMColor *colors, *color, *fallbackcolor; |
461 | guchar *pixtmp; |
462 | GdkPixbuf *pixbuf = NULL; |
463 | gint rowstride; |
464 | |
465 | fallbackcolor = NULL; |
466 | |
467 | buffer = (*get_buf) (op_header, handle); |
468 | if (!buffer) { |
469 | g_set_error_literal (err: error, |
470 | GDK_PIXBUF_ERROR, |
471 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
472 | _("No XPM header found" )); |
473 | return NULL; |
474 | } |
475 | items = sscanf (s: buffer, format: "%d %d %d %d %d %d" , &w, &h, &n_col, &cpp, &x_hot, &y_hot); |
476 | |
477 | if (items != 4 && items != 6) { |
478 | g_set_error_literal (err: error, |
479 | GDK_PIXBUF_ERROR, |
480 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
481 | _("Invalid XPM header" )); |
482 | return NULL; |
483 | } |
484 | |
485 | if (w <= 0) { |
486 | g_set_error_literal (err: error, |
487 | GDK_PIXBUF_ERROR, |
488 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
489 | _("XPM file has image width <= 0" )); |
490 | return NULL; |
491 | |
492 | } |
493 | if (h <= 0) { |
494 | g_set_error_literal (err: error, |
495 | GDK_PIXBUF_ERROR, |
496 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
497 | _("XPM file has image height <= 0" )); |
498 | return NULL; |
499 | |
500 | } |
501 | /* Check from libXpm's ParsePixels() */ |
502 | if ((h > 0 && w >= UINT_MAX / h) || |
503 | w * h >= UINT_MAX / sizeof(unsigned int)) { |
504 | g_set_error_literal (err: error, |
505 | GDK_PIXBUF_ERROR, |
506 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
507 | _("Invalid XPM header" )); |
508 | return NULL; |
509 | } |
510 | if (cpp <= 0 || cpp >= 32 || w >= G_MAXINT / cpp) { |
511 | g_set_error_literal (err: error, |
512 | GDK_PIXBUF_ERROR, |
513 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
514 | _("XPM has invalid number of chars per pixel" )); |
515 | return NULL; |
516 | } |
517 | if (n_col <= 0 || |
518 | n_col >= G_MAXINT / (cpp + 1) || |
519 | n_col >= G_MAXINT / sizeof (XPMColor)) { |
520 | g_set_error_literal (err: error, |
521 | GDK_PIXBUF_ERROR, |
522 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
523 | _("XPM file has invalid number of colors" )); |
524 | return NULL; |
525 | } |
526 | |
527 | /* The hash is used for fast lookups of color from chars */ |
528 | color_hash = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal); |
529 | |
530 | name_buf = g_try_malloc (n_bytes: n_col * (cpp + 1)); |
531 | if (!name_buf) { |
532 | g_set_error_literal (err: error, |
533 | GDK_PIXBUF_ERROR, |
534 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
535 | _("Cannot allocate memory for loading XPM image" )); |
536 | g_hash_table_destroy (hash_table: color_hash); |
537 | return NULL; |
538 | } |
539 | colors = (XPMColor *) g_try_malloc (n_bytes: sizeof (XPMColor) * n_col); |
540 | if (!colors) { |
541 | g_set_error_literal (err: error, |
542 | GDK_PIXBUF_ERROR, |
543 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
544 | _("Cannot allocate memory for loading XPM image" )); |
545 | g_hash_table_destroy (hash_table: color_hash); |
546 | g_free (mem: name_buf); |
547 | return NULL; |
548 | } |
549 | |
550 | for (cnt = 0; cnt < n_col; cnt++) { |
551 | gchar *color_name; |
552 | |
553 | buffer = (*get_buf) (op_cmap, handle); |
554 | if (!buffer) { |
555 | g_set_error_literal (err: error, |
556 | GDK_PIXBUF_ERROR, |
557 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
558 | _("Cannot read XPM colormap" )); |
559 | goto out; |
560 | } |
561 | |
562 | color = &colors[cnt]; |
563 | color->color_string = &name_buf[cnt * (cpp + 1)]; |
564 | strncpy (dest: color->color_string, src: buffer, n: cpp); |
565 | color->color_string[cpp] = 0; |
566 | buffer += strlen (s: color->color_string); |
567 | color->transparent = FALSE; |
568 | |
569 | color_name = xpm_extract_color (buffer); |
570 | |
571 | if ((color_name == NULL) || (g_ascii_strcasecmp (s1: color_name, s2: "None" ) == 0) |
572 | || (parse_color (spec: color_name, colorPtr: color) == FALSE)) { |
573 | color->transparent = TRUE; |
574 | color->red = 0; |
575 | color->green = 0; |
576 | color->blue = 0; |
577 | is_trans = TRUE; |
578 | } |
579 | |
580 | g_free (mem: color_name); |
581 | g_hash_table_insert (hash_table: color_hash, key: color->color_string, value: color); |
582 | |
583 | if (cnt == 0) |
584 | fallbackcolor = color; |
585 | } |
586 | |
587 | pixbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha: is_trans, bits_per_sample: 8, width: w, height: h); |
588 | |
589 | if (!pixbuf) { |
590 | g_set_error_literal (err: error, |
591 | GDK_PIXBUF_ERROR, |
592 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
593 | _("Cannot allocate memory for loading XPM image" )); |
594 | goto out; |
595 | } |
596 | |
597 | rowstride = gdk_pixbuf_get_rowstride (pixbuf); |
598 | |
599 | wbytes = w * cpp; |
600 | |
601 | for (ycnt = 0; ycnt < h; ycnt++) { |
602 | pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * rowstride; |
603 | |
604 | buffer = (*get_buf) (op_body, handle); |
605 | if ((!buffer) || (strlen (s: buffer) < wbytes)) { |
606 | /* Advertised width doesn't match pixels */ |
607 | g_set_error_literal (err: error, |
608 | GDK_PIXBUF_ERROR, |
609 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
610 | _("Dimensions do not match data" )); |
611 | goto out; |
612 | } |
613 | |
614 | for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) { |
615 | strncpy (dest: pixel_str, src: &buffer[n], n: cpp); |
616 | pixel_str[cpp] = 0; |
617 | |
618 | color = g_hash_table_lookup (hash_table: color_hash, key: pixel_str); |
619 | |
620 | /* Bad XPM...punt */ |
621 | if (!color) |
622 | color = fallbackcolor; |
623 | |
624 | *pixtmp++ = color->red >> 8; |
625 | *pixtmp++ = color->green >> 8; |
626 | *pixtmp++ = color->blue >> 8; |
627 | |
628 | if (is_trans && color->transparent) |
629 | *pixtmp++ = 0; |
630 | else if (is_trans) |
631 | *pixtmp++ = 0xFF; |
632 | } |
633 | } |
634 | |
635 | g_hash_table_destroy (hash_table: color_hash); |
636 | g_free (mem: colors); |
637 | g_free (mem: name_buf); |
638 | |
639 | if (items == 6) { |
640 | gchar hot[10]; |
641 | g_snprintf (string: hot, n: 10, format: "%d" , x_hot); |
642 | gdk_pixbuf_set_option (pixbuf, key: "x_hot" , value: hot); |
643 | g_snprintf (string: hot, n: 10, format: "%d" , y_hot); |
644 | gdk_pixbuf_set_option (pixbuf, key: "y_hot" , value: hot); |
645 | |
646 | } |
647 | |
648 | return pixbuf; |
649 | |
650 | out: |
651 | g_hash_table_destroy (hash_table: color_hash); |
652 | g_free (mem: colors); |
653 | g_free (mem: name_buf); |
654 | |
655 | g_clear_object (&pixbuf); |
656 | return NULL; |
657 | } |
658 | |
659 | /* Shared library entry point for file loading */ |
660 | static GdkPixbuf * |
661 | gdk_pixbuf__xpm_image_load (FILE *f, |
662 | GError **error) |
663 | { |
664 | GdkPixbuf *pixbuf; |
665 | struct file_handle h; |
666 | |
667 | memset (s: &h, c: 0, n: sizeof (h)); |
668 | h.infile = f; |
669 | pixbuf = pixbuf_create_from_xpm (get_buf: file_buffer, handle: &h, error); |
670 | g_free (mem: h.buffer); |
671 | |
672 | return pixbuf; |
673 | } |
674 | |
675 | /* Shared library entry point for memory loading */ |
676 | static GdkPixbuf * |
677 | gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data) |
678 | { |
679 | GdkPixbuf *pixbuf; |
680 | struct mem_handle h; |
681 | GError *error = NULL; |
682 | |
683 | h.data = data; |
684 | h.offset = 0; |
685 | |
686 | pixbuf = pixbuf_create_from_xpm (get_buf: mem_buffer, handle: &h, error: &error); |
687 | |
688 | if (error) { |
689 | g_warning ("Inline XPM data is broken: %s" , error->message); |
690 | g_error_free (error); |
691 | error = NULL; |
692 | } |
693 | |
694 | return pixbuf; |
695 | } |
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 | { |
723 | XPMContext *context; |
724 | gint fd; |
725 | |
726 | g_assert (size_func != NULL); |
727 | g_assert (prepared_func != NULL); |
728 | g_assert (updated_func != NULL); |
729 | |
730 | context = g_new (XPMContext, 1); |
731 | context->prepared_func = prepared_func; |
732 | context->updated_func = updated_func; |
733 | context->user_data = user_data; |
734 | context->all_okay = TRUE; |
735 | fd = g_file_open_tmp (tmpl: "gdkpixbuf-xpm-tmp.XXXXXX" , name_used: &context->tempname, |
736 | NULL); |
737 | if (fd < 0) { |
738 | g_free (mem: context); |
739 | return NULL; |
740 | } |
741 | |
742 | context->file = fdopen (fd: fd, modes: "w+" ); |
743 | if (context->file == NULL) { |
744 | g_free (mem: context->tempname); |
745 | g_free (mem: context); |
746 | return NULL; |
747 | } |
748 | |
749 | return context; |
750 | } |
751 | |
752 | static gboolean |
753 | gdk_pixbuf__xpm_image_stop_load (gpointer data, |
754 | GError **error) |
755 | { |
756 | XPMContext *context = (XPMContext*) data; |
757 | GdkPixbuf *pixbuf; |
758 | gboolean retval = FALSE; |
759 | |
760 | g_return_val_if_fail (data != NULL, FALSE); |
761 | |
762 | fflush (stream: context->file); |
763 | rewind (stream: context->file); |
764 | if (context->all_okay) { |
765 | pixbuf = gdk_pixbuf__xpm_image_load (f: context->file, error); |
766 | |
767 | if (pixbuf != NULL) { |
768 | (* context->prepared_func) (pixbuf, |
769 | NULL, |
770 | context->user_data); |
771 | (* context->updated_func) (pixbuf, |
772 | 0, 0, |
773 | gdk_pixbuf_get_width (pixbuf), |
774 | gdk_pixbuf_get_height (pixbuf), |
775 | context->user_data); |
776 | g_object_unref (object: pixbuf); |
777 | |
778 | retval = TRUE; |
779 | } |
780 | } |
781 | |
782 | fclose (stream: context->file); |
783 | g_unlink (filename: context->tempname); |
784 | g_free (mem: context->tempname); |
785 | g_free (mem: (XPMContext *) context); |
786 | |
787 | return retval; |
788 | } |
789 | |
790 | static gboolean |
791 | gdk_pixbuf__xpm_image_load_increment (gpointer data, |
792 | const guchar *buf, |
793 | guint size, |
794 | GError **error) |
795 | { |
796 | XPMContext *context = (XPMContext *) data; |
797 | |
798 | g_return_val_if_fail (data != NULL, FALSE); |
799 | |
800 | if (fwrite (ptr: buf, size: sizeof (guchar), n: size, s: context->file) != size) { |
801 | gint save_errno = errno; |
802 | context->all_okay = FALSE; |
803 | g_set_error_literal (err: error, |
804 | G_FILE_ERROR, |
805 | code: g_file_error_from_errno (err_no: save_errno), |
806 | _("Failed to write to temporary file when loading XPM image" )); |
807 | return FALSE; |
808 | } |
809 | |
810 | return TRUE; |
811 | } |
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 | { |
821 | module->load = gdk_pixbuf__xpm_image_load; |
822 | module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data; |
823 | module->begin_load = gdk_pixbuf__xpm_image_begin_load; |
824 | module->stop_load = gdk_pixbuf__xpm_image_stop_load; |
825 | module->load_increment = gdk_pixbuf__xpm_image_load_increment; |
826 | } |
827 | |
828 | MODULE_ENTRY (fill_info) (GdkPixbufFormat *info) |
829 | { |
830 | static const GdkPixbufModulePattern signature[] = { |
831 | { "/* XPM */" , NULL, 100 }, |
832 | { NULL, NULL, 0 } |
833 | }; |
834 | static const gchar *mime_types[] = { |
835 | "image/x-xpixmap" , |
836 | NULL |
837 | }; |
838 | static const gchar *extensions[] = { |
839 | "xpm" , |
840 | NULL |
841 | }; |
842 | |
843 | info->name = "xpm" ; |
844 | info->signature = (GdkPixbufModulePattern *) signature; |
845 | info->description = NC_("image format" , "XPM" ); |
846 | info->mime_types = (gchar **) mime_types; |
847 | info->extensions = (gchar **) extensions; |
848 | info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; |
849 | info->license = "LGPL" ; |
850 | } |
851 | |