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