/**************************************************************************** * This is a plugin for the GIMP v 1.0 or later. * * Copyright (C) 2001 Valter Marcus Hilden * Based on GTK code from: * rand-noted (Copyright (C) 1998 Miles O'Neal) * pagecurl (Copyright (C) 1996 Federico Mena Quintero) * * 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. * * * Modified for 1.2 and the Graphics Muse Tools CD * by Michael J. Hammel * 01/2002 ****************************************************************************/ #include #include #include #include #include #include #include #include /********************************* * * PLUGIN-SPECIFIC CONSTANTS * ********************************/ #define PROG_UPDATE_TIME ((row % 10) == 0) #define PLUG_IN_NAME "plug_in_enhance_homomorphic" #define PLUG_IN_VERSION "Homomorphic Filter 0.1" /********************************* * * PLUGIN-SPECIFIC STRUCTURES AND DATA * ********************************/ typedef struct { gdouble gain; } HomParams; static HomParams homp = { 10.0 }; static gint hom_run = FALSE; /********************************* * * LOCAL FUNCTIONS * ********************************/ static void query(void); static void run(gchar * name, gint nparams, GimpParam * param, gint * nreturn_vals, GimpParam ** return_vals); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run /* run_proc */ }; static void homomorph(GimpDrawable *drawable); static gint do_dialog(void); static void dialog_ok_callback(GtkWidget *widget, gpointer data); static void dialog_scale_update(GtkAdjustment *adjustment, gdouble *value); /************************************ Guts ***********************************/ MAIN() static void query(void) { static GimpParamDef args[] = { {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"}, {GIMP_PDB_IMAGE, "image", "Input image (unused)"}, {GIMP_PDB_DRAWABLE, "drawable", "Input drawable"}, {GIMP_PDB_FLOAT, "gain", "Set the overall gain for the filter"}, }; static int nargs = sizeof(args) / sizeof(args[0]); static GimpParamDef *return_vals = NULL; static gint nreturn_vals = 0; INIT_I18N(); gimp_install_procedure(PLUG_IN_NAME, "Enhance the image using the homomorphic transform.", "This plug-in enhances the image.", "Valter Marcus Hilden ", "Valter Marcus Hilden ", "2001", "/Filters/Enhance/Homomorphic Filter", "RGB, GRAY", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } /********************************* * * run() - main routine * * This handles the main interaction with the GIMP itself, * and invokes the routine that actually does the work. * ********************************/ 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; char prog_label[32]; run_mode = param[0].data.d_int32; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; *nreturn_vals = 1; *return_vals = values; gimp_get_data(PLUG_IN_NAME, &homp); drawable = gimp_drawable_get(param[2].data.d_drawable); if (gimp_drawable_is_rgb(drawable->id) || gimp_drawable_is_gray(drawable->id)) { switch (run_mode) { case GIMP_RUN_INTERACTIVE: if (!do_dialog()) return; break; case GIMP_RUN_NONINTERACTIVE: if (nparams != 3) status = GIMP_PDB_CALLING_ERROR; break; case GIMP_RUN_WITH_LAST_VALS: break; default: break; } if (status == GIMP_PDB_SUCCESS) { sprintf(prog_label, "%s", PLUG_IN_VERSION); gimp_progress_init(prog_label); homomorph(drawable); if (run_mode != GIMP_RUN_NONINTERACTIVE) gimp_displays_flush(); if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data(PLUG_IN_NAME, &homp, sizeof(HomParams)); } } else { status = GIMP_PDB_EXECUTION_ERROR; } values[0].data.d_status = status; gimp_drawable_detach(drawable); } /*______________________________________________________________ */ static inline void homomorph_prepare_row(GimpPixelRgn* pixel_rgn, guchar *data, int x, int y, int w) { int b; if (y == 0) gimp_pixel_rgn_get_row(pixel_rgn, data, x, y + 1, w); else if (y == pixel_rgn->h) gimp_pixel_rgn_get_row(pixel_rgn, data, x, y - 1, w); else gimp_pixel_rgn_get_row(pixel_rgn, data, x, y, w); /* Fill in edge pixels */ for (b = 0; b < pixel_rgn->bpp; b++) { data[-(gint) pixel_rgn->bpp + b] = data[b]; data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b]; } } /*______________________________________________________________ */ static inline void make_log(guchar *buf, double *row, int w, int bytes) { int col, n; double z; for (col = 0; col < w; col++) { for (n = 0; n < bytes; n++) { z = (double)buf[col * bytes + n] / 25.0; if (z == 0.0) z = 0.0001; *row++ = log(z); } } } /*______________________________________________________________ */ static void homomorph(GimpDrawable *drawable) { static double cof[5][5] = { {0.002675, -0.001526, -0.007420, -0.001526, 0.002675}, {-0.001526, -0.034115, -0.059471, -0.034115, -0.001526}, {-0.007420, -0.059471, 0.902895, -0.059471, -0.007420}, {-0.001526, -0.034115, -0.059471, -0.034115, -0.001526}, {0.002675, -0.001526, -0.007420, -0.001526, 0.002675} }; guchar *dest, *d, *temp; GimpPixelRgn srcPR, destPR; gint width, height; gint bytes; double *pixrow[5], *pixrows, c, *tp; gint row, col; gint x1, y1, x2, y2; gint i, j, n; gint has_alpha; gboolean active_selection; active_selection = gimp_drawable_mask_bounds(drawable->id, &x1, &y1, &x2, &y2); if ((x2 - x1) < 6 || (y2 - y1) < 6) return; width = gimp_drawable_width(drawable->id); height = gimp_drawable_height(drawable->id); bytes = gimp_drawable_bpp(drawable->id); has_alpha = gimp_drawable_has_alpha(drawable->id); pixrows = (double *)malloc(5 * (x2 - x1 + 2) * bytes * sizeof(double)); for (n = 0; n < 5; n++) pixrow[n] = pixrows + n * (x2 - x1 + 2) * bytes; dest = (guchar *) malloc((x2 - x1) * bytes); temp = (guchar *) malloc((x2 - x1 + 2) * bytes); gimp_pixel_rgn_init(&srcPR, drawable, 0, 0, width, height, FALSE, FALSE); gimp_pixel_rgn_init(&destPR, drawable, 0, 0, width, height, TRUE, TRUE); for (n = 0; n < 4; n++) { homomorph_prepare_row(&srcPR, temp + bytes, x1, y1 + n, x2 - x1); make_log(temp, pixrow[n], x2 - x1 + 2, bytes); } for (row = y1 + 2; row < y2 - 2; row++) { homomorph_prepare_row(&srcPR, temp + bytes, x1, row + 2, x2 - x1); make_log(temp, pixrow[4], x2 - x1 + 2, bytes); d = dest + 3 * bytes; for (col = 2; col < (x2 - x1 - 3); col++) { for (n = 0; n < bytes; n++) { c = 0.0; for (i = 0; i < 5; i++) for (j = 0; j < 5; j++) c += cof[i][j] * pixrow[i][(col + j) * bytes + n]; c = exp(c) * 25.0 * homp.gain; if (c > 255.0) c = 255.0; if (c < 0.0) c = 0.0; *d++ = (guchar) c; } } gimp_pixel_rgn_set_row(&destPR, dest, x1, row, (x2 - x1 - 3)); tp = pixrow[0]; pixrow[0] = pixrow[1]; pixrow[1] = pixrow[2]; pixrow[2] = pixrow[3]; pixrow[3] = pixrow[4]; pixrow[4] = tp; if (PROG_UPDATE_TIME) { gimp_progress_update((double)row / (double)(y2 - y1)); gimp_displays_flush(); } } gimp_progress_update((double)100); gimp_drawable_flush(drawable); gimp_drawable_merge_shadow(drawable->id, TRUE); gimp_drawable_update(drawable->id, x1, y1, x2 - x1, y2 - y1); gimp_displays_flush(); free(pixrows); free(dest); free(temp); } /********************************* * * GUI * ********************************/ static gint do_dialog(void) { GtkWidget *dialog; GtkWidget *vbox; GtkWidget *frame; GtkWidget *scale; GtkObject *adjustment; gimp_ui_init("homomorph", FALSE); dialog = gimp_dialog_new(_("Homomorphic Filter"), "homomorph", gimp_standard_help_func, "filters/pagecurl.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); vbox = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, TRUE, TRUE, 0); gtk_widget_show(vbox); frame = gtk_frame_new(_("Filter Gain")); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); gtk_widget_show(frame); adjustment = gtk_adjustment_new(homp.gain * 100, 0.0, 100.0, 1.0, 1.0, 0.0); gtk_signal_connect(adjustment, "value_changed", GTK_SIGNAL_FUNC(dialog_scale_update), &(homp.gain)); scale = gtk_hscale_new(GTK_ADJUSTMENT(adjustment)); gtk_widget_set_usize(GTK_WIDGET(scale), 150, 30); gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_DELAYED); gtk_scale_set_digits(GTK_SCALE(scale), 0); gtk_scale_set_draw_value(GTK_SCALE(scale), TRUE); gtk_container_add(GTK_CONTAINER(frame), scale); gtk_widget_show(scale); gtk_widget_show(dialog); gtk_main(); gdk_flush(); return hom_run; } /*______________________________________________________________ */ static void dialog_ok_callback(GtkWidget *widget, gpointer data) { hom_run = TRUE; gtk_widget_destroy(GTK_WIDGET(data)); } /*______________________________________________________________ */ static void dialog_scale_update(GtkAdjustment *adjustment, gdouble *value) { *value = ((double)adjustment->value) / 20.0; }