diff options
Diffstat (limited to 'plug-ins/common/filter-pack.c')
-rw-r--r-- | plug-ins/common/filter-pack.c | 2178 |
1 files changed, 2178 insertions, 0 deletions
diff --git a/plug-ins/common/filter-pack.c b/plug-ins/common/filter-pack.c new file mode 100644 index 0000000..9664596 --- /dev/null +++ b/plug-ins/common/filter-pack.c @@ -0,0 +1,2178 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#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); + } +} |