aa.c 9.75 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 51
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, 
					   gpointer data);
52 53 54 55 56 57 58

/* 
 * Some global variables.
 */

GPlugInInfo PLUG_IN_INFO =
{
59 60 61 62
  NULL,  /* init_proc */
  NULL,  /* quit_proc */
  query, /* query_proc */
  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 85 86
  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"}
  };
  static int nsave_args = sizeof(save_args) / sizeof(save_args[0]);

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

89 90 91 92 93 94 95 96 97 98 99 100 101
  gimp_install_procedure ("file_aa_save",
			  _("Saves files in various text formats"),
			  _("Saves files in various text formats"),
			  "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);

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

/**
 * Searches aa_formats defined by aalib to find the index of the type
 * specified by string.
 * -1 means it wasn't found.
 */
109 110
static int 
get_type_from_string (char *string)
111
{
112 113 114
  int type = 0;
  aa_format **p = aa_formats;
  
115 116 117 118 119
  while (*p && strcmp ((*p)->formatname, string))
    {
      p++;
      type++;
    }
120 121 122

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

124
  return type;
125 126
}

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

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

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

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

173
  if (!aa_savable (drawable_ID)) 
174 175
    {
      values[0].data.d_status = STATUS_CALLING_ERROR;
176
      goto finish;
177
    }
178

179 180 181
  switch (run_mode) 
    {
    case RUN_INTERACTIVE:
182
      gimp_get_data ("file_aa_save", &last_type);
183 184
      output_type = type_dialog (last_type);
      break;
185

186 187 188 189 190 191 192
    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);
      break;
193

194 195 196 197
    case RUN_WITH_LAST_VALS:
      gimp_get_data ("file_aa_save", &last_type);
      output_type = last_type;
      break;
198

199 200 201
    default:
      break;
    }
202

203 204 205
  if (output_type < 0) 
    {
      status = STATUS_CALLING_ERROR;
206
      goto finish;
207
    }
208

209
  if (save_aa (output_type, param[3].data.d_string, image_ID, drawable_ID))
210 211 212 213 214 215 216
    {
      values[0].data.d_status = STATUS_EXECUTION_ERROR;
      last_type = output_type;
      gimp_set_data ("file_aa_save", &last_type, sizeof(last_type));
    }    
  else
    values[0].data.d_status = STATUS_SUCCESS;
217 218 219

 finish:

220 221
  if (export == EXPORT_EXPORT)
    gimp_image_delete (image_ID);  
222 223 224 225 226 227
}

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

239
  /*fprintf(stderr, "save %s\n", filename); */
240

241 242 243 244
  drawable = gimp_drawable_get (drawable_ID);
  memcpy (&format, aa_formats[output_type], sizeof (format));
  format.width = drawable->width / 2;
  format.height = drawable->height / 2;
245

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

248 249 250
  /* Get a libaa context which will save its output to filename. */
  savedata.name = filename;
  savedata.format = &format;
251

252 253
  context = aa_init (&save_d, &aa_defparams, &savedata);
  if (context == NULL)
254 255
    return TRUE;

256
  gimp2aa (image_ID, drawable_ID, context);
257 258
  aa_flush (context);
  aa_close (context);
259

260
  /*fprintf(stderr, "Success!\n"); */
261 262

  return FALSE;
263 264
}

265
static void
266 267 268
gimp2aa (gint32      image, 
	 gint32      drawable_ID, 
	 aa_context *context)
269
{
270
  gint width, height, x, y;
271 272 273 274
  guchar *buffer;
  GDrawable *drawable = NULL;
  GPixelRgn pixel_rgn;
  aa_renderparams *renderparams = NULL;
275 276
  gint bpp;

277 278
  width = aa_imgwidth (context);
  height = aa_imgheight (context);
279
  /*fprintf(stderr, "gimp2aa %i x %i\n", width, height); */
280 281 282

  drawable = gimp_drawable_get (drawable_ID);

283
  bpp = drawable->bpp;
284 285
  buffer = g_new (guchar, width * bpp);

286 287 288 289 290 291 292 293 294 295
  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. */
296
	  aa_putpixel (context, x, y, buffer[x * bpp]);
297
	}
298
    }
299

300 301 302
  renderparams = aa_getrenderparams ();
  renderparams->dither = AA_FLOYD_S;
  aa_render (context, renderparams, 0, 0, 
303
	     aa_scrwidth (context), aa_scrheight (context));
304 305
}

306
static gboolean 
307
aa_savable (gint32 drawable_ID)
308
{
309
  GDrawableType drawable_type;
310

311
  drawable_type = gimp_drawable_type (drawable_ID);
312

313
  if (drawable_type != GRAY_IMAGE && drawable_type != GRAYA_IMAGE)
314 315 316
    return FALSE;

  return TRUE;
317 318 319 320 321 322
}

/* 
 * User Interface dialog thingie.
 */

323
static void 
324
init_gtk (void)
325 326
{
  gchar **argv;
327 328 329 330 331
  gint    argc;

  argc    = 1;
  argv    = g_new (gchar *, 1);
  argv[0] = g_strdup ("aa");
332 333 334 335

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

337 338 339 340 341 342 343 344
static gint 
type_dialog (int selected) 
{
  GtkWidget *dlg;
  GtkWidget *toggle;
  GtkWidget *frame;
  GtkWidget *toggle_vbox;
  GSList *group;
345

346
  /* Create the actual window. */
347 348 349 350 351
  dlg = gimp_dialog_new (_("Save as Text"), "aa",
			 gimp_plugin_help_func, "filters/aa.html",
			 GTK_WIN_POS_MOUSE,
			 FALSE, TRUE, FALSE,

352 353
			 _("OK"), gtk_widget_destroy,
			 NULL, 1, NULL, TRUE, FALSE,
354 355 356 357
			 _("Cancel"), type_dialog_cancel_callback,
			 NULL, NULL, NULL, FALSE, TRUE,

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

359
  gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
360
		      GTK_SIGNAL_FUNC (gtk_main_quit),
361
		      NULL);
362 363

  /*  file save type  */
364 365 366 367 368 369 370 371
  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);
372 373 374 375
  
  group = NULL;
  {
    aa_format **p = aa_formats;
376 377
    gint current = 0;

378 379 380
    while (*p != NULL) 
      {
	toggle = gtk_radio_button_new_with_label (group, (*p)->formatname);
381 382 383 384
	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),
385 386
			    (*p)->formatname);
	if (current == selected)
387
	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
388 389 390 391 392 393
	
	gtk_widget_show (toggle);
	p++;
	current++;
      }
  }
394

395 396
  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);
397

398 399 400 401 402 403
  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return selected_type;
404 405 406 407 408 409
}

/*
 * Callbacks for the dialog.
 */

410 411 412 413 414
static void 
type_dialog_cancel_callback (GtkWidget *widget, 
			     gpointer   data) 
{
  selected_type = -1;
415 416 417
  gtk_widget_destroy (GTK_WIDGET (data));
}

418 419 420 421 422
static void 
type_dialog_toggle_update (GtkWidget *widget, 
			   gpointer   data) 
{
  selected_type = get_type_from_string ((char *)data);
423
}