gimp/plug-ins/refract/refmain.c

536 lines
15 KiB
C

/* refmain.c, 1/2/98 - this file contains startup routine and dialogs.
* refract: A plug-in for the GIMP 0.99.
* Uses a height field as a lens of specified refraction index.
*
* by Kevin Turner <kevint@poboxes.com>
* http://www.poboxes.com/kevint/gimp/refract.html
*/
/* I require megawidgets to compile! A copy was probably compiled in
the plug-ins directory of your GIMP source distribution, it will
work nicely. Just move me or it somewhere I can see it... */
/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifdef REFRACT_DEBUG
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#endif /* DEBUG */
#include <stdlib.h>
#include <math.h> /* It's not clear to me if this needs be here or no... */
#include "refract.h"
#include "libgimp/gimp.h"
#include "libgimp/gimpui.h"
/* megawidget.h could be in any of several places relative to us... */
/* should this be an autoconf thing? */
#ifdef HAVE_CONFIG_H /* We're part of the GIMP distribution. */
#include <plug-ins/megawidget/megawidget.h>
#else
#include "megawidget.h"
/* #include <megawidget.h> */
#endif
typedef struct {
gint run;
} RefractInterface;
/* go_refract is in refguts.c */
extern void go_refract(GDrawable *drawable,
gint32 image_id);
static void query (void);
static void run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals);
static gint refract_dialog();
static gint map_constrain(gint32 image_id,
gint32 drawable_id,
gpointer data);
static void newl_toggle_callback (GtkWidget *widget,
gpointer data);
static void tooltips_toggle_callback (GtkWidget *widget,
gpointer data);
static void refract_close_callback(GtkWidget *widget,
gpointer data);
static void refract_ok_callback(GtkWidget *widget,
gpointer data);
static void map_menu_callback (gint32 id,
gpointer data);
static GtkWidget* ior_menu_new(GtkWidget *tieto);
static void ior_menu_callback (GtkWidget *widget,
gfloat *data);
GPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
/* refractvals defined in refract.h */
/* not static, used in refguts.c */
RefractValues refractvals =
{
-1, /* Lens map ID */
-1, /* Reflection source ID */
32, /* lens thickness */
0, /* lens to image distance */
64, /* lens to reflection source distance */
1.0003, /* index a */
1.333, /* index b */
WRAP, /* wrap behaviour */
FALSE, /* new layer? */
0, /* offset x */
0, /* offset y */
};
static RefractInterface refractint =
{
FALSE /* run */
};
MAIN ()
static void
query ()
{
static GParamDef args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image" },
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
/* If we did have parameters, these be them: */
{ PARAM_DRAWABLE, "lens_id", "Lens map drawable" },
{ PARAM_DRAWABLE, "refl_id", "Reflection source drawable." },
{ PARAM_INT32, "thick", "Lens thickness" },
{ PARAM_INT32, "refr_dist", "Lens distance from image" },
{ PARAM_INT32, "refl_dist", "Lens distance from reflection source" },
{ PARAM_FLOAT, "na", "Index of Refraction A" },
{ PARAM_FLOAT, "nb", "Index of Refraction B" },
{ PARAM_INT32, "edge", "Background (0), Outside (1), Wrap (2)" },
{ PARAM_INT32, "newl", "New layer?" },
{ PARAM_INT32, "xofs", "X offset" },
{ PARAM_INT32, "yofs", "Y offset" }
};
static GParamDef *return_vals = NULL;
static int nargs = sizeof (args) / sizeof (args[0]);
static int nreturn_vals = 0;
gimp_install_procedure ("plug_in_refract",
"Uses a height field as a lens.",
"Distorts the image by refracting it through a height field 'lens' with a specified index of refraction.",
"Kevin Turner <kevint@poboxes.com>",
"Kevin Turner",
"1997",
"<Image>/Filters/Glass Effects/Refract",
"RGB*, GRAY*, INDEXED*",
PROC_PLUG_IN,
nargs, nreturn_vals,
args, return_vals);
} /* query */
static void
run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals)
{
static GParam values[1];
GDrawable *drawable;
GRunModeType run_mode;
GStatusType status = STATUS_SUCCESS;
#ifdef REFRACT_DEBUG
printf("refract: pid %d\n", getpid());
#endif
/* values=g_new(GParam,1); */
run_mode = param[0].data.d_int32;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = PARAM_STATUS;
values[0].data.d_status = status;
drawable = gimp_drawable_get (param[2].data.d_drawable);
switch (run_mode) {
case RUN_INTERACTIVE:
/* Possibly retrieve data */
gimp_get_data ("plug_in_refract", &refractvals);
/* Acquire info with a dialog */
if (! refract_dialog ()) {
gimp_drawable_detach (drawable);
return;
}
break;
case RUN_NONINTERACTIVE:
if (status == STATUS_SUCCESS) {
refractvals.lens_id = param[3].data.d_drawable;
refractvals.refl_id = param[4].data.d_int32;
refractvals.thick = param[5].data.d_int32;
refractvals.refr_dist = param[6].data.d_float;
refractvals.refl_dist = param[7].data.d_float;
refractvals.na = param[8].data.d_int32;
refractvals.nb = param[9].data.d_int32;
refractvals.edge = param[10].data.d_int32;
refractvals.newl = param[11].data.d_int32;
refractvals.xofs = param[12].data.d_int32;
refractvals.yofs = param[13].data.d_int32;
} /* if */
break;
case RUN_WITH_LAST_VALS:
/* Possibly retrieve data */
gimp_get_data ("plug_in_refract", &refractvals);
break;
default:
break;
} /* switch run_mode */
if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id)) {
gimp_progress_init ("Doing optics homework...");
/* What's this do? */
gimp_tile_cache_ntiles(2 * (drawable->width + gimp_tile_width() - 1)
/ gimp_tile_width());
go_refract (drawable, param[1].data.d_image);
if (run_mode != RUN_NONINTERACTIVE)
gimp_displays_flush ();
if (run_mode == RUN_INTERACTIVE /*|| run_mode == RUN_WITH_LAST_VALS*/)
gimp_set_data ("plug_in_refract", &refractvals, sizeof (RefractValues));
} else {
status = STATUS_EXECUTION_ERROR;
}
values[0].data.d_status = status;
} /* run */
static gint
refract_dialog()
{
gint argc;
gchar **argv;
GtkTooltips *tooltips;
GtkWidget *menu, *option_menu, *ior_a_menu, *ior_b_menu;
GtkWidget *ok_button, *cancel_button;
GtkWidget *layercheck, *toolcheck;
GtkWidget *dlg;
GtkWidget *table;
GtkWidget *label;
#ifdef REFRACT_DEBUG
#if 0
printf("refract: waiting... (pid %d)\n", getpid());
kill(getpid(), SIGSTOP);
#endif
#endif
/* Standard GTK startup sequence */
argc = 1;
argv = g_new (gchar *, 1);
argv[0] = g_strdup ("refract");
gtk_init (&argc, &argv);
gdk_set_use_xshm(gimp_use_xshm());
/* FIXME: Can we use the GIMP colormap when in 8-bit to reduce flashing? */
/* end standard GTK startup */
/* I guess we need a window... */
dlg = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(dlg), REFRACT_TITLE);
gtk_signal_connect(GTK_OBJECT(dlg), "destroy",
(GtkSignalFunc) refract_close_callback,
NULL);
tooltips = gtk_tooltips_new ();
/* Action area: */
/* OK */
ok_button = gtk_button_new_with_label ("OK");
GTK_WIDGET_SET_FLAGS (ok_button, GTK_CAN_DEFAULT);
gtk_signal_connect (GTK_OBJECT (ok_button), "clicked",
(GtkSignalFunc) refract_ok_callback, dlg);
gtk_widget_grab_default (ok_button);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), ok_button,
TRUE, TRUE, 0);
gtk_widget_show (ok_button);
/* Cancel */
cancel_button = gtk_button_new_with_label ("Cancel");
GTK_WIDGET_SET_FLAGS (cancel_button, GTK_CAN_DEFAULT);
gtk_signal_connect_object (GTK_OBJECT (cancel_button), "clicked",
(GtkSignalFunc) refract_close_callback,
NULL);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), cancel_button,
TRUE, TRUE, 0);
gtk_widget_show (cancel_button);
/* Paramater settings: */
table = gtk_table_new(7, 3, FALSE);
gtk_container_add(GTK_CONTAINER (GTK_DIALOG (dlg)->vbox),table);
gtk_widget_show (table);
/* FIXME: add preview box */
/* drop box for lens map */
label = gtk_label_new("Lens map");
option_menu = gtk_option_menu_new();
menu = gimp_drawable_menu_new(map_constrain, map_menu_callback,
NULL, refractvals.lens_id);
gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu),menu);
gtk_tooltips_set_tips (tooltips, option_menu,
"The drawable to use as the lens.");
gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,0,1);
gtk_table_attach_defaults(GTK_TABLE(table),option_menu,1,3,0,1);
gtk_widget_show(label);
gtk_widget_show(option_menu);
/* TODO? Add "Invert lens map" Not anytime soon... */
/* Would require adding all sorts of conditional subtracting stuff
in the main loop... Let them invert it first! :) */
/* Eek. Megawidgets don't return a value I can tie tooltips to.
Maybe I should look in to libgck. */
/* entry/scale for lens thickness */
mw_iscale_entry_new(table, "Thickness",
0, 256,
1, 10, 0,
0, 2, 1, 2,
&refractvals.thick);
/* entry/scale pair for distance */
mw_iscale_entry_new(table, "Distance",
0, 1000,
1, 10, 0/*what's this do?*/,
0, 2, 2, 3,
&refractvals.refr_dist);
/* a entry/scale/drop-menu for each index */
mw_fscale_entry_new(table, "Index A",
INDEX_SCALE_MIN, INDEX_SCALE_MAX,
1.0, 0.1, 0,
0,1, 3, 4,
&refractvals.na);
ior_a_menu = ior_menu_new(NULL/*FIXME*/);
gtk_table_attach_defaults(GTK_TABLE(table),ior_a_menu,2,3,3,4);
gtk_widget_show (ior_a_menu);
gtk_tooltips_set_tips (tooltips, ior_a_menu,
"FIXME (No, it doesn't work.)");
mw_fscale_entry_new(table, "Index B",
INDEX_SCALE_MIN, INDEX_SCALE_MAX,
1.0, 0.1, 0,
0, 1, 4, 5,
&refractvals.nb);
ior_b_menu = ior_menu_new(NULL/*FIXME*/);
gtk_table_attach_defaults(GTK_TABLE(table),ior_b_menu,2,3,4,5);
gtk_widget_show (ior_b_menu);
gtk_tooltips_set_tips (tooltips, ior_b_menu,
"FIXME (No, it doesn't work.)");
/* entry/scale pairs for x and y offsets */
mw_iscale_entry_new(table, "X Offset",
-1000, 1000,
1, 20, 0,
0,2, 5, 6,
&refractvals.xofs);
mw_iscale_entry_new(table, "Y Offset",
-1000, 1000,
1, 20, 0,
0,2, 6, 7,
&refractvals.yofs);
/* radio buttons for wrap/transparent (or bg, if image isn't layered) */
/* button = gtk_check_button_new_with_label ("Wrap?");
toggle_button_callback (button, gpointer data);
gtk_toggle_button_set_state (GtkToggleButton button, refractvals.edge); */
/* Make new layer(s) or dirty the old? */
layercheck = gtk_check_button_new_with_label ("New layer?");
gtk_container_add(GTK_CONTAINER (GTK_DIALOG (dlg)->vbox),layercheck);
gtk_signal_connect (GTK_OBJECT (layercheck), "clicked",
GTK_SIGNAL_FUNC (newl_toggle_callback), NULL);
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (layercheck), refractvals.newl);
gtk_tooltips_set_tips (tooltips, layercheck,
"Put the refracted image on a new layer or dirty this one?");
gtk_widget_show (layercheck);
toolcheck = gtk_check_button_new_with_label ("Tooltips?");
gtk_container_add(GTK_CONTAINER (GTK_DIALOG (dlg)->vbox),toolcheck);
gtk_signal_connect (GTK_OBJECT (toolcheck), "clicked",
GTK_SIGNAL_FUNC (tooltips_toggle_callback), (gpointer) tooltips);
gtk_tooltips_set_tips (tooltips, toolcheck,
"Turn off these dumb tooltips.");
gtk_widget_show (toolcheck);
/* Tooltips OFF by default. */
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toolcheck), FALSE);
gtk_tooltips_disable (tooltips);
gtk_widget_show (dlg);
gtk_main ();
gdk_flush ();
return refractint.run;
} /* refract_dialog */
static GtkWidget*
ior_menu_new(GtkWidget *tieto)
{
GtkWidget *chooser;
GtkWidget *menu, *menuitem;
guint i;
struct foo
{
const gfloat index;
const gchar *name;
};
/* If you change stuff, don't forget to change this. */
#define NUMSTUFF 9
static const struct foo material[NUMSTUFF] =
{
/* Common indicies of refraction (for yellow sodium light, 589 nm) */
/* From my Sears, Zemansky, Young physics book. */
/* For more, check your copy of the CRC or your favorite pov-ray
include file. */
{ 1.0003, "Air" },
{ 1.309, "Ice" },
{ 1.333, "Water"},
{ 1.36, "Alcohol"},
{ 1.473, "Glycerine"},
{ 1.52, "Glass"},
{ 1.544, "Quartz"},
{ 1.923, "Zircon"},
{ 2.417, "Diamond"},
};
chooser = gtk_option_menu_new();
menu = gtk_menu_new();
for (i=0; i < NUMSTUFF; i++) {
menuitem = gtk_menu_item_new_with_label(material[i].name);
gtk_menu_append(GTK_MENU(menu), menuitem);
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
(GtkSignalFunc)ior_menu_callback,(gfloat *)&material[i].index);
gtk_widget_show(menuitem);
}; /* next i */
gtk_option_menu_set_menu(GTK_OPTION_MENU(chooser), menu);
return chooser;
}
static void
ior_menu_callback (GtkWidget *widget, gfloat *data)
{
#ifdef REFRACT_DEBUG
printf("%f\n",*data);
#endif
}
static gint
map_constrain(gint32 image_id, gint32 drawable_id, gpointer data)
{
if (drawable_id == -1)
return TRUE;
return (gimp_drawable_color(drawable_id) || gimp_drawable_gray(drawable_id));
} /* map_constrain */
/* Callbacks */
static void
newl_toggle_callback (GtkWidget *widget, gpointer data)
{
refractvals.newl = GTK_TOGGLE_BUTTON (widget)->active;
}
static void
tooltips_toggle_callback (GtkWidget *widget, gpointer data)
{
GtkTooltips *tooltips;
tooltips= (GtkTooltips *) data;
if (GTK_TOGGLE_BUTTON (widget)->active)
gtk_tooltips_enable (tooltips);
else
gtk_tooltips_disable (tooltips);
}
static void
refract_close_callback (GtkWidget *widget,
gpointer data)
{
gtk_main_quit ();
}
static void
refract_ok_callback (GtkWidget *widget, gpointer data)
{
refractint.run = TRUE;
gtk_widget_destroy (GTK_WIDGET (data));
}
static void
map_menu_callback (gint32 id, gpointer data)
{
refractvals.lens_id = id;
}