aa.c 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/**
 * aa.c version 1.0
 * A plugin that uses libaa (ftp://ftp.ta.jcu.cz/pub/aa) to save images as
 * ASCII.
 * NOTE: This plugin *requires* aalib 1.2 or later. Earlier versions will
 * not work.
 * Code copied from all over the GIMP source.
 * Tim Newsome <nuisance@cmu.edu>
 */

Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
11 12
#include "config.h"

13 14
#include <stdio.h>
#include <stdlib.h>
15
#include <string.h>
16 17 18 19 20

#include <aalib.h>

#include <gtk/gtk.h>

21
#include <libgimp/gimp.h>
22
#include <libgimp/gimpui.h>
23

Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
24 25
#include "libgimp/stdplugins-intl.h"

26 27 28
/* 
 * Declare some local functions.
 */
29
static void     query      (void);
30 31 32 33 34 35 36 37 38 39
static void     run        (const gchar      *name, 
                            gint              nparams, 
                            const GimpParam  *param, 
                            gint             *nreturn_vals, 
                            GimpParam       **return_vals);
static gboolean save_aa    (gint32            drawable_ID,
                            gchar            *filename, 
                            gint              output_type);
static void     gimp2aa    (gint32            drawable_ID, 
                            aa_context       *context);
40 41 42 43 44 45

static gint     type_dialog                 (gint       selected);
static void     type_dialog_toggle_update   (GtkWidget *widget, 
                                             gpointer   data);
static void     type_dialog_cancel_callback (GtkWidget *widget, 
                                             gpointer   data);
46 47 48 49 50

/* 
 * Some global variables.
 */

Sven Neumann's avatar
Sven Neumann committed
51
GimpPlugInInfo PLUG_IN_INFO =
52
{
53 54
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
55
  query, /* query_proc */
56
  run,   /* run_proc   */
57 58 59
};

/**
60
 * Type the user selected. (Global for easier UI coding.)
61
 */
62
static gint selected_type = 0;
63 64


65 66
MAIN ()

67
static void 
68
query (void)
69
{
Sven Neumann's avatar
Sven Neumann committed
70
  static GimpParamDef save_args[] =
71
  {
Sven Neumann's avatar
Sven Neumann committed
72 73 74 75 76 77
    {GIMP_PDB_INT32,    "run_mode",     "Interactive, non-interactive"},
    {GIMP_PDB_IMAGE,    "image",        "Input image"},
    {GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save"},
    {GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in"},
    {GIMP_PDB_STRING,   "raw_filename", "The name entered"},
    {GIMP_PDB_STRING,   "file_type",    "File type to use"}
78 79
  };

80
  gimp_install_procedure ("file_aa_save",
81 82 83
			  "Saves grayscale image in various text formats",
			  "This plug-in uses aalib to save grayscale image "
                          "as ascii art into a variety of text formats",
84 85 86 87
			  "Tim Newsome <nuisance@cmu.edu>",
			  "Tim Newsome <nuisance@cmu.edu>",
			  "1997",
			  "<Save>/AA",
Sven Neumann's avatar
Sven Neumann committed
88
			  "RGB*, GRAY*",
Sven Neumann's avatar
Sven Neumann committed
89
			  GIMP_PLUGIN,
90
			  G_N_ELEMENTS (save_args), 0,
91 92
			  save_args, NULL);

93
  gimp_register_save_handler ("file_aa_save",
Sven Neumann's avatar
Sven Neumann committed
94
			      "ansi,txt,text",
95
			      "");
96 97 98 99 100 101 102
}

/**
 * Searches aa_formats defined by aalib to find the index of the type
 * specified by string.
 * -1 means it wasn't found.
 */
103 104
static gint 
get_type_from_string (const gchar *string)
105
{
106
  gint type = 0;
107
  aa_format **p = (aa_format **) aa_formats;
108

109 110 111 112 113
  while (*p && strcmp ((*p)->formatname, string))
    {
      p++;
      type++;
    }
114 115 116

  if (*p == NULL)
    return -1;
117

118
  return type;
119 120
}

121
static void 
122 123 124 125 126
run (const gchar      *name, 
     gint              nparams, 
     const GimpParam  *param, 
     gint             *nreturn_vals,
     GimpParam       **return_vals)
127
{
128 129 130 131 132 133 134
  static GimpParam      values[2];
  GimpRunMode           run_mode;
  GimpPDBStatusType     status = GIMP_PDB_SUCCESS;
  gint                  output_type = 0;
  gint32                image_ID;
  gint32                drawable_ID;
  GimpExportReturnType  export = GIMP_EXPORT_CANCEL;
135

136 137
  INIT_I18N ();

138 139
  /* Set us up to return a status. */
  *nreturn_vals = 1;
140
  *return_vals  = values;
Sven Neumann's avatar
Sven Neumann committed
141 142
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
143 144 145
  run_mode    = param[0].data.d_int32;
  image_ID    = param[1].data.d_int32;
  drawable_ID = param[2].data.d_int32;
146

147 148 149
  /*  eventually export the image */ 
  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
150 151
    case GIMP_RUN_INTERACTIVE:
    case GIMP_RUN_WITH_LAST_VALS:
152
      gimp_ui_init ("aa", FALSE);
153
      export = gimp_export_image (&image_ID, &drawable_ID, "AA", 
Sven Neumann's avatar
Sven Neumann committed
154 155
				  (GIMP_EXPORT_CAN_HANDLE_RGB  |
                                   GIMP_EXPORT_CAN_HANDLE_GRAY |
156 157
				   GIMP_EXPORT_CAN_HANDLE_ALPHA ));
      if (export == GIMP_EXPORT_CANCEL)
158
	{
Sven Neumann's avatar
Sven Neumann committed
159
	  values[0].data.d_status = GIMP_PDB_CANCEL;
160 161 162 163 164 165
	  return;
	}
      break;
    default:
      break;
    }
166

Sven Neumann's avatar
Sven Neumann committed
167 168
  if (! (gimp_drawable_is_rgb (drawable_ID) ||
         gimp_drawable_is_gray (drawable_ID))) 
169
    {
Sven Neumann's avatar
Sven Neumann committed
170
      status = GIMP_PDB_CALLING_ERROR;
171
    }
172

Sven Neumann's avatar
Sven Neumann committed
173
  if (status == GIMP_PDB_SUCCESS)
174
    {
175 176
      switch (run_mode) 
	{
Sven Neumann's avatar
Sven Neumann committed
177
	case GIMP_RUN_INTERACTIVE:
178 179
	  gimp_get_data ("file_aa_save", &output_type);
	  output_type = type_dialog (output_type);
180
	  if (output_type < 0)
Sven Neumann's avatar
Sven Neumann committed
181
	    status = GIMP_PDB_CANCEL;
182 183
	  break;

Sven Neumann's avatar
Sven Neumann committed
184
	case GIMP_RUN_NONINTERACTIVE:
185 186 187
	  /*  Make sure all the arguments are there!  */
	  if (nparams != 6)
	    {
Sven Neumann's avatar
Sven Neumann committed
188
	      status = GIMP_PDB_CALLING_ERROR;
189 190 191 192 193
	    }
	  else
	    {
	      output_type = get_type_from_string (param[5].data.d_string);
	      if (output_type < 0)
Sven Neumann's avatar
Sven Neumann committed
194
		status = GIMP_PDB_CALLING_ERROR;
195 196 197
	    }
	  break;

Sven Neumann's avatar
Sven Neumann committed
198
	case GIMP_RUN_WITH_LAST_VALS:
199
	  gimp_get_data ("file_aa_save", &output_type);
200 201 202 203 204
	  break;

	default:
	  break;
	}
205
    }
206

Sven Neumann's avatar
Sven Neumann committed
207
  if (status == GIMP_PDB_SUCCESS)
208
    {
209
      if (save_aa (drawable_ID, param[3].data.d_string, output_type))
210
	{
211
	  gimp_set_data ("file_aa_save", &output_type, sizeof (output_type));
212 213 214
	}
      else
	{
215
	  status = GIMP_PDB_EXECUTION_ERROR;
216
	}
217
    }
218

219
  if (export == GIMP_EXPORT_EXPORT)
220
    gimp_image_delete (image_ID);  
221 222

  values[0].data.d_status = status;
223 224 225 226 227 228
}

/**
 * The actual save function. What it's all about.
 * The image type has to be GRAY.
 */
229
static gboolean
230 231 232
save_aa (gint32  drawable_ID,
         gchar  *filename,
         gint    output_type)
233
{
234 235
  aa_savedata  savedata;
  aa_context  *context;
236
  aa_format    format = *aa_formats[output_type];
237

238 239
  format.width  = gimp_drawable_width (drawable_ID)  / 2;
  format.height = gimp_drawable_height (drawable_ID) / 2;
240

241
  /* Get a libaa context which will save its output to filename. */
242
  savedata.name   = filename;
243
  savedata.format = &format;
244

245
  context = aa_init (&save_d, &aa_defparams, &savedata);
246 247 248 249
  if (!context)
    return FALSE;

  gimp2aa (drawable_ID, context);
250 251
  aa_flush (context);
  aa_close (context);
252

253
  return TRUE;
254 255
}

256
static void
257
gimp2aa (gint32      drawable_ID, 
258
	 aa_context *context)
259
{
260 261 262 263 264 265 266 267
  GimpDrawable    *drawable;
  GimpPixelRgn     pixel_rgn;
  aa_renderparams *renderparams;

  gint    width;
  gint    height;
  gint    x, y;
  gint    bpp;
268
  guchar *buffer;
Sven Neumann's avatar
Sven Neumann committed
269
  guchar *p;
270

271 272 273
  drawable = gimp_drawable_get (drawable_ID);

  width  = aa_imgwidth  (context);
274
  height = aa_imgheight (context);
275
  bpp    = drawable->bpp;
276

277 278 279
  gimp_pixel_rgn_init (&pixel_rgn, 
                       drawable, 0, 0, width, height, 
                       FALSE, FALSE);
280 281 282

  buffer = g_new (guchar, width * bpp);

283 284 285
  for (y = 0; y < height; y++) 
    {
      gimp_pixel_rgn_get_row (&pixel_rgn, buffer, 0, y, width);
Sven Neumann's avatar
Sven Neumann committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

      switch (bpp)
        {
        case 1:  /* GRAY */
          for (x = 0, p = buffer; x < width; x++, p++) 
            aa_putpixel (context, x, y, *p);
          break;

        case 2:  /* GRAYA, blend over black */
          for (x = 0, p = buffer; x < width; x++, p += 2) 
            aa_putpixel (context, x, y, (p[0] * (p[1] + 1)) >> 8);
          break;
      
        case 3:  /* RGB */
          for (x = 0, p = buffer; x < width; x++, p += 3) 
            aa_putpixel (context, x, y, INTENSITY (p[0], p[1], p[2]));
          break;
          
        case 4:  /* RGBA, blend over black */
          for (x = 0, p = buffer; x < width; x++, p += 4) 
            aa_putpixel (context, x, y,
                         ((guchar) INTENSITY (p[0], p[1], p[2]) * (p[3] + 1))
                         >> 8);
          break;

        default:
          g_assert_not_reached ();
          break;
        }
315
    }
316

317 318
  g_free (buffer);

319 320
  renderparams = aa_getrenderparams ();
  renderparams->dither = AA_FLOYD_S;
321

322
  aa_render (context, renderparams, 0, 0, 
323
	     aa_scrwidth (context), aa_scrheight (context));
324 325 326 327 328 329
}

/* 
 * User Interface dialog thingie.
 */

330
static gint 
331
type_dialog (gint selected) 
332 333 334 335 336
{
  GtkWidget *dlg;
  GtkWidget *toggle;
  GtkWidget *frame;
  GtkWidget *toggle_vbox;
337
  GSList    *group;
338

339
  /* Create the actual window. */
340
  dlg = gimp_dialog_new (_("Save as Text"), "aa",
341
			 gimp_standard_help_func, "filters/aa.html",
342 343 344
			 GTK_WIN_POS_MOUSE,
			 FALSE, TRUE, FALSE,

345
			 GTK_STOCK_CANCEL, type_dialog_cancel_callback,
346 347
			 NULL, NULL, NULL, FALSE, TRUE,

Sven Neumann's avatar
Sven Neumann committed
348 349 350
			 GTK_STOCK_OK, gtk_widget_destroy,
			 NULL, 1, NULL, TRUE, FALSE,

351
			 NULL);
Sven Neumann's avatar
Sven Neumann committed
352

353
  g_signal_connect (dlg, "destroy",
354 355
                    G_CALLBACK (gtk_main_quit),
                    NULL);
356 357

  /*  file save type  */
358 359 360 361 362 363 364 365
  frame = gtk_frame_new (_("Data Formatting"));
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dlg)->vbox), frame, TRUE, TRUE, 0);

  toggle_vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_set_border_width (GTK_CONTAINER(toggle_vbox), 4);
  gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
366 367 368
  
  group = NULL;
  {
369
    aa_format **p = (aa_format **) aa_formats;
370 371
    gint current = 0;

372 373 374
    while (*p != NULL) 
      {
	toggle = gtk_radio_button_new_with_label (group, (*p)->formatname);
375
	group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
376
	gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
377 378
	gtk_widget_show (toggle);

379
	g_signal_connect (toggle, "toggled",
380 381 382
                          G_CALLBACK (type_dialog_toggle_update),
                          (gpointer) (*p)->formatname);

383
	if (current == selected)
384
	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
385 386 387 388 389
	
	p++;
	current++;
      }
  }
390

391 392
  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);
393

394 395 396 397 398 399
  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return selected_type;
400 401 402 403 404 405
}

/*
 * Callbacks for the dialog.
 */

406 407 408 409 410
static void 
type_dialog_cancel_callback (GtkWidget *widget, 
			     gpointer   data) 
{
  selected_type = -1;
411 412 413
  gtk_widget_destroy (GTK_WIDGET (data));
}

414 415 416 417
static void 
type_dialog_toggle_update (GtkWidget *widget, 
			   gpointer   data) 
{
418
  selected_type = get_type_from_string ((gchar *) data);
419
}