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 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <libgimp/gimp.h>
23 #include <libgimp/gimpui.h>
24 
25 #include "libgimp/stdplugins-intl.h"
26 
27 
28 #define SAVE_PROC      "file-header-save"
29 #define PLUG_IN_BINARY "file-header"
30 #define PLUG_IN_ROLE   "gimp-file-header"
31 
32 
33 /* Declare some local functions.
34  */
35 static void       query         (void);
36 static void       run           (const gchar      *name,
37                                  gint              nparams,
38                                  const GimpParam  *param,
39                                  gint             *nreturn_vals,
40                                  GimpParam       **return_vals);
41 
42 static gboolean   save_image    (GFile            *file,
43                                  gint32            image_ID,
44                                  gint32            drawable_ID,
45                                  GError          **error);
46 
47 static gboolean   print         (GOutputStream    *output,
48                                  GError          **error,
49                                  const gchar      *format,
50                                  ...) G_GNUC_PRINTF (3, 4);
51 
52 
53 const GimpPlugInInfo PLUG_IN_INFO =
54 {
55   NULL,  /* init_proc  */
56   NULL,  /* quit_proc  */
57   query, /* query_proc */
58   run,   /* run_proc   */
59 };
60 
61 
MAIN()62 MAIN ()
63 
64 static void
65 query (void)
66 {
67   static const GimpParamDef save_args[] =
68   {
69     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
70     { GIMP_PDB_IMAGE,    "image",        "Input image" },
71     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save" },
72     { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
73     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to save the image in" }
74   };
75 
76   gimp_install_procedure (SAVE_PROC,
77                           "saves files as C unsigned character array",
78                           "FIXME: write help",
79                           "Spencer Kimball & Peter Mattis",
80                           "Spencer Kimball & Peter Mattis",
81                           "1997",
82                           N_("C source code header"),
83                           "INDEXED, RGB",
84                           GIMP_PLUGIN,
85                           G_N_ELEMENTS (save_args), 0,
86                           save_args, NULL);
87 
88   gimp_register_file_handler_mime (SAVE_PROC, "text/x-chdr");
89   gimp_register_file_handler_uri (SAVE_PROC);
90   gimp_register_save_handler (SAVE_PROC, "h", "");
91 }
92 
93 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)94 run (const gchar      *name,
95      gint              nparams,
96      const GimpParam  *param,
97      gint             *nreturn_vals,
98      GimpParam       **return_vals)
99 {
100   static GimpParam   values[2];
101   GimpRunMode        run_mode;
102   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
103   GError            *error  = NULL;
104 
105   INIT_I18N ();
106   gegl_init (NULL, NULL);
107 
108   run_mode = param[0].data.d_int32;
109 
110   *nreturn_vals = 1;
111   *return_vals  = values;
112   values[0].type          = GIMP_PDB_STATUS;
113   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
114 
115   if (strcmp (name, SAVE_PROC) == 0)
116     {
117       gint32           image_ID;
118       gint32           drawable_ID;
119       GimpExportReturn export = GIMP_EXPORT_CANCEL;
120 
121       image_ID    = param[1].data.d_int32;
122       drawable_ID = param[2].data.d_int32;
123 
124       /*  eventually export the image */
125       switch (run_mode)
126         {
127         case GIMP_RUN_INTERACTIVE:
128         case GIMP_RUN_WITH_LAST_VALS:
129           gimp_ui_init (PLUG_IN_BINARY, FALSE);
130 
131           export = gimp_export_image (&image_ID, &drawable_ID, "Header",
132                                       GIMP_EXPORT_CAN_HANDLE_RGB |
133                                       GIMP_EXPORT_CAN_HANDLE_INDEXED);
134 
135           if (export == GIMP_EXPORT_CANCEL)
136             {
137               values[0].data.d_status = GIMP_PDB_CANCEL;
138               return;
139             }
140           break;
141 
142         default:
143           break;
144         }
145 
146       if (! save_image (g_file_new_for_uri (param[3].data.d_string),
147                         image_ID, drawable_ID, &error))
148         {
149           status = GIMP_PDB_EXECUTION_ERROR;
150         }
151 
152       if (export == GIMP_EXPORT_EXPORT)
153         gimp_image_delete (image_ID);
154     }
155   else
156     {
157       status = GIMP_PDB_CALLING_ERROR;
158     }
159 
160   if (status != GIMP_PDB_SUCCESS && error)
161     {
162       *nreturn_vals = 2;
163       values[1].type          = GIMP_PDB_STRING;
164       values[1].data.d_string = error->message;
165     }
166 
167   values[0].data.d_status = status;
168 }
169 
170 static gboolean
save_image(GFile * file,gint32 image_ID,gint32 drawable_ID,GError ** error)171 save_image (GFile   *file,
172             gint32   image_ID,
173             gint32   drawable_ID,
174             GError **error)
175 {
176   GeglBuffer    *buffer;
177   const Babl    *format;
178   GimpImageType  drawable_type;
179   GOutputStream *output;
180   gint           x, y, b, c;
181   const gchar   *backslash = "\\\\";
182   const gchar   *quote     = "\\\"";
183   const gchar   *newline   = "\"\n\t\"";
184   gchar          buf[4];
185   guchar        *d         = NULL;
186   guchar        *data      = NULL;
187   guchar        *cmap;
188   GCancellable  *cancellable;
189   gint           colors;
190   gint           width;
191   gint           height;
192 
193   output = G_OUTPUT_STREAM (g_file_replace (file,
194                                             NULL, FALSE, G_FILE_CREATE_NONE,
195                                             NULL, error));
196   if (output)
197     {
198       GOutputStream *buffered;
199 
200       buffered = g_buffered_output_stream_new (output);
201       g_object_unref (output);
202 
203       output = buffered;
204     }
205   else
206     {
207       return FALSE;
208     }
209 
210   buffer = gimp_drawable_get_buffer (drawable_ID);
211 
212   width  = gegl_buffer_get_width  (buffer);
213   height = gegl_buffer_get_height (buffer);
214 
215   drawable_type = gimp_drawable_type (drawable_ID);
216 
217   if (! print (output, error,
218                "/*  GIMP header image file format (%s): %s  */\n\n",
219                GIMP_RGB_IMAGE == drawable_type ? "RGB" : "INDEXED",
220                gimp_file_get_utf8_name (file)) ||
221       ! print (output, error,
222                "static unsigned int width = %d;\n", width) ||
223       ! print (output, error,
224                "static unsigned int height = %d;\n\n", height) ||
225       ! print (output, error,
226                "/*  Call this macro repeatedly.  After each use, the pixel data can be extracted  */\n\n"))
227     {
228       goto fail;
229     }
230 
231   switch (drawable_type)
232     {
233     case GIMP_RGB_IMAGE:
234       if (! print (output, error,
235                    "#define HEADER_PIXEL(data,pixel) {\\\n"
236                    "pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \\\n"
237                    "pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \\\n"
238                    "pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \\\n"
239                    "data += 4; \\\n}\n") ||
240           ! print (output, error,
241                    "static char *header_data =\n\t\""))
242         {
243           goto fail;
244         }
245 
246       format = babl_format ("R'G'B' u8");
247 
248       data = g_new (guchar, width * babl_format_get_bytes_per_pixel (format));
249 
250       c = 0;
251       for (y = 0; y < height; y++)
252         {
253           gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
254                            format, data,
255                            GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
256 
257           for (x = 0; x < width; x++)
258             {
259               d = data + x * babl_format_get_bytes_per_pixel (format);
260 
261               buf[0] = ((d[0] >> 2) & 0x3F) + 33;
262               buf[1] = ((((d[0] & 0x3) << 4) | (d[1] >> 4)) & 0x3F) + 33;
263               buf[2] = ((((d[1] & 0xF) << 2) | (d[2] >> 6)) & 0x3F) + 33;
264               buf[3] = (d[2] & 0x3F) + 33;
265 
266               for (b = 0; b < 4; b++)
267                 {
268                   if (buf[b] == '"')
269                     {
270                       if (! print (output, error, "%s", quote))
271                         goto fail;
272                     }
273                   else if (buf[b] == '\\')
274                     {
275                       if (! print (output, error, "%s", backslash))
276                         goto fail;
277                     }
278                   else
279                     {
280                       if (! print (output, error, "%c", buf[b]))
281                         goto fail;
282                     }
283                 }
284 
285               c++;
286               if (c >= 16)
287                 {
288                   if (! print (output, error, "%s", newline))
289                     goto fail;
290 
291                   c = 0;
292                 }
293             }
294         }
295 
296       if (! print (output, error, "\";\n"))
297         goto fail;
298       break;
299 
300     case GIMP_INDEXED_IMAGE:
301       if (! print (output, error,
302                    "#define HEADER_PIXEL(data,pixel) {\\\n"
303                    "pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \\\n"
304                    "pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \\\n"
305                    "pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \\\n"
306                    "data ++; }\n\n"))
307         {
308           goto fail;
309         }
310 
311       /* save colormap */
312       cmap = gimp_image_get_colormap (image_ID, &colors);
313 
314       if (! print (output, error,
315                    "static unsigned char header_data_cmap[256][3] = {") ||
316           ! print (output, error,
317                    "\n\t{%3d,%3d,%3d}",
318                    (gint) cmap[0], (gint) cmap[1], (gint) cmap[2]))
319         {
320           goto fail;
321         }
322 
323       for (c = 1; c < colors; c++)
324         {
325           if (! print (output, error,
326                        ",\n\t{%3d,%3d,%3d}",
327                        (gint) cmap[3 * c],
328                        (gint) cmap[3 * c + 1],
329                        (gint) cmap[3 * c + 2]))
330             {
331               goto fail;
332             }
333         }
334 
335       /* fill the rest */
336       for ( ; c < 256; c++)
337         {
338           if (! print (output, error, ",\n\t{255,255,255}"))
339             goto fail;
340         }
341 
342       /* close bracket */
343       if (! print (output, error, "\n\t};\n"))
344         goto fail;
345 
346       g_free (cmap);
347 
348       /* save image */
349       if (! print (output, error, "static unsigned char header_data[] = {\n\t"))
350         goto fail;
351 
352       data = g_new (guchar, width * 1);
353 
354       c = 0;
355       for (y = 0; y < height; y++)
356         {
357           gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
358                            NULL, data,
359                            GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
360 
361           for (x = 0; x < width  -1; x++)
362             {
363               d = data + x * 1;
364 
365               if (! print (output, error, "%d,", (gint) d[0]))
366                 goto fail;
367 
368               c++;
369               if (c >= 16)
370                 {
371                   if (! print (output, error, "\n\t"))
372                     goto fail;
373 
374                   c = 0;
375                 }
376             }
377 
378           if (y != height - 1)
379             {
380               if (! print (output, error, "%d,\n\t", (gint) d[1]))
381                 goto fail;
382             }
383           else
384             {
385               if (! print (output, error, "%d\n\t", (gint) d[1]))
386                 goto fail;
387             }
388 
389           c = 0; /* reset line counter */
390         }
391 
392       if (! print (output, error, "};\n"))
393         goto fail;
394       break;
395 
396     default:
397       g_warning ("unhandled drawable type (%d)", drawable_type);
398       goto fail;
399     }
400 
401   if (! g_output_stream_close (output, NULL, error))
402     goto fail;
403 
404   g_free (data);
405   g_object_unref (output);
406   g_object_unref (buffer);
407 
408   return TRUE;
409 
410  fail:
411 
412   cancellable = g_cancellable_new ();
413   g_cancellable_cancel (cancellable);
414   g_output_stream_close (output, cancellable, NULL);
415 
416   g_free (data);
417   g_object_unref (output);
418   g_object_unref (buffer);
419   g_object_unref (cancellable);
420 
421   return FALSE;
422 }
423 
424 static gboolean
print(GOutputStream * output,GError ** error,const gchar * format,...)425 print (GOutputStream  *output,
426        GError        **error,
427        const gchar    *format,
428        ...)
429 {
430   va_list  args;
431   gboolean success;
432 
433   va_start (args, format);
434   success = g_output_stream_vprintf (output, NULL, NULL,
435                                      error, format, args);
436   va_end (args);
437 
438   return success;
439 }
440