aa.c 9.83 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
static void       query      (void);
static void       run        (gchar      *name, 
			      gint        nparams, 
			      GParam     *param, 
			      gint       *nreturn_vals, 
			      GParam    **return_vals);
static void       init_gtk   (void);
static gboolean   aa_savable (gint32      drawable_ID);
static gboolean   save_aa    (gint        output_type, 
			      gchar      *filename, 
			      gint32      image,
			      gint32      drawable);
static void       gimp2aa    (gint32      image, 
			      gint32      drawable_ID, 
			      aa_context *context);

static gint   type_dialog                 (int        selected);
static void   type_dialog_ok_callback     (GtkWidget *widget,
					   gpointer   data);
static void   type_dialog_toggle_update   (GtkWidget *widget, 
					   gpointer   data);
static void   type_dialog_cancel_callback (GtkWidget *widget, 
51
					   gpointer   data);
52 53 54 55 56 57 58

/* 
 * Some global variables.
 */

GPlugInInfo PLUG_IN_INFO =
{
59 60
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
61
  query, /* query_proc */
62
  run,   /* run_proc   */
63 64 65
};

/**
66
 * Type the user selected. (Global for easier UI coding.)
67
 */
68
static gint selected_type = 0;
69 70


71 72
MAIN ()

73
static void 
74
query (void)
75
{
76 77 78 79 80 81 82 83 84
  static GParamDef save_args[] =
  {
    {PARAM_INT32,    "run_mode",     "Interactive, non-interactive"},
    {PARAM_IMAGE,    "image",        "Input image"},
    {PARAM_DRAWABLE, "drawable",     "Drawable to save"},
    {PARAM_STRING,   "filename",     "The name of the file to save the image in"},
    {PARAM_STRING,   "raw_filename", "The name entered"},
    {PARAM_STRING,   "file_type",    "File type to use"}
  };
85
  static gint nsave_args = sizeof(save_args) / sizeof(save_args[0]);
86

Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
87 88
  INIT_I18N();

89
  gimp_install_procedure ("file_aa_save",
Marc Lehmann's avatar
Marc Lehmann committed
90 91
			  "Saves files in various text formats",
			  "Saves files in various text formats",
92 93 94 95 96 97 98 99 100
			  "Tim Newsome <nuisance@cmu.edu>",
			  "Tim Newsome <nuisance@cmu.edu>",
			  "1997",
			  "<Save>/AA",
			  "GRAY*",		/* support grayscales */
			  PROC_PLUG_IN,
			  nsave_args, 0,
			  save_args, NULL);

101 102 103
  gimp_register_save_handler ("file_aa_save",
			      "ansi,txt,text,html",
			      "");
104 105 106 107 108 109 110
}

/**
 * Searches aa_formats defined by aalib to find the index of the type
 * specified by string.
 * -1 means it wasn't found.
 */
111
static int 
112
get_type_from_string (gchar *string)
113
{
114
  gint type = 0;
115
  aa_format **p = aa_formats;
116

117 118 119 120 121
  while (*p && strcmp ((*p)->formatname, string))
    {
      p++;
      type++;
    }
122 123 124

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

126
  return type;
127 128
}

129
static void 
130 131
run (gchar   *name, 
     gint     nparams, 
132
     GParam  *param, 
133
     gint    *nreturn_vals,
134
     GParam **return_vals)
135
{
136
  static GParam values[2];
137 138 139 140 141 142
  GRunModeType  run_mode;
  GStatusType   status = STATUS_SUCCESS;
  gint          output_type = 0;
  static int    last_type = 0;
  gint32        image_ID;
  gint32        drawable_ID;
143 144 145 146
  GimpExportReturnType export = EXPORT_CANCEL;

  /* Set us up to return a status. */
  *nreturn_vals = 1;
147 148 149
  *return_vals  = values;
  values[0].type          = PARAM_STATUS;
  values[0].data.d_status = STATUS_EXECUTION_ERROR;
150

151 152 153
  run_mode    = param[0].data.d_int32;
  image_ID    = param[1].data.d_int32;
  drawable_ID = param[2].data.d_int32;
154

155 156 157 158 159
  /*  eventually export the image */ 
  switch (run_mode)
    {
    case RUN_INTERACTIVE:
    case RUN_WITH_LAST_VALS:
Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
160
      INIT_I18N_UI();
161 162
      init_gtk ();
      export = gimp_export_image (&image_ID, &drawable_ID, "AA", 
163 164
				  (CAN_HANDLE_GRAY |
				   CAN_HANDLE_ALPHA));
165 166
      if (export == EXPORT_CANCEL)
	{
167
	  values[0].data.d_status = STATUS_CANCEL;
168 169 170 171
	  return;
	}
      break;
    default:
Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
172
      INIT_I18N();
173 174
      break;
    }
175

176
  if (!aa_savable (drawable_ID)) 
177
    {
178
      status = STATUS_CALLING_ERROR;
179
    }
180

181
  if (status == STATUS_SUCCESS)
182
    {
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
      switch (run_mode) 
	{
	case RUN_INTERACTIVE:
	  gimp_get_data ("file_aa_save", &last_type);
	  output_type = type_dialog (last_type);
	  if (output_type < 0)
	    status = STATUS_CANCEL;
	  break;

	case RUN_NONINTERACTIVE:
	  /*  Make sure all the arguments are there!  */
	  if (nparams != 6)
	    {
	      status = STATUS_CALLING_ERROR;
	    }
	  else
	    {
	      output_type = get_type_from_string (param[5].data.d_string);
	      if (output_type < 0)
		status = STATUS_CALLING_ERROR;
	    }
	  break;

	case RUN_WITH_LAST_VALS:
	  gimp_get_data ("file_aa_save", &last_type);
	  output_type = last_type;
	  break;

	default:
	  break;
	}
214
    }
215

216
  if (status == STATUS_SUCCESS)
217
    {
218 219 220 221 222 223 224 225 226
      if (save_aa (output_type, param[3].data.d_string, image_ID, drawable_ID))
	{
	  status = STATUS_EXECUTION_ERROR;
	}
      else
	{
	  last_type = output_type;
	  gimp_set_data ("file_aa_save", &last_type, sizeof (last_type));
	}
227
    }
228

229 230
  if (export == EXPORT_EXPORT)
    gimp_image_delete (image_ID);  
231 232

  values[0].data.d_status = status;
233 234 235 236 237 238
}

/**
 * The actual save function. What it's all about.
 * The image type has to be GRAY.
 */
239 240 241
static gboolean
save_aa (gint    output_type, 
	 gchar  *filename, 
242
	 gint32  image_ID,
243
	 gint32  drawable_ID)
244
{
245 246 247 248
  aa_savedata savedata = {NULL, NULL};
  aa_context *context  = NULL;
  GDrawable *drawable  = NULL;
  aa_format format;
249

250
  /*fprintf(stderr, "save %s\n", filename); */
251

252 253 254 255
  drawable = gimp_drawable_get (drawable_ID);
  memcpy (&format, aa_formats[output_type], sizeof (format));
  format.width = drawable->width / 2;
  format.height = drawable->height / 2;
256

257
  /*fprintf(stderr, "save_aa %i x %i\n", format.width, format.height); */
258

259 260 261
  /* Get a libaa context which will save its output to filename. */
  savedata.name = filename;
  savedata.format = &format;
262

263 264
  context = aa_init (&save_d, &aa_defparams, &savedata);
  if (context == NULL)
265 266
    return TRUE;

267
  gimp2aa (image_ID, drawable_ID, context);
268 269
  aa_flush (context);
  aa_close (context);
270

271
  /*fprintf(stderr, "Success!\n"); */
272 273

  return FALSE;
274 275
}

276
static void
277 278 279
gimp2aa (gint32      image, 
	 gint32      drawable_ID, 
	 aa_context *context)
280
{
281
  gint width, height, x, y;
282 283 284 285
  guchar *buffer;
  GDrawable *drawable = NULL;
  GPixelRgn pixel_rgn;
  aa_renderparams *renderparams = NULL;
286 287
  gint bpp;

288 289
  width = aa_imgwidth (context);
  height = aa_imgheight (context);
290
  /*fprintf(stderr, "gimp2aa %i x %i\n", width, height); */
291 292 293

  drawable = gimp_drawable_get (drawable_ID);

294
  bpp = drawable->bpp;
295 296
  buffer = g_new (guchar, width * bpp);

297 298 299 300 301 302 303 304 305 306
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width,
		       drawable->height, FALSE, FALSE);

  for (y = 0; y < height; y++) 
    {
      gimp_pixel_rgn_get_row (&pixel_rgn, buffer, 0, y, width);
      for (x = 0; x < width; x++) 
	{
	  /* Just copy one byte. If it's indexed that's all we need. Otherwise
	   * it'll be the most significant one. */
307
	  aa_putpixel (context, x, y, buffer[x * bpp]);
308
	}
309
    }
310

311 312 313
  renderparams = aa_getrenderparams ();
  renderparams->dither = AA_FLOYD_S;
  aa_render (context, renderparams, 0, 0, 
314
	     aa_scrwidth (context), aa_scrheight (context));
315 316
}

317
static gboolean 
318
aa_savable (gint32 drawable_ID)
319
{
320
  GDrawableType drawable_type;
321

322
  drawable_type = gimp_drawable_type (drawable_ID);
323

324
  if (drawable_type != GRAY_IMAGE && drawable_type != GRAYA_IMAGE)
325 326 327
    return FALSE;

  return TRUE;
328 329 330 331 332 333
}

/* 
 * User Interface dialog thingie.
 */

334
static void 
335
init_gtk (void)
336 337
{
  gchar **argv;
338 339 340 341 342
  gint    argc;

  argc    = 1;
  argv    = g_new (gchar *, 1);
  argv[0] = g_strdup ("aa");
343 344 345 346

  gtk_init (&argc, &argv);
  gtk_rc_parse (gimp_gtkrc ());
}
347

348 349 350 351 352 353 354 355
static gint 
type_dialog (int selected) 
{
  GtkWidget *dlg;
  GtkWidget *toggle;
  GtkWidget *frame;
  GtkWidget *toggle_vbox;
  GSList *group;
356

357
  /* Create the actual window. */
358 359 360 361 362
  dlg = gimp_dialog_new (_("Save as Text"), "aa",
			 gimp_plugin_help_func, "filters/aa.html",
			 GTK_WIN_POS_MOUSE,
			 FALSE, TRUE, FALSE,

363 364
			 _("OK"), gtk_widget_destroy,
			 NULL, 1, NULL, TRUE, FALSE,
365 366 367 368
			 _("Cancel"), type_dialog_cancel_callback,
			 NULL, NULL, NULL, FALSE, TRUE,

			 NULL);
Sven Neumann's avatar
Sven Neumann committed
369

370
  gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
371
		      GTK_SIGNAL_FUNC (gtk_main_quit),
372
		      NULL);
373 374

  /*  file save type  */
375 376 377 378 379 380 381 382
  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);
383 384 385 386
  
  group = NULL;
  {
    aa_format **p = aa_formats;
387 388
    gint current = 0;

389 390 391
    while (*p != NULL) 
      {
	toggle = gtk_radio_button_new_with_label (group, (*p)->formatname);
392 393 394 395
	group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
	gtk_box_pack_start (GTK_BOX  (toggle_vbox), toggle, FALSE, FALSE, 0);
	gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
			    GTK_SIGNAL_FUNC (type_dialog_toggle_update),
396 397
			    (*p)->formatname);
	if (current == selected)
398
	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
399 400 401 402 403 404
	
	gtk_widget_show (toggle);
	p++;
	current++;
      }
  }
405

406 407
  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);
408

409 410 411 412 413 414
  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return selected_type;
415 416 417 418 419 420
}

/*
 * Callbacks for the dialog.
 */

421 422 423 424 425
static void 
type_dialog_cancel_callback (GtkWidget *widget, 
			     gpointer   data) 
{
  selected_type = -1;
426 427 428
  gtk_widget_destroy (GTK_WIDGET (data));
}

429 430 431 432
static void 
type_dialog_toggle_update (GtkWidget *widget, 
			   gpointer   data) 
{
433
  selected_type = get_type_from_string ((char *) data);
434
}