aa.c 9.65 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
  };

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

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

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

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

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

120
  return type;
121 122
}

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

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

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

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

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

Sven Neumann's avatar
Sven Neumann committed
175
  if (status == GIMP_PDB_SUCCESS)
176
    {
177 178
      switch (run_mode) 
	{
Sven Neumann's avatar
Sven Neumann committed
179
	case GIMP_RUN_INTERACTIVE:
180 181 182
	  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
183
	    status = GIMP_PDB_CANCEL;
184 185
	  break;

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

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

	default:
	  break;
	}
208
    }
209

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

223
  if (export == GIMP_EXPORT_EXPORT)
224
    gimp_image_delete (image_ID);  
225 226

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

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

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

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

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

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

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

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

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

  return FALSE;
268 269
}

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

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

  drawable = gimp_drawable_get (drawable_ID);

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

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

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

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

316
  drawable_type = gimp_drawable_type (drawable_ID);
317

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

  return TRUE;
322 323 324 325 326 327
}

/* 
 * User Interface dialog thingie.
 */

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

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

343
			 GTK_STOCK_CANCEL, type_dialog_cancel_callback,
344 345
			 NULL, NULL, NULL, FALSE, TRUE,

Sven Neumann's avatar
Sven Neumann committed
346 347 348
			 GTK_STOCK_OK, gtk_widget_destroy,
			 NULL, 1, NULL, TRUE, FALSE,

349
			 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
  
  group = NULL;
  {
367
    aa_format **p = (aa_format **) 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
			    (gpointer) (*p)->formatname);
378
	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
}