aa.c 9.62 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
static void       query      (void);
static void       run        (gchar      *name, 
			      gint        nparams, 
Sven Neumann's avatar
Sven Neumann committed
32
			      GimpParam     *param, 
33
			      gint       *nreturn_vals, 
Sven Neumann's avatar
Sven Neumann committed
34
			      GimpParam    **return_vals);
35 36 37 38 39 40 41 42 43 44 45 46 47
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_toggle_update   (GtkWidget *widget, 
					   gpointer   data);
static void   type_dialog_cancel_callback (GtkWidget *widget, 
48
					   gpointer   data);
49 50 51 52 53

/* 
 * Some global variables.
 */

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

/**
63
 * Type the user selected. (Global for easier UI coding.)
64
 */
65
static gint selected_type = 0;
66 67


68 69
MAIN ()

70
static void 
71
query (void)
72
{
Sven Neumann's avatar
Sven Neumann committed
73
  static GimpParamDef save_args[] =
74
  {
Sven Neumann's avatar
Sven Neumann committed
75 76 77 78 79 80
    {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"}
81
  };
82
  static gint nsave_args = sizeof(save_args) / sizeof(save_args[0]);
83

84
  gimp_install_procedure ("file_aa_save",
Marc Lehmann's avatar
Marc Lehmann committed
85 86
			  "Saves files in various text formats",
			  "Saves files in various text formats",
87 88 89 90 91
			  "Tim Newsome <nuisance@cmu.edu>",
			  "Tim Newsome <nuisance@cmu.edu>",
			  "1997",
			  "<Save>/AA",
			  "GRAY*",		/* support grayscales */
Sven Neumann's avatar
Sven Neumann committed
92
			  GIMP_PLUGIN,
93 94 95
			  nsave_args, 0,
			  save_args, NULL);

96 97 98
  gimp_register_save_handler ("file_aa_save",
			      "ansi,txt,text,html",
			      "");
99 100 101 102 103 104 105
}

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

112 113 114 115 116
  while (*p && strcmp ((*p)->formatname, string))
    {
      p++;
      type++;
    }
117 118 119

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

121
  return type;
122 123
}

124
static void 
125 126
run (gchar   *name, 
     gint     nparams, 
Sven Neumann's avatar
Sven Neumann committed
127
     GimpParam  *param, 
128
     gint    *nreturn_vals,
Sven Neumann's avatar
Sven Neumann committed
129
     GimpParam **return_vals)
130
{
Sven Neumann's avatar
Sven Neumann committed
131 132 133
  static GimpParam values[2];
  GimpRunModeType  run_mode;
  GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
134 135 136 137
  gint          output_type = 0;
  static int    last_type = 0;
  gint32        image_ID;
  gint32        drawable_ID;
138 139 140 141
  GimpExportReturnType export = EXPORT_CANCEL;

  /* Set us up to return a status. */
  *nreturn_vals = 1;
142
  *return_vals  = values;
Sven Neumann's avatar
Sven Neumann committed
143 144
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
145

146 147 148
  run_mode    = param[0].data.d_int32;
  image_ID    = param[1].data.d_int32;
  drawable_ID = param[2].data.d_int32;
149

150 151 152
  /*  eventually export the image */ 
  switch (run_mode)
    {
Sven Neumann's avatar
Sven Neumann committed
153 154
    case GIMP_RUN_INTERACTIVE:
    case GIMP_RUN_WITH_LAST_VALS:
Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
155
      INIT_I18N_UI();
156
      gimp_ui_init ("aa", FALSE);
157
      export = gimp_export_image (&image_ID, &drawable_ID, "AA", 
158 159
				  (CAN_HANDLE_GRAY |
				   CAN_HANDLE_ALPHA));
160 161
      if (export == EXPORT_CANCEL)
	{
Sven Neumann's avatar
Sven Neumann committed
162
	  values[0].data.d_status = GIMP_PDB_CANCEL;
163 164 165 166
	  return;
	}
      break;
    default:
Shirasaki Yasuhiro's avatar
Shirasaki Yasuhiro committed
167
      INIT_I18N();
168 169
      break;
    }
170

171
  if (!aa_savable (drawable_ID)) 
172
    {
Sven Neumann's avatar
Sven Neumann committed
173
      status = GIMP_PDB_CALLING_ERROR;
174
    }
175

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

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

Sven Neumann's avatar
Sven Neumann committed
201
	case GIMP_RUN_WITH_LAST_VALS:
202 203 204 205 206 207 208
	  gimp_get_data ("file_aa_save", &last_type);
	  output_type = last_type;
	  break;

	default:
	  break;
	}
209
    }
210

Sven Neumann's avatar
Sven Neumann committed
211
  if (status == GIMP_PDB_SUCCESS)
212
    {
213 214
      if (save_aa (output_type, param[3].data.d_string, image_ID, drawable_ID))
	{
Sven Neumann's avatar
Sven Neumann committed
215
	  status = GIMP_PDB_EXECUTION_ERROR;
216 217 218 219 220 221
	}
      else
	{
	  last_type = output_type;
	  gimp_set_data ("file_aa_save", &last_type, sizeof (last_type));
	}
222
    }
223

224 225
  if (export == EXPORT_EXPORT)
    gimp_image_delete (image_ID);  
226 227

  values[0].data.d_status = status;
228 229 230 231 232 233
}

/**
 * The actual save function. What it's all about.
 * The image type has to be GRAY.
 */
234 235 236
static gboolean
save_aa (gint    output_type, 
	 gchar  *filename, 
237
	 gint32  image_ID,
238
	 gint32  drawable_ID)
239
{
240 241
  aa_savedata savedata = {NULL, NULL};
  aa_context *context  = NULL;
Sven Neumann's avatar
Sven Neumann committed
242
  GimpDrawable *drawable  = NULL;
243
  aa_format format;
244

245
  /*fprintf(stderr, "save %s\n", filename); */
246

247 248 249 250
  drawable = gimp_drawable_get (drawable_ID);
  memcpy (&format, aa_formats[output_type], sizeof (format));
  format.width = drawable->width / 2;
  format.height = drawable->height / 2;
251

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

254 255 256
  /* Get a libaa context which will save its output to filename. */
  savedata.name = filename;
  savedata.format = &format;
257

258 259
  context = aa_init (&save_d, &aa_defparams, &savedata);
  if (context == NULL)
260 261
    return TRUE;

262
  gimp2aa (image_ID, drawable_ID, context);
263 264
  aa_flush (context);
  aa_close (context);
265

266
  /*fprintf(stderr, "Success!\n"); */
267 268

  return FALSE;
269 270
}

271
static void
272 273 274
gimp2aa (gint32      image, 
	 gint32      drawable_ID, 
	 aa_context *context)
275
{
276
  gint width, height, x, y;
277
  guchar *buffer;
Sven Neumann's avatar
Sven Neumann committed
278 279
  GimpDrawable *drawable = NULL;
  GimpPixelRgn pixel_rgn;
280
  aa_renderparams *renderparams = NULL;
281 282
  gint bpp;

283 284
  width = aa_imgwidth (context);
  height = aa_imgheight (context);
285
  /*fprintf(stderr, "gimp2aa %i x %i\n", width, height); */
286 287 288

  drawable = gimp_drawable_get (drawable_ID);

289
  bpp = drawable->bpp;
290 291
  buffer = g_new (guchar, width * bpp);

292 293 294 295 296 297 298 299 300 301
  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. */
302
	  aa_putpixel (context, x, y, buffer[x * bpp]);
303
	}
304
    }
305

306 307 308
  renderparams = aa_getrenderparams ();
  renderparams->dither = AA_FLOYD_S;
  aa_render (context, renderparams, 0, 0, 
309
	     aa_scrwidth (context), aa_scrheight (context));
310 311
}

312
static gboolean 
313
aa_savable (gint32 drawable_ID)
314
{
Sven Neumann's avatar
Sven Neumann committed
315
  GimpImageType drawable_type;
316

317
  drawable_type = gimp_drawable_type (drawable_ID);
318

Sven Neumann's avatar
Sven Neumann committed
319
  if (drawable_type != GIMP_GRAY_IMAGE && drawable_type != GIMP_GRAYA_IMAGE)
320 321 322
    return FALSE;

  return TRUE;
323 324 325 326 327 328
}

/* 
 * User Interface dialog thingie.
 */

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

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

344 345
			 _("OK"), gtk_widget_destroy,
			 NULL, 1, NULL, TRUE, FALSE,
346 347 348 349
			 _("Cancel"), type_dialog_cancel_callback,
			 NULL, NULL, NULL, FALSE, TRUE,

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

351
  gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
352
		      GTK_SIGNAL_FUNC (gtk_main_quit),
353
		      NULL);
354 355

  /*  file save type  */
356 357 358 359 360 361 362 363
  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);
364 365 366 367
  
  group = NULL;
  {
    aa_format **p = aa_formats;
368 369
    gint current = 0;

370 371 372
    while (*p != NULL) 
      {
	toggle = gtk_radio_button_new_with_label (group, (*p)->formatname);
373 374 375 376
	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),
377 378
			    (*p)->formatname);
	if (current == selected)
379
	  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
380 381 382 383 384 385
	
	gtk_widget_show (toggle);
	p++;
	current++;
      }
  }
386

387 388
  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);
389

390 391 392 393 394 395
  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return selected_type;
396 397 398 399 400 401
}

/*
 * Callbacks for the dialog.
 */

402 403 404 405 406
static void 
type_dialog_cancel_callback (GtkWidget *widget, 
			     gpointer   data) 
{
  selected_type = -1;
407 408 409
  gtk_widget_destroy (GTK_WIDGET (data));
}

410 411 412 413
static void 
type_dialog_toggle_update (GtkWidget *widget, 
			   gpointer   data) 
{
414
  selected_type = get_type_from_string ((char *) data);
415
}