1 /**
2 * aa.c version 1.0
3 * A plugin that uses libaa (ftp://ftp.ta.jcu.cz/pub/aa) to save images as
4 * ASCII.
5 * NOTE: This plugin *requires* aalib 1.2 or later. Earlier versions will
6 * not work.
7 * Code copied from all over the GIMP source.
8 * Tim Newsome <nuisance@cmu.edu>
9 */
10
11 /* GIMP - The GNU Image Manipulation Program
12 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 */
27
28 #include "config.h"
29
30 #include <string.h>
31
32 #include <aalib.h>
33
34 #include <libgimp/gimp.h>
35 #include <libgimp/gimpui.h>
36
37 #include "libgimp/stdplugins-intl.h"
38
39
40 #define SAVE_PROC "file-aa-save"
41 #define PLUG_IN_BINARY "file-aa"
42 #define PLUG_IN_ROLE "gimp-file-aa"
43
44
45 /*
46 * Declare some local functions.
47 */
48 static void query (void);
49 static void run (const gchar *name,
50 gint nparams,
51 const GimpParam *param,
52 gint *nreturn_vals,
53 GimpParam **return_vals);
54 static gboolean save_aa (gint32 drawable_ID,
55 gchar *filename,
56 gint output_type);
57 static void gimp2aa (gint32 drawable_ID,
58 aa_context *context);
59
60 static gint aa_dialog (gint selected);
61
62
63 /*
64 * Some global variables.
65 */
66
67 const GimpPlugInInfo PLUG_IN_INFO =
68 {
69 NULL, /* init_proc */
70 NULL, /* quit_proc */
71 query, /* query_proc */
72 run, /* run_proc */
73 };
74
75
MAIN()76 MAIN ()
77
78 static void
79 query (void)
80 {
81 static const GimpParamDef save_args[] =
82 {
83 {GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
84 {GIMP_PDB_IMAGE, "image", "Input image"},
85 {GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"},
86 {GIMP_PDB_STRING, "filename", "The name of the file to save the image in"},
87 {GIMP_PDB_STRING, "raw-filename", "The name entered"},
88 {GIMP_PDB_STRING, "file-type", "File type to use"}
89 };
90
91 gimp_install_procedure (SAVE_PROC,
92 "Saves grayscale image in various text formats",
93 "This plug-in uses aalib to save grayscale image "
94 "as ascii art into a variety of text formats",
95 "Tim Newsome <nuisance@cmu.edu>",
96 "Tim Newsome <nuisance@cmu.edu>",
97 "1997",
98 N_("ASCII art"),
99 "RGB*, GRAY*, INDEXED*",
100 GIMP_PLUGIN,
101 G_N_ELEMENTS (save_args), 0,
102 save_args, NULL);
103
104 gimp_register_file_handler_mime (SAVE_PROC, "text/plain");
105 gimp_register_save_handler (SAVE_PROC, "txt,ansi,text", "");
106 }
107
108 /**
109 * Searches aa_formats defined by aalib to find the index of the type
110 * specified by string.
111 * -1 means it wasn't found.
112 */
113 static gint
get_type_from_string(const gchar * string)114 get_type_from_string (const gchar *string)
115 {
116 gint type = 0;
117 aa_format **p = (aa_format **) aa_formats;
118
119 while (*p && strcmp ((*p)->formatname, string))
120 {
121 p++;
122 type++;
123 }
124
125 if (*p == NULL)
126 return -1;
127
128 return type;
129 }
130
131 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)132 run (const gchar *name,
133 gint nparams,
134 const GimpParam *param,
135 gint *nreturn_vals,
136 GimpParam **return_vals)
137 {
138 static GimpParam values[2];
139 GimpRunMode run_mode;
140 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
141 gint output_type = 0;
142 gint32 image_ID;
143 gint32 drawable_ID;
144 GimpExportReturn export = GIMP_EXPORT_CANCEL;
145
146 INIT_I18N ();
147 gegl_init (NULL, NULL);
148
149 *nreturn_vals = 1;
150 *return_vals = values;
151
152 values[0].type = GIMP_PDB_STATUS;
153 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
154
155 run_mode = param[0].data.d_int32;
156 image_ID = param[1].data.d_int32;
157 drawable_ID = param[2].data.d_int32;
158
159 switch (run_mode)
160 {
161 case GIMP_RUN_INTERACTIVE:
162 case GIMP_RUN_WITH_LAST_VALS:
163 gimp_ui_init (PLUG_IN_BINARY, FALSE);
164
165 export = gimp_export_image (&image_ID, &drawable_ID, "AA",
166 GIMP_EXPORT_CAN_HANDLE_RGB |
167 GIMP_EXPORT_CAN_HANDLE_GRAY |
168 GIMP_EXPORT_CAN_HANDLE_INDEXED |
169 GIMP_EXPORT_CAN_HANDLE_ALPHA);
170
171 if (export == GIMP_EXPORT_CANCEL)
172 {
173 values[0].data.d_status = GIMP_PDB_CANCEL;
174 return;
175 }
176 break;
177 default:
178 break;
179 }
180
181 if (! (gimp_drawable_is_rgb (drawable_ID) ||
182 gimp_drawable_is_gray (drawable_ID)))
183 {
184 status = GIMP_PDB_CALLING_ERROR;
185 }
186
187 if (status == GIMP_PDB_SUCCESS)
188 {
189 switch (run_mode)
190 {
191 case GIMP_RUN_INTERACTIVE:
192 gimp_get_data (SAVE_PROC, &output_type);
193 output_type = aa_dialog (output_type);
194 if (output_type < 0)
195 status = GIMP_PDB_CANCEL;
196 break;
197
198 case GIMP_RUN_NONINTERACTIVE:
199 /* Make sure all the arguments are there! */
200 if (nparams != 6)
201 {
202 status = GIMP_PDB_CALLING_ERROR;
203 }
204 else
205 {
206 output_type = get_type_from_string (param[5].data.d_string);
207 if (output_type < 0)
208 status = GIMP_PDB_CALLING_ERROR;
209 }
210 break;
211
212 case GIMP_RUN_WITH_LAST_VALS:
213 gimp_get_data (SAVE_PROC, &output_type);
214 break;
215
216 default:
217 break;
218 }
219 }
220
221 if (status == GIMP_PDB_SUCCESS)
222 {
223 if (save_aa (drawable_ID, param[3].data.d_string, output_type))
224 {
225 gimp_set_data (SAVE_PROC, &output_type, sizeof (output_type));
226 }
227 else
228 {
229 status = GIMP_PDB_EXECUTION_ERROR;
230 }
231 }
232
233 if (export == GIMP_EXPORT_EXPORT)
234 gimp_image_delete (image_ID);
235
236 values[0].data.d_status = status;
237 }
238
239 /**
240 * The actual save function. What it's all about.
241 * The image type has to be GRAY.
242 */
243 static gboolean
save_aa(gint32 drawable_ID,gchar * filename,gint output_type)244 save_aa (gint32 drawable_ID,
245 gchar *filename,
246 gint output_type)
247 {
248 aa_savedata savedata;
249 aa_context *context;
250 aa_format format = *aa_formats[output_type];
251
252 format.width = gimp_drawable_width (drawable_ID) / 2;
253 format.height = gimp_drawable_height (drawable_ID) / 2;
254
255 /* Get a libaa context which will save its output to filename. */
256 savedata.name = filename;
257 savedata.format = &format;
258
259 context = aa_init (&save_d, &aa_defparams, &savedata);
260 if (!context)
261 return FALSE;
262
263 gimp2aa (drawable_ID, context);
264 aa_flush (context);
265 aa_close (context);
266
267 return TRUE;
268 }
269
270 static void
gimp2aa(gint32 drawable_ID,aa_context * context)271 gimp2aa (gint32 drawable_ID,
272 aa_context *context)
273 {
274 GeglBuffer *buffer;
275 const Babl *format;
276 aa_renderparams *renderparams;
277 gint width;
278 gint height;
279 gint x, y;
280 gint bpp;
281 guchar *buf;
282 guchar *p;
283
284 buffer = gimp_drawable_get_buffer (drawable_ID);
285
286 width = aa_imgwidth (context);
287 height = aa_imgheight (context);
288
289 switch (gimp_drawable_type (drawable_ID))
290 {
291 case GIMP_GRAY_IMAGE:
292 format = babl_format ("Y' u8");
293 break;
294
295 case GIMP_GRAYA_IMAGE:
296 format = babl_format ("Y'A u8");
297 break;
298
299 case GIMP_RGB_IMAGE:
300 case GIMP_INDEXED_IMAGE:
301 format = babl_format ("R'G'B' u8");
302 break;
303
304 case GIMP_RGBA_IMAGE:
305 case GIMP_INDEXEDA_IMAGE:
306 format = babl_format ("R'G'B'A u8");
307 break;
308
309 default:
310 g_return_if_reached ();
311 break;
312 }
313
314 bpp = babl_format_get_bytes_per_pixel (format);
315
316 buf = g_new (guchar, width * bpp);
317
318 for (y = 0; y < height; y++)
319 {
320 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
321 format, buf,
322 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
323
324 switch (bpp)
325 {
326 case 1: /* GRAY */
327 for (x = 0, p = buf; x < width; x++, p++)
328 aa_putpixel (context, x, y, *p);
329 break;
330
331 case 2: /* GRAYA, blend over black */
332 for (x = 0, p = buf; x < width; x++, p += 2)
333 aa_putpixel (context, x, y, (p[0] * (p[1] + 1)) >> 8);
334 break;
335
336 case 3: /* RGB */
337 for (x = 0, p = buf; x < width; x++, p += 3)
338 aa_putpixel (context, x, y,
339 GIMP_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5);
340 break;
341
342 case 4: /* RGBA, blend over black */
343 for (x = 0, p = buf; x < width; x++, p += 4)
344 aa_putpixel (context, x, y,
345 ((guchar) (GIMP_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5)
346 * (p[3] + 1)) >> 8);
347 break;
348
349 default:
350 g_assert_not_reached ();
351 break;
352 }
353 }
354
355 g_free (buf);
356
357 g_object_unref (buffer);
358
359 renderparams = aa_getrenderparams ();
360 renderparams->dither = AA_FLOYD_S;
361
362 aa_render (context, renderparams, 0, 0,
363 aa_scrwidth (context), aa_scrheight (context));
364 }
365
366 static gint
aa_dialog(gint selected)367 aa_dialog (gint selected)
368 {
369 GtkWidget *dialog;
370 GtkWidget *hbox;
371 GtkWidget *label;
372 GtkWidget *combo;
373 gint i;
374
375 /* Create the actual window. */
376 dialog = gimp_export_dialog_new (_("Text"), PLUG_IN_BINARY, SAVE_PROC);
377
378 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
379 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
380 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
381 hbox, FALSE, FALSE, 0);
382 gtk_widget_show (hbox);
383
384 label = gtk_label_new_with_mnemonic (_("_Format:"));
385 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
386 gtk_widget_show (label);
387
388 combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
389 gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
390 gtk_widget_show (combo);
391
392 gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
393
394 for (i = 0; aa_formats[i]; i++)
395 gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
396 GIMP_INT_STORE_VALUE, i,
397 GIMP_INT_STORE_LABEL, aa_formats[i]->formatname,
398 -1);
399
400 gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), selected,
401 G_CALLBACK (gimp_int_combo_box_get_active),
402 &selected);
403
404 gtk_widget_show (dialog);
405
406 if (gimp_dialog_run (GIMP_DIALOG (dialog)) != GTK_RESPONSE_OK)
407 selected = -1;
408
409 gtk_widget_destroy (dialog);
410
411 return selected;
412 }
413