diff options
Diffstat (limited to 'src/xsane-gamma.c')
-rw-r--r-- | src/xsane-gamma.c | 2504 |
1 files changed, 2504 insertions, 0 deletions
diff --git a/src/xsane-gamma.c b/src/xsane-gamma.c new file mode 100644 index 0000000..d47f6b7 --- /dev/null +++ b/src/xsane-gamma.c @@ -0,0 +1,2504 @@ +/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend + + xsane-gamma.c + + Oliver Rauch <Oliver.Rauch@rauch-domain.de> + Copyright (C) 1998-2002 Oliver Rauch + This file is part of the XSANE package. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +#include "xsane.h" +#include "xsane-back-gtk.h" +#include "xsane-front-gtk.h" +#include "xsane-preferences.h" +#include "xsane-preview.h" +#include "xsane-save.h" + +#ifdef HAVE_LIBPNG +#ifdef HAVE_LIBZ +#include <png.h> +#include <zlib.h> +#endif +#endif + + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +/* forward declarations: */ + +void xsane_clear_histogram(XsanePixmap *hist); +static void xsane_draw_histogram_with_points(XsanePixmap *hist, + SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue, + int show_red, int show_green, int show_blue, int show_inten, double scale); +static void xsane_draw_histogram_with_lines(XsanePixmap *hist, + SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue, + int show_red, int show_green, int show_blue, int show_inten, double scale); +void xsane_draw_slider_level(XsaneSlider *slider); +static void xsane_set_slider(XsaneSlider *slider, double min, double mid, double max); +void xsane_update_slider(XsaneSlider *slider); +void xsane_update_sliders(void); +static gint xsane_slider_callback(GtkWidget *widget, GdkEvent *event, XsaneSlider *slider); +void xsane_create_slider(XsaneSlider *slider); +void xsane_create_histogram(GtkWidget *parent, const char *title, int width, int height, XsanePixmap *hist); +void xsane_get_free_gamma_curve(gfloat *free_color_gamma_data, SANE_Int *gammadata, + int negative, double gamma, double brightness, double contrast, + int len, int maxout); +static void xsane_calculate_auto_enhancement(SANE_Int *count_raw, + SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue); +void xsane_calculate_raw_histogram(void); +void xsane_calculate_enh_histogram(void); +void xsane_update_histogram(int update_raw); +void xsane_histogram_toggle_button_callback(GtkWidget *widget, gpointer data); +void xsane_create_preview_threshold_curve(u_char *gammadata, double threshold, int numbers); +void xsane_create_preview_gamma_curve(u_char *gammadata, int negative, double gamma, + double brightness, double contrast, + double medium_shadow, double medium_highlight, double medium_gamma, + int numbers); +void xsane_create_gamma_curve(SANE_Int *gammadata, + int negative, double gamma, double brightness, double contrast, + double medium_shadow, double medium_highlight, double medium_gamma, + int numbers, int maxout); +void xsane_update_gamma_curve(int update_raw); +static void xsane_enhancement_update(void); +static void xsane_gamma_to_histogram(double *min, double *mid, double *max, + double contrast, double brightness, double gamma); +void xsane_enhancement_by_gamma(void); +void xsane_enhancement_restore_default(void); +void xsane_enhancement_restore(void); +void xsane_enhancement_store(void); +static int xsane_histogram_to_gamma(XsaneSlider *slider, double *contrast, double contrast_offset, double *brightness, double brightness_offset, double *gamma, double gamma_multiplier); +void xsane_enhancement_by_histogram(int update_gamma); +static gint xsane_histogram_win_delete(GtkWidget *widget, gpointer data); +void xsane_create_histogram_dialog(const char *devicetext); +#ifdef HAVE_WORKING_GTK_GAMMACURVE +static gint xsane_gamma_win_delete(GtkWidget *widget, gpointer data); +#endif +void xsane_create_gamma_dialog(const char *devicetext); +void xsane_update_gamma_dialog(void); +void xsane_set_auto_enhancement(void); + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_clear_histogram(XsanePixmap *hist) +{ + GdkRectangle rect; + + DBG(DBG_proc, "xsane_clear_histogram\n"); + + if(hist->pixmap) + { + rect.x=0; + rect.y=0; + rect.width = HIST_WIDTH; + rect.height = HIST_HEIGHT; + + gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT); + gtk_widget_draw(hist->pixmapwid, &rect); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_draw_histogram_with_points(XsanePixmap *hist, + SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue, + int show_red, int show_green, int show_blue, int show_inten, double scale) +{ + GdkRectangle rect; + int i; + int inten, red, green, blue; + + DBG(DBG_proc, "xsane_draw_histogram_with_points\n"); + +#define XD 1 +#define YD 2 + + if(hist->pixmap) + { + rect.x=0; + rect.y=0; + rect.width = HIST_WIDTH; + rect.height = HIST_HEIGHT; + + gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT); + + red = 0; + green = 0; + blue = 0; + + for (i=0; i < HIST_WIDTH; i++) + { + inten = show_inten * count[i] * scale; + + if (xsane.xsane_colors > 1) + { + red = show_red * count_red[i] * scale; + green = show_green * count_green[i] * scale; + blue = show_blue * count_blue[i] * scale; + } + + if (inten > HIST_HEIGHT) + inten = HIST_HEIGHT; + + if (red > HIST_HEIGHT) + red = HIST_HEIGHT; + + if (green > HIST_HEIGHT) + green = HIST_HEIGHT; + + if (blue > HIST_HEIGHT) + blue = HIST_HEIGHT; + + + gdk_draw_rectangle(hist->pixmap, xsane.gc_red, TRUE, i, HIST_HEIGHT - red, XD, YD); + gdk_draw_rectangle(hist->pixmap, xsane.gc_green, TRUE, i, HIST_HEIGHT - green, XD, YD); + gdk_draw_rectangle(hist->pixmap, xsane.gc_blue, TRUE, i, HIST_HEIGHT - blue, XD, YD); + gdk_draw_rectangle(hist->pixmap, xsane.gc_black, TRUE, i, HIST_HEIGHT - inten, XD, YD); + } + + gtk_widget_draw(hist->pixmapwid, &rect); + } +} +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_draw_histogram_with_lines(XsanePixmap *hist, + SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue, + int show_red, int show_green, int show_blue, int show_inten, double scale) +{ + GdkRectangle rect; + int i, j, k; + int inten, red, green, blue; + int inten0=0, red0=0, green0=0, blue0=0; + int val[4]; + int val2[4]; + int color[4]; + int val_swap; + int color_swap; + + DBG(DBG_proc, "xsane_draw_histogram_with_lines\n"); + + if (hist->pixmap) + { + rect.x=0; + rect.y=0; + rect.width = HIST_WIDTH; + rect.height = HIST_HEIGHT; + + gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, HIST_WIDTH, HIST_HEIGHT); + + red = 0; + green = 0; + blue = 0; + + for (i=0; i < HIST_WIDTH; i++) + { + inten = show_inten * count[i] * scale; + + if (xsane.xsane_colors > 1) + { + red = show_red * count_red[i] * scale; + green = show_green * count_green[i] * scale; + blue = show_blue * count_blue[i] * scale; + } + + if (inten > HIST_HEIGHT) + { + inten = HIST_HEIGHT; + } + + if (red > HIST_HEIGHT) + { + red = HIST_HEIGHT; + } + + if (green > HIST_HEIGHT) + { + green = HIST_HEIGHT; + } + + if (blue > HIST_HEIGHT) + { + blue = HIST_HEIGHT; + } + + val[0] = red; color[0] = 0; + val[1] = green; color[1] = 1; + val[2] = blue; color[2] = 2; + val[3] = inten; color[3] = 3; + + for (j = 0; j < 3; j++) + { + for (k = j + 1; k < 4; k++) + { + if (val[j] < val[k]) + { + val_swap = val[j]; + color_swap = color[j]; + val[j] = val[k]; + color[j] = color[k]; + val[k] = val_swap; + color[k] = color_swap; + } + } + } + val2[0] = val[1] + 1; + val2[1] = val[2] + 1; + val2[2] = val[3] + 1; + val2[3] = 0; + + for (j = 0; j < 4; j++) + { + switch(color[j]) + { + case 0: red0 = val2[j]; + break; + case 1: green0 = val2[j]; + break; + case 2: blue0 = val2[j]; + break; + case 3: inten0 = val2[j]; + break; + } + } + + + gdk_draw_line(hist->pixmap, xsane.gc_red, i, HIST_HEIGHT - red, i, HIST_HEIGHT - red0); + gdk_draw_line(hist->pixmap, xsane.gc_green, i, HIST_HEIGHT - green, i, HIST_HEIGHT - green0); + gdk_draw_line(hist->pixmap, xsane.gc_blue, i, HIST_HEIGHT - blue, i, HIST_HEIGHT - blue0); + gdk_draw_line(hist->pixmap, xsane.gc_black, i, HIST_HEIGHT - inten, i, HIST_HEIGHT - inten0); + } + + gtk_widget_draw(hist->pixmapwid, &rect); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_establish_slider(XsaneSlider *slider) +{ + int x, y, pos, len; + guchar buf[XSANE_SLIDER_WIDTH*3]; + GdkRectangle rect; + + DBG(DBG_proc, "xsane_establish_slider\n"); + + buf[0] = buf[1] = buf[2] = 0; + buf[3+0] = buf[3+1] = buf[3+2]= 0; + + for (x = 0; x < 256; x++) + { + buf[3*x+0+6] = x * slider->r; + buf[3*x+1+6] = x * slider->g; + buf[3*x+2+6] = x * slider->b; + } + + buf[258*3+0] = 255 * slider->r; + buf[258*3+1] = 255 * slider->g; + buf[258*3+2] = 255 * slider->b; + + buf[259*3+0] = 255 * slider->r; + buf[259*3+1] = 255 * slider->g; + buf[259*3+2] = 255 * slider->b; + + for (y = 0; y < XSANE_SLIDER_HEIGHT; y++) + { + pos = slider->position[0]-y/2; + len = y; + if (pos<-2) + { + len = len + pos + 2; + pos = -2; + } + pos = pos * 3 + 6; + + for (x = 0; x <= len; x++) + { + if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1)) + { + buf[pos++] = 255; + buf[pos++] = 255; + buf[pos++] = 255; + } + else + { + buf[pos++] = 0; + buf[pos++] = 0; + buf[pos++] = 0; + } + } + + + pos = slider->position[1]-y/2; + len = y; + pos = pos * 3 + 6; + + for (x = 0; x <= len; x++) + { + if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1)) + { + buf[pos++] = 255; + buf[pos++] = 255; + buf[pos++] = 255; + } + else + { + buf[pos++] = 128; + buf[pos++] = 128; + buf[pos++] = 128; + } + } + + + pos = slider->position[2]-y/2; + len = y; + if (pos+len>257) + { + len = 257 - pos; + } + pos = pos * 3 + 6; + + for (x=0; x<=len; x++) + { + if ((x == 0) || (x == len) || (y == XSANE_SLIDER_HEIGHT-1)) + { + buf[pos++] = 0; + buf[pos++] = 0; + buf[pos++] = 0; + } + else + { + buf[pos++] = 255; + buf[pos++] = 255; + buf[pos++] = 255; + } + } + + gtk_preview_draw_row(GTK_PREVIEW(slider->preview),buf, 0, y, XSANE_SLIDER_WIDTH); + } + + rect.x=0; + rect.y=0; + rect.width = XSANE_SLIDER_WIDTH; + rect.height = XSANE_SLIDER_HEIGHT; + + gtk_widget_draw(slider->preview, &rect); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_draw_slider_level(XsaneSlider *slider) +{ + int i; + guchar buf[XSANE_SLIDER_WIDTH*3]; + GdkRectangle rect; + + DBG(DBG_proc, "xsane_draw_slider_level\n"); + + buf[0] = buf[1] = buf[2] = 0; + buf[3+0] = buf[3+1] = buf[3+2]= 0; + + for (i=0; i<256; i++) + { + buf[3*i+0+6] = i * slider->r; + buf[3*i+1+6] = i * slider->g; + buf[3*i+2+6] = i * slider->b; + } + + buf[258*3+0] = 255 * slider->r; + buf[258*3+1] = 255 * slider->g; + buf[258*3+2] = 255 * slider->b; + + buf[259*3+0] = 255 * slider->r; + buf[259*3+1] = 255 * slider->g; + buf[259*3+2] = 255 * slider->b; + + for (i=0; i<XSANE_SLIDER_HEIGHT; i++) + { + gtk_preview_draw_row(GTK_PREVIEW(slider->preview),buf, 0, i, XSANE_SLIDER_WIDTH); + } + + rect.x=0; + rect.y=0; + rect.width = XSANE_SLIDER_WIDTH; + rect.height = XSANE_SLIDER_HEIGHT; + + gtk_widget_draw(slider->preview, &rect); +} +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_set_slider(XsaneSlider *slider, double min, double mid, double max) +{ + DBG(DBG_proc, "xsane_set_slider\n"); + + slider->value[0] = min; + slider->value[1] = mid; + slider->value[2] = max; + + slider->position[0] = min * 2.55; + slider->position[1] = mid * 2.55; + slider->position[2] = max * 2.55; + + xsane_establish_slider(slider); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_slider(XsaneSlider *slider) +{ + DBG(DBG_proc, "xsane_update_slider\n"); + + slider->position[0] = 2.55 * slider->value[0]; + slider->position[1] = 2.55 * slider->value[1]; + slider->position[2] = 2.55 * slider->value[2]; + + xsane_establish_slider(slider); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_sliders() +{ + DBG(DBG_proc, "xsane_update_sliders\n"); + + xsane_update_slider(&xsane.slider_gray); + + if ( (xsane.xsane_colors > 1) && (!xsane.enhancement_rgb_default) ) + { + xsane_update_slider(&xsane.slider_red); + xsane_update_slider(&xsane.slider_green); + xsane_update_slider(&xsane.slider_blue); + + xsane.slider_gray.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */ + xsane.slider_red.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */ + xsane.slider_green.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */ + xsane.slider_blue.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */ + } + else + { + xsane_draw_slider_level(&xsane.slider_red); /* remove slider */ + xsane_draw_slider_level(&xsane.slider_green); /* remove slider */ + xsane_draw_slider_level(&xsane.slider_blue); /* remove slider */ + + xsane.slider_red.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */ + xsane.slider_green.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */ + xsane.slider_blue.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */ + + if (xsane.param.depth == 1) + { + xsane_draw_slider_level(&xsane.slider_gray); /* remove slider */ + xsane.slider_gray.active = XSANE_SLIDER_INACTIVE; /* mark slider inactive */ + } + else + { + xsane.slider_gray.active &= ~XSANE_SLIDER_INACTIVE; /* mark slider active */ + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static gint xsane_slider_hold_event() +{ + DBG(DBG_proc, "xsane_slider_hold_event\n"); + + xsane_enhancement_by_histogram(TRUE); + + gtk_timeout_remove(xsane.slider_timer); + xsane.slider_timer = 0; + + if (xsane.slider_timer_restart) + { + xsane.slider_timer = gtk_timeout_add(XSANE_CONTINUOUS_HOLD_TIME, xsane_slider_hold_event, 0); + xsane.slider_timer_restart = FALSE; + } + + return FALSE; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static gint xsane_slider_callback(GtkWidget *widget, GdkEvent *event, XsaneSlider *slider) +{ + GdkEventButton *button_event; + GdkEventMotion *motion_event; + int distance; + int i = 0; + static int update = FALSE; + static int x; + + DBG(DBG_proc, "xsane_slider_callback\n"); + + if (slider->active == XSANE_SLIDER_INACTIVE) + { + return 0; + } + + switch(event->type) + { + case GDK_BUTTON_PRESS: + gtk_grab_add(widget); + button_event = (GdkEventButton *) event; + + distance = G_MAXINT; + for (i=0; i<3; i++) + { + if (fabs(button_event->x - slider->position[i]) < distance) + { + slider->active = i + 1; + distance = fabs(button_event->x - slider->position[i]); + } + } + if (distance<10) + { + x = button_event->x; + update = TRUE; + } + else + { + slider->active = XSANE_SLIDER_ACTIVE; + } + break; + + case GDK_BUTTON_RELEASE: + gtk_grab_remove(widget); + xsane_enhancement_by_histogram(TRUE); /* slider->active must be unchanged !!! */ + slider->active = XSANE_SLIDER_ACTIVE; /* ok, now we can reset it */ + break; + + case GDK_MOTION_NOTIFY: + + motion_event = (GdkEventMotion *) event; + gdk_window_get_pointer(widget->window, &x, 0, 0); + update = TRUE; + break; + + default: + break; + } + + if (update) + { + update = FALSE; + switch(slider->active) + { + case 1: + slider->value[0] = (x-XSANE_SLIDER_OFFSET) / 2.55; + xsane_bound_double(&slider->value[0], 0.0, slider->value[1] - 1); + break; + + case 2: + slider->value[1] = (x-XSANE_SLIDER_OFFSET) / 2.55; + xsane_bound_double(&slider->value[1], slider->value[0] + 1, slider->value[2] - 1); + break; + + case 3: + slider->value[2] = (x-XSANE_SLIDER_OFFSET) / 2.55; + xsane_bound_double(&slider->value[2], slider->value[1] + 1, 100.0); + break; + + default: + break; + } + xsane_set_slider(slider, slider->value[0], slider->value[1], slider->value[2]); + + if (preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) + { + /* call xsane_enhancement_by_histogram by event handler */ + if (!xsane.slider_timer) + { + xsane.slider_timer = gtk_timeout_add(XSANE_CONTINUOUS_HOLD_TIME, xsane_slider_hold_event, 0); + } + else + { + xsane.slider_timer_restart = TRUE; + } + } + else if (preferences.gtk_update_policy == GTK_UPDATE_DELAYED) + { + if (xsane.slider_timer) /* hold timer active? then remove it, we had a motion */ + { + gtk_timeout_remove(xsane.slider_timer); + } + /* call xsane_slider_hold_event if mouse is not moved for ??? ms */ + xsane.slider_timer = gtk_timeout_add(XSANE_HOLD_TIME, xsane_slider_hold_event, 0); + } + } + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_slider(XsaneSlider *slider) +{ + DBG(DBG_proc, "xsane_create_slider\n"); + + slider->preview = gtk_preview_new(GTK_PREVIEW_COLOR); + gtk_preview_size(GTK_PREVIEW(slider->preview), XSANE_SLIDER_WIDTH, XSANE_SLIDER_HEIGHT); + gtk_widget_set_events(slider->preview, XSANE_SLIDER_EVENTS); + gtk_signal_connect(GTK_OBJECT(slider->preview), "event", GTK_SIGNAL_FUNC(xsane_slider_callback), slider); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_histogram(GtkWidget *parent, const char *title, int width, int height, XsanePixmap *hist) +{ + GdkBitmap *mask=NULL; + + DBG(DBG_proc, "xsane_create_histogram\n"); + + hist->frame = gtk_frame_new(title); + hist->pixmap = gdk_pixmap_new(xsane.histogram_dialog->window, width, height, -1); + hist->pixmapwid = gtk_pixmap_new(hist->pixmap, mask); + gtk_container_add(GTK_CONTAINER(hist->frame), hist->pixmapwid); + gdk_draw_rectangle(hist->pixmap, xsane.gc_backg, TRUE, 0, 0, width, height); + + gtk_box_pack_start(GTK_BOX(parent), hist->frame, FALSE, FALSE, 2); + gtk_widget_show(hist->pixmapwid); + gtk_widget_show(hist->frame); + } + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_calculate_auto_enhancement(SANE_Int *count_raw, + SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue) +{ /* calculate white, medium and black values for auto enhancement */ + int limit, limit_mid; + int points, points_mix, points_red, points_green, points_blue; + int min, mid, max; + int min_red, mid_red, max_red; + int min_green, mid_green, max_green; + int min_blue, mid_blue, max_blue; + int val; + int i; + + DBG(DBG_proc, "xsane_calculate_auto_enhancement\n"); + + if (xsane.preview) + { + points = 0; + points_mix = 0; + points_red = 0; + points_green = 0; + points_blue = 0; + + for (i=0; i<256; i++) + { + points += count_raw[i]; + points_mix += 10 * log(1 + count_raw[i] + count_raw_red[i] + count_raw_green[i] + count_raw_blue[i]); + points_red += 10 * log(1 + count_raw_red[i]); + points_green += 10 * log(1 + count_raw_green[i]); + points_blue += 10 * log(1 + count_raw_blue[i]); + } + + limit = 1 + points / 5000; + + /* ----- gray ----- */ + + min = -1; + val = 0; + while ( (val/4 < limit) && (min < 253) ) + { + min++; + val += count_raw[min] + count_raw_red[min] + count_raw_green[min] + count_raw_blue[min]; + } + + max = HIST_WIDTH; + val = 0; + while ( (val/4 < limit) && (max > min + 1) ) + { + max--; + val += count_raw[max] + count_raw_red[max] + count_raw_green[max] + count_raw_blue[max]; + } + + limit_mid = points_mix / 2.0; + + mid = 0; + val = 0; + while ( (val < limit_mid) && (mid < max - 1) ) + { + mid++; + val += 10 * log(1 + count_raw[mid] + count_raw_red[mid] + count_raw_green[mid] + count_raw_blue[mid]); + } + + xsane_bound_int(&mid, min, max); + + /* ----- red ----- */ + + min_red = -1; + val = 0; + while ( (val < limit) && (min_red < 253) ) + { + min_red++; + val += count_raw_red[min_red]; + } + + max_red = HIST_WIDTH; + val = 0; + while ( (val < limit) && (max_red > min_red + 1) ) + { + max_red--; + val += count_raw_red[max_red]; + } + + limit_mid = points_red / 2.0; + + mid_red = 0; + val = 0; + while ( (val < limit_mid) && (mid_red < max_red - 1) ) + { + mid_red++; + val += 10 * log(1 + count_raw_red[mid_red]); + } + + xsane_bound_int(&mid_red, min_red, max_red); + + /* ----- green ----- */ + + min_green = -1; + val = 0; + while ( (val < limit) && (min_green < 253) ) + { + min_green++; + val += count_raw_green[min_green]; + } + + max_green = HIST_WIDTH; + val = 0; + while ( (val < limit) && (max_green > min_green + 1) ) + { + max_green--; + val += count_raw_green[max_green]; + } + + limit_mid = points_green / 2.0; + + mid_green = 0; + val = 0; + while ( (val < limit_mid) && (mid_green < max_green - 1) ) + { + mid_green++; + val += 10 * log(1 + count_raw_green[mid_green]); + } + + xsane_bound_int(&mid_green, min_green, max_green); + + /* ----- blue ----- */ + + min_blue = -1; + val = 0; + while ( (val < limit) && (min_blue < 253) ) + { + min_blue++; + val += count_raw_blue[min_blue]; + } + + max_blue = HIST_WIDTH; + val = 0; + while ( (val < limit) && (max_blue > min_blue + 1) ) + { + max_blue--; + val += count_raw_blue[max_blue]; + } + + limit_mid = points_blue / 2.0; + + mid_blue = 0; + val = 0; + while ( (val < limit_mid) && (mid_blue < max_blue - 1) ) + { + mid_blue++; + val += 10 * log(1 + count_raw_blue[mid_blue]); + } + + xsane_bound_int(&mid_blue, min_blue, max_blue); + + xsane.auto_white = max/2.55; + xsane.auto_gray = mid/2.55; + xsane.auto_black = min/2.55; + + xsane.auto_white_red = max_red/2.55; + xsane.auto_gray_red = mid_red/2.55; + xsane.auto_black_red = min_red/2.55; + + xsane.auto_white_green = max_green/2.55; + xsane.auto_gray_green = mid_green/2.55; + xsane.auto_black_green = min_green/2.55; + + xsane.auto_white_blue = max_blue/2.55; + xsane.auto_gray_blue = mid_blue/2.55; + xsane.auto_black_blue = min_blue/2.55; + } + + DBG(DBG_proc, "xsane.auto_white = %f\n", xsane.auto_white); + DBG(DBG_proc, "xsane.auto_gray = %f\n", xsane.auto_gray); + DBG(DBG_proc, "xsane.auto_black = %f\n", xsane.auto_black); + DBG(DBG_proc, "xsane.auto_white_red = %f\n", xsane.auto_white_red); + DBG(DBG_proc, "xsane.auto_gray_red = %f\n", xsane.auto_gray_red); + DBG(DBG_proc, "xsane.auto_black_red = %f\n", xsane.auto_black_red); + DBG(DBG_proc, "xsane.auto_white_green = %f\n", xsane.auto_white_green); + DBG(DBG_proc, "xsane.auto_gray_green = %f\n", xsane.auto_gray_green); + DBG(DBG_proc, "xsane.auto_black_green = %f\n", xsane.auto_black_green); + DBG(DBG_proc, "xsane.auto_white_blue = %f\n", xsane.auto_white_blue); + DBG(DBG_proc, "xsane.auto_gray_blue = %f\n", xsane.auto_gray_blue); + DBG(DBG_proc, "xsane.auto_black_blue = %f\n", xsane.auto_black_blue); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_calculate_raw_histogram(void) +{ + SANE_Int *count_raw; + SANE_Int *count_raw_red; + SANE_Int *count_raw_green; + SANE_Int *count_raw_blue; + int i; + int maxval_raw; + double scale_raw; + + DBG(DBG_proc, "xsane_calculate_raw_histogram\n"); + + /* at first reset auto enhancement values */ + + xsane.auto_black = 0.0; + xsane.auto_gray = 50.0; + xsane.auto_white = 100.0; + + xsane.auto_black_red = 0.0; + xsane.auto_gray_red = 50.0; + xsane.auto_white_red = 100.0; + + xsane.auto_black_green = 0.0; + xsane.auto_gray_green = 50.0; + xsane.auto_white_green = 100.0; + + xsane.auto_black_blue = 0.0; + xsane.auto_gray_blue = 50.0; + xsane.auto_white_blue = 100.0; + + if (xsane.preview) /* preview window exists? */ + { + count_raw = calloc(256, sizeof(SANE_Int)); + count_raw_red = calloc(256, sizeof(SANE_Int)); + count_raw_green = calloc(256, sizeof(SANE_Int)); + count_raw_blue = calloc(256, sizeof(SANE_Int)); + + preview_calculate_raw_histogram(xsane.preview, count_raw, count_raw_red, count_raw_green, count_raw_blue); + + if (xsane.param.depth > 1) + { + xsane_calculate_auto_enhancement(count_raw, count_raw_red, count_raw_green, count_raw_blue); + } + + if (xsane.histogram_log) /* logarithmical display */ + { + for (i=0; i<=255; i++) + { + count_raw[i] = (int) (50*log(1.0 + count_raw[i])); + count_raw_red[i] = (int) (50*log(1.0 + count_raw_red[i])); + count_raw_green[i] = (int) (50*log(1.0 + count_raw_green[i])); + count_raw_blue[i] = (int) (50*log(1.0 + count_raw_blue[i])); + } + } + + maxval_raw = 1; /* we do not use 0 here because we divide through this varaible later */ + + /* first and last 10 values are not used for calculating maximum value */ + for (i = 10 ; i < HIST_WIDTH - 10; i++) + { + if (count_raw[i] > maxval_raw) { maxval_raw = count_raw[i]; } + if (count_raw_red[i] > maxval_raw) { maxval_raw = count_raw_red[i]; } + if (count_raw_green[i] > maxval_raw) { maxval_raw = count_raw_green[i]; } + if (count_raw_blue[i] > maxval_raw) { maxval_raw = count_raw_blue[i]; } + } + scale_raw = 100.0/maxval_raw; + + if (xsane.histogram_lines) + { + xsane_draw_histogram_with_lines(&xsane.histogram_raw, + count_raw, count_raw_red, count_raw_green, count_raw_blue, + xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, + xsane.histogram_int, scale_raw); + } + else + { + xsane_draw_histogram_with_points(&xsane.histogram_raw, + count_raw, count_raw_red, count_raw_green, count_raw_blue, + xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, + xsane.histogram_int, scale_raw); + } + + free(count_raw_blue); + free(count_raw_green); + free(count_raw_red); + free(count_raw); + } + else + { + xsane_clear_histogram(&xsane.histogram_raw); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_calculate_enh_histogram(void) +{ + SANE_Int *count_enh; + SANE_Int *count_enh_red; + SANE_Int *count_enh_green; + SANE_Int *count_enh_blue; + int i; + int maxval_enh; + double scale_enh; + + DBG(DBG_proc, "xsane_calculate_enh_histogram\n"); + + if (xsane.preview) /* preview window exists? */ + { + count_enh = calloc(256, sizeof(SANE_Int)); + count_enh_red = calloc(256, sizeof(SANE_Int)); + count_enh_green = calloc(256, sizeof(SANE_Int)); + count_enh_blue = calloc(256, sizeof(SANE_Int)); + + preview_calculate_enh_histogram(xsane.preview, count_enh, count_enh_red, count_enh_green, count_enh_blue); + + if (xsane.histogram_log) /* logarithmical display */ + { + for (i=0; i<=255; i++) + { + count_enh[i] = (int) (50*log(1.0 + count_enh[i])); + count_enh_red[i] = (int) (50*log(1.0 + count_enh_red[i])); + count_enh_green[i] = (int) (50*log(1.0 + count_enh_green[i])); + count_enh_blue[i] = (int) (50*log(1.0 + count_enh_blue[i])); + } + } + + maxval_enh = 1; + + /* first and last 10 values are not used for calculating maximum value */ + for (i = 10 ; i < HIST_WIDTH - 10; i++) + { + if (count_enh[i] > maxval_enh) { maxval_enh = count_enh[i]; } + if (count_enh_red[i] > maxval_enh) { maxval_enh = count_enh_red[i]; } + if (count_enh_green[i] > maxval_enh) { maxval_enh = count_enh_green[i]; } + if (count_enh_blue[i] > maxval_enh) { maxval_enh = count_enh_blue[i]; } + } + scale_enh = 100.0/maxval_enh; + + if (xsane.histogram_lines) + { + xsane_draw_histogram_with_lines(&xsane.histogram_enh, + count_enh, count_enh_red, count_enh_green, count_enh_blue, + xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale_enh); + } + else + { + xsane_draw_histogram_with_points(&xsane.histogram_enh, + count_enh, count_enh_red, count_enh_green, count_enh_blue, + xsane.histogram_red, xsane.histogram_green, xsane.histogram_blue, xsane.histogram_int, scale_enh); + } + + free(count_enh_blue); + free(count_enh_green); + free(count_enh_red); + free(count_enh); + } + else + { + xsane_clear_histogram(&xsane.histogram_enh); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_histogram(int update_raw) +{ + DBG(DBG_proc, "xsane_update_histogram\n"); + + if (preferences.show_histogram) + { + if (update_raw) + { + xsane_calculate_raw_histogram(); + } + xsane_calculate_enh_histogram(); + + gtk_widget_show(xsane.histogram_dialog); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_histogram_toggle_button_callback(GtkWidget *widget, gpointer data) +{ + int *valuep = data; + + DBG(DBG_proc, "xsane_histogram_toggle_button_callback\n"); + + *valuep = (GTK_TOGGLE_BUTTON(widget)->active != 0); + xsane_update_histogram(TRUE); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_preview_threshold_curve(u_char *gammadata, double threshold, int numbers) +{ + int i; + int maxin = numbers-1; + int threshold_level; + + DBG(DBG_proc, "xsane_create_preview_threshold_curve\n"); + + xsane_bound_double(&threshold, 0.0, 100.0); + threshold_level = maxin * threshold / 100.0; + + for (i=0; i < threshold_level; i++) + { + gammadata[i] = 0; + } + + for (i=threshold_level; i <= maxin; i++) + { + gammadata[i] = 255; + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_preview_gamma_curve(u_char *gammadata, int negative, double gamma, + double brightness, double contrast, + double medium_shadow, double medium_highlight, double medium_gamma, + int numbers) +{ + int i; + double midin; + double val; + double m; + double b; + int maxin = numbers-1; + double medium_m; + double medium_mid; + + medium_m = 100.0/(medium_highlight - medium_shadow); + medium_mid = (medium_shadow + medium_highlight)/200.0 * maxin; + + DBG(DBG_proc, "xsane_create_preview_gamma_curve(neg=%d, gam=%3.2f, bri=%3.2f, ctr=%3.2f, nrs=%d)\n", + negative, gamma, brightness, contrast, numbers); + + if (contrast < -100.0) + { + contrast = -100.0; + } + + midin = (int)(numbers / 2.0); + + m = 1.0 + contrast/100.0; + b = (1.0 + brightness/100.0) * midin; + + if (negative) + { + for (i=0; i <= maxin; i++) + { + val = ((double) i); + + /* medium correction */ + val = (val - medium_mid) * medium_m + midin; + xsane_bound_double(&val, 0.0, maxin); + val = maxin - val; /* invert */ + val = maxin * pow( val/maxin, (1.0/medium_gamma) ); + + val = val - midin; + + /* user correction */ + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + gammadata[i] = (u_char) (255 * pow( val/maxin, (1.0/gamma) )); + } + } + else /* positive */ + { + for (i=0; i <= maxin; i++) + { + val = ((double) i); + + /* medium correction */ + val = (val - medium_mid) * medium_m + midin; + xsane_bound_double(&val, 0.0, maxin); + val = maxin * pow( val/maxin, (1.0/medium_gamma) ); + + val = val - midin; + + /* user correction */ + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + gammadata[i] = (u_char) (255 * pow( val/maxin, (1.0/gamma) )); + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_gamma_curve(SANE_Int *gammadata, + int negative, double gamma, double brightness, double contrast, + double medium_shadow, double medium_highlight, double medium_gamma, + int numbers, int maxout) +{ + int i; + double midin; + double val; + double m; + double b; + double medium_m; + double medium_mid; + int maxin = numbers-1; + + DBG(DBG_proc, "xsane_create_gamma_curve(neg=%d, gam=%3.2f, bri=%3.2f, ctr=%3.2f, " + "mshd=%3.2f, mhig=%3.2f, mgam=%3.2f, " + "nrs=%d, max=%d)\n", + negative, gamma, brightness, contrast, + medium_shadow, medium_highlight, medium_gamma, + numbers, maxout); + + midin = (int)(numbers / 2.0); + + if (contrast < -100.0) + { + contrast = -100.0; + } + + medium_m = 100.0/(medium_highlight - medium_shadow); + medium_mid = (medium_shadow + medium_highlight)/200.0 * maxin; + + m = 1.0 + contrast/100.0; + b = (1.0 + brightness/100.0) * midin; + + if (negative) + { + for (i=0; i <= maxin; i++) + { + val = ((double) i); + + /* medium correction */ + val = (val - medium_mid) * medium_m + midin; + xsane_bound_double(&val, 0.0, maxin); + val = maxin - val; /* invert */ + val = maxin * pow( val/maxin, (1.0/medium_gamma) ); + + val = val - midin; + + /* user correction */ + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + gammadata[i] = maxout * pow( val/maxin, (1.0/gamma) ); + } + } + else /* positive */ + { + for (i=0; i <= maxin; i++) + { + val = ((double) i); + + /* medium correction */ + val = (val - medium_mid) * medium_m + midin; + xsane_bound_double(&val, 0.0, maxin); + val = maxin * pow( val/maxin, (1.0/medium_gamma) ); + + val = val - midin; + + /* user correction */ + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + gammadata[i] = maxout * pow( val/maxin, (1.0/gamma) ); + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_gamma_curve(int update_raw) +{ + DBG(DBG_proc, "xsane_update_gamma_curve\n"); + + if (xsane.preview) + { + if (!xsane.preview_gamma_data_red) + { + xsane.preview_gamma_size = pow(2, preferences.preview_gamma_input_bits); + + xsane.preview_gamma_data_red = malloc(xsane.preview_gamma_size); + xsane.preview_gamma_data_green = malloc(xsane.preview_gamma_size); + xsane.preview_gamma_data_blue = malloc(xsane.preview_gamma_size); + + xsane.histogram_gamma_data_red = malloc(xsane.preview_gamma_size); + xsane.histogram_gamma_data_green = malloc(xsane.preview_gamma_size); + xsane.histogram_gamma_data_blue = malloc(xsane.preview_gamma_size); + + xsane.histogram_medium_gamma_data_red = malloc(xsane.preview_gamma_size); + xsane.histogram_medium_gamma_data_green = malloc(xsane.preview_gamma_size); + xsane.histogram_medium_gamma_data_blue = malloc(xsane.preview_gamma_size); + } + + if (xsane.preview->calibration) + { + double pgamma_red; + double pgamma_green; + double pgamma_blue; + + pgamma_red = preferences.preview_gamma * preferences.preview_gamma_red; + pgamma_green = preferences.preview_gamma * preferences.preview_gamma_green; + pgamma_blue = preferences.preview_gamma * preferences.preview_gamma_blue; + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_red, 0, pgamma_red, + 0.0, 0.0, 0.0, 100.0, 1.0, xsane.preview_gamma_size); + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_green, 0, pgamma_green, + 0.0, 0.0, 0.0, 100.0, 1.0, xsane.preview_gamma_size); + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_blue, 0, pgamma_blue, + 0.0, 0.0, 0.0, 100.0, 1.0, xsane.preview_gamma_size); + } + else if (xsane.param.depth == 1) /* for lineart mode with grayscale preview scan */ + { + xsane_create_preview_threshold_curve(xsane.preview_gamma_data_red, xsane.threshold, xsane.preview_gamma_size); + xsane_create_preview_threshold_curve(xsane.preview_gamma_data_green, xsane.threshold, xsane.preview_gamma_size); + xsane_create_preview_threshold_curve(xsane.preview_gamma_data_blue, xsane.threshold, xsane.preview_gamma_size); + + xsane_create_preview_threshold_curve(xsane.histogram_gamma_data_red, xsane.threshold, xsane.preview_gamma_size); + xsane_create_preview_threshold_curve(xsane.histogram_gamma_data_green, xsane.threshold, xsane.preview_gamma_size); + xsane_create_preview_threshold_curve(xsane.histogram_gamma_data_blue, xsane.threshold, xsane.preview_gamma_size); + } + else /* multi bit mode */ + { + double pgamma_red = 1.0; + double pgamma_green = 1.0; + double pgamma_blue = 1.0; + + if ((xsane.mode != XSANE_GIMP_EXTENSION) || (!preferences.disable_gimp_preview_gamma)) + { + pgamma_red = preferences.preview_gamma * preferences.preview_gamma_red; + pgamma_green = preferences.preview_gamma * preferences.preview_gamma_green; + pgamma_blue = preferences.preview_gamma * preferences.preview_gamma_blue; + } + + +#ifdef HAVE_WORKING_GTK_GAMMACURVE +#if 1 + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_gray)->curve), 65536, xsane.free_gamma_data); +#if 1 +{ + int i; + for (i=0; i<100; i++) + { + printf("%1.6f ", xsane.free_gamma_data[i]); + if (i / 10.0 == i / 10) + { + printf("\n"); + } + } + printf("\n"); + for (i=65435; i<65536; i++) + { + printf("%1.6f ", xsane.free_gamma_data[i]); + if (i / 10.0 == i / 10) + { + printf("\n"); + } + } + printf("\n"); + printf("\n"); +} +#endif + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_red)->curve), 65536, xsane.free_gamma_data_red); + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_green)->curve), 65536, xsane.free_gamma_data_green); + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_blue)->curve), 65536, xsane.free_gamma_data_blue); +#endif + + xsane_get_free_gamma_curve(xsane.free_gamma_data_red, xsane.preview_gamma_data_red, xsane.negative, + xsane.gamma * xsane.gamma_red * pgamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, xsane.preview_gamma_size, 255); + + xsane_get_free_gamma_curve(xsane.free_gamma_data_green, xsane.preview_gamma_data_green, xsane.negative, + xsane.gamma * xsane.gamma_green * pgamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, xsane.preview_gamma_size, 255); + + xsane_get_free_gamma_curve(xsane.free_gamma_data_blue, xsane.preview_gamma_data_blue, xsane.negative, + xsane.gamma * xsane.gamma_blue * pgamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue , xsane.preview_gamma_size, 255); + + + xsane_get_free_gamma_curve(xsane.free_gamma_data_red, xsane.histogram_gamma_data_red, xsane.negative, + xsane.gamma * xsane.gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, xsane.preview_gamma_size, 255); + + xsane_get_free_gamma_curve(xsane.free_gamma_data_green, xsane.histogram_gamma_data_green, xsane.negative, + xsane.gamma * xsane.gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, xsane.preview_gamma_size, 255); + + xsane_get_free_gamma_curve(xsane.free_gamma_data_blue, xsane.histogram_gamma_data_blue, xsane.negative, + xsane.gamma * xsane.gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue , xsane.preview_gamma_size, 255); +#else + if ( ( ( (xsane.xsane_colors > 1) && xsane.scanner_gamma_color ) || /* color scan and gamma table for red, green and blue available */ + xsane.scanner_gamma_gray ) && /* grayscale scan and gamma table for gray available */ + (!xsane.no_preview_medium_gamma) ) /* do not use gamma table when disabled */ + { + DBG(DBG_info, "creating preview gamma tables without medium correction\n"); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_red, xsane.negative, + xsane.gamma * xsane.gamma_red * pgamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_green, xsane.negative, + xsane.gamma * xsane.gamma_green * pgamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_blue, xsane.negative, + xsane.gamma * xsane.gamma_blue * pgamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_red, xsane.negative, + xsane.gamma * xsane.gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_green, xsane.negative, + xsane.gamma * xsane.gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_blue, xsane.negative, + xsane.gamma * xsane.gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + if (update_raw) /* to speed up things here */ + { + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_red, xsane.negative, + 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_green, xsane.negative, + 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_blue, xsane.negative, + 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, + xsane.preview_gamma_size); + } + } + else if (xsane.xsane_colors > 1) /* color scan, no color scanner gamma tables available */ + { + DBG(DBG_info, "creating preview gamma tables with medium correction\n"); + + xsane.medium_changed = FALSE; + + preview_display_valid(xsane.preview); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_red, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_red * pgamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_green, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_green * pgamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_blue, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_blue * pgamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + xsane.preview_gamma_size); + + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_red, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_green, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_blue, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + xsane.preview_gamma_size); + + + if (update_raw) /* to speed up things here */ + { + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_red, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_green, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_blue, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + xsane.preview_gamma_size); + } + } + else /* grayscale scan, no gray scanner gamma table available */ + { + DBG(DBG_info, "creating preview gamma tables with medium correction\n"); + + xsane.medium_changed = FALSE; + + preview_display_valid(xsane.preview); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_red, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_red * pgamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_green, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_green * pgamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.preview_gamma_data_blue, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_blue * pgamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_red, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_red, + xsane.brightness + xsane.brightness_red, + xsane.contrast + xsane.contrast_red, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_green, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_green, + xsane.brightness + xsane.brightness_green, + xsane.contrast + xsane.contrast_green, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_gamma_data_blue, xsane.negative != xsane.medium_negative, + xsane.gamma * xsane.gamma_blue, + xsane.brightness + xsane.brightness_blue, + xsane.contrast + xsane.contrast_blue, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + + if (update_raw) /* to speed up things here */ + { + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_red, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_green, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + + xsane_create_preview_gamma_curve(xsane.histogram_medium_gamma_data_blue, xsane.negative != xsane.medium_negative, + 1.0, 0.0, 0.0, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + xsane.preview_gamma_size); + } + } +#endif + } + + preview_gamma_correction(xsane.preview, preferences.preview_gamma_input_bits, + xsane.preview_gamma_data_red, xsane.preview_gamma_data_green, xsane.preview_gamma_data_blue, + xsane.histogram_gamma_data_red, xsane.histogram_gamma_data_green, xsane.histogram_gamma_data_blue, + xsane.histogram_medium_gamma_data_red, xsane.histogram_medium_gamma_data_green, xsane.histogram_medium_gamma_data_blue); + + xsane_update_histogram(update_raw); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_enhancement_update(void) +{ + guint sig_changed=0; + + DBG(DBG_proc, "xsane_enhancement_update\n"); + + if (xsane.param.depth == 1) /* lineart? no gamma */ + { + return; + } + + sig_changed = gtk_signal_lookup("changed", GTK_OBJECT_TYPE(xsane.gamma_widget)); + + GTK_ADJUSTMENT(xsane.gamma_widget)->value = xsane.gamma; + GTK_ADJUSTMENT(xsane.brightness_widget)->value = xsane.brightness; + GTK_ADJUSTMENT(xsane.contrast_widget)->value = xsane.contrast; + + if ( (xsane.xsane_colors > 1) && (!xsane.enhancement_rgb_default) ) + { + GTK_ADJUSTMENT(xsane.gamma_red_widget)->value = xsane.gamma_red; + GTK_ADJUSTMENT(xsane.gamma_green_widget)->value = xsane.gamma_green; + GTK_ADJUSTMENT(xsane.gamma_blue_widget)->value = xsane.gamma_blue; + + GTK_ADJUSTMENT(xsane.brightness_red_widget)->value = xsane.brightness_red; + GTK_ADJUSTMENT(xsane.brightness_green_widget)->value = xsane.brightness_green; + GTK_ADJUSTMENT(xsane.brightness_blue_widget)->value = xsane.brightness_blue; + + GTK_ADJUSTMENT(xsane.contrast_red_widget)->value = xsane.contrast_red; + GTK_ADJUSTMENT(xsane.contrast_green_widget)->value = xsane.contrast_green; + GTK_ADJUSTMENT(xsane.contrast_blue_widget)->value = xsane.contrast_blue; + + gtk_signal_emit(xsane.gamma_red_widget, sig_changed); + gtk_signal_emit(xsane.gamma_green_widget, sig_changed); + gtk_signal_emit(xsane.gamma_blue_widget, sig_changed); + + gtk_signal_emit(xsane.brightness_red_widget, sig_changed); + gtk_signal_emit(xsane.brightness_green_widget, sig_changed); + gtk_signal_emit(xsane.brightness_blue_widget, sig_changed); + + gtk_signal_emit(xsane.contrast_red_widget, sig_changed); + gtk_signal_emit(xsane.contrast_green_widget, sig_changed); + gtk_signal_emit(xsane.contrast_blue_widget, sig_changed); + + } + + gtk_signal_emit(xsane.gamma_widget, sig_changed); + gtk_signal_emit(xsane.brightness_widget, sig_changed); + gtk_signal_emit(xsane.contrast_widget, sig_changed); + + xsane_update_sliders(); /* update histogram slider */ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_gamma_to_histogram(double *min, double *mid, double *max, + double contrast, double brightness, double gamma) +{ + double m; + double b; + + DBG(DBG_proc, "xsane_gamma_to_histogram\n"); + + m = 1.0 + contrast/100.0; + b = (1.0 + brightness/100.0) * 50.0; + + if (m > 0) + { + *min = 50.0 - b/m; + *mid = (100.0 * pow(0.5, gamma)-b) / m + 50.0; + *max = (100.0-b)/m + 50.0; + } + else + { + *min = 0.0; + *mid = 50.0; + *max = 100.0; + } + + xsane_bound_double(min, 0.0, 98.0); + xsane_bound_double(max, *min+1, 100.0); + xsane_bound_double(mid, *min+1, *max-1); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_enhancement_by_gamma(void) +{ + double min, mid, max; + double contrast, brightness, gamma; + + DBG(DBG_proc, "xsane_enhancement_by_gamma\n"); + + xsane_gamma_to_histogram(&min, &mid, &max, xsane.contrast, xsane.brightness, xsane.gamma); + + xsane.slider_gray.value[0] = min; + xsane.slider_gray.value[1] = mid; + xsane.slider_gray.value[2] = max; + + + /* red */ + contrast = xsane.contrast + xsane.contrast_red; + brightness = xsane.brightness + xsane.brightness_red; + gamma = xsane.gamma * xsane.gamma_red; + + if (contrast < -100) + { + contrast = -100; + } + + xsane_gamma_to_histogram(&min, &mid, &max, contrast, brightness, gamma); + + + xsane.slider_red.value[0] = min; + xsane.slider_red.value[1] = mid; + xsane.slider_red.value[2] = max; + + + /* green */ + contrast = xsane.contrast + xsane.contrast_green; + brightness = xsane.brightness + xsane.brightness_green; + gamma = xsane.gamma * xsane.gamma_green; + + if (contrast < -100) + { + contrast = -100; + } + + xsane_gamma_to_histogram(&min, &mid, &max, contrast, brightness, gamma); + + xsane.slider_green.value[0] = min; + xsane.slider_green.value[1] = mid; + xsane.slider_green.value[2] = max; + + + /* blue */ + contrast = xsane.contrast + xsane.contrast_blue; + brightness = xsane.brightness + xsane.brightness_blue; + gamma = xsane.gamma * xsane.gamma_blue; + + if (contrast < -100) + { + contrast = -100; + } + + xsane_gamma_to_histogram(&min, &mid, &max, contrast, brightness, gamma); + + xsane.slider_blue.value[0] = min; + xsane.slider_blue.value[1] = mid; + xsane.slider_blue.value[2] = max; + + + xsane_enhancement_update(); + xsane_update_gamma_curve(FALSE); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_enhancement_restore_default() +{ + DBG(DBG_proc, "xsane_enhancement_restore_default\n"); + + xsane.gamma = 1.0; + xsane.gamma_red = 1.0; + xsane.gamma_green = 1.0; + xsane.gamma_blue = 1.0; + + xsane.brightness = 0.0; + xsane.brightness_red = 0.0; + xsane.brightness_green = 0.0; + xsane.brightness_blue = 0.0; + + xsane.contrast = 0.0; + xsane.contrast_red = 0.0; + xsane.contrast_green = 0.0; + xsane.contrast_blue = 0.0; + + xsane_enhancement_by_gamma(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_enhancement_restore() +{ + DBG(DBG_proc, "xsane_enhancement_restore\n"); + + xsane.gamma = preferences.xsane_gamma; + xsane.gamma_red = preferences.xsane_gamma_red; + xsane.gamma_green = preferences.xsane_gamma_green; + xsane.gamma_blue = preferences.xsane_gamma_blue; + + xsane.brightness = preferences.xsane_brightness; + xsane.brightness_red = preferences.xsane_brightness_red; + xsane.brightness_green = preferences.xsane_brightness_green; + xsane.brightness_blue = preferences.xsane_brightness_blue; + + xsane.contrast = preferences.xsane_contrast; + xsane.contrast_red = preferences.xsane_contrast_red; + xsane.contrast_green = preferences.xsane_contrast_green; + xsane.contrast_blue = preferences.xsane_contrast_blue; + + xsane.enhancement_rgb_default = preferences.xsane_rgb_default; + xsane.negative = preferences.xsane_negative; + + xsane_refresh_dialog(); + xsane_enhancement_by_gamma(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_enhancement_store() +{ + DBG(DBG_proc, "xsane_enhancement_store\n"); + + preferences.xsane_gamma = xsane.gamma; + preferences.xsane_gamma_red = xsane.gamma_red; + preferences.xsane_gamma_green = xsane.gamma_green; + preferences.xsane_gamma_blue = xsane.gamma_blue; + + preferences.xsane_brightness = xsane.brightness; + preferences.xsane_brightness_red = xsane.brightness_red; + preferences.xsane_brightness_green = xsane.brightness_green; + preferences.xsane_brightness_blue = xsane.brightness_blue; + + preferences.xsane_contrast = xsane.contrast; + preferences.xsane_contrast_red = xsane.contrast_red; + preferences.xsane_contrast_green = xsane.contrast_green; + preferences.xsane_contrast_blue = xsane.contrast_blue; + + preferences.xsane_rgb_default = xsane.enhancement_rgb_default; + preferences.xsane_negative = xsane.negative; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static int xsane_histogram_to_gamma(XsaneSlider *slider, + double *contrast, double contrast_offset, + double *brightness, double brightness_offset, + double *gamma, double gamma_multiplier) +{ + double mid; + double range; + int correct_bound = ((slider->active == XSANE_SLIDER_ACTIVE) || (slider->active == XSANE_SLIDER_INACTIVE)); /* slider not moved */ + + DBG(DBG_proc, "xsane_histogram_to_gamma(correct_bound = %d)\n", correct_bound); + + *contrast = (10000.0 / (slider->value[2] - slider->value[0]) - 100.0); + if (correct_bound) + { + xsane_bound_double(contrast, -100.0 + contrast_offset, xsane.contrast_max + contrast_offset); + } + + *brightness = - (slider->value[0] - 50.0) * (*contrast + 100.0)/50.0 - 100.0; + if (correct_bound) + { + xsane_bound_double(brightness, xsane.brightness_min + brightness_offset, xsane.brightness_max + brightness_offset); + } + + mid = slider->value[1] - slider->value[0]; + range = slider->value[2] - slider->value[0]; + + *gamma = log(mid/range) / log(0.5); + if (correct_bound) + { + xsane_bound_double(gamma, XSANE_GAMMA_MIN * gamma_multiplier, XSANE_GAMMA_MAX * gamma_multiplier); + return 1; /* in bound */ + } + else if (xsane_check_bound_double(*contrast, -100.0 + contrast_offset, xsane.contrast_max + contrast_offset) && + xsane_check_bound_double(*brightness, XSANE_BRIGHTNESS_MIN + brightness_offset, xsane.brightness_max + brightness_offset) && + xsane_check_bound_double(*gamma, XSANE_GAMMA_MIN * gamma_multiplier, XSANE_GAMMA_MAX * gamma_multiplier)) + { + return 1; /* in bound */ + } + return 0; /* out of bound */ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_enhancement_by_histogram(int update_gamma) +{ + double gray_brightness; + double gray_contrast; + double gray_gamma; + double brightness; + double contrast; + double gamma; + + DBG(DBG_proc, "xsane_enhancement_by_histogram\n"); + + if (xsane_histogram_to_gamma(&xsane.slider_gray, &gray_contrast, 0, &gray_brightness, 0, &gray_gamma, 1.0)) + { + if (update_gamma) + { + xsane.gamma = gray_gamma; + } + + xsane.brightness = gray_brightness; + xsane.contrast = gray_contrast; + } + + if ( (xsane.xsane_colors > 1) && (!xsane.enhancement_rgb_default) ) /* rgb sliders active */ + { + if ((xsane.slider_gray.active == XSANE_SLIDER_ACTIVE) || + (xsane.slider_gray.active == XSANE_SLIDER_INACTIVE)) /* gray slider not moved */ + { + /* red */ + if (xsane_histogram_to_gamma(&xsane.slider_red, &contrast, gray_contrast, &brightness, gray_brightness, &gamma, gray_gamma)) + { + if (update_gamma) + { + xsane.gamma_red = gamma / gray_gamma; + } + xsane.brightness_red = brightness - gray_brightness; + xsane.contrast_red = contrast - gray_contrast; + } + + /* green */ + if (xsane_histogram_to_gamma(&xsane.slider_green, &contrast, gray_contrast, &brightness, gray_brightness, &gamma, gray_gamma)) + { + if (update_gamma) + { + xsane.gamma_green = gamma / gray_gamma; + } + xsane.brightness_green = brightness - gray_brightness; + xsane.contrast_green = contrast - gray_contrast; + } + + /* blue */ + if (xsane_histogram_to_gamma(&xsane.slider_blue, &contrast, gray_contrast, &brightness, gray_brightness, &gamma, gray_gamma)) + { + if (update_gamma) + { + xsane.gamma_blue = gamma / gray_gamma; + } + xsane.brightness_blue = brightness - gray_brightness; + xsane.contrast_blue = contrast - gray_contrast; + } + + } + } + + xsane_enhancement_by_gamma(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static gint xsane_histogram_win_delete(GtkWidget *widget, gpointer data) +{ + DBG(DBG_proc, "xsane_histogram_win_delete\n"); + + gtk_widget_hide(widget); + preferences.show_histogram = FALSE; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_histogram_widget), preferences.show_histogram); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_histogram_dialog(const char *devicetext) +{ + char windowname[255]; + GtkWidget *xsane_color_hbox; + GtkWidget *xsane_histogram_vbox; + GtkWidget *button; + GdkColor color_black; + GdkColor color_red; + GdkColor color_green; + GdkColor color_blue; + GdkColor color_backg; + GdkColormap *colormap; + GtkStyle *style; + + DBG(DBG_proc, "xsane_create_histogram_dialog\n"); + + xsane.histogram_dialog = gtk_window_new(GTK_WINDOW_DIALOG); + gtk_window_set_policy(GTK_WINDOW(xsane.histogram_dialog), FALSE, FALSE, FALSE); + gtk_widget_set_uposition(xsane.histogram_dialog, XSANE_HISTOGRAM_POS_X, XSANE_HISTOGRAM_POS_Y); + gtk_signal_connect(GTK_OBJECT(xsane.histogram_dialog), "delete_event", GTK_SIGNAL_FUNC(xsane_histogram_win_delete), NULL); + sprintf(windowname, "%s %s", WINDOW_HISTOGRAM, devicetext); + gtk_window_set_title(GTK_WINDOW(xsane.histogram_dialog), windowname); + xsane_set_window_icon(xsane.histogram_dialog, 0); + gtk_accel_group_attach(xsane.accelerator_group, GTK_OBJECT(xsane.histogram_dialog)); + + xsane_histogram_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(xsane_histogram_vbox), 5); + gtk_container_add(GTK_CONTAINER(xsane.histogram_dialog), xsane_histogram_vbox); + gtk_widget_show(xsane_histogram_vbox); + + + /* set gc for histogram drawing */ + gtk_widget_realize(xsane.histogram_dialog); /* realize dialog to get colors and style */ + + style = gtk_widget_get_style(xsane.histogram_dialog); +/* + style = gtk_rc_get_style(xsane.histogram_dialog); + style = gtk_widget_get_default_style(); +*/ + + xsane.gc_trans = style->bg_gc[GTK_STATE_NORMAL]; + xsane.bg_trans = &style->bg[GTK_STATE_NORMAL]; + + colormap = gdk_window_get_colormap(xsane.histogram_dialog->window); + + xsane.gc_black = gdk_gc_new(xsane.histogram_dialog->window); + color_black.red = 0; + color_black.green = 0; + color_black.blue = 0; + gdk_color_alloc(colormap, &color_black); + gdk_gc_set_foreground(xsane.gc_black, &color_black); + + xsane.gc_red = gdk_gc_new(xsane.histogram_dialog->window); + color_red.red = 40000; + color_red.green = 10000; + color_red.blue = 10000; + gdk_color_alloc(colormap, &color_red); + gdk_gc_set_foreground(xsane.gc_red, &color_red); + + xsane.gc_green = gdk_gc_new(xsane.histogram_dialog->window); + color_green.red = 10000; + color_green.green = 40000; + color_green.blue = 10000; + gdk_color_alloc(colormap, &color_green); + gdk_gc_set_foreground(xsane.gc_green, &color_green); + + xsane.gc_blue = gdk_gc_new(xsane.histogram_dialog->window); + color_blue.red = 10000; + color_blue.green = 10000; + color_blue.blue = 40000; + gdk_color_alloc(colormap, &color_blue); + gdk_gc_set_foreground(xsane.gc_blue, &color_blue); + + xsane.gc_backg = gdk_gc_new(xsane.histogram_dialog->window); + color_backg.red = 50000; + color_backg.green = 50000; + color_backg.blue = 50000; + gdk_color_alloc(colormap, &color_backg); + gdk_gc_set_foreground(xsane.gc_backg, &color_backg); + + + /* add histogram images and sliders */ + + xsane_create_histogram(xsane_histogram_vbox, FRAME_RAW_IMAGE, 256, 100, &(xsane.histogram_raw)); + + xsane_separator_new(xsane_histogram_vbox, 0); + + xsane.slider_gray.r = 1; + xsane.slider_gray.g = 1; + xsane.slider_gray.b = 1; + xsane.slider_gray.active = XSANE_SLIDER_ACTIVE; + xsane_create_slider(&xsane.slider_gray); + gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_gray.preview, FALSE, FALSE, 0); + gtk_widget_show(xsane.slider_gray.preview); + gtk_widget_realize(xsane.slider_gray.preview); + + xsane_separator_new(xsane_histogram_vbox, 0); + + xsane.slider_red.r = 1; + xsane.slider_red.g = 0; + xsane.slider_red.b = 0; + xsane.slider_red.active = XSANE_SLIDER_ACTIVE; + xsane_create_slider(&xsane.slider_red); + gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_red.preview, FALSE, FALSE, 0); + gtk_widget_show(xsane.slider_red.preview); + gtk_widget_realize(xsane.slider_red.preview); + + xsane_separator_new(xsane_histogram_vbox, 0); + + xsane.slider_green.r = 0; + xsane.slider_green.g = 1; + xsane.slider_green.b = 0; + xsane.slider_green.active = XSANE_SLIDER_ACTIVE; + xsane_create_slider(&xsane.slider_green); + gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_green.preview, FALSE, FALSE, 0); + gtk_widget_show(xsane.slider_green.preview); + gtk_widget_realize(xsane.slider_green.preview); + + xsane_separator_new(xsane_histogram_vbox, 0); + + xsane.slider_blue.r = 0; + xsane.slider_blue.g = 0; + xsane.slider_blue.b = 1; + xsane.slider_blue.active = XSANE_SLIDER_ACTIVE; + xsane_create_slider(&xsane.slider_blue); + gtk_box_pack_start(GTK_BOX(xsane_histogram_vbox), xsane.slider_blue.preview, FALSE, FALSE, 0); + gtk_widget_show(xsane.slider_blue.preview); + gtk_widget_realize(xsane.slider_blue.preview); + + xsane_draw_slider_level(&xsane.slider_gray); + xsane_draw_slider_level(&xsane.slider_red); + xsane_draw_slider_level(&xsane.slider_green); + xsane_draw_slider_level(&xsane.slider_blue); + + xsane_separator_new(xsane_histogram_vbox, 0); + + xsane_create_histogram(xsane_histogram_vbox, FRAME_ENHANCED_IMAGE, 256, 100, &(xsane.histogram_enh)); + + xsane_color_hbox = gtk_hbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(xsane_color_hbox), 5); + gtk_container_add(GTK_CONTAINER(xsane_histogram_vbox), xsane_color_hbox); + gtk_widget_show(xsane_color_hbox); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, intensity_xpm, DESC_HIST_INTENSITY, + &xsane.histogram_int, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_I, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, red_xpm, DESC_HIST_RED, + &xsane.histogram_red, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_R, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, green_xpm, DESC_HIST_GREEN, + &xsane.histogram_green, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_G, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, blue_xpm, DESC_HIST_BLUE, + &xsane.histogram_blue, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_B, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, pixel_xpm, DESC_HIST_PIXEL, + &xsane.histogram_lines, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_M, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + button = xsane_toggle_button_new_with_pixmap(xsane.histogram_dialog->window, xsane_color_hbox, log_xpm, DESC_HIST_LOG, + &xsane.histogram_log, xsane_histogram_toggle_button_callback); + gtk_widget_add_accelerator(button, "clicked", xsane.accelerator_group, GDK_L, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); + + gtk_widget_show(xsane_color_hbox); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +#ifdef HAVE_WORKING_GTK_GAMMACURVE +/* xsane_get_free_gamma_curve transforms gamma table with 65536 entries and value range 0.0-1.0 to requested gamma table */ +/* it combines the color gamma table given by gamma_widget and the gray gamma table (xsane.gamma_curve_gray) */ +void xsane_get_free_gamma_curve(gfloat *free_color_gamma_data, SANE_Int *gammadata, + int negative, double gamma, double brightness, double contrast, + int len, int maxout) +{ + int i; + gfloat factor; + double midin; + double val; + double m; + double b; + int maxin = len-1; + + DBG(DBG_proc, "xsane_get_free_gamma_curve\n"); + DBG(DBG_proc, "xsane_get_free_gamma_curve(neg=%d, gam=%3.2f, bri=%3.2f, ctr=%3.2f, nrs=%d, max=%d\n", + negative, gamma, brightness, contrast, len, maxout); + + if (contrast < -100.0) + { + contrast = -100.0; + } + + midin = (int)(len / 2.0); + + m = 1.0 + contrast/100.0; + b = (1.0 + brightness/100.0) * midin; + + factor = 65536.0 / len; + + if (1) /* xxxxxxxxxxxx colors available */ + { + if (negative) + { + DBG(DBG_proc, "xsane_get_free_gamma_curve: color transformation, negative\n"); + + for (i=0; i <= maxin; i++) + { + val = ((double) (maxin - i)) - midin; + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + val = maxout * xsane.free_gamma_data[(int) (65535 * free_color_gamma_data[(int) (val * factor)])]; + val = 0.5 + maxout * pow( val/maxin, (1.0/gamma) ); + gammadata[i] = val; + } + } + else /* positive */ + { + DBG(DBG_proc, "xsane_get_free_gamma_curve: color transformation, positive\n"); + + for (i=0; i <= maxin; i++) + { + val = ((double) i) - midin; + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + val = maxout * xsane.free_gamma_data[(int) (65535 * free_color_gamma_data[(int) (val * factor)])]; + val = 0.5 + maxout * pow( val/maxin, (1.0/gamma) ); + gammadata[i] = val; + } + } + } + else + { + if (negative) + { + DBG(DBG_proc, "xsane_get_free_gamma_curve: gray transformation, negative\n"); + + for (i=0; i <= maxin; i++) + { + val = ((double) (maxin - i)) - midin; + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + val = maxout * xsane.free_gamma_data[(int) (val * factor)]; + val = 0.5 + maxout * pow( val/maxin, (1.0/gamma) ); + gammadata[i] = val; + } + } + else /* positive */ + { + DBG(DBG_proc, "xsane_get_free_gamma_curve: gray transformation, positive\n"); + + for (i=0; i <= maxin; i++) + { + val = ((double) i) - midin; + val = val * m + b; + xsane_bound_double(&val, 0.0, maxin); + + val = maxout * xsane.free_gamma_data[(int) (val * factor)]; + val = 0.5 + maxout * pow( val/maxin, (1.0/gamma) ); + gammadata[i] = val; + } + } + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static gint xsane_gamma_win_delete(GtkWidget *widget, gpointer data) +{ + DBG(DBG_proc, "xsane_gamma_win_delete\n"); + + gtk_widget_hide(widget); + preferences.show_gamma = FALSE; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_gamma_widget), preferences.show_gamma); + return TRUE; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +/* xsane_gamma_curve_notebook_page_new creates a notebook page with a gamma curve + of 65536 entries and a value range from 0.0-1.0 */ + +GtkWidget* xsane_gamma_curve_notebook_page_new(GtkWidget *notebook, char *title) +{ + gfloat fmin, fmax, *vector; + GtkWidget *curve, *gamma, *vbox, *label; + int optlen; + + DBG(DBG_proc, "xsane_back_gtk_curve_new\n"); + + optlen = 65536; + fmin = 0.0; + fmax = 1.0; + + label = gtk_label_new((char *) title); + vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); + gtk_widget_show(vbox); + gtk_widget_show(label); + + gamma = gtk_gamma_curve_new(); + gtk_widget_set_usize(gamma, 0, 256); + curve = GTK_GAMMA_CURVE(gamma)->curve; + + vector = alloca(optlen * sizeof(vector[0])); + + gtk_curve_set_range(GTK_CURVE(curve), 0, optlen - 1, fmin, fmax); +#if 1 + gtk_curve_maintain_accuracy(GTK_CURVE(curve), 1.0); +#endif + + gtk_container_set_border_width(GTK_CONTAINER(gamma), 4); + gtk_box_pack_start(GTK_BOX(vbox), gamma, TRUE, TRUE, 0); + gtk_widget_show(gamma); + + return gamma; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_create_gamma_dialog(const char *devicetext) +{ + char windowname[255]; + GtkWidget *xsane_vbox_gamma, *notebook; + + DBG(DBG_proc, "xsane_create_free_gamma_dialog\n"); + + xsane.free_gamma_data = calloc(65536, sizeof(xsane.free_gamma_data[0])); + xsane.free_gamma_data_red = calloc(65536, sizeof(xsane.free_gamma_data_red[0])); + xsane.free_gamma_data_green = calloc(65536, sizeof(xsane.free_gamma_data_green[0])); + xsane.free_gamma_data_blue = calloc(65536, sizeof(xsane.free_gamma_data_green[0])); + + xsane.gamma_dialog = gtk_window_new(GTK_WINDOW_DIALOG); + gtk_window_set_policy(GTK_WINDOW(xsane.gamma_dialog), FALSE, FALSE, FALSE); + gtk_widget_set_uposition(xsane.gamma_dialog, XSANE_GAMMA_POS_X, XSANE_GAMMA_POS_Y); + gtk_signal_connect(GTK_OBJECT(xsane.gamma_dialog), "delete_event", GTK_SIGNAL_FUNC(xsane_gamma_win_delete), NULL); + sprintf(windowname, "%s %s", WINDOW_GAMMA, devicetext); + gtk_window_set_title(GTK_WINDOW(xsane.gamma_dialog), windowname); + xsane_set_window_icon(xsane.gamma_dialog, 0); + gtk_accel_group_attach(xsane.accelerator_group, GTK_OBJECT(xsane.gamma_dialog)); + + xsane_vbox_gamma = gtk_vbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_gamma), 5); + gtk_container_add(GTK_CONTAINER(xsane.gamma_dialog), xsane_vbox_gamma); + gtk_widget_show(xsane_vbox_gamma); + + notebook = gtk_notebook_new(); + gtk_container_set_border_width(GTK_CONTAINER(notebook), 4); + gtk_box_pack_start(GTK_BOX(xsane_vbox_gamma), notebook, TRUE, TRUE, 0); + gtk_widget_show(notebook); + + xsane.gamma_curve_gray = xsane_gamma_curve_notebook_page_new(notebook, "Gamma gray"); + xsane.gamma_curve_red = xsane_gamma_curve_notebook_page_new(notebook, "Gamma red"); + xsane.gamma_curve_green = xsane_gamma_curve_notebook_page_new(notebook, "Gamma green"); + xsane.gamma_curve_blue = xsane_gamma_curve_notebook_page_new(notebook, "Gamma blue"); + + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_gray)->curve), 65536, xsane.free_gamma_data); + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_red)->curve), 65536, xsane.free_gamma_data_red); + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_green)->curve), 65536, xsane.free_gamma_data_green); + gtk_curve_get_vector(GTK_CURVE(GTK_GAMMA_CURVE(xsane.gamma_curve_blue)->curve), 65536, xsane.free_gamma_data_blue); + +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_update_gamma_dialog() +{ + DBG(DBG_proc, "xsane_update_gamma_dialog\n"); + + if (preferences.show_gamma) + { + gtk_widget_show(xsane.gamma_dialog); + } +} +#endif + + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void xsane_set_auto_enhancement() +{ + DBG(DBG_proc, "xsane_set_auto_enhancement\n"); + xsane.slider_gray.value[0] = xsane.auto_black; + xsane.slider_gray.value[1] = xsane.auto_gray; + xsane.slider_gray.value[2] = xsane.auto_white; + + if (xsane.enhancement_rgb_default) /* set same values for color components */ + { + xsane.slider_red.value[0] = xsane.auto_black; + xsane.slider_red.value[1] = xsane.auto_gray; + xsane.slider_red.value[2] = xsane.auto_white; + + xsane.slider_green.value[0] = xsane.auto_black; + xsane.slider_green.value[1] = xsane.auto_gray; + xsane.slider_green.value[2] = xsane.auto_white; + + xsane.slider_blue.value[0] = xsane.auto_black; + xsane.slider_blue.value[1] = xsane.auto_gray; + xsane.slider_blue.value[2] = xsane.auto_white; + } + else /* set different values for each color component */ + { + xsane.slider_red.value[0] = xsane.auto_black_red; + xsane.slider_red.value[1] = xsane.auto_gray_red; + xsane.slider_red.value[2] = xsane.auto_white_red; + + xsane.slider_green.value[0] = xsane.auto_black_green; + xsane.slider_green.value[1] = xsane.auto_gray_green; + xsane.slider_green.value[2] = xsane.auto_white_green; + + xsane.slider_blue.value[0] = xsane.auto_black_blue; + xsane.slider_blue.value[1] = xsane.auto_gray_blue; + xsane.slider_blue.value[2] = xsane.auto_white_blue; + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +void xsane_set_medium(Preferences_medium_t *medium) +{ + const SANE_Option_Descriptor *opt; + int shadow_gray, shadow_red, shadow_green, shadow_blue; + int highlight_gray, highlight_red, highlight_green, highlight_blue; + + + if (!medium) + { + DBG(DBG_proc, "xsane_set_medium: no medium given, using default values\n"); + + xsane.medium_gamma_gray = 1.0; + xsane.medium_gamma_red = 1.0; + xsane.medium_gamma_green = 1.0; + xsane.medium_gamma_blue = 1.0; + + xsane.medium_shadow_gray = 0.0; + xsane.medium_shadow_red = 0.0; + xsane.medium_shadow_green = 0.0; + xsane.medium_shadow_blue = 0.0; + + xsane.medium_highlight_gray = 100.0; + xsane.medium_highlight_red = 100.0; + xsane.medium_highlight_green = 100.0; + xsane.medium_highlight_blue = 100.0; + + xsane.medium_negative = 0; + + return; + } + + DBG(DBG_proc, "xsane_set_medium: setting values for %s\n", medium->name); + + xsane.medium_gamma_red = medium->gamma_red; + xsane.medium_gamma_green = medium->gamma_green; + xsane.medium_gamma_blue = medium->gamma_blue; + + xsane.medium_shadow_red = medium->shadow_red; + xsane.medium_shadow_green = medium->shadow_green; + xsane.medium_shadow_blue = medium->shadow_blue; + + xsane.medium_highlight_red = medium->highlight_red; + xsane.medium_highlight_green = medium->highlight_green; + xsane.medium_highlight_blue = medium->highlight_blue; + + xsane.medium_gamma_gray = medium->gamma_gray; + xsane.medium_shadow_gray = medium->shadow_gray; + xsane.medium_highlight_gray = medium->highlight_gray; + + xsane.medium_negative = medium->negative; + +#if 1 +return; +#endif + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.shadow); + if (!opt) + { + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.shadow_r); + } + else if (!opt) + { + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.highlight); + } + else if (!opt) + { + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.highlight_r); + } + else if (!opt) + { + DBG(DBG_info, "xsane_set_medium_callback: no shadow/highlight values available\n"); + return; + } + + if (opt->type == SANE_TYPE_FIXED) + { + shadow_gray = SANE_FIX(medium->shadow_gray); + shadow_red = SANE_FIX(medium->shadow_red); + shadow_green = SANE_FIX(medium->shadow_green); + shadow_blue = SANE_FIX(medium->shadow_blue); + highlight_gray = SANE_FIX(medium->highlight_gray); + highlight_red = SANE_FIX(medium->highlight_red); + highlight_green = SANE_FIX(medium->highlight_green); + highlight_blue = SANE_FIX(medium->highlight_blue); + } + else if (opt->type == SANE_TYPE_INT) + { + shadow_gray = (int) medium->shadow_gray; + shadow_red = (int) medium->shadow_red; + shadow_green = (int) medium->shadow_green; + shadow_blue = (int) medium->shadow_blue; + highlight_gray = (int) medium->highlight_gray; + highlight_red = (int) medium->highlight_red; + highlight_green = (int) medium->highlight_green; + highlight_blue = (int) medium->highlight_blue; + } + else + { + DBG(DBG_info, "xsane_set_medium_callback: unknown type of shadow/highlight: %d\n", opt->type); + return; + } + + /* shadow values */ + if (!xsane_control_option(xsane.dev, xsane.well_known.shadow, SANE_ACTION_SET_VALUE, &shadow_gray, 0)) + { + xsane.medium_shadow_gray = 0.0; /* we are using hardware shadow */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.shadow_r, SANE_ACTION_SET_VALUE, &shadow_red, 0)) + { + xsane.medium_shadow_red = 0.0; /* we are using hardware shadow */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.shadow_g, SANE_ACTION_SET_VALUE, &shadow_green, 0)) + { + xsane.medium_shadow_green = 0.0; /* we are using hardware shadow */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.shadow_b, SANE_ACTION_SET_VALUE, &shadow_blue, 0)) + { + xsane.medium_shadow_blue = 0.0; /* we are using hardware shadow */ + } + + /* highlight values */ + if (!xsane_control_option(xsane.dev, xsane.well_known.highlight, SANE_ACTION_SET_VALUE, &highlight_gray, 0)) + { + xsane.medium_highlight_gray = 100.0; /* we are using hardware highlight */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.highlight_r, SANE_ACTION_SET_VALUE, &highlight_red, 0)) + { + xsane.medium_highlight_red = 100.0; /* we are using hardware highlight */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.highlight_g, SANE_ACTION_SET_VALUE, &highlight_green, 0)) + { + xsane.medium_highlight_green = 100.0; /* we are using hardware highlight */ + } + + if (!xsane_control_option(xsane.dev, xsane.well_known.highlight_b, SANE_ACTION_SET_VALUE, &highlight_blue, 0)) + { + xsane.medium_highlight_blue = 100.0; /* we are using hardware highlight */ + } + + xsane_back_gtk_refresh_dialog(); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ |