mirror of https://github.com/GNOME/gimp.git
536 lines
15 KiB
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;
|
|
}
|