diff options
Diffstat (limited to 'plug-ins/common/sample-colorize.c')
-rw-r--r-- | plug-ins/common/sample-colorize.c | 3113 |
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; +} |