/* Light to Hight -- rising map 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. */ /* * Light to Height version 0.5, Oct 2001 * This plug-in grows a image according to brightness of a image. * The visual effect which is rising can be acquired easily. * * 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 L2H_NAME "Light to Height" #define L2H_VERSION "ver. 0.5" #define SCALE_WIDTH 128 /*---- Types ---*/ typedef struct { gint32 hmap_id; gdouble grow; gint smooth; gint wrap; gint tile; gint mass; gint horizon; } l2h_vals_t; typedef struct { gint run; } L2HInterface; /*---- Prottype ---*/ static void query (void); static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static void l2h_compute (guchar *dest_buf, gint dest_length, gint dest_bpp, gint dest_has_alpha, guchar *hmap_buf, gint hmap_length, gint hmap_bpp, gint hmap_has_alpha); static void light_to_hight (void); static gint l2h_dialog (void); static void dialog_ok_callback (GtkWidget *widget, gpointer data); static gint dialog_constrain (gint32 image_id, gint32 drawable_id, gpointer data); static void dialog_hmap_callback (gint32 id, gpointer data); /*---- Variables ----*/ GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run /* run_proc */ }; static l2h_vals_t l2hvals = { -1, /* hight map id */ 20, /* grow length */ TRUE, /* smooth */ FALSE,/* wrap */ FALSE,/* tile */ TRUE, /* mass */ FALSE /* horizon */ }; /* the image and drawable that will be used later */ static GimpDrawable *active_drawable = NULL; static gint32 image_ID = -1; static L2HInterface l2hint = { FALSE /* have we run? */ }; /* Functions */ 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_DRAWABLE, "map", "Light data drawable" }, { GIMP_PDB_FLOAT, "grow", "Growing max height" }, { GIMP_PDB_INT32, "smooth", "Smooth growing" }, { GIMP_PDB_INT32, "wrap", "Wrap hight map" }, { GIMP_PDB_INT32, "tile", "Tilable growing" }, { GIMP_PDB_INT32, "mass", "Grow with mass or plane" }, { GIMP_PDB_INT32, "horizon", "Turn height to horizon" } }; static gint nargs = sizeof (args) / sizeof (args[0]); gimp_install_procedure ("l2hight", L2H_NAME, "This Plug-in is image mapper along height," "Image's height is measured by light.", "Kyoichiro Suda ", "Kyoichiro Suda", "Aug 2001", N_("/Filters/Map/Light to Height"), "RGB*, GRAY*", GIMP_PLUGIN, nargs, 0, args, NULL); } static void run (gchar *name, gint nparams, GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { /* Get the runmode from the in-parameters */ GimpRunModeType run_mode = param[0].data.d_int32; /* status variable, use it to check for errors in invocation usualy only during non-interactive calling */ GimpPDBStatusType status = GIMP_PDB_SUCCESS; /*always return at least the status to the caller. */ static GimpParam values[1]; /* initialize the return of the status */ values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; *nreturn_vals = 1; *return_vals = values; INIT_I18N_UI (); /* get image and drawable */ image_ID = param[1].data.d_int32; active_drawable = gimp_drawable_get (param[2].data.d_drawable); switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (name, &l2hvals); /* Get information from the dialog */ if (!l2h_dialog ()) return; break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are present */ if (nparams != 10) { status = GIMP_PDB_CALLING_ERROR; } else { l2hvals.hmap_id = (gint32) param[3].data.d_drawable; l2hvals.grow = (gdouble) param[4].data.d_float; l2hvals.smooth = param[5].data.d_int32 ? TRUE : FALSE; l2hvals.wrap = param[6].data.d_int32 ? TRUE : FALSE; l2hvals.tile = param[7].data.d_int32 ? TRUE : FALSE; l2hvals.mass = param[8].data.d_int32 ? TRUE : FALSE; l2hvals.horizon = param[9].data.d_int32 ? TRUE : FALSE; } break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (name, &l2hvals); break; default: break; } if (status == GIMP_PDB_SUCCESS) { if ((gimp_drawable_is_rgb(active_drawable->id) || gimp_drawable_is_gray(active_drawable->id))) { /* Run! */ light_to_hight (); /* If run mode is interactive, flush displays */ if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush (); /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data (name, &l2hvals, sizeof (l2h_vals_t)); } } else status = GIMP_PDB_EXECUTION_ERROR; values[0].data.d_status = status; } static void l2h_compute (guchar *dest_buf, gint dest_len, gint dest_bpp, gint dest_has_alpha, guchar *hmap_buf, gint hmap_len, gint hmap_bpp, gint hmap_has_alpha) { gint i, j, b; gdouble a, alpha; gint dest_bpp_a = dest_bpp; gint hmap_bpp_a = hmap_bpp; gdouble capa; gdouble volume; gdouble reach; gdouble smooth; gdouble sum[dest_bpp]; /* alpha */ if (dest_has_alpha) dest_bpp --; if (hmap_has_alpha) hmap_bpp --; for (i = 0; i < dest_len; i ++) { /* init */ capa = 1; alpha = 0; for (b = 0; b < dest_bpp_a; b ++) sum[b] = 0; //if (l2hvals.tile == FALSE && i == dest_len -1) break; /* grow up */ for (j = MIN (i + l2hvals.grow, dest_len - 1); j >= i; j --) { if (j >= hmap_len) break; /* alpha */ if (dest_has_alpha) a = dest_buf[j * dest_bpp_a + dest_bpp] / 255; else a = 1; /* reach length */ volume = 0; for (b = 0; b < hmap_bpp; b ++) volume += 1.0 * hmap_buf[j * hmap_bpp_a + b] / 255 / hmap_bpp; if (volume > 1) fprintf (stderr, "DEBUG: Volume range over %g\n", volume); reach = 1.0 * l2hvals.grow * volume - (j - i - capa); if (l2hvals.smooth && reach >= 1 && (j + 1) < dest_len) { smooth = (reach - 1 - (capa / 2)) / (l2hvals.grow * volume - 1); smooth = CLAMP(smooth, 0, 1); if (dest_has_alpha) { gdouble a0, a1; a0 = dest_buf[ j * dest_bpp_a + dest_bpp] / 255; a1 = dest_buf[(j + 1) * dest_bpp_a + dest_bpp] / 255; a = (1 - smooth) * a0 + smooth * a1; for (b = 0; b < dest_bpp; b ++) sum[b] += ((1 - smooth) * dest_buf[j * dest_bpp_a + b] * a0 + smooth * dest_buf[(j + 1) * dest_bpp_a + b] * a1) * capa; alpha += a * capa; sum[dest_bpp] += 255 * a * capa; //capa -= a * capa; capa = 0; } else { for (b = 0; b < dest_bpp_a; b ++) sum[b] += ((1 - smooth) * dest_buf[j * dest_bpp_a + b] + smooth * dest_buf[(j + 1) * dest_bpp_a + b]) * capa; capa = 0; } } else if (a == 0 && l2hvals.mass == FALSE) { continue; } else if (l2hvals.smooth == FALSE && reach > 0) { for (b = 0; b < dest_bpp_a; b ++) sum[b] = dest_buf[j * dest_bpp_a + b]; alpha = 1; capa = 0; } else if (reach >= capa) { /* full covered */ for (b = 0; b < dest_bpp; b ++) sum[b] += 1.0 * dest_buf[j * dest_bpp_a + b] * a * capa; if (dest_has_alpha) sum[dest_bpp] += 255 * a * capa; alpha += a * capa; //capa -= a * capa; capa = 0; } else if (reach > 0 && reach < capa) { /* reached */ for (b = 0; b < dest_bpp; b ++) sum[b] += 1.0 * dest_buf[j * dest_bpp_a + b] * a * reach; if (dest_has_alpha) sum[dest_bpp] += 255 * a * reach; alpha += a * reach; //capa -= a * reach; capa -= reach; } if (capa <= 0) break; } if (capa > 0) { /*if (dest_has_alpha) { a = dest_buf[i * dest_bpp_a + dest_bpp] / 255; for (b = 0; b < dest_bpp; b ++) sum[b] += dest_buf[i * dest_bpp_a + b] * a * capa; sum[dest_bpp] += 255 * a * capa; alpha += a * capa; } else*/ for (b = 0; b < dest_bpp_a; b ++) sum[b] += dest_buf[i * dest_bpp_a + b] * capa; } if (dest_has_alpha) { for (b = 0; b < dest_bpp; b ++) { if (sum[b] > 255 || sum[b] < 0) fprintf (stderr, "DEBUG: Color[%d] range over %6.2f\n", b, sum[b]); dest_buf[i * dest_bpp_a + b] = CLAMP (sum[b] / alpha, 0, 255); } dest_buf[i * dest_bpp_a + dest_bpp] = CLAMP(sum[dest_bpp], 0, 255); } else { for (b = 0; b < dest_bpp_a; b ++) { if (sum[b] > 255 || sum[b] < 0) fprintf (stderr, "DEBUG: Color[%d] range over %6.2f\n", b, sum[b]); dest_buf[i * dest_bpp_a + b] = CLAMP (sum[b], 0, 255); } } } } static void light_to_hight (void) { gint sel_x1, sel_y1, sel_x2, sel_y2; gint sel_width, sel_height; gint col, col_max, hmap_col, hmap_col_max; GimpPixelRgn region, cpy_region, hmap_region; guchar *dest_buf, *hmap_buf; gint dest_len, hmap_len; // guchar *pr; gint bpp, has_alpha, hmap_bpp, hmap_has_alpha; gint i; GimpDrawable *hmap_drawable = gimp_drawable_get (l2hvals.hmap_id); /* Get selection area */ gimp_drawable_mask_bounds (active_drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); sel_width = sel_x2 - sel_x1; sel_height = sel_y2 - sel_y1; gimp_tile_cache_ntiles (ROUND(sel_width / gimp_tile_width()) + ROUND(sel_height / gimp_tile_height())); gimp_pixel_rgn_init (®ion, active_drawable, sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE); gimp_undo_push_group_start (image_ID); /* alpha */ bpp = active_drawable->bpp; has_alpha = gimp_drawable_has_alpha (active_drawable->id); hmap_bpp = hmap_drawable->bpp; hmap_has_alpha = gimp_drawable_has_alpha (hmap_drawable->id); /* buffer */ if (l2hvals.horizon == FALSE) { col_max = sel_width; hmap_col_max = hmap_drawable->width; dest_len = sel_height; hmap_len = hmap_drawable->height; } else { col_max = sel_height; hmap_col_max = hmap_drawable->height; dest_len = sel_width; hmap_len = hmap_drawable->width; } dest_buf = g_malloc0 (dest_len * bpp); hmap_buf = g_malloc0 (MAX (dest_len, hmap_len) * hmap_bpp); gimp_progress_init ("Light to Height.."); for (col = 0; col < col_max; col ++) { if (col < hmap_col_max) hmap_col = col; else { if (l2hvals.wrap) hmap_col = fmod (col, hmap_col_max); /* wrap around */ else { gimp_progress_update ((double) (col + 1) / (double) col_max); continue; /* not wrap */ } } /* make height map region each round */ gimp_pixel_rgn_init (&hmap_region, hmap_drawable, 0, 0, hmap_drawable->width, hmap_drawable->height, FALSE, FALSE); if (l2hvals.horizon == FALSE) gimp_pixel_rgn_get_rect (&hmap_region,hmap_buf, hmap_col,0, 1,hmap_len); else gimp_pixel_rgn_get_rect (&hmap_region,hmap_buf, 0,hmap_col, hmap_len,1); /* copy fo wrap */ if (l2hvals.wrap && dest_len > hmap_len) for (i = dest_len - hmap_len; i > 0; i -= hmap_len) memcpy (hmap_buf + hmap_len * hmap_bpp, hmap_buf, MIN (dest_len - hmap_len, hmap_len) * hmap_bpp); /* make copy region each round */ gimp_pixel_rgn_init (&cpy_region, active_drawable, sel_x1, sel_y1, sel_width, sel_height, FALSE, FALSE); if (l2hvals.horizon == FALSE) gimp_pixel_rgn_get_rect (&cpy_region, dest_buf, col, 0, 1, dest_len); else gimp_pixel_rgn_get_rect (&cpy_region, dest_buf, 0, col, dest_len, 1); l2h_compute (dest_buf, dest_len, bpp, has_alpha, hmap_buf, l2hvals.wrap ? dest_len : hmap_len, hmap_bpp, hmap_has_alpha); if (l2hvals.horizon == FALSE) gimp_pixel_rgn_set_rect (®ion, dest_buf, col, 0, 1, dest_len); else gimp_pixel_rgn_set_rect (®ion, dest_buf, 0, col, dest_len, 1); gimp_progress_update ((double) (col + 1) / (double) region.h); } g_free (hmap_buf); g_free (dest_buf); /* update */ gimp_drawable_flush (active_drawable); gimp_drawable_merge_shadow (active_drawable->id, TRUE); gimp_drawable_update (active_drawable->id, sel_x1, sel_y1, sel_width, sel_height); gimp_undo_push_group_end (image_ID); return; } static gint l2h_dialog (void) { GtkWidget *dialog; GtkWidget *frame; GtkWidget *toggle; GtkWidget *table; GtkWidget *option_menu; GtkWidget *menu; GtkObject *adj; gimp_ui_init ("l2height", TRUE); dialog = gimp_dialog_new (L2H_NAME, "l2height", gimp_standard_help_func, "filters/light_to_height.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 (dialog), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); gimp_help_init (); /* frame */ frame = gtk_frame_new (_("Parameter Settings")); 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 (dialog)->vbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); /* table */ table = gtk_table_new (4, 3, 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); /* wrap around */ toggle = gtk_check_button_new_with_label (_("Wrap")); gimp_help_set_help_data (toggle, _("Map data wrap around"), NULL); gtk_table_attach (GTK_TABLE (table), toggle, 3, 4, 1, 2, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), l2hvals.wrap); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &l2hvals.wrap); gtk_widget_show (toggle); /* smooth */ toggle = gtk_check_button_new_with_label (_("Smooth")); gimp_help_set_help_data (toggle, _("Smooth growing"), NULL); gtk_table_attach (GTK_TABLE (table), toggle, 3, 4, 0, 1, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), l2hvals.smooth); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &l2hvals.smooth); gtk_widget_show (toggle); /* turn to horizon */ toggle = gtk_check_button_new_with_label (_("Horizon")); gimp_help_set_help_data (toggle, _("Turn height to horizon"), NULL); gtk_table_attach (GTK_TABLE (table), toggle, 3, 4, 2, 3, GTK_SHRINK | GTK_FILL, GTK_FILL, 1, 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), l2hvals.horizon); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", GTK_SIGNAL_FUNC (gimp_toggle_button_update), &l2hvals.horizon); gtk_widget_show (toggle); /* map */ option_menu = gtk_option_menu_new (); menu = gimp_drawable_menu_new (dialog_constrain, dialog_hmap_callback, NULL, l2hvals.hmap_id); gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Light:"), 1.0, 0.5, option_menu, 2, TRUE); gimp_help_set_help_data (menu, _("Map of light"), NULL); /* adjudtment */ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Height:"), SCALE_WIDTH, 0, l2hvals.grow, 1.0, 100.0, 1.0, 10.0, 1, TRUE, 0, 0, _("Growing max height"), NULL); gtk_signal_connect (GTK_OBJECT (adj), "value_changed", GTK_SIGNAL_FUNC (gimp_double_adjustment_update), &l2hvals.grow); gtk_widget_show (dialog); gtk_main (); gimp_help_free (); gdk_flush (); return l2hint.run; } static gint dialog_constrain (gint32 image_id, gint32 drawable_id, gpointer data) { if (drawable_id == -1) return TRUE; return (gimp_drawable_is_rgb (drawable_id) || gimp_drawable_is_gray (drawable_id)); } static void dialog_hmap_callback (gint32 id, gpointer data) { l2hvals.hmap_id = id; } static void dialog_ok_callback (GtkWidget *widget, gpointer data) { l2hint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); }