/* Ellipse * Copyright (C) Bernhard C. Maerz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * History: * V1.0.3 (02nd May 2001) * - added Windows compiled EXE for V1.2 * V1.0.2 (30th Mar. 2001) * - added a small Readme.txt file. * - added Windows compiled EXE for V1.1 * V1.0.1 (8th Aug. 1999) * - on some machines there was a problem in compiling with '\' at the * end of a line. * V1.0 (5th Aug. 1999) * - No Changes, only the Version is changed to final * V1.0beta (15. Jun. 1999) * - Make one Qualitybar instead of before "X-Quality" and "Y-Quality" * V0.3 (3rd Dec. 1998) * - Finally there is a GUI :-) * V0.2 (5th Nov. 1998) * - GUI is still missing :-( (but will follow soon) * - Create Image * - Scale to Circle * - Improved Quality by Scaling Up and Down !!! * - Possibility of changing the Backcolor * V0.1 (2nd Nov. 1998) * first release */ //#define GIMP_ENABLE_COMPAT_CRUFT #include #include #include #include #include "libgimp/gimp.h" #include "gtk/gtk.h" typedef struct { gint circleBool; gint new_image; gdouble XScale; gdouble YScale; gint BackColor; } SettingsTyp; typedef struct { gint run; } InterfaceTyp; static SettingsTyp Settings = { TRUE, /* make circle */ TRUE, /* new_image: will be set to TRUE, if MakeCircle or Scale>1 */ 1, /* XScale */ 1, /* YScale */ 255, /* BackColor */ }; static InterfaceTyp Interface = { FALSE, /* run */ }; /* Declare local functions. */ static void query(void); static void run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals); static gint32 ellipse(gint32 image_id,gint32 drawable_id,gint32 *layer_id); static void Dialog_close_callback (GtkWidget *widget,gpointer data); static void Dialog_ok_callback (GtkWidget *widget,gpointer data); static void Dialog_toggle_update (GtkWidget *widget,gpointer data); static void Dialog_entry_update(GtkWidget *widget, gdouble *value); static void Dialog_scale_update (GtkAdjustment *adjustment, gdouble *value); static void Dialog_create_value(char *title,GtkTable *table,int row, gdouble *value,double left,double right, gdouble IsInteger); static gint use_dialog (); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; MAIN () static void query () { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, }; static GimpParamDef *return_vals = NULL; static int nargs = sizeof (args) / sizeof (args[0]); static int nreturn_vals = 0; gimp_install_procedure ("plug_in_make_ellipse", "Makes an ellipse", "This plugin transforms the image to an ellipse (or circle)", "Bernhard C. Maerz", "Bernhard C. Maerz", "2001", "/Filters/Glass Effects/Ellipse", "RGB*, GRAY*, INDEXED*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } static void Dialog_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void Dialog_ok_callback (GtkWidget *widget, gpointer data) { Interface.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void Dialog_toggle_update (GtkWidget *widget, gpointer data) { gint *toggle_val; toggle_val = (int *) data; if (GTK_TOGGLE_BUTTON (widget)->active) *toggle_val = TRUE; else *toggle_val = FALSE; } /* * "Thanks to Quartic for these Dialog Functions." (And I got it from "noisify.c") */ #define SCALE_WIDTH 125 #define ENTRY_WIDTH 60 static void Dialog_entry_update(GtkWidget *widget, gdouble *value) { GtkAdjustment *adjustment; gdouble new_value; new_value = atof(gtk_entry_get_text(GTK_ENTRY(widget))); if (*value != new_value) { adjustment = gtk_object_get_user_data(GTK_OBJECT(widget)); if ((new_value >= adjustment->lower) && (new_value <= adjustment->upper)) { *value = new_value; adjustment->value = new_value; gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed"); } } } static void Dialog_scale_update (GtkAdjustment *adjustment, gdouble *value) { GtkWidget *entry; char buf[256]; if (*value != adjustment->value) { *value = adjustment->value; entry = gtk_object_get_user_data(GTK_OBJECT(adjustment)); if (entry->private_flags) sprintf(buf, "%0.0f", *value); else sprintf(buf, "%0.2f", *value); gtk_signal_handler_block_by_data(GTK_OBJECT(entry), value); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), value); } } static void Dialog_create_value(char *title, GtkTable *table, int row, gdouble *value, double left, double right, gdouble IsInteger) { GtkWidget *label; GtkWidget *scale; GtkWidget *entry; GtkObject *scale_data; char buf[256]; label = gtk_label_new(title); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(label); scale_data = gtk_adjustment_new(*value, left, right, (right - left) / 200.0, (right - left) / 200.0, 0.0); gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed", (GtkSignalFunc) Dialog_scale_update, value); scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data)); gtk_widget_set_usize(scale, SCALE_WIDTH, 0); gtk_table_attach(table, scale, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE); gtk_scale_set_digits(GTK_SCALE(scale), 3); gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS); gtk_widget_show(scale); entry = gtk_entry_new(); gtk_object_set_user_data(GTK_OBJECT(entry), scale_data); gtk_object_set_user_data(scale_data, entry); gtk_widget_set_usize(entry, ENTRY_WIDTH, 0); entry->private_flags = IsInteger; if (entry->private_flags) sprintf(buf, "%0.0f", *value); else sprintf(buf, "%0.2f", *value); gtk_entry_set_text(GTK_ENTRY(entry), buf); gtk_signal_connect(GTK_OBJECT(entry), "changed", (GtkSignalFunc) Dialog_entry_update, value); gtk_table_attach(GTK_TABLE(table), entry, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, 4, 0); gtk_widget_show(entry); } static gint use_dialog () { GtkWidget *dlg; GtkWidget *button; GtkWidget *toggle; GtkWidget *table; GtkWidget *frame; gchar **argv; gint argc; gdouble BackColor; BackColor = (gdouble) Settings.BackColor; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("Ellipse"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), "Ellipse"); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) Dialog_close_callback, NULL); /* Action area */ button = gtk_button_new_with_label ("OK"); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) Dialog_ok_callback, dlg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); button = gtk_button_new_with_label ("Cancel"); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); /* parameter settings */ frame = gtk_frame_new ("Parameter Settings"); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); table = gtk_table_new (6, 3, FALSE); gtk_container_border_width (GTK_CONTAINER (table), 10); gtk_container_add (GTK_CONTAINER (frame), table); gtk_table_set_row_spacing (GTK_TABLE (table), 2, 10); gtk_table_set_row_spacing (GTK_TABLE (table), 3, 10); gtk_table_set_col_spacing (GTK_TABLE (table), 0, 5); Dialog_create_value("Quality", GTK_TABLE(table), 1, &Settings.XScale, 1.0, 5.0, FALSE); Dialog_create_value("Back Color", GTK_TABLE(table), 3, &BackColor, 0, 255, TRUE); toggle = gtk_check_button_new_with_label ("New Image (Autoselected if \"Make Circle\" or \"Quality > 1\")"); gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 4,5, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) Dialog_toggle_update, &Settings.new_image); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), Settings.new_image); gtk_widget_show (toggle); toggle = gtk_check_button_new_with_label ("Make Circle"); gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) Dialog_toggle_update, &Settings.circleBool); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), Settings.circleBool); gtk_widget_show (toggle); gtk_widget_show (table); gtk_widget_show (frame); gtk_widget_show (dlg); gtk_main (); gdk_flush (); Settings.YScale = Settings.XScale; Settings.BackColor = (gint) BackColor; if ((Settings.XScale > 1) || (Settings.YScale > 1) || (Settings.circleBool)) Settings.new_image = TRUE; return Interface.run; } static void run (char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) { static GimpParam values[3]; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 new_layer; run_mode = param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[1].type = GIMP_PDB_IMAGE; values[2].type = GIMP_PDB_LAYER; switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data ("plug_in_make_ellipse", &Settings); /* First acquire information with a dialog */ if (! use_dialog ()) return; break; case GIMP_RUN_NONINTERACTIVE: /* ### */ case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data ("plug_in_make_ellipse", &Settings); break; default: break; } gimp_progress_init("Transforming..."); gimp_tile_cache_ntiles (2 * (gimp_drawable_width (param[2].data.d_drawable)+1 / gimp_tile_width () + 1)); values[1].data.d_image = ellipse(param[1].data.d_image, param[2].data.d_drawable, &new_layer); values[2].data.d_layer = new_layer; /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data ("plug_in_make_ellipse", &Settings, sizeof (SettingsTyp)); if (run_mode != GIMP_RUN_NONINTERACTIVE) { if (Settings.new_image) gimp_display_new (values[1].data.d_image); else gimp_displays_flush (); } values[0].data.d_status = status; } static gint32 ellipse(gint32 image_id,gint32 drawable_id,gint32 *layer_id) { GimpDrawable *drawable; long width, height; long bytes; GimpPixelRgn srcPR, destPR; GimpImageBaseType image_type; gint x1, y1, x2, y2; gint row, col, c; guchar *cur_row, *cur_pixel; short row2, col2; float row_norm, col_norm; int maxHeight; gint32 new_image_id; gint32 new_layer_id; GimpDrawable *new_drawable; /* initialize */ new_image_id = 0; new_layer_id = 0; image_type = GIMP_RGB; /* Get the specified drawable */ drawable = gimp_drawable_get (drawable_id); /* Get the input */ width = drawable->width; height = drawable->height; bytes = drawable->bpp; gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2); width = Settings.XScale*width; height = Settings.YScale*height; if (Settings.circleBool) maxHeight = width; else maxHeight = height; /* allocate buffers */ cur_row = (guchar *) malloc (width * bytes); cur_pixel = (guchar *) malloc (bytes); if (Settings.new_image) { x1 = 0; y1 = 0; x2 = width; y2 = height; /* Create new Image with one Layer */ switch (gimp_drawable_type (drawable_id)) { case GIMP_RGB_IMAGE : case GIMP_RGBA_IMAGE: image_type = GIMP_RGB; break; case GIMP_GRAY_IMAGE : case GIMP_GRAYA_IMAGE: image_type = GIMP_GRAY; break; case GIMP_INDEXED_IMAGE : case GIMP_INDEXEDA_IMAGE: image_type = GIMP_INDEXED; break; } new_image_id=gimp_image_new(width/Settings.XScale, maxHeight/Settings.YScale, image_type); new_layer_id = gimp_layer_new (new_image_id, "Background", width, maxHeight, gimp_drawable_type (drawable_id), 100, GIMP_NORMAL_MODE); gimp_image_add_layer (new_image_id, new_layer_id, 0); new_drawable = gimp_drawable_get (new_layer_id); /* copy the colormap, if necessary */ if (image_type == GIMP_INDEXED) { int ncols; guchar *cmap; cmap = gimp_image_get_cmap (image_id, &ncols); gimp_image_set_cmap (new_image_id, cmap, ncols); g_free (cmap); } } else new_drawable = gimp_drawable_get (drawable_id); /* Initialize Region */ gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init (&destPR, new_drawable, 0, 0, width, maxHeight, TRUE, TRUE); y2 = y2 * maxHeight / height; for (row = y1; row < y2; row++) { gimp_pixel_rgn_get_row(&destPR, cur_row, x1, row, (x2-x1)); row_norm = 2 * (float) row / maxHeight - 1; for (col = x1; col < x2; col++) { col_norm = 2 * (float) col / width - 1; if (row_norm * row_norm + col_norm * col_norm <= 1) { if (1 - row_norm * row_norm > 0) col2 = width / 2 * (1+col_norm / sqrt (1 - row_norm*row_norm)); else col2 = col; if (1 - col_norm * col_norm > 0) row2 = height / 2 * (1+row_norm / sqrt (1 - col_norm*col_norm)); else row2 = row; gimp_pixel_rgn_get_pixel(&srcPR, cur_pixel, col2/Settings.XScale, row2/Settings.YScale); for (c=0; cid, TRUE); gimp_drawable_update(new_drawable->id, x1, y1, (x2-x1), (y2-y1)); if (Settings.new_image) /* ### 1=Create new Image ; 0=use same Image */ { gimp_progress_init("Scaling..."); gimp_progress_update(0); gimp_layer_scale (new_layer_id, width/Settings.XScale, maxHeight/Settings.YScale, 0); } gimp_progress_update(1); return new_image_id; }