summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/sample-colorize.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/common/sample-colorize.c')
-rw-r--r--plug-ins/common/sample-colorize.c3113
1 files changed, 3113 insertions, 0 deletions
diff --git a/plug-ins/common/sample-colorize.c b/plug-ins/common/sample-colorize.c
new file mode 100644
index 0000000..bae918b
--- /dev/null
+++ b/plug-ins/common/sample-colorize.c
@@ -0,0 +1,3113 @@
+/* sample_colorize.c
+ * A GIMP Plug-In by Wolfgang Hofer
+ */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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 <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+#define RESPONSE_RESET 1
+#define RESPONSE_GET_COLORS 2
+
+#define PLUG_IN_PROC "plug-in-sample-colorize"
+#define PLUG_IN_BINARY "sample-colorize"
+#define PLUG_IN_ROLE "gimp-sample-colorize"
+#define NUMBER_IN_ARGS 13
+
+#define LUMINOSITY_0(X) ((X[0] * 30 + X[1] * 59 + X[2] * 11))
+#define LUMINOSITY_1(X) ((X[0] * 30 + X[1] * 59 + X[2] * 11) / 100)
+#define MIX_CHANNEL(a, b, m) (((a * m) + (b * (255 - m))) / 255)
+
+#define SMP_GRADIENT -444
+#define SMP_INV_GRADIENT -445
+
+
+#define PREVIEW_BPP 3
+#define PREVIEW_SIZE_X 256
+#define PREVIEW_SIZE_Y 256
+#define DA_WIDTH 256
+#define DA_HEIGHT 25
+#define GRADIENT_HEIGHT 15
+#define CONTROL_HEIGHT DA_HEIGHT - GRADIENT_HEIGHT
+#define LEVELS_DA_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)
+
+#define LOW_INPUT 0x1
+#define GAMMA 0x2
+#define HIGH_INPUT 0x4
+#define LOW_OUTPUT 0x8
+#define HIGH_OUTPUT 0x10
+#define INPUT_LEVELS 0x20
+#define OUTPUT_LEVELS 0x40
+#define INPUT_SLIDERS 0x80
+#define OUTPUT_SLIDERS 0x100
+#define DRAW 0x200
+#define REFRESH_DST 0x400
+#define ALL 0xFFF
+
+#define MC_GET_SAMPLE_COLORS 1
+#define MC_DST_REMAP 2
+#define MC_ALL (MC_GET_SAMPLE_COLORS | MC_DST_REMAP)
+
+typedef struct
+{
+ gint32 dst_id;
+ gint32 sample_id;
+
+ gint32 hold_inten; /* TRUE or FALSE */
+ gint32 orig_inten; /* TRUE or FALSE */
+ gint32 rnd_subcolors; /* TRUE or FALSE */
+ gint32 guess_missing; /* TRUE or FALSE */
+ gint32 lvl_in_min; /* 0 up to 254 */
+ gint32 lvl_in_max; /* 1 up to 255 */
+ float lvl_in_gamma; /* 0.1 up to 10.0 (1.0 == linear) */
+ gint32 lvl_out_min; /* 0 up to 254 */
+ gint32 lvl_out_max; /* 1 up to 255 */
+
+ float tol_col_err; /* 0.0% up to 100.0%
+ * this is used to find out colors of the same
+ * colortone, while analyzing sample colors,
+ * It does not make much sense for the user to adjust this
+ * value. (I used a param file to find out a suitable value)
+ */
+} t_values;
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkWidget *sample_preview;
+ GtkWidget *dst_preview;
+ GtkWidget *sample_colortab_preview;
+ GtkWidget *sample_drawarea;
+ GtkWidget *in_lvl_gray_preview;
+ GtkWidget *in_lvl_drawarea;
+ GtkAdjustment *adj_lvl_in_min;
+ GtkAdjustment *adj_lvl_in_max;
+ GtkAdjustment *adj_lvl_in_gamma;
+ GtkAdjustment *adj_lvl_out_min;
+ GtkAdjustment *adj_lvl_out_max;
+ GtkWidget *orig_inten_button;
+ gint active_slider;
+ gint slider_pos[5]; /* positions for the five sliders */
+
+ gboolean enable_preview_update;
+ gboolean sample_show_selection;
+ gboolean dst_show_selection;
+ gboolean sample_show_color;
+ gboolean dst_show_color;
+} t_samp_interface;
+
+
+
+typedef struct
+{
+ guchar color[4]; /* R,G,B,A */
+ gint32 sum_color; /* nr. of sourcepixels with (nearly the same) color */
+ void *next;
+} t_samp_color_elem;
+
+
+
+typedef struct
+{
+ gint32 all_samples; /* number of all source pixels with this luminosity */
+ gint from_sample; /* TRUE: color found in sample, FALSE: interpolated color added */
+ t_samp_color_elem *col_ptr; /* List of sample colors at same luminosity */
+} t_samp_table_elem;
+
+
+typedef struct
+{
+ gint32 drawable_id;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+ void *sel_gdrw;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */
+ gint bpp;
+ gint tile_width;
+ gint tile_height;
+ gint shadow;
+ gint32 seldeltax;
+ gint32 seldeltay;
+} t_GDRW;
+
+/*
+ * Some globals
+ */
+
+static t_samp_interface g_di; /* global dialog interface variables */
+static t_values g_values = { -1, -1, 1, 1, 0, 1, 0, 255, 1.0, 0, 255, 5.5 };
+static t_samp_table_elem g_lum_tab[256];
+static guchar g_lvl_trans_tab[256];
+static guchar g_out_trans_tab[256];
+static guchar g_sample_color_tab[256 * 3];
+static guchar g_dst_preview_buffer[PREVIEW_SIZE_X * PREVIEW_SIZE_Y * 4 ]; /* color copy with mask of dst in previewsize */
+
+static gint32 g_tol_col_err;
+static gint32 g_max_col_err;
+static gint g_Sdebug = FALSE;
+static gint g_show_progress = FALSE;
+
+/* Declare a local function.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint main_colorize (gint mc_flags);
+static void get_filevalues (void);
+static void smp_dialog (void);
+static void refresh_dst_preview (GtkWidget *preview,
+ guchar *src_buffer);
+static void update_preview (gint32 *id_ptr);
+static void clear_tables (void);
+static void free_colors (void);
+static void levels_update (gint update);
+static gint level_in_events (GtkWidget *widget,
+ GdkEvent *event);
+static gint level_out_events (GtkWidget *widget,
+ GdkEvent *event);
+static void calculate_level_transfers (void);
+static void get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ gboolean shadow);
+static void end_gdrw (t_GDRW *gdrw);
+static gint32 is_layer_alive (gint32 drawable_id);
+static void remap_pixel (guchar *pixel,
+ const guchar *original,
+ gint bpp2);
+static void guess_missing_colors (void);
+static void fill_missing_colors (void);
+static void smp_get_colors (GtkWidget *dialog);
+static void get_gradient (gint mode);
+static void clear_preview (GtkWidget *preview);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[]=
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "dst-drawable", "The drawable to be colorized (Type GRAY* or RGB*)" },
+ { GIMP_PDB_DRAWABLE, "sample-drawable", "Sample drawable (should be of Type RGB or RGBA)" },
+ { GIMP_PDB_INT32, "hold-inten", "hold brightness intensity levels (TRUE, FALSE)" },
+ { GIMP_PDB_INT32, "orig-inten", "TRUE: hold brightness of original intensity levels. FALSE: Hold Intensity of input levels" },
+ { GIMP_PDB_INT32, "rnd-subcolors", "TRUE: Use all subcolors of same intensity, FALSE: use only one color per intensity" },
+ { GIMP_PDB_INT32, "guess-missing", "TRUE: guess samplecolors for the missing intensity values FALSE: use only colors found in the sample" },
+ { GIMP_PDB_INT32, "in-low", "intensity of lowest input (0 <= in_low <= 254)" },
+ { GIMP_PDB_INT32, "in-high", "intensity of highest input (1 <= in_high <= 255)" },
+ { GIMP_PDB_FLOAT, "gamma", "gamma adjustment factor (0.1 <= gamma <= 10) where 1.0 is linear" },
+ { GIMP_PDB_INT32, "out-low", "lowest sample color intensity (0 <= out_low <= 254)" },
+ { GIMP_PDB_INT32, "out-high", "highest sample color intensity (1 <= out_high <= 255)" }
+ };
+
+ static gchar *help_string =
+ "This plug-in colorizes the contents of the specified (gray) layer"
+ " with the help of a sample (color) layer."
+ " It analyzes all colors in the sample layer."
+ " The sample colors are sorted by brightness (== intentisty) and amount"
+ " and stored in a sample colortable (where brightness is the index)"
+ " The pixels of the destination layer are remapped with the help of the"
+ " sample colortable. If use_subcolors is TRUE, the remapping process uses"
+ " all sample colors of the corresponding brightness-intensity and"
+ " distributes the subcolors according to their amount in the sample"
+ " (If the sample has 5 green, 3 yellow, and 1 red pixel of the "
+ " intensity value 105, the destination pixels at intensity value 105"
+ " are randomly painted in green, yellow and red in a relation of 5:3:1"
+ " If use_subcolors is FALSE only one sample color per intensity is used."
+ " (green will be used in this example)"
+ " The brightness intensity value is transformed at the remapping process"
+ " according to the levels: out_lo, out_hi, in_lo, in_high and gamma"
+ " The in_low / in_high levels specify an initial mapping of the intensity."
+ " The gamma value determines how intensities are interpolated between"
+ " the in_lo and in_high levels. A gamma value of 1.0 results in linear"
+ " interpolation. Higher gamma values results in more high-level intensities"
+ " Lower gamma values results in more low-level intensities"
+ " The out_low/out_high levels constrain the resulting intensity index"
+ " The intensity index is used to pick the corresponding color"
+ " in the sample colortable. If hold_inten is FALSE the picked color"
+ " is used 1:1 as resulting remap_color."
+ " If hold_inten is TRUE The brightness of the picked color is adjusted"
+ " back to the origial intensity value (only hue and saturation are"
+ " taken from the picked sample color)"
+ " (or to the input level, if orig_inten is set FALSE)"
+ " Works on both Grayscale and RGB image with/without alpha channel."
+ " (the image with the dst_drawable is converted to RGB if necessary)"
+ " The sample_drawable should be of type RGB or RGBA";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Colorize image using a sample image as a guide"),
+ help_string,
+ "Wolfgang Hofer",
+ "hof@hotbot.com",
+ "02/2000",
+ N_("_Sample Colorize..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Map");
+}
+
+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;
+ const gchar *env;
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ env = g_getenv ("SAMPLE_COLORIZE_DEBUG");
+ if (env != NULL && (*env != 'n') && (*env != 'N'))
+ g_Sdebug = TRUE;
+
+ if (g_Sdebug)
+ g_printf ("sample colorize run\n");
+ g_show_progress = FALSE;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_PROC, &g_values);
+ if (g_values.sample_id == SMP_GRADIENT ||
+ g_values.sample_id == SMP_INV_GRADIENT)
+ g_values.sample_id = -1;
+
+ /* fix value */
+ g_values.tol_col_err = 5.5;
+
+ /* Get the specified dst_drawable */
+ g_values.dst_id = drawable_id;
+
+ clear_tables ();
+
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ smp_dialog ();
+ free_colors ();
+ gimp_set_data (PLUG_IN_PROC, &g_values, sizeof (t_values));
+ gimp_displays_flush ();
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams == NUMBER_IN_ARGS)
+ {
+ g_values.sample_id = param[3].data.d_drawable;
+ g_values.hold_inten = param[4].data.d_int32;
+ g_values.orig_inten = param[5].data.d_int32;
+ g_values.rnd_subcolors = param[6].data.d_int32;
+ g_values.guess_missing = param[7].data.d_int32;
+ g_values.lvl_in_min = param[8].data.d_int32;
+ g_values.lvl_in_max = param[9].data.d_int32;
+ g_values.lvl_in_gamma = param[10].data.d_float;
+ g_values.lvl_out_min = param[11].data.d_int32;
+ g_values.lvl_out_max = param[12].data.d_int32;
+ if (main_colorize (MC_GET_SAMPLE_COLORS) >= 0)
+ {
+ main_colorize (MC_DST_REMAP);
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* ============================================================================
+ * callback and constraint procedures for the dialog
+ * ============================================================================
+ */
+
+static void
+smp_response_callback (GtkWidget *widget,
+ gint response_id)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+
+ levels_update (ALL);
+ break;
+
+ case RESPONSE_GET_COLORS:
+ smp_get_colors (widget);
+ break;
+
+ case GTK_RESPONSE_APPLY:
+ g_show_progress = TRUE;
+ if (main_colorize (MC_DST_REMAP) == 0)
+ {
+ gimp_displays_flush ();
+ g_show_progress = FALSE;
+ return;
+ }
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (widget),
+ GTK_RESPONSE_APPLY, FALSE);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ gtk_main_quit ();
+ break;
+ }
+}
+
+static void
+smp_toggle_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gboolean *toggle_val = (gboolean *)data;
+
+ *toggle_val = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if ((data == &g_di.sample_show_selection) ||
+ (data == &g_di.sample_show_color))
+ {
+ update_preview (&g_values.sample_id);
+ return;
+ }
+
+ if ((data == &g_di.dst_show_selection) ||
+ (data == &g_di.dst_show_color))
+ {
+ update_preview (&g_values.dst_id);
+ return;
+ }
+
+ if ((data == &g_values.hold_inten) ||
+ (data == &g_values.orig_inten) ||
+ (data == &g_values.rnd_subcolors))
+ {
+ if (g_di.orig_inten_button)
+ gtk_widget_set_sensitive (g_di.orig_inten_button,g_values.hold_inten);
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ }
+
+ if (data == &g_values.guess_missing)
+ {
+ if (g_values.guess_missing)
+ guess_missing_colors ();
+ else
+ fill_missing_colors ();
+ smp_get_colors (NULL);
+ }
+}
+
+static void
+smp_sample_combo_callback (GtkWidget *widget)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ g_values.sample_id = value;
+
+ if (value == SMP_GRADIENT || value == SMP_INV_GRADIENT)
+ {
+ get_gradient (value);
+ smp_get_colors (NULL);
+
+ if (g_di.sample_preview)
+ clear_preview (g_di.sample_preview);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ GTK_RESPONSE_APPLY, TRUE);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, FALSE);
+ }
+ else
+ {
+ update_preview (&g_values.sample_id);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, TRUE);
+ }
+}
+
+static void
+smp_dest_combo_callback (GtkWidget *widget)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &g_values.dst_id);
+
+ update_preview (&g_values.dst_id);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, TRUE);
+}
+
+static gint
+smp_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ if (image_id < 0)
+ return FALSE;
+
+ /* don't accept layers from indexed images */
+ if (gimp_drawable_is_indexed (drawable_id))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+smp_adj_lvl_in_max_upd_callback (GtkAdjustment *adjustment)
+{
+ gint32 value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 1, 255);
+
+ if (value != g_values.lvl_in_max)
+ {
+ g_values.lvl_in_max = value;
+ upd_flags = INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_in_max < g_values.lvl_in_min)
+ {
+ g_values.lvl_in_min = g_values.lvl_in_max;
+ upd_flags |= LOW_INPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_adj_lvl_in_min_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0, 254);
+
+ if (value != g_values.lvl_in_min)
+ {
+ g_values.lvl_in_min = value;
+ upd_flags = INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_in_min > g_values.lvl_in_max)
+ {
+ g_values.lvl_in_max = g_values.lvl_in_min;
+ upd_flags |= HIGH_INPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_text_gamma_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0.1, 10.0);
+
+ if (value != g_values.lvl_in_gamma)
+ {
+ g_values.lvl_in_gamma = value;
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST);
+ }
+}
+
+static void
+smp_adj_lvl_out_max_upd_callback (GtkAdjustment *adjustment)
+{
+ gint32 value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 1, 255);
+
+ if (value != g_values.lvl_out_max)
+ {
+ g_values.lvl_out_max = value;
+ upd_flags = OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_out_max < g_values.lvl_out_min)
+ {
+ g_values.lvl_out_min = g_values.lvl_out_max;
+ upd_flags |= LOW_OUTPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_adj_lvl_out_min_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0, 254);
+
+ if (value != g_values.lvl_out_min)
+ {
+ g_values.lvl_out_min = value;
+ upd_flags = OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_out_min > g_values.lvl_out_max)
+ {
+ g_values.lvl_out_max = g_values.lvl_out_min;
+ upd_flags |= HIGH_OUTPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+/* ============================================================================
+ * DIALOG helper procedures
+ * (workers for the updates on the preview widgets)
+ * ============================================================================
+ */
+
+static void
+refresh_dst_preview (GtkWidget *preview,
+ guchar *src_buffer)
+{
+ guchar allrowsbuf[3 * PREVIEW_SIZE_X * PREVIEW_SIZE_Y];
+ guchar *ptr;
+ guchar *src_ptr;
+ guchar lum;
+ guchar maskbyte;
+ gint x, y;
+ gint preview_bpp;
+ gint src_bpp;
+
+ preview_bpp = PREVIEW_BPP;
+ src_bpp = PREVIEW_BPP +1; /* 3 colors + 1 maskbyte */
+ src_ptr = src_buffer;
+
+ ptr = allrowsbuf;
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ if ((maskbyte = src_ptr[3]) == 0)
+ {
+ ptr[0] = src_ptr[0];
+ ptr[1] = src_ptr[1];
+ ptr[2] = src_ptr[2];
+ }
+ else
+ {
+ if (g_di.dst_show_color)
+ {
+ remap_pixel (ptr, src_ptr, 3);
+ }
+ else
+ {
+ /* lum = g_out_trans_tab[g_lvl_trans_tab[LUMINOSITY_1(src_ptr)]]; */
+ /* get brightness from (uncolorized) original */
+
+ lum = g_lvl_trans_tab[LUMINOSITY_1 (src_ptr)];
+ /* get brightness from (uncolorized) original */
+
+ ptr[0] = lum;
+ ptr[1] = lum;
+ ptr[2] = lum;
+ }
+
+ if (maskbyte < 255)
+ {
+ ptr[0] = MIX_CHANNEL (ptr[0], src_ptr[0], maskbyte);
+ ptr[1] = MIX_CHANNEL (ptr[1], src_ptr[1], maskbyte);
+ ptr[2] = MIX_CHANNEL (ptr[2], src_ptr[2], maskbyte);
+ }
+ }
+ ptr += preview_bpp;
+ src_ptr += src_bpp;
+ }
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGB_IMAGE,
+ allrowsbuf,
+ PREVIEW_SIZE_X * 3);
+}
+
+static void
+clear_preview (GtkWidget *preview)
+{
+ gimp_preview_area_fill (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ 170, 170, 170);
+}
+
+static void
+update_pv (GtkWidget *preview,
+ gboolean show_selection,
+ t_GDRW *gdrw,
+ guchar *dst_buffer,
+ gboolean is_color)
+{
+ guchar allrowsbuf[4 * PREVIEW_SIZE_X * PREVIEW_SIZE_Y];
+ guchar pixel[4];
+ guchar *ptr;
+ gint x, y;
+ gint x2, y2;
+ gint ofx, ofy;
+ gint sel_width, sel_height;
+ double scale_x, scale_y;
+ guchar *buf_ptr;
+ guchar dummy[4];
+ guchar maskbytes[4];
+ gint dstep;
+ guchar alpha;
+
+
+ if (!preview)
+ return;
+
+ /* init gray pixel (if we are called without a sourceimage (gdwr == NULL) */
+ pixel[0] = pixel[1] =pixel[2] = pixel[3] = 127;
+
+ /* calculate scale factors and offsets */
+ if (show_selection)
+ {
+ sel_width = gdrw->x2 - gdrw->x1;
+ sel_height = gdrw->y2 - gdrw->y1;
+
+ if (sel_height > sel_width)
+ {
+ scale_y = (gfloat) sel_height / PREVIEW_SIZE_Y;
+ scale_x = scale_y;
+ ofx = (gdrw->x1 +
+ ((sel_width - (PREVIEW_SIZE_X * scale_x)) / 2));
+ ofy = gdrw->y1;
+ }
+ else
+ {
+ scale_x = (gfloat) sel_width / PREVIEW_SIZE_X;
+ scale_y = scale_x;
+ ofx = gdrw->x1;
+ ofy = (gdrw->y1 +
+ ((sel_height - (PREVIEW_SIZE_Y * scale_y)) / 2));
+ }
+ }
+ else
+ {
+ if (gdrw->height > gdrw->width)
+ {
+ scale_y = (gfloat) gdrw->height / PREVIEW_SIZE_Y;
+ scale_x = scale_y;
+ ofx = (gdrw->width - (PREVIEW_SIZE_X * scale_x)) / 2;
+ ofy = 0;
+ }
+ else
+ {
+ scale_x = (gfloat) gdrw->width / PREVIEW_SIZE_X;
+ scale_y = scale_x;
+ ofx = 0;
+ ofy = (gdrw->height - (PREVIEW_SIZE_Y * scale_y)) / 2;
+ }
+ }
+
+ /* check if output goes to previw widget or to dst_buffer */
+ if (dst_buffer)
+ {
+ buf_ptr = dst_buffer;
+ dstep = PREVIEW_BPP +1;
+ }
+ else
+ {
+ buf_ptr = &dummy[0];
+ dstep = 0;
+ }
+
+
+ /* render preview */
+ ptr = allrowsbuf;
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ if (gdrw->drawable_id > 0)
+ {
+ x2 = ofx + (x * scale_x);
+ y2 = ofy + (y * scale_y);
+ get_pixel (gdrw, x2, y2, &pixel[0]);
+ if (gdrw->sel_gdrw)
+ {
+ get_pixel (gdrw->sel_gdrw,
+ x2 + gdrw->seldeltax,
+ y2 + gdrw->seldeltay,
+ &maskbytes[0]);
+ }
+ else
+ {
+ maskbytes[0] = 255;
+ }
+ }
+
+ alpha = pixel[gdrw->index_alpha];
+ if (is_color && (gdrw->bpp > 2))
+ {
+ buf_ptr[0] = ptr[0] = pixel[0];
+ buf_ptr[1] = ptr[1] = pixel[1];
+ buf_ptr[2] = ptr[2] = pixel[2];
+ }
+ else
+ {
+ if (gdrw->bpp > 2)
+ *ptr = LUMINOSITY_1 (pixel);
+ else
+ *ptr = pixel[0];
+
+ *buf_ptr = *ptr;
+ buf_ptr[1] = ptr[1] = *ptr;
+ buf_ptr[2] = ptr[2] = *ptr;
+ }
+ if (gdrw->index_alpha == 0) /* has no alpha channel */
+ buf_ptr[3] = ptr[3] = 255;
+ else
+ buf_ptr[3] = ptr[3] = MIN (maskbytes[0], alpha);
+ buf_ptr += dstep; /* advance (or stay at dummy byte) */
+ ptr += 4;
+ }
+
+ }
+
+ if (dst_buffer == NULL)
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGBA_IMAGE,
+ allrowsbuf,
+ PREVIEW_SIZE_X * 4);
+ gtk_widget_queue_draw (preview);
+ }
+}
+
+static void
+update_preview (gint32 *id_ptr)
+{
+ t_GDRW gdrw;
+ gboolean drawable = FALSE;
+
+ if (g_Sdebug)
+ g_printf ("UPD PREVIEWS ID:%d ENABLE_UPD:%d\n",
+ id_ptr ? (int) *id_ptr : -1, (int)g_di.enable_preview_update);
+
+ if (id_ptr == NULL || !g_di.enable_preview_update)
+ return;
+ if (is_layer_alive (*id_ptr) < 0)
+ {
+ /* clear preview on invalid drawable id
+ * (SMP_GRADIENT and SMP_INV_GRADIENT)
+ */
+ if (id_ptr == &g_values.sample_id)
+ clear_preview (g_di.sample_preview);
+ if (id_ptr == &g_values.dst_id)
+ clear_preview (g_di.dst_preview);
+ return;
+ }
+
+ if (id_ptr == &g_values.sample_id)
+ {
+ drawable = TRUE;
+
+ init_gdrw (&gdrw, *id_ptr, FALSE);
+ update_pv (g_di.sample_preview, g_di.sample_show_selection, &gdrw,
+ NULL, g_di.sample_show_color);
+ }
+ else if (id_ptr == &g_values.dst_id)
+ {
+ drawable = TRUE;
+
+ init_gdrw (&gdrw, *id_ptr, FALSE);
+ update_pv (g_di.dst_preview, g_di.dst_show_selection, &gdrw,
+ &g_dst_preview_buffer[0], g_di.dst_show_color);
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ }
+
+ if (drawable)
+ end_gdrw (&gdrw);
+}
+
+static void
+levels_draw_slider (cairo_t *cr,
+ GdkColor *border_color,
+ GdkColor *fill_color,
+ gint xpos)
+{
+ cairo_move_to (cr, xpos, 0);
+ cairo_line_to (cr, xpos - (CONTROL_HEIGHT - 1) / 2, CONTROL_HEIGHT - 1);
+ cairo_line_to (cr, xpos + (CONTROL_HEIGHT - 1) / 2, CONTROL_HEIGHT - 1);
+ cairo_line_to (cr, 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
+smp_get_colors (GtkWidget *dialog)
+{
+ gint i;
+ guchar buffer[3 * DA_WIDTH * GRADIENT_HEIGHT];
+
+ update_preview (&g_values.sample_id);
+
+ if (dialog && main_colorize (MC_GET_SAMPLE_COLORS) >= 0) /* do not colorize, just analyze sample colors */
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ GTK_RESPONSE_APPLY, TRUE);
+ for (i = 0; i < GRADIENT_HEIGHT; i++)
+ memcpy (buffer + i * 3 * DA_WIDTH, g_sample_color_tab, 3 * DA_WIDTH);
+
+ update_preview (&g_values.dst_id);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (g_di.sample_colortab_preview),
+ 0, 0, DA_WIDTH, GRADIENT_HEIGHT,
+ GIMP_RGB_IMAGE,
+ buffer,
+ DA_WIDTH * 3);
+}
+
+
+static void
+levels_update (gint update)
+{
+ gint i;
+
+ if (g_Sdebug)
+ g_printf ("levels_update: update reques %x\n", update);
+
+ /* Recalculate the transfer array */
+ calculate_level_transfers ();
+ if (update & REFRESH_DST)
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+
+ /* update the spinbutton entry widgets */
+ if (update & LOW_INPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_min,
+ g_values.lvl_in_min);
+ if (update & GAMMA)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_gamma,
+ g_values.lvl_in_gamma);
+ if (update & HIGH_INPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_max,
+ g_values.lvl_in_max);
+ if (update & LOW_OUTPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_out_min,
+ g_values.lvl_out_min);
+ if (update & HIGH_OUTPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_out_max,
+ g_values.lvl_out_max);
+ if (update & INPUT_LEVELS)
+ {
+ guchar buffer[DA_WIDTH * GRADIENT_HEIGHT];
+ for (i = 0; i < GRADIENT_HEIGHT; i++)
+ memcpy (buffer + DA_WIDTH * i, g_lvl_trans_tab, DA_WIDTH);
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (g_di.in_lvl_gray_preview),
+ 0, 0, DA_WIDTH, GRADIENT_HEIGHT,
+ GIMP_GRAY_IMAGE,
+ buffer,
+ DA_WIDTH);
+ }
+
+ if (update & INPUT_SLIDERS)
+ {
+ GtkStyle *style = gtk_widget_get_style (g_di.in_lvl_drawarea);
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (g_di.in_lvl_drawarea));
+ gdouble width, mid, tmp;
+
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 1.0);
+
+ g_di.slider_pos[0] = DA_WIDTH * ((double) g_values.lvl_in_min / 255.0);
+ g_di.slider_pos[2] = DA_WIDTH * ((double) g_values.lvl_in_max / 255.0);
+
+ width = (double) (g_di.slider_pos[2] - g_di.slider_pos[0]) / 2.0;
+ mid = g_di.slider_pos[0] + width;
+ tmp = log10 (1.0 / g_values.lvl_in_gamma);
+ g_di.slider_pos[1] = (int) (mid + width * tmp + 0.5);
+
+ levels_draw_slider (cr,
+ &style->black,
+ &style->dark[GTK_STATE_NORMAL],
+ g_di.slider_pos[1]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[0]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->white,
+ g_di.slider_pos[2]);
+
+ cairo_destroy (cr);
+ }
+
+ if (update & OUTPUT_SLIDERS)
+ {
+ GtkStyle *style = gtk_widget_get_style (g_di.sample_drawarea);
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (g_di.sample_drawarea));
+
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 1.0);
+
+ g_di.slider_pos[3] = DA_WIDTH * ((double) g_values.lvl_out_min / 255.0);
+ g_di.slider_pos[4] = DA_WIDTH * ((double) g_values.lvl_out_max / 255.0);
+
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[3]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[4]);
+
+ cairo_destroy (cr);
+ }
+}
+
+static gboolean
+level_in_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gdouble width, mid, tmp;
+ gint x, distance;
+ gint i;
+ gint update = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_EXPOSE\n");
+ if (widget == g_di.in_lvl_drawarea)
+ levels_update (INPUT_SLIDERS);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_BUTTON_PRESS\n");
+ gtk_grab_add (widget);
+ bevent = (GdkEventButton *) event;
+
+ distance = G_MAXINT;
+ for (i = 0; i < 3; i++)
+ {
+ if (fabs (bevent->x - g_di.slider_pos[i]) < distance)
+ {
+ g_di.active_slider = i;
+ distance = fabs (bevent->x - g_di.slider_pos[i]);
+ }
+ }
+ x = bevent->x;
+ update = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_BUTTON_RELEASE\n");
+ gtk_grab_remove (widget);
+ switch (g_di.active_slider)
+ {
+ case 0: /* low input */
+ levels_update (LOW_INPUT | GAMMA | DRAW);
+ break;
+
+ case 1: /* gamma */
+ levels_update (GAMMA);
+ break;
+
+ case 2: /* high input */
+ levels_update (HIGH_INPUT | GAMMA | DRAW);
+ break;
+ }
+
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_MOTION_NOTIFY\n");
+ mevent = (GdkEventMotion *) event;
+ x = mevent->x;
+ gdk_event_request_motions (mevent);
+ update = TRUE;
+ break;
+
+ default:
+ if (g_Sdebug)
+ g_printf ("EVENT: default\n");
+ break;
+ }
+
+ if (update)
+ {
+ if (g_Sdebug)
+ g_printf ("EVENT: ** update **\n");
+ switch (g_di.active_slider)
+ {
+ case 0: /* low input */
+ g_values.lvl_in_min = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_in_min = CLAMP (g_values.lvl_in_min,
+ 0, g_values.lvl_in_max);
+ break;
+
+ case 1: /* gamma */
+ width = (double) (g_di.slider_pos[2] - g_di.slider_pos[0]) / 2.0;
+ mid = g_di.slider_pos[0] + width;
+
+ x = CLAMP (x, g_di.slider_pos[0], g_di.slider_pos[2]);
+ tmp = (double) (x - mid) / width;
+ g_values.lvl_in_gamma = 1.0 / pow (10, tmp);
+
+ /* round the gamma value to the nearest 1/100th */
+ g_values.lvl_in_gamma =
+ floor (g_values.lvl_in_gamma * 100 + 0.5) / 100.0;
+ break;
+
+ case 2: /* high input */
+ g_values.lvl_in_max = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_in_max = CLAMP (g_values.lvl_in_max,
+ g_values.lvl_in_min, 255);
+ break;
+ }
+
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+level_out_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gint x, distance;
+ gint i;
+ gint update = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_EXPOSE\n");
+ if (widget == g_di.sample_drawarea)
+ levels_update (OUTPUT_SLIDERS);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_BUTTON_PRESS\n");
+ bevent = (GdkEventButton *) event;
+
+ distance = G_MAXINT;
+ for (i = 3; i < 5; i++)
+ {
+ if (fabs (bevent->x - g_di.slider_pos[i]) < distance)
+ {
+ g_di.active_slider = i;
+ distance = fabs (bevent->x - g_di.slider_pos[i]);
+ }
+ }
+
+ x = bevent->x;
+ update = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_BUTTON_RELEASE\n");
+ switch (g_di.active_slider)
+ {
+ case 3: /* low output */
+ levels_update (LOW_OUTPUT | DRAW);
+ break;
+
+ case 4: /* high output */
+ levels_update (HIGH_OUTPUT | DRAW);
+ break;
+ }
+
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_MOTION_NOTIFY\n");
+ mevent = (GdkEventMotion *) event;
+ x = mevent->x;
+ gdk_event_request_motions (mevent);
+ update = TRUE;
+ break;
+
+ default:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: default\n");
+ break;
+ }
+
+ if (update)
+ {
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: ** update **\n");
+ switch (g_di.active_slider)
+ {
+ case 3: /* low output */
+ g_values.lvl_out_min = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_out_min = CLAMP (g_values.lvl_out_min,
+ 0, g_values.lvl_out_max);
+ break;
+
+ case 4: /* high output */
+ g_values.lvl_out_max = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_out_max = CLAMP (g_values.lvl_out_max,
+ g_values.lvl_out_min, 255);
+ break;
+ }
+
+ levels_update (OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW);
+ }
+
+ return FALSE;
+}
+
+
+/* ============================================================================
+ * smp_dialog
+ * The Interactive Dialog
+ * ============================================================================
+ */
+static void
+smp_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *vbox2;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *check_button;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *spinbutton;
+ GtkObject *data;
+ gint ty;
+
+ /* set flags for check buttons from mode value bits */
+ if (g_Sdebug)
+ g_print ("smp_dialog START\n");
+
+ /* init some dialog variables */
+ g_di.enable_preview_update = FALSE;
+ g_di.sample_show_selection = TRUE;
+ g_di.dst_show_selection = TRUE;
+ g_di.dst_show_color = TRUE;
+ g_di.sample_show_color = TRUE;
+ g_di.orig_inten_button = NULL;
+
+ /* Init GTK */
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /* Main Dialog */
+ g_di.dialog = dialog =
+ gimp_dialog_new (_("Sample Colorize"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("Get _Sample Colors"), RESPONSE_GET_COLORS,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+ _("_Apply"), GTK_RESPONSE_APPLY,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_GET_COLORS,
+ RESPONSE_RESET,
+ GTK_RESPONSE_APPLY,
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (smp_response_callback),
+ NULL);
+
+ /* table for values */
+ table = gtk_table_new (7, 5, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_col_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 (dialog))),
+ table, TRUE, TRUE, 0);
+
+ ty = 0;
+ /* layer combo_box (Dst) */
+ label = gtk_label_new (_("Destination:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, ty, ty + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_layer_combo_box_new (smp_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), g_values.dst_id,
+ G_CALLBACK (smp_dest_combo_callback),
+ NULL);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, ty, ty + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ /* layer combo_box (Sample) */
+ label = gtk_label_new (_("Sample:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 3, 4, ty, ty + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_layer_combo_box_new (smp_constrain, NULL);
+
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, SMP_INV_GRADIENT,
+ GIMP_INT_STORE_LABEL, _("From reverse gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, SMP_GRADIENT,
+ GIMP_INT_STORE_LABEL, _("From gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), g_values.sample_id,
+ G_CALLBACK (smp_sample_combo_callback),
+ NULL);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 4, 5, ty, ty + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ ty++;
+
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty + 1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Sho_w selection"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.dst_show_selection);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.dst_show_selection);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show co_lor"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.dst_show_color);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.dst_show_color);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty + 1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show selec_tion"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.sample_show_selection);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.sample_show_selection);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show c_olor"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.sample_show_color);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.sample_show_color);
+
+ ty++;
+
+ /* Preview (Dst) */
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 0, 2, ty, ty + 1, 0, 0, 0, 0);
+ gtk_widget_show (frame);
+
+ g_di.dst_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.dst_preview,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), g_di.dst_preview);
+ gtk_widget_show (g_di.dst_preview);
+
+ /* Preview (Sample)*/
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 3, 5, ty, ty + 1, 0, 0, 0, 0);
+ gtk_widget_show (frame);
+
+ g_di.sample_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.sample_preview,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), g_di.sample_preview);
+ gtk_widget_show (g_di.sample_preview);
+
+ ty++;
+
+ /* The levels graylevel prevev */
+ frame = gtk_frame_new (NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 0, 2, ty, ty + 1, 0, 0, 0, 0);
+
+ g_di.in_lvl_gray_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.in_lvl_gray_preview,
+ DA_WIDTH, GRADIENT_HEIGHT);
+ gtk_widget_set_events (g_di.in_lvl_gray_preview, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.in_lvl_gray_preview, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.in_lvl_gray_preview);
+
+ g_signal_connect (g_di.in_lvl_gray_preview, "event",
+ G_CALLBACK (level_in_events),
+ NULL);
+
+ /* The levels drawing area */
+ g_di.in_lvl_drawarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (g_di.in_lvl_drawarea,
+ DA_WIDTH, CONTROL_HEIGHT);
+ gtk_widget_set_events (g_di.in_lvl_drawarea, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.in_lvl_drawarea, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.in_lvl_drawarea);
+
+ g_signal_connect (g_di.in_lvl_drawarea, "event",
+ G_CALLBACK (level_in_events),
+ NULL);
+
+ gtk_widget_show (vbox2);
+ gtk_widget_show (frame);
+
+ /* The sample_colortable prevev */
+ frame = gtk_frame_new (NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 3, 5, ty, ty + 1, 0, 0, 0, 0);
+
+ g_di.sample_colortab_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.sample_colortab_preview,
+ DA_WIDTH, GRADIENT_HEIGHT);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.sample_colortab_preview, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.sample_colortab_preview);
+
+ /* The levels drawing area */
+ g_di.sample_drawarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (g_di.sample_drawarea,
+ DA_WIDTH, CONTROL_HEIGHT);
+ gtk_widget_set_events (g_di.sample_drawarea, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.sample_drawarea, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.sample_drawarea);
+
+ g_signal_connect (g_di.sample_drawarea, "event",
+ G_CALLBACK (level_out_events),
+ NULL);
+
+ gtk_widget_show (vbox2);
+ gtk_widget_show (frame);
+
+
+ ty++;
+
+ /* Horizontal box for INPUT levels text widget */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new (_("Input levels:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* min input spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_min, 0.0, 254.0, 1, 10, 0);
+ g_di.adj_lvl_in_min = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_min, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_min, "value-changed",
+ G_CALLBACK (smp_adj_lvl_in_min_upd_callback),
+ &g_di);
+
+ /* input gamma spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_gamma, 0.1, 10.0, 0.02, 0.2, 0);
+ g_di.adj_lvl_in_gamma = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_gamma, 0.5, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_gamma, "value-changed",
+ G_CALLBACK (smp_text_gamma_upd_callback),
+ &g_di);
+
+ /* high input spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_max, 1.0, 255.0, 1, 10, 0);
+ g_di.adj_lvl_in_max = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_max, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_max, "value-changed",
+ G_CALLBACK (smp_adj_lvl_in_max_upd_callback),
+ &g_di);
+
+ gtk_widget_show (hbox);
+
+ /* Horizontal box for OUTPUT levels text widget */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new (_("Output levels:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* min output spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_out_min, 0.0, 254.0, 1, 10, 0);
+ g_di.adj_lvl_out_min = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_out_min, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_out_min, "value-changed",
+ G_CALLBACK (smp_adj_lvl_out_min_upd_callback),
+ &g_di);
+
+ /* high output spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_out_max, 0.0, 255.0, 1, 10, 0);
+ g_di.adj_lvl_out_max = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_out_max, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_out_max, "value-changed",
+ G_CALLBACK (smp_adj_lvl_out_max_upd_callback),
+ &g_di);
+
+ gtk_widget_show (hbox);
+
+ ty++;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Hold _intensity"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.hold_inten);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.hold_inten);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Original i_ntensity"));
+ g_di.orig_inten_button = check_button;
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive (g_di.orig_inten_button, g_values.hold_inten);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.orig_inten);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.orig_inten);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Us_e subcolors"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.rnd_subcolors);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.rnd_subcolors);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("S_mooth samples"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.guess_missing);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.guess_missing);
+
+ ty++;
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ /* set old_id's different (to force updates of the previews) */
+ g_di.enable_preview_update = TRUE;
+ smp_get_colors (NULL);
+ update_preview (&g_values.dst_id);
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW);
+
+ gtk_main ();
+}
+
+/* -----------------------------
+ * DEBUG print procedures START
+ * -----------------------------
+ */
+
+static void
+print_ppm (const gchar *ppm_name)
+{
+ FILE *fp;
+ gint idx;
+ gint cnt;
+ gint r;
+ gint g;
+ gint b;
+ t_samp_color_elem *col_ptr;
+
+ if (ppm_name == NULL)
+ return;
+
+ fp = g_fopen (ppm_name, "w");
+ if (fp)
+ {
+ fprintf (fp, "P3\n# CREATOR: Gimp sample coloros\n256 256\n255\n");
+ for (idx = 0; idx < 256; idx++)
+ {
+ col_ptr = g_lum_tab[idx].col_ptr;
+
+ for (cnt = 0; cnt < 256; cnt++)
+ {
+ r = g = b = 0;
+ if (col_ptr)
+ {
+ if ((col_ptr->sum_color > 0) && (cnt != 20))
+ {
+ r = (gint) col_ptr->color[0];
+ g = (gint) col_ptr->color[1];
+ b = (gint) col_ptr->color[2];
+ }
+
+ if (cnt > 20)
+ col_ptr = col_ptr->next;
+ }
+ fprintf (fp, "%d %d %d\n", r, g, b);
+ }
+ }
+ fclose (fp);
+ }
+}
+
+static void
+print_color_list (FILE *fp,
+ t_samp_color_elem *col_ptr)
+{
+ if (fp == NULL)
+ return;
+
+ while (col_ptr)
+ {
+ fprintf (fp, " RGBA: %03d %03d %03d %03d sum: [%d]\n",
+ (gint)col_ptr->color[0],
+ (gint)col_ptr->color[1],
+ (gint)col_ptr->color[2],
+ (gint)col_ptr->color[3],
+ (gint)col_ptr->sum_color);
+
+ col_ptr = col_ptr->next;
+ }
+}
+
+static void
+print_table (FILE *fp)
+{
+ gint idx;
+
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "---------------------------\n");
+ fprintf (fp, "print_table\n");
+ fprintf (fp, "---------------------------\n");
+
+ for (idx = 0; idx < 256; idx++)
+ {
+ fprintf (fp, "LUM [%03d] pixcount:%d\n",
+ idx, (int)g_lum_tab[idx].all_samples);
+ print_color_list (fp, g_lum_tab[idx].col_ptr);
+ }
+}
+
+static void
+print_transtable (FILE *fp)
+{
+ gint idx;
+
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "---------------------------\n");
+ fprintf (fp, "print_transtable\n");
+ fprintf (fp, "---------------------------\n");
+
+ for (idx = 0; idx < 256; idx++)
+ {
+ fprintf (fp, "LVL_TRANS [%03d] in_lvl: %3d out_lvl: %3d\n",
+ idx, (int)g_lvl_trans_tab[idx], (int)g_out_trans_tab[idx]);
+ }
+}
+
+static void
+print_values (FILE *fp)
+{
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "sample_colorize: params\n");
+ fprintf (fp, "g_values.hold_inten :%d\n", (int)g_values.hold_inten);
+ fprintf (fp, "g_values.orig_inten :%d\n", (int)g_values.orig_inten);
+ fprintf (fp, "g_values.rnd_subcolors :%d\n", (int)g_values.rnd_subcolors);
+ fprintf (fp, "g_values.guess_missing :%d\n", (int)g_values.guess_missing);
+ fprintf (fp, "g_values.lvl_in_min :%d\n", (int)g_values.lvl_in_min);
+ fprintf (fp, "g_values.lvl_in_max :%d\n", (int)g_values.lvl_in_max);
+ fprintf (fp, "g_values.lvl_in_gamma :%f\n", g_values.lvl_in_gamma);
+ fprintf (fp, "g_values.lvl_out_min :%d\n", (int)g_values.lvl_out_min);
+ fprintf (fp, "g_values.lvl_out_max :%d\n", (int)g_values.lvl_out_max);
+
+ fprintf (fp, "g_values.tol_col_err :%f\n", g_values.tol_col_err);
+}
+
+/* -----------------------------
+ * DEBUG print procedures END
+ * -----------------------------
+ */
+
+/* DEBUG: read values from file */
+static void
+get_filevalues (void)
+{
+ FILE *fp;
+ gchar buf[1000];
+
+/*
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+*/
+ g_values.tol_col_err = 5.5;
+
+ fp = g_fopen ("sample_colorize.values", "r");
+ if (fp != NULL)
+ {
+ fgets (buf, 999, fp);
+ sscanf (buf, "%f", &g_values.tol_col_err);
+ fclose (fp);
+ }
+
+ g_printf ("g_values.tol_col_err :%f\n", g_values.tol_col_err);
+}
+
+static gint32
+color_error (guchar ref_red, guchar ref_green, guchar ref_blue,
+ guchar cmp_red, guchar cmp_green, guchar cmp_blue)
+{
+ glong ff;
+ glong fs;
+ glong cmp_h, ref_h;
+
+ /* 1. Brightness differences */
+ cmp_h = (3 * cmp_red + 6 * cmp_green + cmp_blue) / 10;
+ ref_h = (3 * ref_red + 6 * ref_green + ref_blue) / 10;
+
+ fs = labs (ref_h - cmp_h);
+ ff = fs * fs;
+
+ /* 2. add Red Color differences */
+ fs = abs (ref_red - cmp_red);
+ ff += (fs * fs);
+
+ /* 3. add Green Color differences */
+ fs = abs (ref_green - cmp_green);
+ ff += (fs * fs);
+
+ /* 4. add Blue Color differences */
+ fs = abs (ref_blue - cmp_blue);
+ ff += (fs * fs);
+
+ return ((gint32)(ff));
+}
+
+/* get pixel value
+ * return light gray transparent pixel if out of bounds
+ * (should occur in the previews only)
+ */
+static void
+get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ gegl_buffer_sample (gdrw->buffer, x, y, NULL, pixel, gdrw->format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+}
+
+/* clear table */
+static void
+clear_tables (void)
+{
+ guint i;
+
+ for (i = 0; i < 256; i++)
+ {
+ g_lum_tab[i].col_ptr = NULL;
+ g_lum_tab[i].all_samples = 0;
+ g_lvl_trans_tab[i] = i;
+ g_out_trans_tab[i] = i;
+ g_sample_color_tab[3 * i + 0] = i;
+ g_sample_color_tab[3 * i + 1] = i;
+ g_sample_color_tab[3 * i + 2] = i;
+ }
+}
+
+/* free all allocated sample colors in table g_lum_tab */
+static void
+free_colors (void)
+{
+ gint lum;
+ t_samp_color_elem *col_ptr;
+ t_samp_color_elem *next_ptr;
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = next_ptr)
+ {
+ next_ptr = (t_samp_color_elem *)col_ptr->next;
+ g_free (col_ptr);
+ }
+
+ g_lum_tab[lum].col_ptr = NULL;
+ g_lum_tab[lum].all_samples = 0;
+ }
+}
+
+/* setup lum transformer table according to input_levels, gamma and output levels
+ * (uses sam algorithm as GIMP Level Tool)
+ */
+static void
+calculate_level_transfers (void)
+{
+ double inten;
+ gint i;
+ gint in_min, in_max;
+ gint out_min, out_max;
+
+ if (g_values.lvl_in_max >= g_values.lvl_in_min)
+ {
+ in_max = g_values.lvl_in_max;
+ in_min = g_values.lvl_in_min;
+ }
+ else
+ {
+ in_max = g_values.lvl_in_min;
+ in_min = g_values.lvl_in_max;
+ }
+ if (g_values.lvl_out_max >= g_values.lvl_out_min)
+ {
+ out_max = g_values.lvl_out_max;
+ out_min = g_values.lvl_out_min;
+ }
+ else
+ {
+ out_max = g_values.lvl_out_min;
+ out_min = g_values.lvl_out_max;
+ }
+
+ /* Recalculate the levels arrays */
+ for (i = 0; i < 256; i++)
+ {
+ /* determine input intensity */
+ inten = (double) i / 255.0;
+ if (g_values.lvl_in_gamma != 0.0)
+ {
+ inten = pow (inten, (1.0 / g_values.lvl_in_gamma));
+ }
+ inten = (double) (inten * (in_max - in_min) + in_min);
+ inten = CLAMP (inten, 0.0, 255.0);
+ g_lvl_trans_tab[i] = (guchar) (inten + 0.5);
+
+ /* determine the output intensity */
+ inten = (double) i / 255.0;
+ inten = (double) (inten * (out_max - out_min) + out_min);
+ inten = CLAMP (inten, 0.0, 255.0);
+ g_out_trans_tab[i] = (guchar) (inten + 0.5);
+ }
+}
+
+
+
+/* alloc and init new col Element */
+static t_samp_color_elem *
+new_samp_color (const guchar *color)
+{
+ t_samp_color_elem *col_ptr;
+
+ col_ptr = g_new0 (t_samp_color_elem, 1);
+
+ memcpy (&col_ptr->color[0], color, 4);
+
+ col_ptr->sum_color = 1;
+ col_ptr->next = NULL;
+
+ return col_ptr;
+}
+
+
+/* store color in g_lum_tab */
+static void
+add_color (const guchar *color)
+{
+ gint32 lum;
+ t_samp_color_elem *col_ptr;
+
+ lum = LUMINOSITY_1(color);
+
+ g_lum_tab[lum].all_samples++;
+ g_lum_tab[lum].from_sample = TRUE;
+
+ /* check if exactly the same color is already in the list */
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ if ((color[0] == col_ptr->color[0]) &&
+ (color[1] == col_ptr->color[1]) &&
+ (color[2] == col_ptr->color[2]))
+ {
+ col_ptr->sum_color++;
+ return;
+ }
+ }
+
+ /* alloc and init element for the new color */
+ col_ptr = new_samp_color (color);
+
+ if (col_ptr != NULL)
+ {
+ /* add new color element as 1.st of the list */
+ col_ptr->next = g_lum_tab[lum].col_ptr;
+ g_lum_tab[lum].col_ptr = col_ptr;
+ }
+}
+
+/* sort Sublists (color) by descending sum_color in g_lum_tab
+ */
+static void
+sort_color (gint32 lum)
+{
+ t_samp_color_elem *col_ptr;
+ t_samp_color_elem *next_ptr;
+ t_samp_color_elem *prev_ptr;
+ t_samp_color_elem *sorted_col_ptr;
+ gint32 min;
+ gint32 min_next;
+
+ sorted_col_ptr = NULL;
+ min_next = 0;
+
+ while (g_lum_tab[lum].col_ptr != NULL)
+ {
+ min = min_next;
+ next_ptr = NULL;
+ prev_ptr = NULL;
+
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = next_ptr)
+ {
+ next_ptr = col_ptr->next;
+ if (col_ptr->sum_color > min)
+ {
+ /* check min value for next loop */
+ if ((col_ptr->sum_color < min_next) || (min == min_next))
+ {
+ min_next = col_ptr->sum_color;
+ }
+ prev_ptr = col_ptr;
+ }
+ else
+ {
+ /* add element at head of sorted list */
+ col_ptr->next = sorted_col_ptr;
+ sorted_col_ptr = col_ptr;
+
+ /* remove element from list */
+ if (prev_ptr == NULL)
+ {
+ g_lum_tab[lum].col_ptr = next_ptr; /* remove 1.st element */
+ }
+ else
+ {
+ prev_ptr->next = next_ptr;
+ }
+ }
+ }
+ }
+
+ g_lum_tab[lum].col_ptr = sorted_col_ptr;
+}
+
+static void
+cnt_same_sample_colortones (t_samp_color_elem *ref_ptr,
+ guchar *prev_color,
+ guchar *color_tone,
+ gint *csum)
+{
+ gint32 col_error, ref_error;
+ t_samp_color_elem *col_ptr;
+
+ ref_error = 0;
+ if (prev_color != NULL)
+ {
+ ref_error = color_error (ref_ptr->color[0], ref_ptr->color[1], ref_ptr->color[2],
+ prev_color[0], prev_color[1], prev_color[2]);
+ }
+
+ /* collect colors that are (nearly) the same */
+ for (col_ptr = ref_ptr->next;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ if (col_ptr->sum_color < 1)
+ continue;
+ col_error = color_error (ref_ptr->color[0], ref_ptr->color[1], ref_ptr->color[2],
+ col_ptr->color[0], col_ptr->color[1], col_ptr->color[2]);
+
+ if (col_error <= g_tol_col_err)
+ {
+ /* cout color of the same colortone */
+ *csum += col_ptr->sum_color;
+ /* mark the already checked color with negative sum_color value */
+ col_ptr->sum_color = 0 - col_ptr->sum_color;
+
+ if (prev_color != NULL)
+ {
+ col_error = color_error (col_ptr->color[0], col_ptr->color[1], col_ptr->color[2],
+ prev_color[0], prev_color[1], prev_color[2]);
+ if (col_error < ref_error)
+ {
+ /* use the color that is closest to prev_color */
+ memcpy (color_tone, &col_ptr->color[0], 3);
+ ref_error = col_error;
+ }
+ }
+ }
+ }
+}
+
+/* find the dominant colortones (out of all sample colors)
+ * for each available brightness intensity value.
+ * and store them in g_sample_color_tab
+ */
+static void
+ideal_samples (void)
+{
+ gint32 lum;
+ t_samp_color_elem *col_ptr;
+ guchar *color;
+
+ guchar color_tone[4];
+ guchar color_ideal[4];
+ gint csum, maxsum;
+
+ color = NULL;
+ for (lum = 0; lum < 256; lum++)
+ {
+ if (g_lum_tab[lum].col_ptr == NULL)
+ continue;
+
+ sort_color (lum);
+ col_ptr = g_lum_tab[lum].col_ptr;
+ memcpy (&color_ideal[0], &col_ptr->color[0], 3);
+
+ maxsum = 0;
+
+ /* collect colors that are (nearly) the same */
+ for (;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ csum = 0;
+ if (col_ptr->sum_color > 0)
+ {
+ memcpy (&color_tone[0], &col_ptr->color[0], 3);
+ cnt_same_sample_colortones (col_ptr, color,
+ &color_tone[0], &csum);
+ if (csum > maxsum)
+ {
+ maxsum = csum;
+ memcpy (&color_ideal[0], &color_tone[0], 3);
+ }
+ }
+ else
+ col_ptr->sum_color = abs (col_ptr->sum_color);
+ }
+
+ /* store ideal color and keep track of the color */
+ color = &g_sample_color_tab[3 * lum];
+ memcpy (color, &color_ideal[0], 3);
+ }
+}
+
+static void
+guess_missing_colors (void)
+{
+ gint32 lum;
+ gint32 idx;
+ gfloat div;
+
+ guchar lo_color[4];
+ guchar hi_color[4];
+ guchar new_color[4];
+
+ lo_color[0] = 0;
+ lo_color[1] = 0;
+ lo_color[2] = 0;
+ lo_color[3] = 255;
+
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ hi_color[3] = 255;
+
+ new_color[0] = 0;
+ new_color[1] = 0;
+ new_color[2] = 0;
+ new_color[3] = 255;
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ if ((g_lum_tab[lum].col_ptr == NULL) ||
+ (g_lum_tab[lum].from_sample == FALSE))
+ {
+ if (lum > 0)
+ {
+ for (idx = lum; idx < 256; idx++)
+ {
+ if ((g_lum_tab[idx].col_ptr != NULL) &&
+ g_lum_tab[idx].from_sample)
+ {
+ memcpy (&hi_color[0],
+ &g_sample_color_tab[3 * idx], 3);
+ break;
+ }
+ if (idx == 255)
+ {
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ break;
+ }
+ }
+
+ div = idx - (lum -1);
+ new_color[0] = lo_color[0] + ((float)(hi_color[0] - lo_color[0]) / div);
+ new_color[1] = lo_color[1] + ((float)(hi_color[1] - lo_color[1]) / div);
+ new_color[2] = lo_color[2] + ((float)(hi_color[2] - lo_color[2]) / div);
+
+/*
+ * printf ("LO: %03d %03d %03d HI: %03d %03d %03d NEW: %03d %03d %03d\n",
+ * (int)lo_color[0], (int)lo_color[1], (int)lo_color[2],
+ * (int)hi_color[0], (int)hi_color[1], (int)hi_color[2],
+ * (int)new_color[0], (int)new_color[1], (int)new_color[2]);
+ */
+
+ }
+ g_lum_tab[lum].col_ptr = new_samp_color (&new_color[0]);
+ g_lum_tab[lum].from_sample = FALSE;
+ memcpy (&g_sample_color_tab [3 * lum], &new_color[0], 3);
+ }
+ memcpy (&lo_color[0], &g_sample_color_tab [3 * lum], 3);
+ }
+}
+
+static void
+fill_missing_colors (void)
+{
+ gint32 lum;
+ gint32 idx;
+ gint32 lo_idx;
+
+ guchar lo_color[4];
+ guchar hi_color[4];
+ guchar new_color[4];
+
+ lo_color[0] = 0;
+ lo_color[1] = 0;
+ lo_color[2] = 0;
+ lo_color[3] = 255;
+
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ hi_color[3] = 255;
+
+ new_color[0] = 0;
+ new_color[1] = 0;
+ new_color[2] = 0;
+ new_color[3] = 255;
+
+ lo_idx = 0;
+ for (lum = 0; lum < 256; lum++)
+ {
+ if ((g_lum_tab[lum].col_ptr == NULL) ||
+ (g_lum_tab[lum].from_sample == FALSE))
+ {
+ if (lum > 0)
+ {
+ for (idx = lum; idx < 256; idx++)
+ {
+ if ((g_lum_tab[idx].col_ptr != NULL) &&
+ (g_lum_tab[idx].from_sample))
+ {
+ memcpy (&hi_color[0],
+ &g_sample_color_tab[3 * idx], 3);
+ break;
+ }
+
+ if (idx == 255)
+ {
+/*
+ * hi_color[0] = 255;
+ * hi_color[1] = 255;
+ * hi_color[2] = 255;
+ */
+ memcpy (&hi_color[0], &lo_color[0], 3);
+ break;
+ }
+ }
+
+ if ((lum > (lo_idx + ((idx - lo_idx ) / 2))) ||
+ (lo_idx == 0))
+ {
+ new_color[0] = hi_color[0];
+ new_color[1] = hi_color[1];
+ new_color[2] = hi_color[2];
+ }
+ else
+ {
+ new_color[0] = lo_color[0];
+ new_color[1] = lo_color[1];
+ new_color[2] = lo_color[2];
+ }
+ }
+
+ g_lum_tab[lum].col_ptr = new_samp_color (&new_color[0]);
+ g_lum_tab[lum].from_sample = FALSE;
+ memcpy (&g_sample_color_tab[3 * lum], &new_color[0], 3);
+ }
+ else
+ {
+ lo_idx = lum;
+ memcpy (&lo_color[0], &g_sample_color_tab[3 * lum], 3);
+ }
+ }
+}
+
+/* get 256 samples of active gradient (optional in inverse order) */
+static void
+get_gradient (gint mode)
+{
+ gchar *name;
+ gint n_f_samples;
+ gdouble *f_samples;
+ gdouble *f_samp; /* float samples */
+ gint lum;
+
+ free_colors ();
+
+ name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (name, 256 /* n_samples */,
+ mode == SMP_INV_GRADIENT,
+ &n_f_samples, &f_samples);
+
+ g_free (name);
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ f_samp = &f_samples[lum * 4];
+
+ g_sample_color_tab[3 * lum + 0] = f_samp[0] * 255;
+ g_sample_color_tab[3 * lum + 1] = f_samp[1] * 255;
+ g_sample_color_tab[3 * lum + 2] = f_samp[2] * 255;
+
+ g_lum_tab[lum].col_ptr =
+ new_samp_color (&g_sample_color_tab[3 * lum]);
+ g_lum_tab[lum].from_sample = TRUE;
+ g_lum_tab[lum].all_samples = 1;
+ }
+
+ g_free (f_samples);
+}
+
+static gint32
+is_layer_alive (gint32 drawable_id)
+{
+ /* return -1 if layer has become invalid */
+ if (drawable_id < 0)
+ return -1;
+
+ if (gimp_item_get_image (drawable_id) < 0)
+ {
+ g_printf ("sample colorize: unknown layer_id %d (Image closed?)\n",
+ (int)drawable_id);
+ return -1;
+ }
+
+ return drawable_id;
+}
+
+static void
+end_gdrw (t_GDRW *gdrw)
+{
+ t_GDRW *sel_gdrw = (t_GDRW *) gdrw->sel_gdrw;
+
+ if (sel_gdrw && sel_gdrw->buffer)
+ {
+ g_object_unref (sel_gdrw->buffer);
+ sel_gdrw->buffer = NULL;
+ }
+
+ g_object_unref (gdrw->buffer);
+ gdrw->buffer = NULL;
+}
+
+static void
+init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ gboolean shadow)
+{
+ gint32 image_id;
+ gint32 sel_channel_id;
+ gint32 x1, x2, y1, y2;
+ gint offsetx, offsety;
+ gint w, h;
+ gint sel_offsetx, sel_offsety;
+ t_GDRW *sel_gdrw;
+ gint32 non_empty;
+
+ if (g_Sdebug)
+ g_printf ("\np_init_gdrw: drawable_ID: %d\n", drawable_id);
+
+ gdrw->drawable_id = drawable_id;
+
+ if (shadow)
+ gdrw->buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ else
+ gdrw->buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gdrw->width = gimp_drawable_width (drawable_id);
+ gdrw->height = gimp_drawable_height (drawable_id);
+ gdrw->tile_width = gimp_tile_width ();
+ gdrw->tile_height = gimp_tile_height ();
+ gdrw->shadow = shadow;
+ gdrw->seldeltax = 0;
+ gdrw->seldeltay = 0;
+ /* get offsets within the image */
+ gimp_drawable_offsets (gdrw->drawable_id, &offsetx, &offsety);
+
+ if (! gimp_drawable_mask_intersect (gdrw->drawable_id,
+ &gdrw->x1, &gdrw->y1, &w, &h))
+ return;
+
+ gdrw->x2 = gdrw->x1 + w;
+ gdrw->y2 = gdrw->y1 + h;
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ gdrw->format = babl_format ("R'G'B'A u8");
+ else
+ gdrw->format = babl_format ("R'G'B' u8");
+
+ gdrw->bpp = babl_format_get_bytes_per_pixel (gdrw->format);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ {
+ /* index of the alpha channelbyte {1|3} */
+ gdrw->index_alpha = gdrw->bpp -1;
+ }
+ else
+ {
+ gdrw->index_alpha = 0; /* there is no alpha channel */
+ }
+
+ image_id = gimp_item_get_image (gdrw->drawable_id);
+
+ /* check and see if we have a selection mask */
+ sel_channel_id = gimp_image_get_selection (image_id);
+
+ if (g_Sdebug)
+ {
+ g_printf ("init_gdrw: image_id %d sel_channel_id: %d\n",
+ (int)image_id, (int)sel_channel_id);
+ g_printf ("init_gdrw: BOUNDS x1: %d y1: %d x2:%d y2: %d\n",
+ (int)gdrw->x1, (int)gdrw->y1, (int)gdrw->x2,(int)gdrw->y2);
+ g_printf ("init_gdrw: OFFS x: %d y: %d\n",
+ (int)offsetx, (int)offsety );
+ }
+
+ gimp_selection_bounds (image_id, &non_empty, &x1, &y1, &x2, &y2);
+
+ if (non_empty && (sel_channel_id >= 0))
+ {
+ /* selection is TRUE */
+ sel_gdrw = g_new0 (t_GDRW, 1);
+ sel_gdrw->drawable_id = sel_channel_id;
+
+ sel_gdrw->buffer = gimp_drawable_get_buffer (sel_channel_id);
+ sel_gdrw->format = babl_format ("Y u8");
+
+ sel_gdrw->width = gimp_drawable_width (sel_channel_id);
+ sel_gdrw->height = gimp_drawable_height (sel_channel_id);
+
+ sel_gdrw->tile_width = gimp_tile_width ();
+ sel_gdrw->tile_height = gimp_tile_height ();
+ sel_gdrw->shadow = shadow;
+ sel_gdrw->x1 = x1;
+ sel_gdrw->y1 = y1;
+ sel_gdrw->x2 = x2;
+ sel_gdrw->y2 = y2;
+ sel_gdrw->seldeltax = 0;
+ sel_gdrw->seldeltay = 0;
+ sel_gdrw->bpp = babl_format_get_bytes_per_pixel (sel_gdrw->format);
+ sel_gdrw->index_alpha = 0; /* there is no alpha channel */
+ sel_gdrw->sel_gdrw = NULL;
+
+ /* offset delta between drawable and selection
+ * (selection always has image size and should always have offsets of 0 )
+ */
+ gimp_drawable_offsets (sel_channel_id, &sel_offsetx, &sel_offsety);
+ gdrw->seldeltax = offsetx - sel_offsetx;
+ gdrw->seldeltay = offsety - sel_offsety;
+
+ gdrw->sel_gdrw = (t_GDRW *) sel_gdrw;
+
+ if (g_Sdebug)
+ {
+ g_printf ("init_gdrw: SEL_BOUNDS x1: %d y1: %d x2:%d y2: %d\n",
+ (int)sel_gdrw->x1, (int)sel_gdrw->y1,
+ (int)sel_gdrw->x2, (int)sel_gdrw->y2);
+ g_printf ("init_gdrw: SEL_OFFS x: %d y: %d\n",
+ (int)sel_offsetx, (int)sel_offsety );
+ g_printf ("init_gdrw: SEL_DELTA x: %d y: %d\n",
+ (int)gdrw->seldeltax, (int)gdrw->seldeltay );
+ }
+ }
+ else
+ gdrw->sel_gdrw = NULL; /* selection is FALSE */
+}
+
+/* analyze the colors in the sample_drawable */
+static int
+sample_analyze (t_GDRW *sample_gdrw)
+{
+ gint32 sample_pixels;
+ gint32 row, col;
+ gint32 first_row, first_col, last_row, last_col;
+ gint32 x, y;
+ gint32 x2, y2;
+ float progress_step;
+ float progress_max;
+ float progress;
+ guchar color[4];
+ FILE *prot_fp;
+
+ sample_pixels = 0;
+
+ /* init progress */
+ progress_max = (sample_gdrw->x2 - sample_gdrw->x1);
+ progress_step = 1.0 / progress_max;
+ progress = 0.0;
+ if (g_show_progress)
+ gimp_progress_init (_("Sample analyze"));
+
+ prot_fp = NULL;
+ if (g_Sdebug)
+ prot_fp = g_fopen ("sample_colors.dump", "w");
+ print_values (prot_fp);
+
+ /* ------------------------------------------------
+ * foreach pixel in the SAMPLE_drawable:
+ * calculate brightness intensity LUM
+ * ------------------------------------------------
+ * the inner loops (x/y) are designed to process
+ * all pixels of one tile in the sample drawable, the outer loops (row/col) do step
+ * to the next tiles. (this was done to reduce tile swapping)
+ */
+
+ first_row = sample_gdrw->y1 / sample_gdrw->tile_height;
+ last_row = (sample_gdrw->y2 / sample_gdrw->tile_height);
+ first_col = sample_gdrw->x1 / sample_gdrw->tile_width;
+ last_col = (sample_gdrw->x2 / sample_gdrw->tile_width);
+
+ for (row = first_row; row <= last_row; row++)
+ {
+ for (col = first_col; col <= last_col; col++)
+ {
+ if (col == first_col)
+ x = sample_gdrw->x1;
+ else
+ x = col * sample_gdrw->tile_width;
+ if (col == last_col)
+ x2 = sample_gdrw->x2;
+ else
+ x2 = (col +1) * sample_gdrw->tile_width;
+
+ for ( ; x < x2; x++)
+ {
+ if (row == first_row)
+ y = sample_gdrw->y1;
+ else
+ y = row * sample_gdrw->tile_height;
+ if (row == last_row)
+ y2 = sample_gdrw->y2;
+ else
+ y2 = (row +1) * sample_gdrw->tile_height ;
+
+ /* printf ("X: %4d Y:%4d Y2:%4d\n", (int)x, (int)y, (int)y2); */
+
+ for ( ; y < y2; y++)
+ {
+ /* check if the pixel is in the selection */
+ if (sample_gdrw->sel_gdrw)
+ {
+ get_pixel (sample_gdrw->sel_gdrw,
+ (x + sample_gdrw->seldeltax),
+ (y + sample_gdrw->seldeltay),
+ &color[0]);
+
+ if (color[0] == 0)
+ continue;
+ }
+ get_pixel (sample_gdrw, x, y, &color[0]);
+
+ /* if this is a visible (non-transparent) pixel */
+ if ((sample_gdrw->index_alpha < 1) ||
+ (color[sample_gdrw->index_alpha] != 0))
+ {
+ /* store color in the sublists of g_lum_tab */
+ add_color (&color[0]);
+ sample_pixels++;
+ }
+ }
+
+ if (g_show_progress)
+ gimp_progress_update (progress += progress_step);
+ }
+ }
+ }
+
+ if (g_show_progress)
+ gimp_progress_update (1.0);
+
+ if (g_Sdebug)
+ g_printf ("ROWS: %d - %d COLS: %d - %d\n",
+ (int)first_row, (int)last_row,
+ (int)first_col, (int)last_col);
+
+ print_table (prot_fp);
+
+ if (g_Sdebug)
+ print_ppm ("sample_color_all.ppm");
+
+ /* find out ideal sample colors for each brightness intensity (lum)
+ * and set g_sample_color_tab to the ideal colors.
+ */
+ ideal_samples ();
+ calculate_level_transfers ();
+
+ if (g_values.guess_missing)
+ guess_missing_colors ();
+ else
+ fill_missing_colors ();
+
+ print_table (prot_fp);
+ if (g_Sdebug)
+ print_ppm ("sample_color_2.ppm");
+ print_transtable (prot_fp);
+ if (prot_fp)
+ fclose (prot_fp);
+
+ /* check if there was at least one visible pixel */
+ if (sample_pixels == 0)
+ {
+ g_printf ("Error: Source sample has no visible Pixel\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+rnd_remap (gint32 lum,
+ guchar *mapped_color)
+{
+ t_samp_color_elem *col_ptr;
+ gint rnd;
+ gint ct;
+ gint idx;
+
+ if (g_lum_tab[lum].all_samples > 1)
+ {
+ rnd = g_random_int_range (0, g_lum_tab[lum].all_samples);
+ ct = 0;
+ idx = 0;
+
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ ct += col_ptr->sum_color;
+
+ if (rnd < ct)
+ {
+ /* printf ("RND_remap: rnd: %d all:%d ct:%d idx:%d\n",
+ * rnd, (int)g_lum_tab[lum].all_samples, ct, idx);
+ */
+ memcpy (mapped_color, &col_ptr->color[0], 3);
+ return;
+ }
+ idx++;
+ }
+ }
+
+ memcpy (mapped_color, &g_sample_color_tab[lum + lum + lum], 3);
+}
+
+static void
+remap_pixel (guchar *pixel,
+ const guchar *original,
+ gint bpp2)
+{
+ guchar mapped_color[4];
+ gint lum;
+ double orig_lum, mapped_lum;
+ double grn, red, blu;
+ double mg, mr, mb;
+ double dg, dr, db;
+ double dlum;
+
+ /* get brightness from (uncolorized) original */
+ lum = g_out_trans_tab[g_lvl_trans_tab[LUMINOSITY_1 (original)]];
+ if (g_values.rnd_subcolors)
+ rnd_remap (lum, mapped_color);
+ else
+ memcpy (mapped_color, &g_sample_color_tab[3 * lum], 3);
+
+ if (g_values.hold_inten)
+ {
+ if (g_values.orig_inten)
+ orig_lum = LUMINOSITY_0(original);
+ else
+ orig_lum = 100.0 * g_lvl_trans_tab[LUMINOSITY_1 (original)];
+
+ mapped_lum = LUMINOSITY_0 (mapped_color);
+
+ if (mapped_lum == 0)
+ {
+ /* convert black to greylevel with desired brightness value */
+ mapped_color[0] = orig_lum / 100.0;
+ mapped_color[1] = mapped_color[0];
+ mapped_color[2] = mapped_color[0];
+ }
+ else
+ {
+ /* Calculate theoretical RGB to reach given intensity LUM
+ * value (orig_lum)
+ */
+ mr = mapped_color[0];
+ mg = mapped_color[1];
+ mb = mapped_color[2];
+
+ if (mr > 0.0)
+ {
+ red =
+ orig_lum / (30 + (59 * mg / mr) + (11 * mb / mr));
+ grn = mg * red / mr;
+ blu = mb * red / mr;
+ }
+ else if (mg > 0.0)
+ {
+ grn =
+ orig_lum / ((30 * mr / mg) + 59 + (11 * mb / mg));
+ red = mr * grn / mg;
+ blu = mb * grn / mg;
+ }
+ else
+ {
+ blu =
+ orig_lum / ((30 * mr / mb) + (59 * mg / mb) + 11);
+ grn = mg * blu / mb;
+ red = mr * blu / mb;
+ }
+
+ /* on overflow: Calculate real RGB values
+ * (this may change the hue and saturation,
+ * more and more into white)
+ */
+
+ if (red > 255)
+ {
+ if ((blu < 255) && (grn < 255))
+ {
+ /* overflow in the red channel (compensate with green and blue) */
+ dlum = (red - 255.0) * 30.0;
+ if (mg > 0)
+ {
+ dg = dlum / (59.0 + (11.0 * mb / mg));
+ db = dg * mb / mg;
+ }
+ else if (mb > 0)
+ {
+ db = dlum / (11.0 + (59.0 * mg / mb));
+ dg = db * mg / mb;
+ }
+ else
+ {
+ db = dlum / (11.0 + 59.0);
+ dg = dlum / (59.0 + 11.0);
+ }
+
+ grn += dg;
+ blu += db;
+ }
+
+ red = 255.0;
+
+ if (grn > 255)
+ {
+ grn = 255.0;
+ blu = (orig_lum - 22695) / 11; /* 22695 = (255 * 30) + (255 * 59) */
+ }
+ if (blu > 255)
+ {
+ blu = 255.0;
+ grn = (orig_lum - 10455) / 59; /* 10455 = (255 * 30) + (255 * 11) */
+ }
+ }
+ else if (grn > 255)
+ {
+ if ((blu < 255) && (red < 255))
+ {
+ /* overflow in the green channel (compensate with red and blue) */
+ dlum = (grn - 255.0) * 59.0;
+
+ if (mr > 0)
+ {
+ dr = dlum / (30.0 + (11.0 * mb / mr));
+ db = dr * mb / mr;
+ }
+ else if (mb > 0)
+ {
+ db = dlum / (11.0 + (30.0 * mr / mb));
+ dr = db * mr / mb;
+ }
+ else
+ {
+ db = dlum / (11.0 + 30.0);
+ dr = dlum / (30.0 + 11.0);
+ }
+
+ red += dr;
+ blu += db;
+ }
+
+ grn = 255.0;
+
+ if (red > 255)
+ {
+ red = 255.0;
+ blu = (orig_lum - 22695) / 11; /* 22695 = (255*59) + (255*30) */
+ }
+ if (blu > 255)
+ {
+ blu = 255.0;
+ red = (orig_lum - 17850) / 30; /* 17850 = (255*59) + (255*11) */
+ }
+ }
+ else if (blu > 255)
+ {
+ if ((red < 255) && (grn < 255))
+ {
+ /* overflow in the blue channel (compensate with green and red) */
+ dlum = (blu - 255.0) * 11.0;
+
+ if (mg > 0)
+ {
+ dg = dlum / (59.0 + (30.0 * mr / mg));
+ dr = dg * mr / mg;
+ }
+ else if (mr > 0)
+ {
+ dr = dlum / (30.0 + (59.0 * mg / mr));
+ dg = dr * mg / mr;
+ }
+ else
+ {
+ dr = dlum / (30.0 + 59.0);
+ dg = dlum / (59.0 + 30.0);
+ }
+
+ grn += dg;
+ red += dr;
+ }
+
+ blu = 255.0;
+
+ if (grn > 255)
+ {
+ grn = 255.0;
+ red = (orig_lum - 17850) / 30; /* 17850 = (255*11) + (255*59) */
+ }
+ if (red > 255)
+ {
+ red = 255.0;
+ grn = (orig_lum - 10455) / 59; /* 10455 = (255*11) + (255*30) */
+ }
+ }
+
+ mapped_color[0] = CLAMP0255 (red + 0.5);
+ mapped_color[1] = CLAMP0255 (grn + 0.5);
+ mapped_color[2] = CLAMP0255 (blu + 0.5);
+ }
+ }
+
+ /* set colorized pixel in shadow pr */
+ memcpy (pixel, &mapped_color[0], bpp2);
+}
+
+static void
+colorize_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gboolean has_alpha)
+{
+ if (has_alpha)
+ {
+ bpp--;
+ dest[bpp] = src[bpp];
+ }
+
+ remap_pixel (dest, src, bpp);
+}
+
+static void
+colorize_drawable (gint32 drawable_id)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gboolean has_alpha;
+ gint bpp;
+ gint x, y, w, h;
+ gint total_area;
+ gint area_so_far;
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &x, &y, &w, &h))
+ return;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ has_alpha = gimp_drawable_has_alpha (drawable_id);
+
+ if (has_alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ if (g_show_progress)
+ gimp_progress_init (_("Remap colorized"));
+
+
+ total_area = w * h;
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ goto out;
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x, y, w, h), 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (x, y, w, h), 0 , format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->items[0].data;
+ guchar *dest = iter->items[1].data;
+ gint row;
+
+ for (row = 0; row < iter->items[0].roi.height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint pixels = iter->items[0].roi.width;
+
+ while (pixels--)
+ {
+ colorize_func (s, d, bpp, has_alpha);
+
+ s += bpp;
+ d += bpp;
+ }
+
+ src += iter->items[0].roi.width * bpp;
+ dest += iter->items[1].roi.width * bpp;
+ }
+
+ area_so_far += iter->items[0].roi.width * iter->items[0].roi.height;
+
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x, y, w, h);
+
+ out:
+ if (g_show_progress)
+ gimp_progress_update (0.0);
+}
+
+/* colorize dst_drawable like sample_drawable */
+static int
+main_colorize (gint mc_flags)
+{
+ t_GDRW sample_gdrw;
+ gboolean sample_drawable = FALSE;
+ gint32 max;
+ gint32 id;
+ gint rc;
+
+ if (g_Sdebug)
+ get_filevalues (); /* for debugging: read values from file */
+
+ /* calculate value of tolerable color error */
+ max = color_error (0,0, 0, 255, 255, 255); /* 260100 */
+ g_tol_col_err = (((float)max * (g_values.tol_col_err * g_values.tol_col_err)) / (100.0 *100.0));
+ g_max_col_err = max;
+
+ rc = 0;
+
+ if (mc_flags & MC_GET_SAMPLE_COLORS)
+ {
+ id = g_values.sample_id;
+ if ((id == SMP_GRADIENT) || (id == SMP_INV_GRADIENT))
+ {
+ get_gradient (id);
+ }
+ else
+ {
+ if (is_layer_alive (id) < 0)
+ return -1;
+
+ sample_drawable = TRUE;
+
+ init_gdrw (&sample_gdrw, id, FALSE);
+ free_colors ();
+ rc = sample_analyze (&sample_gdrw);
+ }
+ }
+
+ if ((mc_flags & MC_DST_REMAP) && (rc == 0))
+ {
+ if (is_layer_alive (g_values.dst_id) < 0)
+ return -1;
+
+ if (gimp_drawable_is_gray (g_values.dst_id) &&
+ (mc_flags & MC_DST_REMAP))
+ {
+ gimp_image_convert_rgb (gimp_item_get_image (g_values.dst_id));
+ }
+
+ colorize_drawable (g_values.dst_id);
+ }
+
+ if (sample_drawable)
+ end_gdrw (&sample_gdrw);
+
+ return rc;
+}