1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 /* XPM plugin version 1.2.7 */
19
20 /*
21 1.2.7 fixes saving unused transparency (bug #4560)
22
23 1.2.6 fixes crash when saving indexed images (bug #109567)
24
25 1.2.5 only creates a "None" color entry if the image has alpha (bug #108034)
26
27 1.2.4 displays an error message if saving fails (bug #87588)
28
29 1.2.3 fixes bug when running in noninteractive mode
30 changes alpha_threshold range from [0, 1] to [0,255] for consistency with
31 the threshold_alpha plugin
32
33 1.2.2 fixes bug that generated bad digits on images with more than 20000
34 colors. (thanks, yanele)
35 parses gtkrc (thanks, yosh)
36 doesn't load parameter screen on images that don't have alpha
37
38 1.2.1 fixes some minor bugs -- spaces in #XXXXXX strings, small typos in code.
39
40 1.2 compute color indexes so that we don't have to use XpmSaveXImage*
41
42 Previous...Inherited code from Ray Lehtiniemi, who inherited it from S & P.
43 */
44
45 #include "config.h"
46
47 #include <string.h>
48
49 #include <glib/gstdio.h>
50
51 #include <gdk/gdk.h> /* For GDK_WINDOWING_WIN32 */
52
53 #ifndef GDK_WINDOWING_X11
54 #ifndef XPM_NO_X
55 #define XPM_NO_X
56 #endif
57 #else
58 #include <X11/Xlib.h>
59 #endif
60
61 #include <X11/xpm.h>
62
63 #include <libgimp/gimp.h>
64 #include <libgimp/gimpui.h>
65
66 #include "libgimp/stdplugins-intl.h"
67
68
69 static const gchar linenoise [] =
70 " .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyz\
71 ABCDEFGHIJKLMNOPQRSTUVWXYZ`";
72
73 #define LOAD_PROC "file-xpm-load"
74 #define SAVE_PROC "file-xpm-save"
75 #define PLUG_IN_BINARY "file-xpm"
76 #define PLUG_IN_ROLE "gimp-file-xpm"
77 #define SCALE_WIDTH 125
78
79 /* Structs for the save dialog */
80 typedef struct
81 {
82 gint threshold;
83 } XpmSaveVals;
84
85 typedef struct
86 {
87 guchar r;
88 guchar g;
89 guchar b;
90 } rgbkey;
91
92 /* whether the image is color or not. global so I only have to pass
93 * one user value to the GHFunc
94 */
95 static gboolean color;
96
97 /* bytes per pixel. global so I only have to pass one user value
98 * to the GHFunc
99 */
100 static gint cpp;
101
102 /* Declare local functions */
103 static void query (void);
104 static void run (const gchar *name,
105 gint nparams,
106 const GimpParam *param,
107 gint *nreturn_vals,
108 GimpParam **return_vals);
109
110 static gint32 load_image (const gchar *filename,
111 GError **error);
112 static guchar * parse_colors (XpmImage *xpm_image);
113 static void parse_image (gint32 image_ID,
114 XpmImage *xpm_image,
115 guchar *cmap);
116 static gboolean save_image (const gchar *filename,
117 gint32 image_ID,
118 gint32 drawable_ID,
119 GError **error);
120 static gboolean save_dialog (void);
121
122
123 const GimpPlugInInfo PLUG_IN_INFO =
124 {
125 NULL, /* init_proc */
126 NULL, /* quit_proc */
127 query, /* query_proc */
128 run, /* run_proc */
129 };
130
131 static XpmSaveVals xpmvals =
132 {
133 127 /* alpha threshold */
134 };
135
136
MAIN()137 MAIN ()
138
139 static void
140 query (void)
141 {
142 static const GimpParamDef load_args[] =
143 {
144 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
145 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
146 { GIMP_PDB_STRING, "raw-filename", "The name entered" }
147 };
148
149 static const GimpParamDef load_return_vals[] =
150 {
151 { GIMP_PDB_IMAGE, "image", "Output image" }
152 };
153
154 static const GimpParamDef save_args[] =
155 {
156 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
157 { GIMP_PDB_IMAGE, "image", "Input image" },
158 { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
159 { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
160 { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
161 { GIMP_PDB_INT32, "threshold", "Alpha threshold (0-255)" }
162 };
163
164 gimp_install_procedure (LOAD_PROC,
165 "Load files in XPM (X11 Pixmap) format.",
166 "Load files in XPM (X11 Pixmap) format. "
167 "XPM is a portable image format designed to be "
168 "included in C source code. XLib provides utility "
169 "functions to read this format. Newer code should "
170 "however be using gdk-pixbuf-csource instead. "
171 "XPM supports colored images, unlike the XBM "
172 "format which XPM was designed to replace.",
173 "Spencer Kimball & Peter Mattis & Ray Lehtiniemi",
174 "Spencer Kimball & Peter Mattis",
175 "1997",
176 N_("X PixMap image"),
177 NULL,
178 GIMP_PLUGIN,
179 G_N_ELEMENTS (load_args),
180 G_N_ELEMENTS (load_return_vals),
181 load_args, load_return_vals);
182
183 gimp_register_file_handler_mime (LOAD_PROC, "image/x-xpixmap");
184 gimp_register_magic_load_handler (LOAD_PROC,
185 "xpm",
186 "",
187 "0, string,/*\\040XPM\\040*/");
188
189 gimp_install_procedure (SAVE_PROC,
190 "Export files in XPM (X11 Pixmap) format.",
191 "Export files in XPM (X11 Pixmap) format. "
192 "XPM is a portable image format designed to be "
193 "included in C source code. XLib provides utility "
194 "functions to read this format. Newer code should "
195 "however be using gdk-pixbuf-csource instead. "
196 "XPM supports colored images, unlike the XBM "
197 "format which XPM was designed to replace.",
198 "Spencer Kimball & Peter Mattis & Ray Lehtiniemi & Nathan Summers",
199 "Spencer Kimball & Peter Mattis",
200 "1997",
201 N_("X PixMap image"),
202 "RGB*, GRAY*, INDEXED*",
203 GIMP_PLUGIN,
204 G_N_ELEMENTS (save_args), 0,
205 save_args, NULL);
206
207 gimp_register_file_handler_mime (SAVE_PROC, "image/x-xpixmap");
208 gimp_register_save_handler (SAVE_PROC, "xpm", "");
209 }
210
211 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)212 run (const gchar *name,
213 gint nparams,
214 const GimpParam *param,
215 gint *nreturn_vals,
216 GimpParam **return_vals)
217 {
218 static GimpParam values[2];
219 GimpRunMode run_mode;
220 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
221 gint32 image_ID;
222 gint32 drawable_ID;
223 GimpExportReturn export = GIMP_EXPORT_CANCEL;
224 GError *error = NULL;
225
226 INIT_I18N ();
227 gegl_init (NULL, NULL);
228
229 run_mode = param[0].data.d_int32;
230
231 *nreturn_vals = 1;
232 *return_vals = values;
233
234 values[0].type = GIMP_PDB_STATUS;
235 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
236
237 if (strcmp (name, LOAD_PROC) == 0)
238 {
239 image_ID = load_image (param[1].data.d_string, &error);
240
241 if (image_ID != -1)
242 {
243 *nreturn_vals = 2;
244 values[1].type = GIMP_PDB_IMAGE;
245 values[1].data.d_image = image_ID;
246 }
247 else
248 {
249 status = GIMP_PDB_EXECUTION_ERROR;
250 }
251 }
252 else if (strcmp (name, SAVE_PROC) == 0)
253 {
254 gimp_ui_init (PLUG_IN_BINARY, FALSE);
255
256 image_ID = param[1].data.d_int32;
257 drawable_ID = param[2].data.d_int32;
258
259 /* eventually export the image */
260 switch (run_mode)
261 {
262 case GIMP_RUN_INTERACTIVE:
263 case GIMP_RUN_WITH_LAST_VALS:
264 export = gimp_export_image (&image_ID, &drawable_ID, "XPM",
265 GIMP_EXPORT_CAN_HANDLE_RGB |
266 GIMP_EXPORT_CAN_HANDLE_GRAY |
267 GIMP_EXPORT_CAN_HANDLE_INDEXED |
268 GIMP_EXPORT_CAN_HANDLE_ALPHA);
269
270 if (export == GIMP_EXPORT_CANCEL)
271 {
272 values[0].data.d_status = GIMP_PDB_CANCEL;
273 return;
274 }
275 break;
276 default:
277 break;
278 }
279
280 switch (run_mode)
281 {
282 case GIMP_RUN_INTERACTIVE:
283 /* Possibly retrieve data */
284 gimp_get_data ("file_xpm_save", &xpmvals);
285
286 /* First acquire information with a dialog */
287 if (gimp_drawable_has_alpha (drawable_ID))
288 if (! save_dialog ())
289 status = GIMP_PDB_CANCEL;
290 break;
291
292 case GIMP_RUN_NONINTERACTIVE:
293 /* Make sure all the arguments are there! */
294 if (nparams != 6)
295 {
296 status = GIMP_PDB_CALLING_ERROR;
297 }
298 else
299 {
300 xpmvals.threshold = param[5].data.d_int32;
301
302 if (xpmvals.threshold < 0 ||
303 xpmvals.threshold > 255)
304 status = GIMP_PDB_CALLING_ERROR;
305 }
306 break;
307
308 case GIMP_RUN_WITH_LAST_VALS:
309 /* Possibly retrieve data */
310 gimp_get_data ("file_xpm_save", &xpmvals);
311 break;
312
313 default:
314 break;
315 }
316
317 if (status == GIMP_PDB_SUCCESS)
318 {
319 if (save_image (param[3].data.d_string,
320 image_ID, drawable_ID, &error))
321 {
322 gimp_set_data ("file_xpm_save", &xpmvals, sizeof (XpmSaveVals));
323 }
324 else
325 {
326 status = GIMP_PDB_EXECUTION_ERROR;
327 }
328 }
329
330 if (export == GIMP_EXPORT_EXPORT)
331 gimp_image_delete (image_ID);
332 }
333 else
334 {
335 status = GIMP_PDB_CALLING_ERROR;
336 }
337
338 if (status != GIMP_PDB_SUCCESS && error)
339 {
340 *nreturn_vals = 2;
341 values[1].type = GIMP_PDB_STRING;
342 values[1].data.d_string = error->message;
343 }
344
345 values[0].data.d_status = status;
346 }
347
348 static gint32
load_image(const gchar * filename,GError ** error)349 load_image (const gchar *filename,
350 GError **error)
351 {
352 XpmImage xpm_image;
353 guchar *cmap;
354 gint32 image_ID;
355
356 gimp_progress_init_printf (_("Opening '%s'"),
357 gimp_filename_to_utf8 (filename));
358
359 /* read the raw file */
360 switch (XpmReadFileToXpmImage ((char *) filename, &xpm_image, NULL))
361 {
362 case XpmSuccess:
363 break;
364
365 case XpmOpenFailed:
366 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
367 _("Error opening file '%s'"),
368 gimp_filename_to_utf8 (filename));
369 return -1;
370
371 case XpmFileInvalid:
372 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
373 "%s", _("XPM file invalid"));
374 return -1;
375
376 default:
377 return -1;
378 }
379
380 /* parse out the colors into a cmap */
381 cmap = parse_colors (&xpm_image);
382
383 /* create the new image */
384 image_ID = gimp_image_new (xpm_image.width,
385 xpm_image.height,
386 GIMP_RGB);
387
388 /* name it */
389 gimp_image_set_filename (image_ID, filename);
390
391 /* fill it */
392 parse_image (image_ID, &xpm_image, cmap);
393
394 /* clean up and exit */
395 g_free (cmap);
396
397 return image_ID;
398 }
399
400 static guchar *
parse_colors(XpmImage * xpm_image)401 parse_colors (XpmImage *xpm_image)
402 {
403 #ifndef XPM_NO_X
404 Display *display;
405 Colormap colormap;
406 #endif
407 gint i, j;
408 guchar *cmap;
409
410 #ifndef XPM_NO_X
411 /* open the display and get the default color map */
412 display = XOpenDisplay (NULL);
413 if (display == NULL)
414 g_printerr ("Could not open display\n");
415
416 colormap = DefaultColormap (display, DefaultScreen (display));
417 #endif
418
419 /* alloc a buffer to hold the parsed colors */
420 cmap = g_new0 (guchar, 4 * xpm_image->ncolors);
421
422 /* parse each color in the file */
423 for (i = 0, j = 0; i < xpm_image->ncolors; i++)
424 {
425 gchar *colorspec = "None";
426 XpmColor *xpm_color;
427 #ifndef XPM_NO_X
428 XColor xcolor;
429 #else
430 GdkColor xcolor;
431 #endif
432
433 xpm_color = &(xpm_image->colorTable[i]);
434
435 /* pick the best spec available */
436 if (xpm_color->c_color)
437 colorspec = xpm_color->c_color;
438 else if (xpm_color->g_color)
439 colorspec = xpm_color->g_color;
440 else if (xpm_color->g4_color)
441 colorspec = xpm_color->g4_color;
442 else if (xpm_color->m_color)
443 colorspec = xpm_color->m_color;
444
445 /* parse if it's not transparent */
446 if (strcmp (colorspec, "None") != 0)
447 {
448 #ifndef XPM_NO_X
449 XParseColor (display, colormap, colorspec, &xcolor);
450 #else
451 gdk_color_parse (colorspec, &xcolor);
452 #endif
453 cmap[j++] = xcolor.red >> 8;
454 cmap[j++] = xcolor.green >> 8;
455 cmap[j++] = xcolor.blue >> 8;
456 cmap[j++] = ~0;
457 }
458 else
459 {
460 j += 4;
461 }
462 }
463 #ifndef XPM_NO_X
464 XCloseDisplay (display);
465 #endif
466 return cmap;
467 }
468
469 static void
parse_image(gint32 image_ID,XpmImage * xpm_image,guchar * cmap)470 parse_image (gint32 image_ID,
471 XpmImage *xpm_image,
472 guchar *cmap)
473 {
474 GeglBuffer *buffer;
475 gint tile_height;
476 gint scanlines;
477 gint val;
478 guchar *buf;
479 guchar *dest;
480 guint *src;
481 gint32 layer_ID;
482 gint i;
483
484 layer_ID = gimp_layer_new (image_ID,
485 _("Color"),
486 xpm_image->width,
487 xpm_image->height,
488 GIMP_RGBA_IMAGE,
489 100,
490 gimp_image_get_default_new_layer_mode (image_ID));
491
492 gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
493
494 buffer = gimp_drawable_get_buffer (layer_ID);
495
496 tile_height = gimp_tile_height ();
497
498 buf = g_new (guchar, tile_height * xpm_image->width * 4);
499
500 src = xpm_image->data;
501 for (i = 0; i < xpm_image->height; i += tile_height)
502 {
503 gint j;
504
505 dest = buf;
506 scanlines = MIN (tile_height, xpm_image->height - i);
507 j = scanlines * xpm_image->width;
508 while (j--)
509 {
510 {
511 val = *(src++) * 4;
512 *(dest) = cmap[val];
513 *(dest+1) = cmap[val+1];
514 *(dest+2) = cmap[val+2];
515 *(dest+3) = cmap[val+3];
516 dest += 4;
517 }
518
519 if ((j % 100) == 0)
520 gimp_progress_update ((double) i / (double) xpm_image->height);
521 }
522
523 gegl_buffer_set (buffer,
524 GEGL_RECTANGLE (0, i, xpm_image->width, scanlines), 0,
525 NULL, buf, GEGL_AUTO_ROWSTRIDE);
526 }
527
528 g_free (buf);
529 g_object_unref (buffer);
530
531 gimp_progress_update (1.0);
532 }
533
534 static guint
rgbhash(rgbkey * c)535 rgbhash (rgbkey *c)
536 {
537 return ((guint)c->r) ^ ((guint)c->g) ^ ((guint)c->b);
538 }
539
540 static guint
compare(rgbkey * c1,rgbkey * c2)541 compare (rgbkey *c1,
542 rgbkey *c2)
543 {
544 return (c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b);
545 }
546
547 static void
set_XpmImage(XpmColor * array,guint index,gchar * colorstring)548 set_XpmImage (XpmColor *array,
549 guint index,
550 gchar *colorstring)
551 {
552 gchar *p;
553 gint i, charnum, indtemp;
554
555 indtemp=index;
556 array[index].string = p = g_new (gchar, cpp+1);
557
558 /*convert the index number to base sizeof(linenoise)-1 */
559 for (i=0; i<cpp; ++i)
560 {
561 charnum = indtemp % (sizeof (linenoise) - 1);
562 indtemp = indtemp / (sizeof (linenoise) - 1);
563 *p++ = linenoise[charnum];
564 }
565 /* *p++=linenoise[indtemp]; */
566
567 *p = '\0'; /* C and its stupid null-terminated strings... */
568
569 array[index].symbolic = NULL;
570 array[index].m_color = NULL;
571 array[index].g4_color = NULL;
572
573 if (color)
574 {
575 array[index].g_color = NULL;
576 array[index].c_color = colorstring;
577 }
578 else
579 {
580 array[index].c_color = NULL;
581 array[index].g_color = colorstring;
582 }
583 }
584
585 static void
create_colormap_from_hash(gpointer gkey,gpointer value,gpointer user_data)586 create_colormap_from_hash (gpointer gkey,
587 gpointer value,
588 gpointer user_data)
589 {
590 rgbkey *key = gkey;
591 gchar *string = g_new (gchar, 8);
592
593 sprintf (string, "#%02X%02X%02X", (int)key->r, (int)key->g, (int)key->b);
594 set_XpmImage (user_data, *((int *) value), string);
595 }
596
597 static void
decrement_hash_values(gpointer gkey,gpointer value,gpointer user_data)598 decrement_hash_values (gpointer gkey,
599 gpointer value,
600 gpointer user_data)
601 {
602 --(*((guint*) value));
603 }
604
605 static gboolean
save_image(const gchar * filename,gint32 image_ID,gint32 drawable_ID,GError ** error)606 save_image (const gchar *filename,
607 gint32 image_ID,
608 gint32 drawable_ID,
609 GError **error)
610 {
611 GeglBuffer *buffer;
612 const Babl *format;
613 gint width;
614 gint height;
615 gint ncolors = 1;
616 gint *indexno;
617 gboolean indexed;
618 gboolean alpha;
619 gboolean alpha_used = FALSE;
620 XpmColor *colormap;
621 XpmImage *image;
622 guint *ibuff = NULL;
623 guchar *buf;
624 guchar *data;
625 GHashTable *hash = NULL;
626 gint i, j, k;
627 gint threshold = xpmvals.threshold;
628 gboolean success = FALSE;
629
630 buffer = gimp_drawable_get_buffer (drawable_ID);
631
632 width = gegl_buffer_get_width (buffer);
633 height = gegl_buffer_get_height (buffer);
634
635 alpha = gimp_drawable_has_alpha (drawable_ID);
636 color = !gimp_drawable_is_gray (drawable_ID);
637 indexed = gimp_drawable_is_indexed (drawable_ID);
638
639 switch (gimp_drawable_type (drawable_ID))
640 {
641 case GIMP_RGB_IMAGE:
642 format = babl_format ("R'G'B' u8");
643 break;
644
645 case GIMP_RGBA_IMAGE:
646 format = babl_format ("R'G'B'A u8");
647 break;
648
649 case GIMP_GRAY_IMAGE:
650 format = babl_format ("Y' u8");
651 break;
652
653 case GIMP_GRAYA_IMAGE:
654 format = babl_format ("Y'A u8");
655 break;
656
657 case GIMP_INDEXED_IMAGE:
658 case GIMP_INDEXEDA_IMAGE:
659 format = gegl_buffer_get_format (buffer);
660 break;
661
662 default:
663 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
664 _("Unsupported drawable type"));
665 g_object_unref (buffer);
666 return FALSE;
667 }
668
669 /* allocate buffer making the assumption that ibuff is 32 bit aligned... */
670 ibuff = g_new (guint, width * height);
671
672 hash = g_hash_table_new ((GHashFunc) rgbhash, (GCompareFunc) compare);
673
674 gimp_progress_init_printf (_("Exporting '%s'"),
675 gimp_filename_to_utf8 (filename));
676
677 ncolors = alpha ? 1 : 0;
678
679 /* allocate a pixel region to work with */
680 buf = g_new (guchar,
681 gimp_tile_height () * width *
682 babl_format_get_bytes_per_pixel (format));
683
684 /* process each row of tiles */
685 for (i = 0; i < height; i += gimp_tile_height ())
686 {
687 gint scanlines;
688
689 /* read the next row of tiles */
690 scanlines = MIN (gimp_tile_height(), height - i);
691
692 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scanlines), 1.0,
693 format, buf,
694 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
695
696 data = buf;
697
698 /* process each pixel row */
699 for (j = 0; j < scanlines; j++)
700 {
701 /* go to the start of this row in each image */
702 guint *idata = ibuff + (i+j) * width;
703
704 /* do each pixel in the row */
705 for (k = 0; k < width; k++)
706 {
707 rgbkey *key = g_new (rgbkey, 1);
708 guchar a;
709
710 /* get pixel data */
711 key->r = *(data++);
712 key->g = color && !indexed ? *(data++) : key->r;
713 key->b = color && !indexed ? *(data++) : key->r;
714 a = alpha ? *(data++) : 255;
715
716 if (a < threshold)
717 {
718 *(idata++) = 0;
719 alpha_used = TRUE;
720 }
721 else
722 {
723 if (indexed)
724 {
725 *(idata++) = (key->r) + (alpha ? 1 : 0);
726 }
727 else
728 {
729 indexno = g_hash_table_lookup (hash, key);
730 if (!indexno)
731 {
732 indexno = g_new (gint, 1);
733 *indexno = ncolors++;
734 g_hash_table_insert (hash, key, indexno);
735 key = g_new (rgbkey, 1);
736 }
737 *(idata++) = *indexno;
738 }
739 }
740 }
741
742 /* kick the progress bar */
743 gimp_progress_update ((gdouble) (i+j) / (gdouble) height);
744 }
745 }
746
747 g_free (buf);
748
749 /* remove alpha if not actually used */
750 if (alpha && !alpha_used)
751 {
752 gint i;
753 --ncolors;
754 for (i = 0; i < width * height; ++i)
755 --ibuff[i];
756
757 g_hash_table_foreach (hash, decrement_hash_values, NULL);
758 }
759
760 if (indexed)
761 {
762 guchar *cmap = gimp_image_get_colormap (image_ID, &ncolors);
763 guchar *c;
764
765 c = cmap;
766
767 if (alpha_used)
768 ncolors++;
769
770 colormap = g_new (XpmColor, ncolors);
771 cpp =
772 1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
773
774 if (alpha_used)
775 set_XpmImage (colormap, 0, "None");
776
777 for (i = alpha_used ? 1 : 0; i < ncolors; i++)
778 {
779 gchar *string;
780 guchar r, g, b;
781
782 r = *c++;
783 g = *c++;
784 b = *c++;
785
786 string = g_new (gchar, 8);
787 sprintf (string, "#%02X%02X%02X", (int)r, (int)g, (int)b);
788 set_XpmImage (colormap, i, string);
789 }
790
791 g_free (cmap);
792 }
793 else
794 {
795 colormap = g_new (XpmColor, ncolors);
796 cpp =
797 1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
798
799 if (alpha_used)
800 set_XpmImage (colormap, 0, "None");
801
802 g_hash_table_foreach (hash, create_colormap_from_hash, colormap);
803 }
804
805 image = g_new (XpmImage, 1);
806
807 image->width = width;
808 image->height = height;
809 image->ncolors = ncolors;
810 image->cpp = cpp;
811 image->colorTable = colormap;
812 image->data = ibuff;
813
814 /* do the save */
815 switch (XpmWriteFileFromXpmImage ((char *) filename, image, NULL))
816 {
817 case XpmSuccess:
818 success = TRUE;
819 break;
820
821 case XpmOpenFailed:
822 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
823 _("Error opening file '%s'"),
824 gimp_filename_to_utf8 (filename));
825 break;
826
827 case XpmFileInvalid:
828 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
829 "%s", _("XPM file invalid"));
830 break;
831
832 default:
833 break;
834 }
835
836 g_object_unref (buffer);
837 g_free (ibuff);
838
839 if (hash)
840 g_hash_table_destroy (hash);
841
842 gimp_progress_update (1.0);
843
844 return success;
845 }
846
847 static gboolean
save_dialog(void)848 save_dialog (void)
849 {
850 GtkWidget *dialog;
851 GtkWidget *table;
852 GtkObject *scale_data;
853 gboolean run;
854
855 dialog = gimp_export_dialog_new (_("XPM"), PLUG_IN_BINARY, SAVE_PROC);
856
857 table = gtk_table_new (1, 3, FALSE);
858 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
859 gtk_container_set_border_width (GTK_CONTAINER (table), 12);
860 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
861 table, TRUE, TRUE, 0);
862 gtk_widget_show (table);
863
864 scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
865 _("_Alpha threshold:"), SCALE_WIDTH, 0,
866 xpmvals.threshold, 0, 255, 1, 8, 0,
867 TRUE, 0, 0,
868 NULL, NULL);
869
870 g_signal_connect (scale_data, "value-changed",
871 G_CALLBACK (gimp_int_adjustment_update),
872 &xpmvals.threshold);
873
874 gtk_widget_show (dialog);
875
876 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
877
878 gtk_widget_destroy (dialog);
879
880 return run;
881 }
882