/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for GIMP. * * Copyright (C) Pavel Grinfeld (pavel@ml.com) * * * 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 3 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, see . */ #include "config.h" #include #include #include #include #include "libgimp/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-filter-pack" #define PLUG_IN_BINARY "filter-pack" #define PLUG_IN_ROLE "gimp-filter-pack" #define MAX_PREVIEW_SIZE 125 #define MAX_ROUGHNESS 128 #define RANGE_HEIGHT 15 #define PR_BX_BRDR 4 #define MARGIN 4 #define RANGE_ADJUST_MASK (GDK_EXPOSURE_MASK | \ GDK_ENTER_NOTIFY_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_BUTTON1_MOTION_MASK | \ GDK_POINTER_MOTION_HINT_MASK) typedef struct { gboolean run; } fpInterface; typedef struct { gint width; gint height; guchar *rgb; gdouble *hsv; guchar *mask; } ReducedImage; typedef enum { SHADOWS, MIDTONES, HIGHLIGHTS, INTENSITIES } FPIntensity; enum { NONEATALL = 0, CURRENT = 1, HUE = 2, SATURATION = 4, VALUE = 8 }; enum { BY_HUE, BY_SAT, BY_VAL, JUDGE_BY }; enum { RED, GREEN, BLUE, CYAN, YELLOW, MAGENTA, ALL_PRIMARY }; enum { DOWN = -1, UP = 1 }; typedef struct { GtkWidget *window; GtkWidget *range_preview; GtkWidget *aliasing_preview; GtkWidget *aliasing_graph; } AdvancedWindow; typedef struct { gdouble roughness; gdouble aliasing; gdouble preview_size; FPIntensity intensity_range; gint value_by; gint selection_only; guchar offset; guchar visible_frames; guchar cutoff[INTENSITIES]; gboolean touched[JUDGE_BY]; gint red_adjust[JUDGE_BY][256]; gint blue_adjust[JUDGE_BY][256]; gint green_adjust[JUDGE_BY][256]; gint sat_adjust[JUDGE_BY][256]; } FPValues; typedef struct { GtkWidget *roughness_scale; GtkWidget *aliasing_scale; GtkWidget *preview_size_scale; GtkWidget *range_label[12]; } FPWidgets; static void fp_show_hide_frame (GtkWidget *button, GtkWidget *frame); static ReducedImage * fp_reduce_image (GimpDrawable *drawable, GimpDrawable *mask, gint longer_size, gint selection); static void fp_render_preview (GtkWidget *preview, gint change_what, gint change_which); static void update_current_fp (gint change_what, gint change_which); static void fp_create_nudge (gint *adj_array); static gboolean fp_dialog (void); static void fp_advanced_dialog (GtkWidget *parent); static void fp_selection_made (GtkWidget *widget, gpointer data); static void fp_scale_update (GtkAdjustment *adjustment, gdouble *scale_val); static void fp_reset_filter_packs (void); static void fp_create_smoothness_graph (GtkWidget *preview); static void fp_range_preview_spill (GtkWidget *preview, gint type); static void fp_adjust_preview_sizes (gint width, gint height); static void fp_redraw_all_windows (void); static void fp_refresh_previews (gint which); static void fp_init_filter_packs (void); static void fp_preview_scale_update (GtkAdjustment *adjustment, gdouble *scale_val); static void fp (GimpDrawable *drawable); static GtkWidget * fp_create_bna (void); static GtkWidget * fp_create_rough (void); static GtkWidget * fp_create_range (void); static GtkWidget * fp_create_circle_palette (GtkWidget *parent); static GtkWidget * fp_create_lnd (GtkWidget *parent); static GtkWidget * fp_create_show (void); static GtkWidget * fp_create_msnls (GtkWidget *parent); static GtkWidget * fp_create_pixels_select_by (void); static void update_range_labels (void); static gboolean fp_range_change_events (GtkWidget *widget, GdkEvent *event, FPValues *current); static void fp_create_preview (GtkWidget **preview, GtkWidget **frame, gint preview_width, gint preview_height); static void fp_create_table_entry (GtkWidget **box, GtkWidget *smaller_frame, const gchar *description); static void fp_frames_checkbutton_in_box (GtkWidget *vbox, const gchar *label, GCallback func, GtkWidget *frame, gboolean clicked); static void fp_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation); #define RESPONSE_RESET 1 /* These values are translated for the GUI but also used internally to figure out which button the user pushed, etc. Not my design, please don't blame me -- njl */ static const gchar *hue_red = N_("Red:"); static const gchar *hue_green = N_("Green:"); static const gchar *hue_blue = N_("Blue:"); static const gchar *hue_cyan = N_("Cyan:"); static const gchar *hue_yellow = N_("Yellow:"); static const gchar *hue_magenta = N_("Magenta:"); static const gchar *val_darker = N_("Darker:"); static const gchar *val_lighter = N_("Lighter:"); static const gchar *sat_more = N_("More Sat:"); static const gchar *sat_less = N_("Less Sat:"); static const gchar *current_val = N_("Current:"); static const gint colorSign[3][ALL_PRIMARY]= {{1,-1,-1,-1,1,1},{-1,1,-1,1,1,-1},{-1,-1,1,1,-1,1}}; static AdvancedWindow AW = { NULL, NULL, NULL, NULL }; static FPWidgets fp_widgets = { NULL, NULL, NULL }; static gint nudgeArray[256]; static GtkWidget *origPreview, *curPreview; static GtkWidget *rPreview, *gPreview, *bPreview; static GtkWidget *cPreview, *yPreview, *mPreview; static GtkWidget *centerPreview; static GtkWidget *darkerPreview, *lighterPreview, *middlePreview; static GtkWidget *dlg; static GtkWidget *plusSatPreview, *SatPreview, *minusSatPreview; static struct { GtkWidget *bna; GtkWidget *palette; GtkWidget *rough; GtkWidget *range; GtkWidget *show; GtkWidget *lnd; GtkWidget *pixelsBy; GtkWidget *frameSelect; GtkWidget *satur; } fp_frames; static fpInterface FPint = { FALSE /* run */ }; static ReducedImage *reduced; static FPValues fpvals = { .25, /* Initial Roughness */ .6, /* Initial Degree of Aliasing */ 80, /* Initial preview size */ MIDTONES, /* Initial Range */ BY_VAL, /* Initial God knows what */ TRUE, /* Selection Only */ 0, /* Offset */ 0, /* Visible frames */ { 32, 224, 255 }, /* cutoffs */ { FALSE, FALSE, FALSE } /* touched */ }; static GimpDrawable *drawable = NULL; static GimpDrawable *mask = NULL; static void query (void); static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); const GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; MAIN() static void query (void) { GimpParamDef args[] = { { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, { GIMP_PDB_IMAGE, "image", "Input image (used for indexed images)" }, { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" } }; gimp_install_procedure (PLUG_IN_PROC, N_("Interactively modify the image colors"), "Interactively modify the image colors.", "Pavel Grinfeld (pavel@ml.com)", "Pavel Grinfeld (pavel@ml.com)", "27th March 1997", N_("_Filter Pack..."), "RGB*", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); } /********************************STANDARD RUN*************************/ static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpRunMode run_mode; *nreturn_vals = 1; *return_vals = values; run_mode = param[0].data.d_int32; INIT_I18N (); values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; fp_init_filter_packs(); drawable = gimp_drawable_get (param[2].data.d_drawable); if (gimp_selection_is_empty (param[1].data.d_image)) mask = NULL; else mask = gimp_drawable_get (gimp_image_get_selection (param[1].data.d_image)); switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_PROC, &fpvals); if (gimp_drawable_is_indexed (drawable->drawable_id) || gimp_drawable_is_gray (drawable->drawable_id) ) { gimp_message (_("FP can only be used on RGB images.")); status = GIMP_PDB_EXECUTION_ERROR; } else if (! fp_dialog()) { status = GIMP_PDB_CANCEL; } break; case GIMP_RUN_NONINTERACTIVE: gimp_message (_("FP can only be run interactively.")); status = GIMP_PDB_CALLING_ERROR; break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (PLUG_IN_PROC, &fpvals); break; default: break; } if (status == GIMP_PDB_SUCCESS) { /* Make sure that the drawable is gray or RGB color */ if (gimp_drawable_is_rgb (drawable->drawable_id)) { gimp_progress_init (_("Applying filter pack")); gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1)); fp (drawable); /* Store data */ if (run_mode == GIMP_RUN_INTERACTIVE) gimp_set_data (PLUG_IN_PROC, &fpvals, sizeof (FPValues)); gimp_displays_flush (); } else { status = GIMP_PDB_EXECUTION_ERROR; } } gimp_drawable_detach (drawable); if (mask) gimp_drawable_detach (mask); values[0].data.d_status = status; } static void fp_func (const guchar *src, guchar *dest, gint bpp, gpointer data) { gint bytenum, k; gint JudgeBy, Intensity = 0, P[3]; GimpRGB rgb; GimpHSV hsv; gint M, m, middle; P[0] = src[0]; P[1] = src[1]; P[2] = src[2]; gimp_rgb_set_uchar (&rgb, (guchar) P[0], (guchar) P[1], (guchar) P[2]); gimp_rgb_to_hsv (&rgb, &hsv); for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++) { if (!fpvals.touched[JudgeBy]) continue; switch (JudgeBy) { case BY_HUE: Intensity = 255 * hsv.h; break; case BY_SAT: Intensity = 255 * hsv.s; break; case BY_VAL: Intensity = 255 * hsv.v; break; } /* It's important to take care of Saturation first!!! */ m = MIN (MIN (P[0], P[1]), P[2]); M = MAX (MAX (P[0], P[1]), P[2]); middle = (M + m) / 2; for (k = 0; k < 3; k++) if (P[k] != m && P[k] != M) middle = P[k]; for (k = 0; k < 3; k++) if (M != m) { if (P[k] == M) P[k] = MAX (P[k] + fpvals.sat_adjust[JudgeBy][Intensity], middle); else if (P[k] == m) P[k] = MIN (P[k] - fpvals.sat_adjust[JudgeBy][Intensity], middle); } P[0] += fpvals.red_adjust[JudgeBy][Intensity]; P[1] += fpvals.green_adjust[JudgeBy][Intensity]; P[2] += fpvals.blue_adjust[JudgeBy][Intensity]; P[0] = CLAMP0255(P[0]); P[1] = CLAMP0255(P[1]); P[2] = CLAMP0255(P[2]); } dest[0] = P[0]; dest[1] = P[1]; dest[2] = P[2]; for (bytenum = 3; bytenum < bpp; bytenum++) dest[bytenum] = src[bytenum]; } static void fp (GimpDrawable *drawable) { GimpPixelRgn srcPR, destPR; gint x1, y1, x2, y2; gpointer pr; gint total_area; gint area_so_far; gint count; g_return_if_fail (drawable != NULL); gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2); total_area = (x2 - x1) * (y2 - y1); area_so_far = 0; if (total_area <= 0) return; /* Initialize the pixel regions. */ gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE); gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE); for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0; pr != NULL; pr = gimp_pixel_rgns_process (pr), count++) { const guchar *src = srcPR.data; guchar *dest = destPR.data; gint row; for (row = 0; row < srcPR.h; row++) { const guchar *s = src; guchar *d = dest; gint pixels = srcPR.w; while (pixels--) { fp_func (s, d, srcPR.bpp, NULL); s += srcPR.bpp; d += destPR.bpp; } src += srcPR.rowstride; dest += destPR.rowstride; } area_so_far += srcPR.w * srcPR.h; if ((count % 16) == 0) gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area); } /* update the processed region */ gimp_drawable_flush (drawable); gimp_drawable_merge_shadow (drawable->drawable_id, TRUE); gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1)); } /***********************************************************/ /************ Main Dialog Window ******************/ /***********************************************************/ static GtkWidget * fp_create_bna (void) { GtkWidget *table; GtkWidget *label; GtkWidget *bframe, *aframe; fp_create_preview (&origPreview, &bframe, reduced->width, reduced->height); fp_create_preview (&curPreview, &aframe, reduced->width, reduced->height); table = gtk_table_new (2, 2, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_table_set_col_spacings (GTK_TABLE (table), 6); label = gtk_label_new (_("Original:")); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0); gtk_widget_show (label); gtk_table_attach (GTK_TABLE (table), bframe, 0, 1, 1, 2, GTK_EXPAND, 0, 0, 0); label = gtk_label_new (_("Current:")); gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0); gtk_widget_show (label); gtk_table_attach (GTK_TABLE (table), aframe, 1, 2, 1, 2, GTK_EXPAND, 0, 0, 0); gtk_widget_show (table); return table; } /* close a sub dialog (from window manager) by simulating toggle click */ static gboolean sub_dialog_destroy (GtkWidget *dialog, GdkEvent *ev, gpointer dummy) { GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "ctrlButton")); gtk_button_clicked (GTK_BUTTON (button)); return TRUE; } static GtkWidget * fp_create_circle_palette (GtkWidget *parent) { GtkWidget *table; GtkWidget *rVbox, *rFrame; GtkWidget *gVbox, *gFrame; GtkWidget *bVbox, *bFrame; GtkWidget *cVbox, *cFrame; GtkWidget *yVbox, *yFrame; GtkWidget *mVbox, *mFrame; GtkWidget *centerVbox, *centerFrame; GtkWidget *win; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL); gtk_window_set_title (GTK_WINDOW (win), _("Hue Variations")); gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent)); g_signal_connect (win, "delete-event", G_CALLBACK (sub_dialog_destroy), NULL); table = gtk_table_new (11, 11, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_container_set_border_width (GTK_CONTAINER (table), 12); gtk_container_add (GTK_CONTAINER (win), table); gtk_widget_show (table); fp_create_preview (&rPreview, &rFrame, reduced->width, reduced->height); fp_create_preview (&gPreview, &gFrame, reduced->width, reduced->height); fp_create_preview (&bPreview, &bFrame, reduced->width, reduced->height); fp_create_preview (&cPreview, &cFrame, reduced->width, reduced->height); fp_create_preview (&yPreview, &yFrame, reduced->width, reduced->height); fp_create_preview (&mPreview, &mFrame, reduced->width, reduced->height); fp_create_preview (¢erPreview, ¢erFrame, reduced->width, reduced->height); fp_create_table_entry (&rVbox, rFrame, hue_red); fp_create_table_entry (&gVbox, gFrame, hue_green); fp_create_table_entry (&bVbox, bFrame, hue_blue); fp_create_table_entry (&cVbox, cFrame, hue_cyan); fp_create_table_entry (&yVbox, yFrame, hue_yellow); fp_create_table_entry (&mVbox, mFrame, hue_magenta); fp_create_table_entry (¢erVbox, centerFrame, current_val); gtk_table_attach (GTK_TABLE (table), rVbox, 8, 11 ,4 , 7, GTK_EXPAND , GTK_EXPAND, 0 ,0); gtk_table_attach (GTK_TABLE (table), gVbox, 2, 5, 0, 3, GTK_EXPAND, GTK_EXPAND, 0, 0); gtk_table_attach (GTK_TABLE (table), bVbox, 2, 5, 8, 11, GTK_EXPAND, GTK_EXPAND,0,0); gtk_table_attach (GTK_TABLE (table), cVbox, 0, 3, 4, 7, GTK_EXPAND, GTK_EXPAND, 0 ,0); gtk_table_attach (GTK_TABLE (table), yVbox, 6, 9, 0, 3, GTK_EXPAND, GTK_EXPAND, 0 ,0); gtk_table_attach (GTK_TABLE (table), mVbox, 6, 9, 8, 11, GTK_EXPAND, GTK_EXPAND, 0 ,0); gtk_table_attach (GTK_TABLE (table), centerVbox, 4, 7, 4, 7, GTK_EXPAND, GTK_EXPAND, 0 ,0); return win; } static GtkWidget * fp_create_rough (void) { GtkWidget *frame, *vbox, *scale; GtkAdjustment *data; frame = gimp_frame_new (_("Roughness")); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); data = (GtkAdjustment *) gtk_adjustment_new (fpvals.roughness, 0, 1.0, 0.05, 0.01, 0.0); fp_widgets.roughness_scale = scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, data); gtk_widget_set_size_request (scale, 60, -1); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_scale_set_digits (GTK_SCALE (scale), 2); gtk_widget_show (scale); g_signal_connect (data, "value-changed", G_CALLBACK (fp_scale_update), &fpvals.roughness); gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); return frame; } static void fp_change_current_range (GtkWidget *widget, gpointer data) { gimp_radio_button_update (widget, data); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { fp_refresh_previews (fpvals.visible_frames); if (AW.window && gtk_widget_get_visible (AW.window)) fp_create_smoothness_graph (AW.aliasing_preview); } } static GtkWidget * fp_create_range (void) { GtkWidget *frame; frame = gimp_int_radio_group_new (TRUE, _("Affected Range"), G_CALLBACK (fp_change_current_range), &fpvals.intensity_range, fpvals.intensity_range, _("Sha_dows"), SHADOWS, NULL, _("_Midtones"), MIDTONES, NULL, _("H_ighlights"), HIGHLIGHTS, NULL, NULL); gtk_widget_show (frame); return frame; } static GtkWidget * fp_create_control (void) { GtkWidget *frame, *box; frame = gimp_frame_new (_("Windows")); box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add (GTK_CONTAINER (frame), box); gtk_widget_show (box); fp_frames_checkbutton_in_box (box, _("_Hue"), G_CALLBACK (fp_show_hide_frame), fp_frames.palette, fpvals.visible_frames & HUE); fp_frames_checkbutton_in_box (box, _("_Saturation"), G_CALLBACK (fp_show_hide_frame), fp_frames.satur, fpvals.visible_frames & SATURATION); fp_frames_checkbutton_in_box (box, _("_Value"), G_CALLBACK (fp_show_hide_frame), fp_frames.lnd, fpvals.visible_frames & VALUE); fp_frames_checkbutton_in_box (box, _("A_dvanced"), G_CALLBACK (fp_show_hide_frame), AW.window, FALSE); gtk_widget_show (frame); return frame; } static GtkWidget * fp_create_lnd (GtkWidget *parent) { GtkWidget *table, *lighterFrame, *middleFrame, *darkerFrame; GtkWidget *lighterVbox, *middleVbox, *darkerVbox; GtkWidget *win; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL); gtk_window_set_title (GTK_WINDOW (win), _("Value Variations")); gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent)); g_signal_connect (win, "delete-event", G_CALLBACK (sub_dialog_destroy), NULL); fp_create_preview (&lighterPreview, &lighterFrame, reduced->width, reduced->height); fp_create_preview (&middlePreview, &middleFrame, reduced->width, reduced->height); fp_create_preview (&darkerPreview, &darkerFrame, reduced->width, reduced->height); fp_create_table_entry (&lighterVbox, lighterFrame, val_lighter); fp_create_table_entry (&middleVbox, middleFrame, current_val); fp_create_table_entry (&darkerVbox, darkerFrame, val_darker); table = gtk_table_new (1, 11, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_container_set_border_width (GTK_CONTAINER (table), 12); gtk_container_add (GTK_CONTAINER (win), table); gtk_widget_show (table); gtk_table_attach (GTK_TABLE (table), lighterVbox, 0, 3, 0, 1, GTK_EXPAND , GTK_EXPAND, 0, 0); gtk_table_attach (GTK_TABLE (table), middleVbox, 4, 7, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); gtk_table_attach (GTK_TABLE (table), darkerVbox, 8, 11, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); return win; } static GtkWidget * fp_create_msnls (GtkWidget *parent) { GtkWidget *table, *lessFrame, *middleFrame, *moreFrame; GtkWidget *lessVbox, *middleVbox, *moreVbox; GtkWidget *win; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL); gtk_window_set_title (GTK_WINDOW (win), _("Saturation Variations")); gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent)); g_signal_connect (win, "delete-event", G_CALLBACK (sub_dialog_destroy), NULL); fp_create_preview (&minusSatPreview, &lessFrame, reduced->width, reduced->height); fp_create_preview (&SatPreview, &middleFrame, reduced->width, reduced->height); fp_create_preview (&plusSatPreview, &moreFrame, reduced->width, reduced->height); fp_create_table_entry (&moreVbox, moreFrame, sat_more); fp_create_table_entry (&middleVbox, middleFrame, current_val); fp_create_table_entry (&lessVbox, lessFrame, sat_less); table = gtk_table_new (1, 11, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_container_set_border_width (GTK_CONTAINER (table), 12); gtk_container_add (GTK_CONTAINER (win), table); gtk_widget_show (table); gtk_table_attach (GTK_TABLE (table), moreVbox, 0, 3, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); gtk_table_attach (GTK_TABLE (table), middleVbox, 4, 7, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); gtk_table_attach (GTK_TABLE (table), lessVbox, 8, 11, 0, 1, GTK_EXPAND, GTK_EXPAND, 0, 0); return win; } static void fp_change_current_pixels_by (GtkWidget *widget, gpointer data) { gimp_radio_button_update (widget, data); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { fp_refresh_previews (fpvals.visible_frames); if (AW.window && gtk_widget_get_visible (AW.window) && AW.range_preview) fp_range_preview_spill (AW.range_preview,fpvals.value_by); } } static GtkWidget * fp_create_pixels_select_by (void) { GtkWidget *frame; frame = gimp_int_radio_group_new (TRUE, _("Select Pixels By"), G_CALLBACK (fp_change_current_pixels_by), &fpvals.value_by, fpvals.value_by, _("H_ue"), 0, NULL, _("Satu_ration"), 1, NULL, _("V_alue"), 2, NULL, NULL); gtk_widget_show (frame); return frame; } static void fp_change_selection (GtkWidget *widget, gpointer data) { gimp_radio_button_update (widget, data); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { fp_redraw_all_windows (); } } static GtkWidget * fp_create_show (void) { GtkWidget *frame; frame = gimp_int_radio_group_new (TRUE, _("Show"), G_CALLBACK (fp_change_selection), &fpvals.selection_only, fpvals.selection_only, _("_Entire image"), 0, NULL, _("Se_lection only"), 1, NULL, _("Selec_tion in context"), 2, NULL, NULL); gtk_widget_show (frame); return frame; } static void fp_create_preview (GtkWidget **preview, GtkWidget **frame, gint preview_width, gint preview_height) { *frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (*frame), GTK_SHADOW_IN); gtk_widget_show (*frame); *preview = gimp_preview_area_new (); gtk_widget_set_size_request (*preview, preview_width, preview_height); g_signal_connect (*preview, "size-allocate", G_CALLBACK (fp_preview_size_allocate), NULL); gtk_widget_show (*preview); gtk_container_add (GTK_CONTAINER (*frame), *preview); } static void fp_frames_checkbutton_in_box (GtkWidget *vbox, const gchar *label, GCallback function, GtkWidget *frame, gboolean clicked) { GtkWidget *button; button = gtk_check_button_new_with_mnemonic (label); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); g_object_set_data (G_OBJECT (frame), "ctrlButton", (gpointer) button); gtk_widget_show (button); g_signal_connect (button, "clicked", function, frame); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), clicked); } static void fp_create_table_entry (GtkWidget **box, GtkWidget *smaller_frame, const gchar *description) { GtkWidget *label; GtkWidget *button; GtkWidget *table; *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1); gtk_container_set_border_width (GTK_CONTAINER (*box), PR_BX_BRDR); gtk_widget_show (*box); /* Delayed translation applied here */ label = gtk_label_new (gettext (description)); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_widget_show (label); table = gtk_table_new (2, 1, FALSE); gtk_widget_show (table); gtk_box_pack_start (GTK_BOX (*box), table, TRUE, TRUE, 0); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 0, 0, 0, 0); if (description != current_val) { button = gtk_button_new (); gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2, 0, 0, 0, 4); gtk_widget_show (button); gtk_container_add (GTK_CONTAINER (button), smaller_frame); g_signal_connect (button, "clicked", G_CALLBACK (fp_selection_made), (gchar *) description); } else { gtk_table_attach (GTK_TABLE (table), smaller_frame, 0, 1, 1, 2, 0, 0, 0, 4); } } static void fp_redraw_all_windows (void) { if (reduced) { g_free (reduced->rgb); g_free (reduced->hsv); g_free (reduced->mask); g_free (reduced); } reduced = fp_reduce_image (drawable, mask, fpvals.preview_size, fpvals.selection_only); fp_adjust_preview_sizes (reduced->width, reduced->height); gtk_widget_queue_draw (fp_frames.palette); gtk_widget_queue_draw (fp_frames.satur); gtk_widget_queue_draw (fp_frames.lnd); gtk_widget_queue_draw (dlg); fp_refresh_previews (fpvals.visible_frames); } static void fp_show_hide_frame (GtkWidget *button, GtkWidget *frame) { gint prev = fpvals.visible_frames; if (frame == NULL) return; if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) { if (! gtk_widget_get_visible (frame)) { gtk_widget_show (frame); if (frame==fp_frames.palette) fpvals.visible_frames |= HUE; else if (frame==fp_frames.satur) fpvals.visible_frames |= SATURATION; else if (frame==fp_frames.lnd) fpvals.visible_frames |= VALUE; fp_refresh_previews (fpvals.visible_frames & ~prev); fp_create_smoothness_graph (AW.aliasing_preview); fp_range_preview_spill (AW.range_preview,fpvals.value_by); } } else { if (gtk_widget_get_visible (frame)) { gtk_widget_hide (frame); if (frame==fp_frames.palette) fpvals.visible_frames &= ~HUE; else if (frame==fp_frames.satur) fpvals.visible_frames &= ~SATURATION; else if (frame==fp_frames.lnd) fpvals.visible_frames &= ~VALUE; } } } static void fp_adjust_preview_sizes (gint width, gint height) { gtk_widget_set_size_request (origPreview, width, height); gtk_widget_set_size_request (curPreview, width, height); gtk_widget_set_size_request (rPreview, width, height); gtk_widget_set_size_request (gPreview, width, height); gtk_widget_set_size_request (bPreview, width, height); gtk_widget_set_size_request (cPreview, width, height); gtk_widget_set_size_request (yPreview, width, height); gtk_widget_set_size_request (mPreview, width, height); gtk_widget_set_size_request (centerPreview, width, height); gtk_widget_set_size_request (lighterPreview, width, height); gtk_widget_set_size_request (darkerPreview, width, height); gtk_widget_set_size_request (middlePreview, width, height); gtk_widget_set_size_request (minusSatPreview, width, height); gtk_widget_set_size_request (SatPreview, width, height); gtk_widget_set_size_request (plusSatPreview, width, height); } static void fp_selection_made (GtkWidget *widget, gpointer data) { fpvals.touched[fpvals.value_by] = TRUE; if (data == (gpointer) hue_red) { update_current_fp (HUE, RED); } else if (data == (gpointer) hue_green) { update_current_fp (HUE, GREEN); } else if (data == (gpointer) hue_blue) { update_current_fp (HUE, BLUE); } else if (data == (gpointer) hue_cyan) { update_current_fp (HUE, CYAN); } else if (data == (gpointer) hue_yellow) { update_current_fp (HUE, YELLOW); } else if (data == (gpointer) hue_magenta) { update_current_fp (HUE, MAGENTA); } else if (data == (gpointer) val_darker) { update_current_fp (VALUE, DOWN); } else if (data == (gpointer) val_lighter) { update_current_fp (VALUE, UP); } else if (data == (gpointer) sat_more) { update_current_fp (SATURATION, UP); } else if (data == (gpointer) sat_less) { update_current_fp (SATURATION, DOWN); } fp_refresh_previews (fpvals.visible_frames); } static void fp_refresh_previews (gint which) { fp_create_nudge (nudgeArray); fp_render_preview (origPreview, NONEATALL, 0); fp_render_preview (curPreview, CURRENT, 0); if (which & HUE) { fp_render_preview (rPreview, HUE, RED); fp_render_preview (gPreview, HUE, GREEN); fp_render_preview (bPreview, HUE, BLUE); fp_render_preview (cPreview, HUE, CYAN); fp_render_preview (yPreview, HUE, YELLOW); fp_render_preview (mPreview, HUE, MAGENTA); fp_render_preview (centerPreview, CURRENT, 0); } if (which & VALUE) { fp_render_preview (lighterPreview, VALUE, UP); fp_render_preview (middlePreview, CURRENT, 0); fp_render_preview (darkerPreview, VALUE, DOWN); } if (which & SATURATION) { fp_render_preview (plusSatPreview, SATURATION, UP); fp_render_preview (SatPreview, CURRENT, 0); fp_render_preview (minusSatPreview, SATURATION, DOWN); } } static void fp_response (GtkWidget *widget, gint response_id, gpointer data) { switch (response_id) { case RESPONSE_RESET: fp_reset_filter_packs (); break; case GTK_RESPONSE_OK: FPint.run = TRUE; gtk_widget_destroy (widget); break; default: gtk_widget_destroy (widget); break; } } static void fp_scale_update (GtkAdjustment *adjustment, gdouble *scale_val) { static gdouble prevValue = 0.25; *scale_val = gtk_adjustment_get_value (adjustment); if (prevValue != gtk_adjustment_get_value (adjustment)) { fp_create_nudge (nudgeArray); fp_refresh_previews (fpvals.visible_frames); if (AW.window != NULL && gtk_widget_get_visible (AW.window)) fp_create_smoothness_graph (AW.aliasing_preview); prevValue = gtk_adjustment_get_value (adjustment); } } static gboolean fp_dialog (void) { GtkWidget *bna; GtkWidget *palette; GtkWidget *lnd; GtkWidget *show; GtkWidget *rough; GtkWidget *range; GtkWidget *pixelsBy; GtkWidget *satur; GtkWidget *control; GtkWidget *table; reduced = fp_reduce_image (drawable, mask, fpvals.preview_size, fpvals.selection_only); gimp_ui_init (PLUG_IN_BINARY, FALSE); dlg = gimp_dialog_new (_("Filter Pack Simulation"), PLUG_IN_ROLE, NULL, 0, gimp_standard_help_func, PLUG_IN_PROC, _("_Reset"), RESPONSE_RESET, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg), RESPONSE_RESET, GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gimp_window_set_transient (GTK_WINDOW (dlg)); g_signal_connect (dlg, "response", G_CALLBACK (fp_response), dlg); g_signal_connect (dlg, "destroy", G_CALLBACK (gtk_main_quit), NULL); fp_advanced_dialog (dlg); fp_frames.bna = bna = fp_create_bna (); fp_frames.rough = rough = fp_create_rough (); fp_frames.range = range = fp_create_range (); fp_frames.palette = palette = fp_create_circle_palette (dlg); fp_frames.lnd = lnd = fp_create_lnd (dlg); fp_frames.show = show = fp_create_show (); fp_frames.satur = satur = fp_create_msnls (dlg); fp_frames.pixelsBy = pixelsBy = fp_create_pixels_select_by (); control = fp_create_control (); /********************************************************************/ /******************** PUT EVERYTHING TOGETHER ******************/ table = gtk_table_new (4, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_container_set_border_width (GTK_CONTAINER (table), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), table, TRUE, TRUE, 0); gtk_widget_show (table); gtk_table_attach (GTK_TABLE (table), bna, 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_table_attach (GTK_TABLE (table), control, 1, 2, 1, 3, GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (table), rough, 1, 2, 3, 4, GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (table), show, 0, 1, 1, 2, GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (table), range, 0, 1, 2, 3, GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (table), pixelsBy, 0, 1, 3, 4, GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0); gtk_widget_show (dlg); fp_refresh_previews (fpvals.visible_frames); gtk_main (); return FPint.run; } /***********************************************************/ /************ Advanced Options Window ******************/ /***********************************************************/ static void fp_preview_scale_update (GtkAdjustment *adjustment, gdouble *scale_val) { fpvals.preview_size = gtk_adjustment_get_value (adjustment); fp_redraw_all_windows(); } static void fp_advanced_dialog (GtkWidget *parent) { const gchar *rangeNames[] = { N_("Shadows:"), N_("Midtones:"), N_("Highlights:") }; GtkWidget *frame, *hbox; GtkAdjustment *smoothnessData; GtkWidget *graphFrame, *scale; GtkWidget *vbox, *label, *labelTable, *alignment; GtkWidget *inner_vbox, *innermost_vbox; gint i; AW.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gimp_help_connect (AW.window, gimp_standard_help_func, PLUG_IN_PROC, NULL); gtk_window_set_title (GTK_WINDOW (AW.window), _("Advanced Filter Pack Options")); gtk_window_set_transient_for (GTK_WINDOW (AW.window), GTK_WINDOW (parent)); g_signal_connect (AW.window, "delete-event", G_CALLBACK (sub_dialog_destroy), NULL); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); gtk_container_add (GTK_CONTAINER (AW.window), hbox); gtk_widget_show (hbox); frame = gimp_frame_new (_("Affected Range")); gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); graphFrame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1, TRUE); gtk_frame_set_shadow_type (GTK_FRAME (graphFrame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (graphFrame), 0); gtk_box_pack_start (GTK_BOX (vbox), graphFrame, FALSE, FALSE, 0); gtk_widget_show (graphFrame); inner_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (graphFrame), inner_vbox); gtk_widget_show (inner_vbox); alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_box_pack_start (GTK_BOX (inner_vbox), alignment, TRUE, TRUE, 0); gtk_widget_show (alignment); innermost_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (alignment), innermost_vbox); gtk_widget_show (innermost_vbox); AW.aliasing_preview = gimp_preview_area_new (); gtk_widget_set_size_request (AW.aliasing_preview, 256, MAX_ROUGHNESS); gtk_box_pack_start (GTK_BOX (innermost_vbox), AW.aliasing_preview, TRUE, TRUE, 0); gtk_widget_show (AW.aliasing_preview); fp_create_smoothness_graph (AW.aliasing_preview); AW.range_preview = gimp_preview_area_new (); gtk_widget_set_size_request (AW.range_preview, 256, RANGE_HEIGHT); gtk_box_pack_start(GTK_BOX (innermost_vbox), AW.range_preview, TRUE, TRUE, 0); gtk_widget_show (AW.range_preview); fp_range_preview_spill (AW.range_preview, fpvals.value_by); labelTable = gtk_table_new (3, 4, FALSE); gtk_table_set_col_spacings (GTK_TABLE (labelTable), 6); gtk_table_set_row_spacings (GTK_TABLE (labelTable), 6); gtk_box_pack_start (GTK_BOX (vbox), labelTable, FALSE, FALSE, 0); gtk_widget_show (labelTable); /************************************************************/ AW.aliasing_graph = gtk_drawing_area_new (); gtk_widget_set_size_request (AW.aliasing_graph, 2 * MARGIN + 256, RANGE_HEIGHT); gtk_box_pack_start (GTK_BOX (inner_vbox), AW.aliasing_graph, TRUE, TRUE, 0); gtk_widget_show (AW.aliasing_graph); gtk_widget_set_events (AW.aliasing_graph, RANGE_ADJUST_MASK); g_signal_connect (AW.aliasing_graph, "event", G_CALLBACK (fp_range_change_events), &fpvals); /************************************************************/ for (i = 0; i < 12; i++) { label = fp_widgets.range_label[i] = gtk_label_new ("-"); if (!(i % 4)) { gtk_label_set_text (GTK_LABEL(label), gettext (rangeNames[i/4])); gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1); gtk_label_set_xalign (GTK_LABEL (label), 1.0); gtk_label_set_yalign (GTK_LABEL (label), 1.0); } gtk_widget_show (label); gtk_table_attach (GTK_TABLE (labelTable), label, i%4, i%4+1, i/4, i/4+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); } smoothnessData = (GtkAdjustment *) gtk_adjustment_new (fpvals.aliasing, 0, 1.0, 0.05, 0.01, 0.0); fp_widgets.aliasing_scale = scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, smoothnessData); gtk_widget_set_size_request (scale, 200, -1); gtk_scale_set_digits (GTK_SCALE (scale), 2); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); gtk_widget_show (scale); g_signal_connect (smoothnessData, "value-changed", G_CALLBACK (fp_scale_update), &fpvals.aliasing); /******************* MISC OPTIONS ***************************/ frame = gimp_frame_new (_("Preview Size")); gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0); gtk_widget_show (frame); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (frame), vbox); gtk_widget_show (vbox); smoothnessData = (GtkAdjustment *) gtk_adjustment_new (fpvals.preview_size, 50, MAX_PREVIEW_SIZE, 5, 5, 0.0); fp_widgets.preview_size_scale = scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, smoothnessData); gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); gtk_widget_set_size_request (scale, 100, -1); gtk_scale_set_digits (GTK_SCALE (scale), 0); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); gtk_widget_show (scale); g_signal_connect (smoothnessData, "value-changed", G_CALLBACK (fp_preview_scale_update), &fpvals.preview_size); update_range_labels (); } static void slider_erase (GdkWindow *window, int xpos) { gdk_window_clear_area (window, MARGIN + xpos - (RANGE_HEIGHT - 1) / 2, 0, RANGE_HEIGHT, RANGE_HEIGHT); } static void draw_slider (cairo_t *cr, GdkColor *border_color, GdkColor *fill_color, gint xpos) { cairo_move_to (cr, MARGIN + xpos, 0); cairo_line_to (cr, MARGIN + xpos - (RANGE_HEIGHT - 1) / 2, RANGE_HEIGHT - 1); cairo_line_to (cr, MARGIN + xpos + (RANGE_HEIGHT - 1) / 2, RANGE_HEIGHT - 1); cairo_line_to (cr, MARGIN + xpos, 0); gdk_cairo_set_source_color (cr, fill_color); cairo_fill_preserve (cr); gdk_cairo_set_source_color (cr, border_color); cairo_stroke (cr); } static void draw_it (GtkWidget *widget) { GtkStyle *style = gtk_widget_get_style (AW.aliasing_graph); cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (AW.aliasing_graph)); cairo_translate (cr, 0.5, 0.5); cairo_set_line_width (cr, 1.0); draw_slider (cr, &style->black, &style->dark[GTK_STATE_NORMAL], fpvals.cutoff[SHADOWS]); draw_slider (cr, &style->black, &style->dark[GTK_STATE_NORMAL], fpvals.cutoff[MIDTONES]); draw_slider (cr, &style->black, &style->dark[GTK_STATE_SELECTED], fpvals.offset); cairo_destroy (cr); } static gboolean fp_range_change_events (GtkWidget *widget, GdkEvent *event, FPValues *current) { GdkEventButton *bevent; GdkEventMotion *mevent; gint shad, mid, offset, min; static guchar *new; gint x; switch (event->type) { case GDK_EXPOSE: draw_it (NULL); break; case GDK_BUTTON_PRESS: bevent= (GdkEventButton *) event; shad = abs (bevent->x - fpvals.cutoff[SHADOWS]); mid = abs (bevent->x - fpvals.cutoff[MIDTONES]); offset = abs (bevent->x - fpvals.offset); min = MIN (MIN (shad, mid), offset); if (bevent->x >0 && bevent->x<256) { if (min == shad) new = &fpvals.cutoff[SHADOWS]; else if (min == mid) new = &fpvals.cutoff[MIDTONES]; else new = &fpvals.offset; slider_erase (gtk_widget_get_window (AW.aliasing_graph), *new); *new = bevent->x; } draw_it (NULL); fp_range_preview_spill (AW.range_preview, fpvals.value_by); update_range_labels (); fp_create_smoothness_graph (AW.aliasing_preview); break; case GDK_BUTTON_RELEASE: fp_refresh_previews (fpvals.visible_frames); break; case GDK_MOTION_NOTIFY: mevent = (GdkEventMotion *) event; x = mevent->x; if (x >= 0 && x < 256) { slider_erase (gtk_widget_get_window (AW.aliasing_graph), *new); *new = x; draw_it (NULL); fp_range_preview_spill (AW.range_preview, fpvals.value_by); update_range_labels (); fp_create_smoothness_graph (AW.aliasing_preview); } gdk_event_request_motions (mevent); break; default: break; } return FALSE; } static void update_range_labels (void) { gchar buffer[4]; gtk_label_set_text (GTK_LABEL(fp_widgets.range_label[1]), "0"); g_snprintf (buffer, sizeof (buffer), "%d", fpvals.cutoff[SHADOWS]); gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[3]), buffer); gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[5]), buffer); g_snprintf (buffer, sizeof (buffer), "%d", fpvals.cutoff[MIDTONES]); gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[7]), buffer); gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[9]), buffer); gtk_label_set_text (GTK_LABEL(fp_widgets.range_label[11]), "255"); } static void fp_init_filter_packs (void) { gint i, j; for (i = 0; i < 256; i++) for (j = BY_HUE; j < JUDGE_BY; j++) { fpvals.red_adjust [j][i] = 0; fpvals.green_adjust [j][i] = 0; fpvals.blue_adjust [j][i] = 0; fpvals.sat_adjust [j][i] = 0; } } static void fp_reset_filter_packs (void) { fp_init_filter_packs (); fp_refresh_previews (fpvals.visible_frames); } static ReducedImage * fp_reduce_image (GimpDrawable *drawable, GimpDrawable *mask, gint longer_size, gint selection) { gint RH, RW, bytes = drawable->bpp; gint x, y, width, height; ReducedImage *temp = g_new0 (ReducedImage, 1); guchar *tempRGB, *src_row, *tempmask, *src_mask_row, R, G, B; gint i, j, whichcol, whichrow; GimpPixelRgn srcPR, srcMask; gdouble *tempHSV; GimpRGB rgb; GimpHSV hsv; switch (selection) { case 0: x = 0; width = drawable->width; y = 0; height = drawable->height; break; case 1: if (! gimp_drawable_mask_intersect (drawable->drawable_id, &x, &y, &width, &height)) return temp; break; case 2: if (! gimp_drawable_mask_intersect (drawable->drawable_id, &x, &y, &width, &height) || ! gimp_rectangle_intersect (x - width / 2, y - height / 2, 2 * width, 2 * height, 0, 0, drawable->width, drawable->height, &x, &y, &width, &height)) return temp; break; default: return temp; } if (width > height) { RW = longer_size; RH = (gdouble) height * (gdouble) longer_size / (gdouble) width; } else { RH = longer_size; RW = (gdouble) width * (gdouble) longer_size / (gdouble) height; } tempRGB = g_new (guchar, RW * RH * bytes); tempHSV = g_new (gdouble, RW * RH * bytes); tempmask = g_new (guchar, RW * RH); src_row = g_new (guchar, width * bytes); src_mask_row = g_new (guchar, width); gimp_pixel_rgn_init (&srcPR, drawable, x, y, width, height, FALSE, FALSE); if (mask) { gimp_pixel_rgn_init (&srcMask, mask, x, y, width, height, FALSE, FALSE); } else { memset (src_mask_row, 255, width); } for (i = 0; i < RH; i++) { whichrow = (gdouble) i * (gdouble) height / (gdouble) RH; gimp_pixel_rgn_get_row (&srcPR, src_row, x, y + whichrow, width); if (mask) gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x, y + whichrow, width); for (j = 0; j < RW; j++) { whichcol = (gdouble) j * (gdouble) width / (gdouble) RW; tempmask[i * RW + j] = src_mask_row[whichcol]; R = src_row[whichcol * bytes + 0]; G = src_row[whichcol * bytes + 1]; B = src_row[whichcol * bytes + 2]; gimp_rgb_set_uchar (&rgb, R, G, B); gimp_rgb_to_hsv (&rgb, &hsv); tempRGB[i * RW * bytes + j * bytes + 0] = R; tempRGB[i * RW * bytes + j * bytes + 1] = G; tempRGB[i * RW * bytes + j * bytes + 2] = B; tempHSV[i * RW * bytes + j * bytes + 0] = hsv.h; tempHSV[i * RW * bytes + j * bytes + 1] = hsv.s; tempHSV[i * RW * bytes + j * bytes + 2] = hsv.v; if (bytes == 4) { tempRGB[i * RW * bytes + j * bytes + 3] = src_row[whichcol * bytes + 3]; } } } g_free (src_row); g_free (src_mask_row); temp->width = RW; temp->height = RH; temp->rgb = tempRGB; temp->hsv = tempHSV; temp->mask = tempmask; return temp; } static void fp_render_preview (GtkWidget *preview, gint change_what, gint change_which) { guchar *a; gint Inten; gint bytes = drawable->bpp; gint i, j, k, nudge, M, m, middle, JudgeBy; gdouble partial; gint RW = reduced->width; gint RH = reduced->height; gint backupP[3]; gint P[3]; gint tempSat[JUDGE_BY][256]; a = g_new (guchar, 4 * RW * RH); if (change_what == SATURATION) for (k = 0; k < 256; k++) { for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++) tempSat[JudgeBy][k] = 0; tempSat[fpvals.value_by][k] += change_which * nudgeArray[(k + fpvals.offset) % 256]; } for (i = 0; i < RH; i++) { for (j = 0; j < RW; j++) { backupP[0] = P[0] = reduced->rgb[i * RW * bytes + j * bytes + 0]; backupP[1] = P[1] = reduced->rgb[i * RW * bytes + j * bytes + 1]; backupP[2] = P[2] = reduced->rgb[i * RW * bytes + j * bytes + 2]; m = MIN (MIN (P[0], P[1]), P[2]); M = MAX (MAX (P[0], P[1]), P[2]); middle = (M + m) / 2; for (k = 0; k < 3; k++) if (P[k] != m && P[k] != M) middle = P[k]; partial = reduced->mask[i * RW + j] / 255.0; for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++) { if (!fpvals.touched[JudgeBy]) continue; Inten = reduced->hsv[i * RW * bytes + j * bytes + JudgeBy] * 255.0; /*DO SATURATION FIRST*/ if (change_what != NONEATALL) { gint adjust = partial * fpvals.sat_adjust[JudgeBy][Inten]; if (M != m) { for (k = 0; k < 3; k++) if (backupP[k] == M) { P[k] = MAX (P[k] + adjust, middle); } else if (backupP[k] == m) { P[k] = MIN (P[k] - adjust, middle); } } P[0] += partial * fpvals.red_adjust[JudgeBy][Inten]; P[1] += partial * fpvals.green_adjust[JudgeBy][Inten]; P[2] += partial * fpvals.blue_adjust[JudgeBy][Inten]; } } Inten = reduced->hsv[i * RW * bytes + j * bytes + fpvals.value_by] * 255.0; nudge = partial * nudgeArray[(Inten + fpvals.offset) % 256]; switch (change_what) { case HUE: P[0] += colorSign[RED][change_which] * nudge; P[1] += colorSign[GREEN][change_which] * nudge; P[2] += colorSign[BLUE][change_which] * nudge; break; case SATURATION: for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++) { gint adjust = partial * tempSat[JudgeBy][Inten]; for (k = 0; k < 3; k++) if (M != m) { if (backupP[k] == M) { P[k] = MAX (P[k] + adjust, middle); } else if (backupP[k] == m) { P[k] = MIN (P[k] - adjust, middle); } } } break; case VALUE: P[0] += change_which * nudge; P[1] += change_which * nudge; P[2] += change_which * nudge; break; default: break; } a[(i * RW + j) * 4 + 0] = CLAMP0255 (P[0]); a[(i * RW + j) * 4 + 1] = CLAMP0255 (P[1]); a[(i * RW + j) * 4 + 2] = CLAMP0255 (P[2]); if (bytes == 4) a[(i * RW + j) * 4 + 3] = reduced->rgb[i * RW * bytes + j * bytes + 3]; else a[(i * RW + j) * 4 + 3] = 255; } } gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview), 0, 0, RW, RH, GIMP_RGBA_IMAGE, a, RW * 4); g_free (a); } static void update_current_fp (gint change_what, gint change_which) { gint i; for (i = 0; i < 256; i++) { gint nudge; fp_create_nudge (nudgeArray); nudge = nudgeArray[(i + fpvals.offset) % 256]; switch (change_what) { case HUE: fpvals.red_adjust[fpvals.value_by][i] += colorSign[RED][change_which] * nudge; fpvals.green_adjust[fpvals.value_by][i] += colorSign[GREEN][change_which] * nudge; fpvals.blue_adjust[fpvals.value_by][i] += colorSign[BLUE][change_which] * nudge; break; case SATURATION: fpvals.sat_adjust[fpvals.value_by][i] += change_which * nudge; break; case VALUE: fpvals.red_adjust[fpvals.value_by][i] += change_which * nudge; fpvals.green_adjust[fpvals.value_by][i] += change_which * nudge; fpvals.blue_adjust[fpvals.value_by][i] += change_which * nudge; break; default: break; } } } static void fp_create_smoothness_graph (GtkWidget *preview) { guchar data[256 * MAX_ROUGHNESS * 3]; gint nArray[256]; gint i, j; gboolean toBeBlack; fp_create_nudge(nArray); for (i = 0; i < MAX_ROUGHNESS; i++) { gint coor = MAX_ROUGHNESS - i; for (j = 0; j < 256; j++) { data[3 * (i * 256 + j) + 0] = 255; data[3 * (i * 256 + j) + 1] = 255; data[3 * (i * 256 + j) + 2] = 255; if (!(i % (MAX_ROUGHNESS / 4))) { data[3 * (i * 256 + j) + 0] = 255; data[3 * (i * 256 + j) + 1] = 128; data[3 * (i * 256 + j) + 2] = 128; } if (!((j + 1) % 32)) { data[3 * (i * 256 + j) + 0] = 255; data[3 * (i * 256 + j) + 1] = 128; data[3 * (i * 256 + j) + 2] = 128; } toBeBlack = FALSE; if (nArray[j] == coor) toBeBlack = TRUE; if (j < 255) { gint jump = abs (nArray[j] - nArray[j+1]); if (abs (coor - nArray[j]) < jump && abs (coor - nArray[j + 1]) < jump) toBeBlack = TRUE; } if (toBeBlack) { data[3 * (i * 256 + j) + 0] = 0; data[3 * (i * 256 + j) + 1] = 0; data[3 * (i * 256 + j) + 2] = 0; } } } gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview), 0, 0, 256, MAX_ROUGHNESS, GIMP_RGB_IMAGE, data, 256 * 3); } static void fp_range_preview_spill (GtkWidget *preview, gint type) { gint i, j; guchar data[256 * RANGE_HEIGHT * 3]; for (i = 0; i < RANGE_HEIGHT; i++) { for (j = 0; j < 256; j++) { GimpRGB rgb; GimpHSV hsv; if (! ((j + 1) % 32)) { data[3 * (i * 256 + j) + 0] = 255; data[3 * (i * 256 + j) + 1] = 128; data[3 * (i * 256 + j) + 2] = 128; } else { switch (type) { case BY_VAL: data[3 * (i * 256 + j) + 0] = j - fpvals.offset; data[3 * (i * 256 + j) + 1] = j - fpvals.offset; data[3 * (i * 256 + j) + 2] = j - fpvals.offset; break; case BY_HUE: gimp_hsv_set (&hsv, ((j - fpvals.offset + 256) % 256) / 255.0, 1.0, 0.5); gimp_hsv_to_rgb (&hsv, &rgb); gimp_rgb_get_uchar (&rgb, &data[3 * (i * 256 + j) + 0], &data[3 * (i * 256 + j) + 1], &data[3 * (i * 256 + j) + 2]); break; case BY_SAT: gimp_hsv_set (&hsv, 0.5, ((j - (gint) fpvals.offset + 256) % 256) / 255.0, 0.5); gimp_hsv_to_rgb (&hsv, &rgb); gimp_rgb_get_uchar (&rgb, &data[3 * (i * 256 + j) + 0], &data[3 * (i * 256 + j) + 1], &data[3 * (i * 256 + j) + 2]); break; } } } } gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview), 0, 0, 256, RANGE_HEIGHT, GIMP_RGB_IMAGE, data, 256 * 3); } static void fp_create_nudge (gint *adj_array) { gint left, right, middle,i; /* The following function was determined by trial and error */ gdouble Steepness = pow (1 - fpvals.aliasing, 4) * .8; left = (fpvals.intensity_range == SHADOWS) ? 0 : fpvals.cutoff[fpvals.intensity_range - 1]; right = fpvals.cutoff[fpvals.intensity_range]; middle = (left + right)/2; if (fpvals.aliasing) for (i = 0; i < 256; i++) if (i <= middle) adj_array[i] = MAX_ROUGHNESS * fpvals.roughness * (1 + tanh (Steepness * (i - left))) / 2; else adj_array[i] = MAX_ROUGHNESS * fpvals.roughness * (1 + tanh (Steepness * (right - i))) / 2; else for (i = 0; i < 256; i++) adj_array[i] = (left <= i && i <= right) ? MAX_ROUGHNESS * fpvals.roughness : 0; } static void fp_preview_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { gint which = fpvals.visible_frames; if (widget == origPreview) fp_render_preview (origPreview, NONEATALL, 0); else if (widget == curPreview) fp_render_preview (curPreview, CURRENT, 0); if (which & HUE) { if (widget == rPreview) fp_render_preview (rPreview, HUE, RED); else if (widget == gPreview) fp_render_preview (gPreview, HUE, GREEN); else if (widget == bPreview) fp_render_preview (bPreview, HUE, BLUE); else if (widget == cPreview) fp_render_preview (cPreview, HUE, CYAN); else if (widget == yPreview) fp_render_preview (yPreview, HUE, YELLOW); else if (widget == mPreview) fp_render_preview (mPreview, HUE, MAGENTA); else if (widget == centerPreview) fp_render_preview (centerPreview, CURRENT, 0); } if (which & VALUE) { if (widget == lighterPreview) fp_render_preview (lighterPreview, VALUE, UP); else if (widget == middlePreview) fp_render_preview (middlePreview, CURRENT, 0); else if (widget == darkerPreview) fp_render_preview (darkerPreview, VALUE, DOWN); } if (which & SATURATION) { if (widget == plusSatPreview) fp_render_preview (plusSatPreview, SATURATION, UP); else if (widget == SatPreview) fp_render_preview (SatPreview, CURRENT, 0); else if (widget == minusSatPreview) fp_render_preview (minusSatPreview, SATURATION, DOWN); } }