/* Water Surface -- creates waves textures plug-in. * Copyright (C) 2001 Kyoichiro Suda * * The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Water Surface plug-in version 1.2, Aug 2001 * This plug-in is simple implement of drawing waves like the water surface. * * Modified for 1.2 and the Graphics Muse Tools CD * by Michael J. Hammel * 01/2002 */ #include #include #include #include #include #include #include /* #include "libgimp/stdplugins-intl.h" */ #include #ifndef PACKAGE #define PACKAGE "gimp-plugins-das" #endif #ifdef ENAVLE_NLS #define INIT_I18N() G_STMT_START{ \ setlocale(LC_MESSAGES, ""); \ bindtextdomain("gimp-libgimp", LOCALEDIR); \ bindtextdomain(PACKAGE, LOCALEDIR); \ textdomain(PACKAGE); \ }G_STMT_END #else #define INIT_I18N() G_STMT_START{ \ bindtextdomain("gimp-libgimp", LOCALEDIR); \ bindtextdomain(PACKAGE, LOCALEDIR); \ textdomain(PACKAGE); \ }G_STMT_END #endif #define INIT_I18N_UI() G_STMT_START{ \ gtk_set_locale(); \ setlocale (LC_NUMERIC, "C"); \ INIT_I18N(); \ }G_STMT_END /*---- Defines ----*/ #define PLUG_IN_NAME "plug_in_water_surface" #define PLUG_IN_VERSION "Water Surface 1.1" #define PREVIEW_SIZE 100 #define SCALE_WIDTH 100 #define LINEAR 0 #define SINUSOIDAL 1 #define SPHERICAL 2 #define SAWTOOTH 3 #define TRIANGULAR 4 #define POTTER 5 #define HALFPIPE 6 #define RANDOM -1 #define AVERAGE 0 #define TERRITORY 1 #define GREATER 2 #define LESSER 3 /*---- Typedefs ----*/ typedef struct { gint points; gint timeseed; gint seed; gdouble scale; gdouble width; gdouble disorder; gdouble reduce; gdouble lag; gdouble slide_x; gdouble slide_y; gint tilable; gdouble flat; gdouble phase; gint style; gint mode; gint areas; } WSurfaceValues; typedef struct { gdouble xpos; gdouble ypos; gdouble width; gdouble vanish; gdouble phase; } Waves; typedef struct { guchar fg[4]; guchar bg[4]; } Color; /*---- Prototypes ----*/ static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static void water_surface (GimpDrawable *drawable); static void subvals_init (gint width, gint height); static Waves *waves_new (Waves *waves, gint width, gint height); static Waves *waves_tilable (Waves *waves, gint width, gint height); static gint render_waves (Waves *waves, GimpPixelRgn *region); static gdouble waves_areas (Waves *point, gdouble cycle, gdouble level); static gdouble pixel_level (Waves *waves, gfloat x, gfloat y); static gdouble waves_distance (Waves *point, gdouble x, gdouble y); static gdouble waves_cycle (Waves *point, gdouble length, gdouble phase); static gdouble waves_vanish (Waves *point, gdouble length); static gdouble waves_level (gdouble cycle); static void pixel_coloring (guchar *pixel, gdouble level); static void preview_water_surface (GimpDrawable *drawable); static GtkWidget *preview_widget (GimpDrawable *drawable); static void preview_update (GtkObject *adjustment, gpointer data); static void preview_update (GtkObject *obj, gpointer data); static void ws_int_adjustment_update (GtkAdjustment *adjustment, gpointer data); static void ws_double_adjustment_update (GtkAdjustment *adjustment, gpointer data); static void ws_toggle_button_update (GtkWidget *toggle, gpointer data); static void ws_menu_item_update (GtkWidget *widget, gpointer data); static gint water_surface_dialog (GimpDrawable *drawable); static void dialog_ok_callback (GtkWidget *widget, gpointer data); /*---- Variables ----*/ GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run /* run_proc */ }; static WSurfaceValues vals = { 16, /* points */ FALSE, /* seed_type */ 5910, /* seed */ 2.0, /* scale */ 0.30, /* width */ 0.5, /* disorder */ 0.00, /* reduce */ 1.0, /* lat */ 0.0, /* slide_x */ 0.0, /* slide_y */ FALSE, /* tilable */ 0.5, /* flat */ 0.0, /* phase */ HALFPIPE, /* style */ TERRITORY, /* mode */ 0 /* anti areasing */ }; static WSurfaceValues subvals ; static Color color; static gint has_alpha; static gint channels; static GtkWidget *preview; static GimpDrawable *_drawable; static gint do_preview = TRUE; typedef struct { gint run; } WSurfaceInterface; static WSurfaceInterface wint = { FALSE /* have we run? */ }; MAIN () static void query () { static GimpParamDef args[] = { { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, { GIMP_PDB_IMAGE, "image", "Input image" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }, { GIMP_PDB_INT32, "points", "Number of wave points ( > 1)" }, { GIMP_PDB_INT32, "timeseed", "Use time seed (TRUE/FALSE)" }, { GIMP_PDB_INT32, "seed", "Random seed" }, { GIMP_PDB_FLOAT, "scale", "Range ratio of pointing ( > 0)" }, { GIMP_PDB_FLOAT, "width", "Ratio of Wavelength ( > 0)" }, { GIMP_PDB_FLOAT, "disorder", "Disorder of width (0.0 - 1.0)" }, { GIMP_PDB_FLOAT, "reduce", "Decay ratio of wave (0 = eternity)" }, { GIMP_PDB_FLOAT, "lag", "Jump start ratio, Cycle phase lag (0.0 - 1.0)" }, { GIMP_PDB_FLOAT, "slide_x", "Slide view point ratio" }, { GIMP_PDB_FLOAT, "slide_y", "Slide view point ratio" }, { GIMP_PDB_INT32, "tilable", "Create a tilable output (TRUE/FALSE)" }, { GIMP_PDB_FLOAT, "flat", "Level to gain (0.0 - 1.0)" }, { GIMP_PDB_FLOAT, "phase", "Phase of cycle(0.0 - 1.0)" }, { GIMP_PDB_INT32, "style", "Mode of forming level (0: Linear, 1: Sinusoidal, 2: Sawtooth, 3: Triangular, 4: Potter, 3: Halfpipe, -1: Random)" }, { GIMP_PDB_INT32, "mode", "Mode of mixing level (0: Average, 1: Territory, 2: Greater, 3: Lesser)" }, { GIMP_PDB_INT32, "areas", "Anti areasing (TRUE/FALSE)" } }; static gint nargs = sizeof (args) / sizeof (args[0]); gimp_install_procedure (PLUG_IN_NAME, "Generate waves texture", "Generate waves texture like the water surface, " "it can make abstract image with many waves, " "also pop style with symple mathmatics.", "Kyoichiro Suda ", "Kyoichiro Suda", "Aug 2001", N_("/Filters/Render/Nature/Water Surface..."), "RGB*, GRAY*", GIMP_PLUGIN, nargs, 0, args, NULL); } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpDrawable *drawable; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; //gchar prog_label[32]; run_mode = param[0].data.d_int32; drawable = gimp_drawable_get(param[2].data.d_drawable); _drawable = drawable; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; *nreturn_vals = 1; *return_vals = values; INIT_I18N_UI (); if (gimp_drawable_is_rgb (drawable->id) || gimp_drawable_is_gray (drawable->id)) { switch (run_mode) { case GIMP_RUN_INTERACTIVE: gimp_get_data (PLUG_IN_NAME, &vals); if (!water_surface_dialog (drawable)) /* return on Cancel */ return; break; case GIMP_RUN_NONINTERACTIVE: if ((strcmp (name, PLUG_IN_NAME) == 0) && (nparams == 19)) { vals.points = (gint) param[3].data.d_int32; vals.points = (gint) CLAMP (vals.points, 0, 255); vals.timeseed = (param[4].data.d_int32) ? TRUE : FALSE; vals.seed = (gint) param[5].data.d_int32; vals.scale = (gdouble) param[6].data.d_float; vals.scale = (gdouble) MAX (0.0, vals.scale); vals.width = (gdouble) param[7].data.d_float; vals.width = (gdouble) MAX (0.0, vals.width); vals.disorder = (gdouble) param[8].data.d_float; vals.disorder = (gdouble) CLAMP (vals.disorder, 0.0, 1.0); vals.reduce = (gdouble) param[9].data.d_float; vals.reduce = (gdouble) MAX (0.0, vals.reduce); vals.lag = (gdouble) param[10].data.d_float; vals.lag = (gdouble) CLAMP (vals.lag, 0.0, 1.0); vals.slide_x = (gdouble) param[11].data.d_float; vals.slide_y = (gdouble) param[12].data.d_float; vals.tilable = (param[13].data.d_int32) ? TRUE : FALSE; vals.flat = (gdouble) param[14].data.d_float; vals.flat = (gdouble) CLAMP (vals.flat, 0.0, 1.0); vals.phase = (gdouble) param[15].data.d_float; vals.phase = (gdouble) CLAMP (vals.phase, 0.0, 1.0); vals.style = (gint) param[16].data.d_int32; vals.style = (gint) CLAMP (vals.style, -1, 9); vals.mode = (gint) param[17].data.d_int32; vals.mode = (gint) CLAMP (vals.mode, 0, 3); vals.areas = (param[18].data.d_int32) ? TRUE : FALSE; } else { status = GIMP_PDB_CALLING_ERROR; } break; case GIMP_RUN_WITH_LAST_VALS: gimp_get_data (PLUG_IN_NAME, &vals); break; default: break; } /* Create texture */ if (status == GIMP_PDB_SUCCESS) { /* Set the tile cache size (all col tiles ?) */ gimp_tile_cache_ntiles ((drawable->width + gimp_tile_width() - 1) / gimp_tile_width()); /* Run! */ water_surface (drawable); /* If we ran interactively (even repeating) update the display. */ if (run_mode != GIMP_RUN_NONINTERACTIVE) { gimp_displays_flush (); } /* If we use the dialog popup, set the data for future use. */ if (run_mode == GIMP_RUN_INTERACTIVE) { gimp_set_data (PLUG_IN_NAME, &vals, sizeof (WSurfaceValues)); } } } else { /* * If we got the wrong drawable type, we need to complain. */ status = GIMP_PDB_EXECUTION_ERROR; } /* * DONE! * Set the status where the GIMP can see it, and let go * of the drawable. */ values[0].data.d_status = status; gimp_drawable_detach (drawable); } static void subvals_init (gint width, gint height) { /* ratio to pixel */ subvals.width = (width < height ? width : height) / 2 * vals.width; subvals.slide_x = width * vals.scale * vals.slide_x; subvals.slide_y = height * vals.scale * vals.slide_y; subvals.seed = (vals.timeseed == TRUE) ? time (NULL) : vals.seed; subvals.areas = vals.areas; /* Get colors */ gimp_palette_get_foreground (&color.fg[0], &color.fg[1], &color.fg[2]); gimp_palette_get_background (&color.bg[0], &color.bg[1], &color.bg[2]); /* Channels and Alpha */ channels = gimp_drawable_bpp(_drawable->id); has_alpha = gimp_drawable_has_alpha(_drawable->id); if (has_alpha) channels --; } static Waves * waves_new (Waves *waves, gint width, gint height) { gint range_x = width * vals.scale; gint range_y = height * vals.scale; gint zero_x = width / 2; gint zero_y = height / 2; gint i; srand(subvals.seed); for (i = 0; i < vals.points; i ++) { waves->xpos = zero_x + (range_x * ((1.0*rand()/G_MAXRAND) - 0.5)); waves->ypos = zero_y + (range_y * ((1.0*rand()/G_MAXRAND) - 0.5)); waves->width = subvals.width + (vals.disorder * subvals.width * ((2.0*rand()/G_MAXRAND) - 1)); waves->vanish = (vals.reduce > 0) ? (waves->width / vals.reduce) : 0; waves->phase = (vals.lag * (1.0*rand()/RAND_MAX)); waves ++; } return waves; } static Waves * waves_tilable (Waves *waves, gint width, gint height) { Waves *tmpwaves; gint points; gint i, x, y; gint offset; tmpwaves = g_malloc (vals.points * sizeof(Waves)); /* pre parse */ points = 0; for (i = 0; i < vals.points; i ++) { /* slide */ (waves + i)->xpos -= subvals.slide_x; (waves + i)->ypos -= subvals.slide_y; if ((waves + i)->xpos > 0 && (waves + i)->xpos < width && (waves + i)->ypos > 0 && (waves + i)->ypos < height) { memcpy (tmpwaves + points, waves + i, sizeof(Waves)); points ++; } } subvals.points = points * 9; if (points < 1) { return NULL; } /* limit */ for (i=0; i < points; i ++) { if ((tmpwaves + i)->vanish == 0) { (tmpwaves + i)->vanish = MIN(width, height); } else { (tmpwaves + i)->vanish = MIN((tmpwaves + i)->vanish, width); (tmpwaves + i)->vanish = MIN((tmpwaves + i)->vanish, height); } } /* copy back 9th */ for (i = 0; i < 9; i ++) { memcpy (waves + (i * points), tmpwaves, (points * sizeof(Waves))); } g_free (tmpwaves); for (y = 0; y < 3; y ++) { for (x = 0; x < 3; x ++) { offset = points * ((y * 3) + x); for (i = 0; i< points; i ++) { (waves + offset + i)->xpos += width * (x - 1); (waves + offset + i)->ypos += height * (y - 1); } } } return waves; } static gdouble waves_distance (Waves *point, gdouble x, gdouble y) { gdouble length_x, length_y, length; x += subvals.slide_x; y += subvals.slide_y; if ((point->xpos * x) >= 0) length_x = abs(point->xpos - x); else length_x = abs(point->xpos) + abs(x); if ((point->ypos * y) >= 0) length_y = abs(point->ypos - y); else length_y = abs(point->ypos) + abs(y); length = hypot(length_x, length_y); return length; } static gdouble waves_cycle (Waves *point, gdouble length, gdouble phase) { gdouble cycle; cycle = fmod(((length / point->width) + point->phase - phase), 1); if (cycle < 0) cycle += 1; return cycle; } static gdouble waves_vanish (Waves *point, gdouble length) { gdouble vanish; if (point->vanish > 0) vanish = 1 - (length / point->vanish); else vanish = 1; return vanish; } static gdouble waves_areas (Waves *point, gdouble cycle, gdouble level) { gdouble areas; if (cycle * point->width < 0.5) areas = cycle * point->width; else if ((1 - cycle) * point->width < 0.5) areas = (1 - cycle) * point->width; else return level; switch (vals.style) { case LINEAR: case SINUSOIDAL: case SPHERICAL: level = level * (0.5 + areas) + (1 - level) * (0.5 - areas); break; } return level; } static gdouble waves_level (gdouble cycle) { gdouble level; switch (vals.style) { case LINEAR: level = cycle; break; case SINUSOIDAL: level = (sin((cycle - 0.5) * G_PI) + 1) /2; break; case SPHERICAL: if (cycle < 0.5) level = sin(cycle * G_PI) / 2; else level = 1 + sin((cycle - 1) * G_PI) / 2; break; case SAWTOOTH: if (cycle < 0.5) level = cycle * 2; else level = 1 - fmod((cycle * 2), 1); break; case TRIANGULAR: level = (sin(cycle * 2 * G_PI) + 1) / 2; break; case HALFPIPE: level = sin((cycle + 1) * G_PI) + 1; break; case POTTER: if (cycle < 0.5) level = sin(cycle * G_PI); else level = sin((cycle + 0.5) * G_PI) + 1; break; case RANDOM: level = (1.0*rand()/G_MAXRAND) * cycle; break; default: level = cycle; } return level; } static gdouble pixel_level (Waves *waves, gfloat x, gfloat y) { gint i; gdouble length; gdouble cycle; gdouble vanish; gdouble level; gdouble level_sum; gdouble level_div; level = level_sum = level_div = 0; if (vals.mode == GREATER || vals.mode == LESSER) level_sum = vals.flat; for (i = 0; i < subvals.points; i++) { length = waves_distance(waves + i, x, y); cycle = waves_cycle(waves + i, length, vals.phase); vanish = waves_vanish(waves + i, length); level = waves_level(cycle); if (subvals.areas == TRUE) level = waves_areas(waves + i, cycle, level); switch (vals.mode) { case AVERAGE: if (vanish >= 1) { level_sum += level; } else if (vanish > 0) { level = (level * vanish) + (vals.flat * (1 - vanish)); level_sum += level; } else { level_sum += vals.flat; } break; case TERRITORY: if (vanish >= 1) { level_sum += level; level_div += 1; } else if (vanish > 0) { level_sum += level * vanish; level_div += vanish; } break; case GREATER: if (vanish >= 1) { if (level_sum < level) level_sum = level; } else if (vanish > 0) { level = (level * vanish) + (vals.flat * (1- vanish)); if (level_sum < level) level_sum = level; } else if (level_sum < vals.flat) level_sum = vals.flat; break; case LESSER: if (vanish >= 1) { if (level_sum > level) level_sum = level; } else if (vanish > 0) { level = (level * vanish) + (vals.flat * (1- vanish)); if (level_sum > level) level_sum = level; } else if (level_sum > vals.flat) level_sum = vals.flat; break; } } switch (vals.mode) { case AVERAGE: level = level_sum / subvals.points; break; case TERRITORY: if (level_div > 1 ) level = level_sum / level_div; else if (level_div > 0 ) level = (level_sum * level_div) + (vals.flat * (1 - level_div)); else level = vals.flat; break; case GREATER: case LESSER: level = level_sum; break; } return level; } static void pixel_coloring (guchar *pixel, gdouble level) { gint i; for (i = 0; i < channels; i++) { pixel[i] = ((gdouble) color.fg[i] * level) + ((gdouble) color.bg[i] * (1 - level)); } if (has_alpha) pixel[channels] = 255; } static gint render_waves (Waves *waves, GimpPixelRgn *region) { gint x, y; gdouble level; gint bpp = channels + has_alpha; //guchar *buf; //buf = g_malloc0 (region->w * bpp); for (y = 0; y < region->h; y++) { for (x = 0; x < region->w; x++) { level = pixel_level (waves, x + region->x, y + region->y); /* DEBUG */ if (level < 0 || level > 1.0) { fprintf(stderr, "DEBUG: *** level over/under (%d,%d): %5.3f\n", x, y, level); level = CLAMP (level, 0.0, 1.0); } pixel_coloring (region->data + (region->rowstride * y) + (bpp * x), level); //pixel_coloring (buf + ((channels + has_alpha) * x), level); } } return (region->w * region->h); } static void water_surface (GimpDrawable *drawable) { Waves waves[vals.points * 9]; gint width, height; GimpPixelRgn region; gint sel_x1, sel_y1, sel_x2, sel_y2; gint sel_width, sel_height; guint progress, max_progress; guchar *pr; width = drawable->width; height = drawable->height; subvals_init (width, height); waves_new (waves, width, height); /* tilable */ if (vals.tilable) { waves_tilable (waves, width, height); if (subvals.points == 0) return; subvals.slide_x = subvals.slide_y = 0; } else subvals.points = vals.points; /* Get selection area */ gimp_drawable_mask_bounds (drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); sel_width = sel_x2 - sel_x1; sel_height = sel_y2 - sel_y1; gimp_pixel_rgn_init (®ion, drawable, sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE); /* One, two, three, go! */ progress = 0; max_progress = sel_width * sel_height; gimp_progress_init ("Water Surface.."); for (pr = gimp_pixel_rgns_register (1, ®ion); pr != NULL; pr = gimp_pixel_rgns_process (pr)) { progress += render_waves (waves, ®ion); gimp_progress_update ((double) progress / (double) max_progress); } /* Update the drawable */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->id, TRUE); gimp_drawable_update (drawable->id, sel_x1, sel_y1, sel_width, sel_height); } static void preview_water_surface (GimpDrawable *drawable) { Waves waves[vals.points * 9]; gint width, height; GimpPixelRgn region; guchar *pr; guchar *srcbuf, *distbuf; gint m; gint x, y; gint bpp, preview_bpp = 3; /* preview size with aspect ratio */ width = drawable->width; height = drawable->height; m = MAX (width, height); width = ROUND(PREVIEW_SIZE * (1.0 * width / m)); height = ROUND(PREVIEW_SIZE * (1.0 * height / m)); width = MAX(width, 1); height = MAX(height, 1); /* gimp_tile_cache_ntiles ((width + gimp_tile_width() - 1) / gimp_tile_width() * (height + gimp_tile_height() - 1) / gimp_tile_height()); */ subvals_init (width, height); subvals.areas = FALSE; /* has_alpha = 0; channels = 3; */ bpp = channels + has_alpha; waves_new (waves, width, height); /* tilable */ if (vals.tilable) { waves_tilable (waves, width, height); if (subvals.points == 0) return; subvals.slide_x = subvals.slide_y = 0; } else subvals.points = vals.points; gtk_preview_size (GTK_PREVIEW (preview), width, height); gimp_pixel_rgn_init (®ion, drawable, 0, 0, width, height, TRUE, TRUE); for (pr = gimp_pixel_rgns_register (1, ®ion); pr != NULL; pr = gimp_pixel_rgns_process (pr)) render_waves (waves, ®ion); srcbuf = g_malloc (width * bpp); distbuf = g_malloc (width * preview_bpp); for (y = 0; y < height; y ++) { if (channels == preview_bpp && ! has_alpha) { gimp_pixel_rgn_get_row (®ion, distbuf, 0, y, width); } else if (channels != preview_bpp || has_alpha) { gimp_pixel_rgn_get_row (®ion, srcbuf, 0, y, width); for (x = 0; x < width ; x ++) { *(distbuf + x * preview_bpp) = *(distbuf + x * preview_bpp + 1) = *(distbuf + x * preview_bpp + 2) = *(srcbuf + x * bpp); } } gtk_preview_draw_row(GTK_PREVIEW (preview), distbuf, 0, y, width); } g_free (srcbuf); g_free (distbuf); /* Update */ gtk_widget_queue_draw (preview); //vals.seed = subval.seed; } static GtkWidget * preview_widget (GimpDrawable *drawable) { GtkWidget *preview; guchar *buf; gint y; preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_preview_size (GTK_PREVIEW (preview), PREVIEW_SIZE, PREVIEW_SIZE); buf = g_malloc0 (PREVIEW_SIZE * 3); for (y = 0; y < PREVIEW_SIZE; y++) gtk_preview_draw_row (GTK_PREVIEW (preview), buf, 0, y, PREVIEW_SIZE); g_free (buf); return preview; } static void preview_update (GtkObject *obj, gpointer data) { /* gimp_double_adjustment_update (obj, data); */ /* drawable = gtk_object_get_data (GTK_OBJECT (obj), "drawable"); */ preview_water_surface (_drawable); } static void ws_int_adjustment_update (GtkAdjustment *adjustment, gpointer data) { gimp_int_adjustment_update (adjustment, data); if (do_preview) preview_water_surface (_drawable); } static void ws_double_adjustment_update (GtkAdjustment *adjustment, gpointer data) { gimp_double_adjustment_update (adjustment, data); if (do_preview) preview_water_surface (_drawable); } static void ws_toggle_button_update (GtkWidget *toggle, gpointer data) { gimp_toggle_button_update (toggle, data); if (do_preview) preview_water_surface (_drawable); } static void ws_menu_item_update (GtkWidget *widget, gpointer data) { gimp_menu_item_update (widget, data); if (do_preview) preview_water_surface (_drawable); } static gint water_surface_dialog (GimpDrawable *drawable) { GtkWidget *dlg; GtkWidget *main_hbox; GtkWidget *sub_vbox; GtkWidget *abox; GtkWidget *vbox; /* GtkWidget *hbox; */ GtkWidget *frame; GtkWidget *table; GtkWidget *toggle; GtkWidget *seed_hbox; GtkWidget *spinbutton; GtkWidget *menu; GtkWidget *button; GtkWidget *sep; GtkWidget *r1, *r2, *r3, *r4, *r5, *r6, *r7, *r8; GtkObject *adj; gimp_ui_init ("wsurface", TRUE); dlg = gimp_dialog_new (PLUG_IN_VERSION, "wsurface", gimp_standard_help_func, "filters/water_surface.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("OK"), dialog_ok_callback, NULL, NULL, NULL, TRUE, FALSE, _("Cancel"), gtk_widget_destroy, NULL, 1, NULL, FALSE, TRUE, NULL); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gimp_help_init (); main_hbox = gtk_hbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 4); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_hbox, TRUE, TRUE, 0); gtk_widget_show (main_hbox); /* left side */ sub_vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (sub_vbox), 0); gtk_box_pack_start (GTK_BOX (main_hbox), sub_vbox, TRUE, TRUE, 0); gtk_widget_show (sub_vbox); /* * Parameter settings * * First set up the basic containers, label them, etc. */ frame = gtk_frame_new (_("Point setting")); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_border_width (GTK_CONTAINER(frame), 4); gtk_box_pack_start (GTK_BOX(sub_vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); table = gtk_table_new (3, 5, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 4); gtk_container_add (GTK_CONTAINER (frame), table); gtk_widget_show (table); /* Random Seed */ seed_hbox = gimp_random_seed_new (&vals.seed, &vals.timeseed, TRUE, FALSE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Random Seed:"), 1.0, 0.5, seed_hbox, 1, TRUE); gtk_signal_connect (GTK_OBJECT (GIMP_RANDOM_SEED_SPINBUTTON_ADJ (seed_hbox)), "value_changed", GTK_SIGNAL_FUNC (ws_int_adjustment_update), &vals.seed); /* Number of wave point (1 to 255) */ spinbutton = gimp_spin_button_new (&adj, vals.points, 1, 255, 1, 8, 0, 1, 0); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Points:"), 1.0, 0.5, spinbutton, 1, TRUE); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_int_adjustment_update), &vals.points); gimp_help_set_help_data (spinbutton, _("Number of wave points"), NULL); /* Tilable */ toggle = gtk_check_button_new_with_label (_("Tilable")); gtk_table_attach (GTK_TABLE (table), toggle, 2, 3, 1, 2, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), vals.tilable); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (ws_toggle_button_update), &vals.tilable); gtk_widget_show (toggle); /* Point setting range (0 to 8) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Range:"), SCALE_WIDTH, 0, vals.scale, 0.001, 8.0, 0.2, 1.0, 3, TRUE, 0, 0, _("Point setting range"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.scale); /* Slide view point X */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("X Slide:"), SCALE_WIDTH, 0, vals.slide_x, -8.0, 8.0, 0.05, 1.0, 3, TRUE, 0, 0, _("Slide view point"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.slide_x); /* Slide view point Y */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Y Slide:"), SCALE_WIDTH, 0, vals.slide_y, -8.0, 8.0, 0.05, 1.0, 3, TRUE, 0, 0, _("Slide view point"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.slide_y); /* * Wave and Cycle */ frame = gtk_frame_new (_("Wave and Cycle")); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_border_width (GTK_CONTAINER(frame), 4); gtk_box_pack_start (GTK_BOX (sub_vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); table = gtk_table_new (3, 6, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 4); gtk_container_add (GTK_CONTAINER (frame), table); gtk_widget_show (table); /* Wave width ratio (0 to 8) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Wavelength:"), SCALE_WIDTH, 0, vals.width, 0.001, 8.0, 0.05, 1.0, 3, TRUE, 0, 0, _("Ratio of wave width"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.width); /* Disorder of wave width (0 to 1) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, _("Disorder:"), SCALE_WIDTH, 0, vals.disorder, 0.0, 1.0, 0.1, 1.0, 3, TRUE, 0, 0, _("Disorder of wave width"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.disorder); /* Decay ratio of wave (0 to 8) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Reduce:"), SCALE_WIDTH, 0, vals.reduce, 0.0, 8.0, 0.05, 1.0, 3, TRUE, 0, 0, _("Decay ratio of wave"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.reduce); sep = gtk_hseparator_new (); gtk_table_attach (GTK_TABLE (table), sep, 0, 3, 3, 4, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 2); gtk_widget_show (sep); /* Jump start ratio (0 to 1) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Lag:"), SCALE_WIDTH, 0, vals.lag, 0.0, 1.0, 0.1, 0.2, 3, TRUE, 0, 0, _("Disorder of phase"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.lag); /* Phase of Cycle (0.0 to 1.0) */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Phase:"), SCALE_WIDTH, 0, vals.phase, 0.0, 1.0, 0.1, 0.2, 3, TRUE, 0, 0, _("Phase of Cycle"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.phase); /* right side */ sub_vbox = gtk_vbox_new (FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (sub_vbox), 0); gtk_box_pack_start (GTK_BOX (main_hbox), sub_vbox, TRUE, TRUE, 0); gtk_widget_show (sub_vbox); /* * make a nice preview frame */ frame = gtk_frame_new (_("Preview")); gtk_container_set_border_width (GTK_CONTAINER (frame), 4); gtk_box_pack_start (GTK_BOX (sub_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_container_set_border_width (GTK_CONTAINER (abox), 4); gtk_container_add (GTK_CONTAINER (frame), abox); gtk_widget_show (abox); vbox = gtk_vbox_new (FALSE, 4); gtk_container_add (GTK_CONTAINER (abox), vbox); gtk_widget_show (vbox); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); preview = preview_widget (drawable); /* we are here */ gtk_container_add (GTK_CONTAINER (frame), preview); preview_water_surface (drawable); /* preview image */ gtk_widget_show (preview); button = gtk_button_new_with_label (_("Update Preview")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (preview_update), drawable); gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0); gtk_widget_show (button); toggle = gtk_check_button_new_with_label (_("Auto update")); gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), do_preview); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (ws_toggle_button_update), &do_preview); gtk_widget_show (toggle); /* * Syle and Mode */ frame = gtk_frame_new (_("Style and Mode")); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_set_border_width (GTK_CONTAINER(frame), 4); gtk_box_pack_start (GTK_BOX (sub_vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); table = gtk_table_new (3, 5, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_row_spacings (GTK_TABLE (table), 2); gtk_container_set_border_width (GTK_CONTAINER (table), 4); gtk_container_add (GTK_CONTAINER (frame), table); gtk_widget_show (table); /* Anti areasing */ toggle = gtk_check_button_new_with_label (_("Anti areasing:")); gtk_table_attach (GTK_TABLE (table), toggle, 1, 2, 0, 1, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), vals.areas); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &vals.areas); gtk_widget_show (toggle); /* Level to gain (0 to 1) */ spinbutton = gimp_spin_button_new (&adj, vals.flat, 0.0, 1.0, 0.1, 0.25, 0, 1, 2); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Flat:"), 1.0, 0.5, spinbutton, 1, TRUE); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (ws_double_adjustment_update), &vals.flat); sep = gtk_hseparator_new (); gtk_table_attach (GTK_TABLE (table), sep, 0, 3, 2, 3, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 2); gtk_widget_show (sep); /* Style of cycle to level */ menu = gimp_option_menu_new2 (FALSE, ws_menu_item_update, &vals.style, (gpointer) vals.style, _("Linear"), (gpointer) LINEAR, &r1, _("Sinusoidal"), (gpointer) SINUSOIDAL, &r2, _("Spherical"), (gpointer) SPHERICAL, &r3, _("Sawtooth Wave"),(gpointer) SAWTOOTH, &r4, _("Triangular Wave"), (gpointer) TRIANGULAR, &r5, _("Potter"), (gpointer) POTTER, &r6, _("Half Pipe"), (gpointer) HALFPIPE, &r7, _("Random"), (gpointer) RANDOM, &r8, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 3, _("Style:"), 1.0, 0.5, menu, 1, TRUE); gtk_widget_show (menu); gimp_help_set_help_data (r1, _("Discontinuity Wave"), NULL); gimp_help_set_help_data (r2, _("High contrast Wave"), NULL); gimp_help_set_help_data (r3, _("Soft contrast Wave"), NULL); gimp_help_set_help_data (r4, _("Stiff Wave"), NULL); gimp_help_set_help_data (r5, _("Smooth Wave"), NULL); gimp_help_set_help_data (r6, _("kneaded Wave"), NULL); gimp_help_set_help_data (r7, _("Conspicuous Wave"), NULL); gimp_help_set_help_data (r8, _("Rundom Dots"), NULL); /* Mode of level mixing */ menu = gimp_option_menu_new2 (FALSE, ws_menu_item_update, &vals.mode, (gpointer) vals.mode, _("Average"), (gpointer) AVERAGE, &r1, _("Territory"), (gpointer) TERRITORY, &r2, _("Greater"), (gpointer) GREATER, &r3, _("Lesser"), (gpointer) LESSER, &r4, NULL); gimp_table_attach_aligned (GTK_TABLE (table), 0, 4, _("Mode:"), 1.0, 0.5, menu, 1, TRUE); gtk_widget_show (menu); gimp_help_set_help_data (r1, _("Average of All"), NULL); gimp_help_set_help_data (r2, _("Vanished with possess"), NULL); gimp_help_set_help_data (r3, _("Lighten(Darken) only"), NULL); gimp_help_set_help_data (r4, _("Darken(Lighten) only"), NULL); gtk_widget_show (dlg); gtk_main (); gimp_help_free (); gdk_flush (); return wint.run; } static void dialog_ok_callback (GtkWidget *widget, gpointer data) { wint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); }