aa.c 9.51 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
static void       query      (void);
static void       run        (gchar      *name, 
			      gint        nparams, 
			      GParam     *param, 
			      gint       *nreturn_vals, 
			      GParam    **return_vals);
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 54 55

/* 
 * Some global variables.
 */

GPlugInInfo PLUG_IN_INFO =
{
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
{
73 74 75 76 77 78 79 80 81
  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"}
  };
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 92 93 94 95
			  "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);

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, 
127
     GParam  *param, 
128
     gint    *nreturn_vals,
129
     GParam **return_vals)
130
{
131
  static GParam values[2];
132 133 134 135 136 137
  GRunModeType  run_mode;
  GStatusType   status = STATUS_SUCCESS;
  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 143 144
  *return_vals  = values;
  values[0].type          = PARAM_STATUS;
  values[0].data.d_status = STATUS_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 153 154
  /*  eventually export the image */ 
  switch (run_mode)
    {
    case RUN_INTERACTIVE:
    case 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)
	{
162
	  values[0].data.d_status = STATUS_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
    {
173
      status = STATUS_CALLING_ERROR;
174
    }
175

176
  if (status == STATUS_SUCCESS)
177
    {
178 179 180 181 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
      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;
	}
209
    }
210

211
  if (status == STATUS_SUCCESS)
212
    {
213 214 215 216 217 218 219 220 221
      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));
	}
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 242 243
  aa_savedata savedata = {NULL, NULL};
  aa_context *context  = NULL;
  GDrawable *drawable  = NULL;
  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 278 279 280
  guchar *buffer;
  GDrawable *drawable = NULL;
  GPixelRgn pixel_rgn;
  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
{
315
  GDrawableType drawable_type;
316

317
  drawable_type = gimp_drawable_type (drawable_ID);
318

319
  if (drawable_type != GRAY_IMAGE && drawable_type != 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
}