1 /* CSource - GIMP Plugin to dump image data in RGB(A) format for C source
2  * Copyright (C) 1999 Tim Janik
3  *
4  * This program is free software: you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (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  * This plugin is heavily based on the header plugin by Spencer Kimball and
18  * Peter Mattis.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 #include <errno.h>
25 
26 #include <glib/gstdio.h>
27 
28 #include <libgimp/gimp.h>
29 #include <libgimp/gimpui.h>
30 
31 #include "libgimp/stdplugins-intl.h"
32 
33 
34 #define SAVE_PROC      "file-csource-save"
35 #define PLUG_IN_BINARY "file-csource"
36 #define PLUG_IN_ROLE   "gimp-file-csource"
37 
38 
39 typedef struct
40 {
41   gchar    *prefixed_name;
42   gchar    *comment;
43   gboolean  use_comment;
44   gboolean  glib_types;
45   gboolean  alpha;
46   gboolean  rgb565;
47   gboolean  use_macros;
48   gboolean  use_rle;
49   gdouble   opacity;
50 } Config;
51 
52 
53 static void     query           (void);
54 static void     run             (const gchar      *name,
55                                  gint              nparams,
56                                  const GimpParam  *param,
57                                  gint             *nreturn_vals,
58                                  GimpParam       **return_vals);
59 
60 static gboolean save_image      (GFile            *file,
61                                  Config           *config,
62                                  gint32            image_ID,
63                                  gint32            drawable_ID,
64                                  GError          **error);
65 static gboolean run_save_dialog (Config           *config);
66 
67 
68 const GimpPlugInInfo PLUG_IN_INFO =
69 {
70   NULL,  /* init_proc  */
71   NULL,  /* quit_proc  */
72   query, /* query_proc */
73   run,   /* run_proc   */
74 };
75 
76 static Config config =
77 {
78   "gimp_image", /* prefixed_name */
79   NULL,         /* comment */
80   FALSE,        /* use_comment */
81   TRUE,         /* glib_types */
82   FALSE,        /* alpha */
83   FALSE,        /* rgb565 */
84   FALSE,        /* use_macros */
85   FALSE,        /* use_rle */
86   100.0,        /* opacity */
87 };
88 
89 
MAIN()90 MAIN ()
91 
92 
93 static void
94 query (void)
95 {
96   static const GimpParamDef save_args[] =
97   {
98     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0) }" },
99     { GIMP_PDB_IMAGE,    "image",        "Input image" },
100     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save" },
101     { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
102     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to save the image in" }
103   };
104 
105   gimp_install_procedure (SAVE_PROC,
106                           "Dump image data in RGB(A) format for C source",
107                           "CSource cannot be run non-interactively.",
108                           "Tim Janik",
109                           "Tim Janik",
110                           "1999",
111                           N_("C source code"),
112                           "*",
113                           GIMP_PLUGIN,
114                           G_N_ELEMENTS (save_args), 0,
115                           save_args, NULL);
116 
117   gimp_register_file_handler_mime (SAVE_PROC, "text/x-csrc");
118   gimp_register_file_handler_uri (SAVE_PROC);
119   gimp_register_save_handler (SAVE_PROC, "c", "");
120 }
121 
122 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)123 run (const gchar      *name,
124      gint              nparams,
125      const GimpParam  *param,
126      gint             *nreturn_vals,
127      GimpParam       **return_vals)
128 {
129   static GimpParam   values[2];
130   GimpRunMode        run_mode;
131   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
132   GimpExportReturn   export = GIMP_EXPORT_CANCEL;
133   GError            *error  = NULL;
134 
135   INIT_I18N ();
136   gegl_init (NULL, NULL);
137 
138   run_mode = param[0].data.d_int32;
139 
140   *nreturn_vals = 1;
141   *return_vals  = values;
142 
143   values[0].type          = GIMP_PDB_STATUS;
144   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
145 
146   if (run_mode == GIMP_RUN_INTERACTIVE &&
147       strcmp (name, SAVE_PROC) == 0)
148     {
149       gint32         image_ID    = param[1].data.d_int32;
150       gint32         drawable_ID = param[2].data.d_int32;
151       GimpParasite  *parasite;
152       gchar         *x;
153 
154       gimp_get_data (SAVE_PROC, &config);
155 
156       config.prefixed_name = "gimp_image";
157       config.comment       = NULL;
158       config.alpha         = gimp_drawable_has_alpha (drawable_ID);
159 
160       parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
161       if (parasite)
162         {
163           config.comment = g_strndup (gimp_parasite_data (parasite),
164                                       gimp_parasite_data_size (parasite));
165           gimp_parasite_free (parasite);
166         }
167       x = config.comment;
168 
169       gimp_ui_init (PLUG_IN_BINARY, FALSE);
170 
171       export = gimp_export_image (&image_ID, &drawable_ID, "C Source",
172                                   GIMP_EXPORT_CAN_HANDLE_RGB |
173                                   GIMP_EXPORT_CAN_HANDLE_ALPHA);
174 
175       if (export == GIMP_EXPORT_CANCEL)
176         {
177           values[0].data.d_status = GIMP_PDB_CANCEL;
178           return;
179         }
180 
181       if (run_save_dialog (&config))
182         {
183           if (x != config.comment &&
184               !(x && config.comment && strcmp (x, config.comment) == 0))
185             {
186               if (!config.comment || !config.comment[0])
187                 {
188                   gimp_image_detach_parasite (image_ID, "gimp-comment");
189                 }
190               else
191                 {
192                   parasite = gimp_parasite_new ("gimp-comment",
193                                                 GIMP_PARASITE_PERSISTENT,
194                                                 strlen (config.comment) + 1,
195                                                 config.comment);
196                   gimp_image_attach_parasite (image_ID, parasite);
197                   gimp_parasite_free (parasite);
198                 }
199             }
200 
201           if (! save_image (g_file_new_for_uri (param[3].data.d_string),
202                             &config, image_ID, drawable_ID, &error))
203             {
204               status = GIMP_PDB_EXECUTION_ERROR;
205 
206               if (error)
207                 {
208                   *nreturn_vals = 2;
209                   values[1].type          = GIMP_PDB_STRING;
210                   values[1].data.d_string = error->message;
211                 }
212             }
213           else
214             {
215               gimp_set_data (SAVE_PROC, &config, sizeof (config));
216             }
217         }
218       else
219         {
220           status = GIMP_PDB_CANCEL;
221         }
222 
223       if (export == GIMP_EXPORT_EXPORT)
224         gimp_image_delete (image_ID);
225     }
226   else
227     {
228       status = GIMP_PDB_CALLING_ERROR;
229     }
230 
231   values[0].data.d_status = status;
232 }
233 
234 static gboolean
diff2_rgb565(guint8 * ip)235 diff2_rgb565 (guint8 *ip)
236 {
237   return ip[0] != ip[2] || ip[1] != ip[3];
238 }
239 
240 static gboolean
diff2_rgb(guint8 * ip)241 diff2_rgb (guint8 *ip)
242 {
243   return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
244 }
245 
246 static gboolean
diff2_rgba(guint8 * ip)247 diff2_rgba (guint8 *ip)
248 {
249   return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
250 }
251 
252 static guint8 *
rl_encode_rgbx(guint8 * bp,guint8 * ip,guint8 * limit,guint bpp)253 rl_encode_rgbx (guint8 *bp,
254                 guint8 *ip,
255                 guint8 *limit,
256                 guint   bpp)
257 {
258   gboolean (*diff2_pix) (guint8 *);
259   guint8 *ilimit = limit - bpp;
260 
261   switch (bpp)
262     {
263     case 2: diff2_pix = diff2_rgb565; break;
264     case 3: diff2_pix = diff2_rgb; break;
265     case 4: diff2_pix = diff2_rgba; break;
266     default: g_assert_not_reached ();
267     }
268 
269   while (ip < limit)
270     {
271       g_assert (ip < ilimit); /* paranoid */
272 
273       if (diff2_pix (ip))
274         {
275           guint8 *s_ip = ip;
276           guint l = 1;
277 
278           ip += bpp;
279           while (l < 127 && ip < ilimit && diff2_pix (ip))
280             { ip += bpp; l += 1; }
281           if (ip == ilimit && l < 127)
282             { ip += bpp; l += 1; }
283           *(bp++) = l;
284           memcpy (bp, s_ip, l * bpp);
285           bp += l * bpp;
286         }
287       else
288         {
289           guint l = 2;
290 
291           ip += bpp;
292           while (l < 127 && ip < ilimit && !diff2_pix (ip))
293             { ip += bpp; l += 1; }
294           *(bp++) = l | 128;
295           memcpy (bp, ip, bpp);
296           ip += bpp;
297           bp += bpp;
298         }
299       if (ip == ilimit)
300         {
301           *(bp++) = 1;
302           memcpy (bp, ip, bpp);
303           ip += bpp;
304           bp += bpp;
305         }
306     }
307 
308   return bp;
309 }
310 
311 static gboolean print (GOutputStream  *stream,
312                        GError        **error,
313                        const gchar    *format,
314                        ...) G_GNUC_PRINTF (3, 4);
315 
316 static gboolean
print(GOutputStream * stream,GError ** error,const gchar * format,...)317 print (GOutputStream  *stream,
318        GError        **error,
319        const gchar    *format,
320        ...)
321 {
322   va_list  args;
323   gboolean success;
324 
325   va_start (args, format);
326   success = g_output_stream_vprintf (stream, NULL, NULL,
327                                      error, format, args);
328   va_end (args);
329 
330   return success;
331 }
332 
333 static inline gboolean
save_rle_decoder(GOutputStream * output,const gchar * macro_name,const gchar * s_uint,const gchar * s_uint_8,guint bpp,GError ** error)334 save_rle_decoder (GOutputStream  *output,
335                   const gchar    *macro_name,
336                   const gchar    *s_uint,
337                   const gchar    *s_uint_8,
338                   guint           bpp,
339                   GError        **error)
340 {
341   return
342     print (output, error,
343            "#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
344            macro_name) &&
345     print (output, error,
346            "{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n",
347            s_uint, s_uint_8, s_uint_8) &&
348     print (output, error,
349            "  __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n"
350            "  __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n"
351            "    while (__ip < __il) { %s __l = *(__rd++); \\\n",
352            s_uint) &&
353     print (output, error,
354            "      if (__l & 128) { __l = __l - 128; \\\n"
355            "        do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n"
356            "      } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n"
357            "               __ip += __l; __rd += __l; } } \\\n"
358            "  } else if (__bpp == 3) { /* RGB */ \\\n"
359            "    while (__ip < __il) { %s __l = *(__rd++); \\\n",
360            s_uint) &&
361     print (output, error,
362            "      if (__l & 128) { __l = __l - 128; \\\n"
363            "        do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n"
364            "      } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n"
365            "               __ip += __l; __rd += __l; } } \\\n"
366            "  } else { /* RGB16 */ \\\n"
367            "    while (__ip < __il) { %s __l = *(__rd++); \\\n",
368            s_uint) &&
369     print (output, error,
370            "      if (__l & 128) { __l = __l - 128; \\\n"
371            "        do { memcpy (__ip, __rd, 2); __ip += 2; } while (--__l); __rd += 2; \\\n"
372            "      } else { __l *= 2; memcpy (__ip, __rd, __l); \\\n"
373            "               __ip += __l; __rd += __l; } } \\\n"
374            "  } } while (0)\n");
375 }
376 
377 static inline gboolean
save_uchar(GOutputStream * output,guint * c,guint8 d,Config * config,GError ** error)378 save_uchar (GOutputStream  *output,
379             guint          *c,
380             guint8          d,
381             Config         *config,
382             GError        **error)
383 {
384   static guint8 pad = 0;
385 
386   if (*c > 74)
387     {
388       if (! config->use_macros)
389         {
390           if (! print (output, error, "\"\n  \""))
391             return FALSE;
392 
393           *c = 3;
394         }
395       else
396         {
397           if (! print (output, error, "\"\n \""))
398             return FALSE;
399 
400           *c = 2;
401         }
402     }
403 
404   if (d < 33 || (d >= 48 && d <= 57)  || d > 126)
405     {
406       if (! print (output, error, "\\%03o", d))
407         return FALSE;
408 
409       *c += 1 + 1 + (d > 7) + (d > 63);
410       pad = d < 64;
411 
412       return TRUE;
413     }
414 
415   if (d == '\\')
416     {
417       if (! print (output, error, "\\\\"))
418         return FALSE;
419 
420       *c += 2;
421     }
422   else if (d == '"')
423     {
424       if (! print (output, error, "\\\""))
425         return FALSE;
426 
427       *c += 2;
428     }
429   else if (pad && d >= '0' && d <= '9')
430     {
431       if (! print (output, error, "\"\"%c", d))
432         return FALSE;
433 
434       *c += 3;
435     }
436   else
437     {
438       if (! print (output, error, "%c", d))
439         return FALSE;
440 
441       *c += 1;
442     }
443 
444   pad = 0;
445 
446   return TRUE;
447 }
448 
449 static gboolean
save_image(GFile * file,Config * config,gint32 image_ID,gint32 drawable_ID,GError ** error)450 save_image (GFile   *file,
451             Config  *config,
452             gint32   image_ID,
453             gint32   drawable_ID,
454             GError **error)
455 {
456   GOutputStream *output;
457   GeglBuffer    *buffer;
458   GCancellable  *cancellable;
459   GimpImageType  drawable_type = gimp_drawable_type (drawable_ID);
460   gchar         *s_uint_8, *s_uint, *s_char, *s_null;
461   guint          c;
462   gchar         *macro_name;
463   guint8        *img_buffer, *img_buffer_end;
464   gchar         *basename;
465   guint8        *data, *p;
466   gint           width;
467   gint           height;
468   gint           x, y, pad, n_bytes, bpp;
469   const Babl    *drawable_format;
470   gint           drawable_bpp;
471 
472   output = G_OUTPUT_STREAM (g_file_replace (file,
473                                             NULL, FALSE, G_FILE_CREATE_NONE,
474                                             NULL, error));
475   if (output)
476     {
477       GOutputStream *buffered;
478 
479       buffered = g_buffered_output_stream_new (output);
480       g_object_unref (output);
481 
482       output = buffered;
483     }
484   else
485     {
486       return FALSE;
487     }
488 
489   buffer = gimp_drawable_get_buffer (drawable_ID);
490 
491   width  = gegl_buffer_get_width  (buffer);
492   height = gegl_buffer_get_height (buffer);
493 
494   if (gimp_drawable_has_alpha (drawable_ID))
495     drawable_format = babl_format ("R'G'B'A u8");
496   else
497     drawable_format = babl_format ("R'G'B' u8");
498 
499   drawable_bpp = babl_format_get_bytes_per_pixel (drawable_format);
500 
501   bpp = config->rgb565 ? 2 : (config->alpha ? 4 : 3);
502   n_bytes = width * height * bpp;
503   pad = width * drawable_bpp;
504   if (config->use_rle)
505     pad = MAX (pad, 130 + n_bytes / 127);
506 
507   data = g_new (guint8, pad + n_bytes);
508   p = data + pad;
509 
510   for (y = 0; y < height; y++)
511     {
512       gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
513                        drawable_format, data,
514                        GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
515 
516       if (bpp == 2)
517         {
518           for (x = 0; x < width; x++)
519             {
520               guint8 *d = data + x * drawable_bpp;
521               guint8 r, g, b;
522               gushort rgb16;
523               gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
524 
525               alpha *= config->opacity / 25500.0;
526               r = (0.5 + alpha * (gdouble) d[0]);
527               g = (0.5 + alpha * (gdouble) d[1]);
528               b = (0.5 + alpha * (gdouble) d[2]);
529               r >>= 3;
530               g >>= 2;
531               b >>= 3;
532               rgb16 = (r << 11) + (g << 5) + b;
533               *(p++) = (guchar) rgb16;
534               *(p++) = (guchar) (rgb16 >> 8);
535             }
536         }
537       else if (config->alpha)
538         {
539           for (x = 0; x < width; x++)
540             {
541               guint8 *d = data + x * drawable_bpp;
542               gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
543 
544               alpha *= config->opacity / 100.0;
545               *(p++) = d[0];
546               *(p++) = d[1];
547               *(p++) = d[2];
548               *(p++) = alpha + 0.5;
549             }
550         }
551       else
552         {
553           for (x = 0; x < width; x++)
554             {
555               guint8 *d = data + x * drawable_bpp;
556               gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
557 
558               alpha *= config->opacity / 25500.0;
559               *(p++) = 0.5 + alpha * (gdouble) d[0];
560               *(p++) = 0.5 + alpha * (gdouble) d[1];
561               *(p++) = 0.5 + alpha * (gdouble) d[2];
562             }
563         }
564     }
565 
566   img_buffer = data + pad;
567   if (config->use_rle)
568     {
569       img_buffer_end = rl_encode_rgbx (data, img_buffer,
570                                        img_buffer + n_bytes, bpp);
571       img_buffer = data;
572     }
573   else
574     {
575       img_buffer_end = img_buffer + n_bytes;
576     }
577 
578   if (!config->use_macros && config->glib_types)
579     {
580       s_uint_8 =  "guint8 ";
581       s_uint  =   "guint  ";
582       s_char =    "gchar  ";
583       s_null =    "NULL";
584     }
585   else if (!config->use_macros)
586     {
587       s_uint_8 =  "unsigned char";
588       s_uint =    "unsigned int ";
589       s_char =    "char         ";
590       s_null =    "(char*) 0";
591     }
592   else if (config->use_macros && config->glib_types)
593     {
594       s_uint_8 =  "guint8";
595       s_uint  =   "guint";
596       s_char =    "gchar";
597       s_null =    "NULL";
598     }
599   else /* config->use_macros && !config->glib_types */
600     {
601       s_uint_8 =  "unsigned char";
602       s_uint =    "unsigned int";
603       s_char =    "char";
604       s_null =    "(char*) 0";
605     }
606 
607   macro_name = g_ascii_strup (config->prefixed_name, -1);
608 
609   basename = g_file_get_basename (file);
610 
611   if (! print (output, error,
612                "/* GIMP %s C-Source image dump %s(%s) */\n\n",
613                config->alpha ? "RGBA" : "RGB",
614                config->use_rle ? "1-byte-run-length-encoded " : "",
615                basename))
616     goto fail;
617 
618   g_free (basename);
619 
620   if (config->use_rle && !config->use_macros)
621     {
622       if (! save_rle_decoder (output,
623                               macro_name,
624                               config->glib_types ? "guint" : "unsigned int",
625                               config->glib_types ? "guint8" : "unsigned char",
626                               bpp,
627                               error))
628         goto fail;
629     }
630 
631   if (!config->use_macros)
632     {
633       if (! print (output, error,
634                    "static const struct {\n"
635                    "  %s\t width;\n"
636                    "  %s\t height;\n"
637                    "  %s\t bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ \n",
638                    s_uint, s_uint, s_uint))
639         goto fail;
640 
641       if (config->use_comment)
642         {
643           if (! print (output, error, "  %s\t*comment;\n", s_char))
644             goto fail;
645         }
646 
647       if (! print (output, error,
648                    "  %s\t %spixel_data[",
649                    s_uint_8,
650                    config->use_rle ? "rle_" : ""))
651         goto fail;
652 
653       if (config->use_rle)
654         {
655           if (! print (output, error,
656                        "%u + 1];\n",
657                        (guint) (img_buffer_end - img_buffer)))
658             goto fail;
659         }
660       else
661         {
662           if (! print (output, error,
663                        "%u * %u * %u + 1];\n",
664                        width,
665                        height,
666                        bpp))
667             goto fail;
668         }
669 
670       if (! print (output, error, "} %s = {\n", config->prefixed_name))
671         goto fail;
672 
673       if (! print (output, error,
674                    "  %u, %u, %u,\n",
675                    width,
676                    height,
677                    bpp))
678         goto fail;
679     }
680   else /* use macros */
681     {
682       if (! print (output, error,
683                    "#define %s_WIDTH (%u)\n"
684                    "#define %s_HEIGHT (%u)\n"
685                    "#define %s_BYTES_PER_PIXEL (%u) /* 2:RGB16, 3:RGB, 4:RGBA */\n",
686                    macro_name, width,
687                    macro_name, height,
688                    macro_name, bpp))
689         {
690           goto fail;
691         }
692     }
693 
694   if (config->use_comment && !config->comment)
695     {
696       if (! config->use_macros)
697         {
698           if (! print (output, error, "  %s,\n", s_null))
699             goto fail;
700         }
701       else
702         {
703           if (! print (output, error,
704                        "#define %s_COMMENT (%s)\n",
705                        macro_name, s_null))
706             goto fail;
707         }
708     }
709   else if (config->use_comment)
710     {
711       gchar *p = config->comment - 1;
712 
713       if (config->use_macros)
714         {
715           if (! print (output, error, "#define %s_COMMENT \\\n", macro_name))
716             goto fail;
717         }
718 
719       if (! print (output, error, "  \""))
720         goto fail;
721 
722       while (*(++p))
723         {
724           gboolean success = FALSE;
725 
726           if (*p == '\\')
727             success = print (output, error, "\\\\");
728           else if (*p == '"')
729             success = print (output, error, "\\\"");
730           else if (*p == '\n' && p[1])
731             success = print (output, error,
732                              "\\n\"%s\n  \"",
733                              config->use_macros ? " \\" : "");
734           else if (*p == '\n')
735             success = print (output, error, "\\n");
736           else if (*p == '\r')
737             success = print (output, error, "\\r");
738           else if (*p == '\b')
739             success = print (output, error, "\\b");
740           else if (*p == '\f')
741             success = print (output, error, "\\f");
742           else if (( *p >= 32 && *p <= 47 ) || (*p >= 58 && *p <= 126))
743             success = print (output, error, "%c", *p);
744           else
745             success = print (output, error, "\\%03o", *p);
746 
747           if (! success)
748             goto fail;
749         }
750 
751       if (! config->use_macros)
752         {
753           if (! print (output, error, "\",\n"))
754             goto fail;
755         }
756       else
757         {
758           if (! print (output, error, "\"\n"))
759             goto fail;
760         }
761     }
762 
763   if (config->use_macros)
764     {
765       if (! print (output, error,
766                    "#define %s_%sPIXEL_DATA ((%s*) %s_%spixel_data)\n",
767                    macro_name,
768                    config->use_rle ? "RLE_" : "",
769                    s_uint_8,
770                    macro_name,
771                    config->use_rle ? "rle_" : ""))
772         goto fail;
773 
774       if (config->use_rle)
775         {
776           if (! save_rle_decoder (output,
777                                   macro_name,
778                                   s_uint,
779                                   s_uint_8,
780                                   bpp,
781                                   error))
782             goto fail;
783         }
784 
785       if (! print (output, error,
786                    "static const %s %s_%spixel_data[",
787                    s_uint_8,
788                    macro_name,
789                    config->use_rle ? "rle_" : ""))
790         goto fail;
791 
792       if (config->use_rle)
793         {
794           if (! print (output, error,
795                        "%u + 1] =\n",
796                        (guint) (img_buffer_end - img_buffer)))
797             goto fail;
798         }
799       else
800         {
801           if (! print (output, error,
802                        "%u * %u * %u + 1] =\n",
803                        width,
804                        height,
805                        bpp))
806             goto fail;
807         }
808 
809       if (! print (output, error, "(\""))
810         goto fail;
811 
812       c = 2;
813     }
814   else
815     {
816       if (! print (output, error, "  \""))
817         goto fail;
818 
819       c = 3;
820     }
821 
822   switch (drawable_type)
823     {
824     case GIMP_RGB_IMAGE:
825     case GIMP_RGBA_IMAGE:
826       do
827         {
828           if (! save_uchar (output, &c, *(img_buffer++), config, error))
829             goto fail;
830         }
831       while (img_buffer < img_buffer_end);
832       break;
833 
834     default:
835       g_warning ("unhandled drawable type (%d)", drawable_type);
836       goto fail;
837     }
838 
839   if (! config->use_macros)
840     {
841       if (! print (output, error, "\",\n};\n\n"))
842         goto fail;
843     }
844   else
845     {
846       if (! print (output, error, "\");\n\n"))
847         goto fail;
848     }
849 
850   if (! g_output_stream_close (output, NULL, error))
851     goto fail;
852 
853   g_object_unref (output);
854   g_object_unref (buffer);
855 
856   return TRUE;
857 
858  fail:
859 
860   cancellable = g_cancellable_new ();
861   g_cancellable_cancel (cancellable);
862   g_output_stream_close (output, cancellable, NULL);
863   g_object_unref (cancellable);
864 
865   g_object_unref (output);
866   g_object_unref (buffer);
867 
868   return FALSE;
869 }
870 
871 static void
rgb565_toggle_button_update(GtkWidget * toggle,gpointer data)872 rgb565_toggle_button_update (GtkWidget *toggle,
873                              gpointer   data)
874 {
875   GtkWidget *widget;
876   gboolean   active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle));
877 
878   gimp_toggle_button_update (toggle, data);
879 
880   widget = g_object_get_data (G_OBJECT (toggle), "set-insensitive-1");
881   if (widget)
882     gtk_widget_set_sensitive (widget, ! active);
883 }
884 
885 static gboolean
run_save_dialog(Config * config)886 run_save_dialog (Config *config)
887 {
888   GtkWidget *dialog;
889   GtkWidget *vbox;
890   GtkWidget *table;
891   GtkWidget *prefixed_name;
892   GtkWidget *centry;
893   GtkWidget *toggle;
894   GtkWidget *alpha_toggle;
895   GtkObject *adj;
896   gboolean   run;
897 
898   dialog = gimp_export_dialog_new (_("C-Source"), PLUG_IN_BINARY, SAVE_PROC);
899 
900   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
901   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
902   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
903                       vbox, TRUE, TRUE, 0);
904   gtk_widget_show (vbox);
905 
906   table = gtk_table_new (2, 2, FALSE);
907   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
908   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
909   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
910   gtk_widget_show (table);
911 
912   /* Prefixed Name
913    */
914   prefixed_name = gtk_entry_new ();
915   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
916                              _("_Prefixed name:"), 0.0, 0.5,
917                              prefixed_name, 1, FALSE);
918   gtk_entry_set_text (GTK_ENTRY (prefixed_name),
919                       config->prefixed_name ? config->prefixed_name : "");
920 
921   /* Comment Entry
922    */
923   centry = gtk_entry_new ();
924   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
925                              _("Co_mment:"), 0.0, 0.5,
926                              centry, 1, FALSE);
927   gtk_entry_set_text (GTK_ENTRY (centry),
928                       config->comment ? config->comment : "");
929 
930   /* Use Comment
931    */
932   toggle = gtk_check_button_new_with_mnemonic (_("_Save comment to file"));
933   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
934   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
935                                 config->use_comment);
936   gtk_widget_show (toggle);
937 
938   g_signal_connect (toggle, "toggled",
939                     G_CALLBACK (gimp_toggle_button_update),
940                     &config->use_comment);
941 
942   /* GLib types
943    */
944   toggle = gtk_check_button_new_with_mnemonic (_("_Use GLib types (guint8*)"));
945   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
946   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
947                                 config->glib_types);
948   gtk_widget_show (toggle);
949 
950   g_signal_connect (toggle, "toggled",
951                     G_CALLBACK (gimp_toggle_button_update),
952                     &config->glib_types);
953 
954   /* Use Macros
955    */
956   toggle =
957     gtk_check_button_new_with_mnemonic (_("Us_e macros instead of struct"));
958   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
959   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
960                                 config->use_macros);
961   gtk_widget_show (toggle);
962 
963   g_signal_connect (toggle, "toggled",
964                     G_CALLBACK (gimp_toggle_button_update),
965                     &config->use_macros);
966 
967   /* Use RLE
968    */
969   toggle =
970     gtk_check_button_new_with_mnemonic (_("Use _1 byte Run-Length-Encoding"));
971   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
972   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
973                                 config->use_rle);
974   gtk_widget_show (toggle);
975 
976   g_signal_connect (toggle, "toggled",
977                     G_CALLBACK (gimp_toggle_button_update),
978                     &config->use_rle);
979 
980   /* Alpha
981    */
982   alpha_toggle = toggle =
983     gtk_check_button_new_with_mnemonic (_("Sa_ve alpha channel (RGBA/RGB)"));
984   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
985   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
986                                 config->alpha);
987   gtk_widget_show (toggle);
988 
989   g_signal_connect (toggle, "toggled",
990                     G_CALLBACK (gimp_toggle_button_update),
991                     &config->alpha);
992 
993   /* RGB-565
994    */
995   toggle = gtk_check_button_new_with_mnemonic (_("Save as _RGB565 (16-bit)"));
996   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
997 
998   /* Alpha setting is not used with RGB-565 */
999   g_object_set_data (G_OBJECT (toggle), "set-insensitive-1", alpha_toggle);
1000 
1001   g_signal_connect (toggle, "toggled",
1002                     G_CALLBACK (rgb565_toggle_button_update),
1003                     &config->rgb565);
1004   gtk_widget_show (toggle);
1005 
1006   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1007                                 config->rgb565);
1008 
1009   /* Max Alpha Value
1010    */
1011   table = gtk_table_new (1, 3, FALSE);
1012   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
1013   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1014   gtk_widget_show (table);
1015 
1016   adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
1017                               _("Op_acity:"), 100, 0,
1018                               config->opacity, 0, 100, 1, 10, 1,
1019                               TRUE, 0, 0,
1020                               NULL, NULL);
1021   g_signal_connect (adj, "value-changed",
1022                     G_CALLBACK (gimp_double_adjustment_update),
1023                     &config->opacity);
1024 
1025   gtk_widget_show (dialog);
1026 
1027   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1028 
1029   if (run)
1030     {
1031       config->prefixed_name =
1032         g_strdup (gtk_entry_get_text (GTK_ENTRY (prefixed_name)));
1033       config->comment = g_strdup (gtk_entry_get_text (GTK_ENTRY (centry)));
1034     }
1035 
1036   gtk_widget_destroy (dialog);
1037 
1038   if (!config->prefixed_name || !config->prefixed_name[0])
1039     config->prefixed_name = "tmp";
1040 
1041   if (config->comment && !config->comment[0])
1042     config->comment = NULL;
1043 
1044   return run;
1045 }
1046