summaryrefslogtreecommitdiff
path: root/frontend/xsane-preview.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/xsane-preview.c')
-rw-r--r--frontend/xsane-preview.c3257
1 files changed, 3257 insertions, 0 deletions
diff --git a/frontend/xsane-preview.c b/frontend/xsane-preview.c
new file mode 100644
index 0000000..a75b12a
--- /dev/null
+++ b/frontend/xsane-preview.c
@@ -0,0 +1,3257 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-preview.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 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. */
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/*
+
+ The preview strategy is as follows:
+ -----------------------------------
+
+ 1) The preview is done on the full scan area or a part of it.
+
+ 2) The preview is zoomable so the user can precisely pick
+ the selection area even for small scans on a large scan
+ surface.
+
+ 3) The preview window is resizeable.
+
+ 4) The preview scan resolution depends on preview window size
+ and the selected preview surface (zoom area).
+
+ 5) We let the user/backend pick whether a preview is in color,
+ grayscale, lineart or what not. The only options that the
+ preview may (temporarily) modify are:
+
+ - resolution (set so the preview fills the window)
+ - scan area options (top-left corner, bottom-right corner)
+ - preview option (to let the backend know we're doing a preview)
+ - gamma table is set to default (gamma=1.0)
+
+ 5) The initial size of the scan surface is determined based on the constraints
+ of the four corner coordinates. Missing constraints are replaced
+ by 0/+INF as appropriate (0 for top-left, +INF for bottom-right coords).
+
+ 6) Given the preview window size and the scan surface size, we
+ select the resolution so the acquired preview image just fits
+ in the preview window. The resulting resolution may be out
+ of range in which case we pick the minum/maximum if there is
+ a range or word-list constraint or a default value if there is
+ no such constraint.
+
+ 7) Once a preview image has been acquired, we know the size of the
+ preview image (in pixels). An initial scale factor is chosen
+ so the image fits into the preview window.
+
+*/
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+/* #include <sys/param.h> */
+#include "xsane-back-gtk.h"
+#include "xsane-front-gtk.h"
+#include "xsane-preview.h"
+#include "xsane-preferences.h"
+#include "xsane-gamma.h"
+
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+extern const char *prog_name;
+extern const char *device_text;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* Cut fp conversion routines some slack: */
+#define GROSSLY_DIFFERENT(f1,f2) (fabs ((f1) - (f2)) > 1e-3)
+#define GROSSLY_EQUAL(f1,f2) (fabs ((f1) - (f2)) < 1e-3)
+
+#ifdef __alpha__
+ /* This seems to be necessary for at least some XFree86 3.1.2
+ servers. It's known to be necessary for the XF86_TGA server for
+ Linux/Alpha. Fortunately, it's no great loss so we turn this on
+ by default for now. */
+# define XSERVER_WITH_BUGGY_VISUALS
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#define PRESET_AREA_ITEMS 11
+typedef struct
+{
+ char *name;
+ float width;
+ float height;
+} Preset_area;
+
+static const Preset_area preset_area[] =
+{
+ { "full size", INF, INF },
+ { "DIN A3", 296.98, 420.0 },
+ { "DIN A4", 210.0, 296.98 },
+ { "DIN A4H", 296.98, 210.0 },
+ { "DIN A5", 148.5, 210.0 },
+ { "DIN A5H", 210.0, 148.5 },
+ { "9x13 cm", 90.0, 130.0 },
+ { "13x9 cm", 130.0, 90.0 },
+ { "legal", 215.9, 355.6 },
+ { "letter", 215.9, 279.4 },
+ { "custom", INF, INF }
+};
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static SANE_Int *preview_gamma_data_red = 0;
+static SANE_Int *preview_gamma_data_green = 0;
+static SANE_Int *preview_gamma_data_blue = 0;
+
+static SANE_Int *histogram_gamma_data_red = 0;
+static SANE_Int *histogram_gamma_data_green = 0;
+static SANE_Int *histogram_gamma_data_blue = 0;
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations */
+static void preview_order_selection(Preview *p);
+static void preview_bound_selection(Preview *p);
+static void preview_draw_rect(Preview *p, GdkWindow *win, GdkGC *gc, float coord[4]);
+static void preview_draw_selection(Preview *p);
+static void preview_update_selection(Preview *p);
+static void preview_establish_selection(Preview *p);
+/* static void preview_update_batch_selection(Preview *p); */
+static void preview_get_scale_device_to_image(Preview *p, float *xscalep, float *yscalep);
+static void preview_get_scale_device_to_preview(Preview *p, float *xscalep, float *yscalep);
+static void preview_get_scale_preview_to_image(Preview *p, float *xscalep, float *yscalep);
+static void preview_paint_image(Preview *p);
+static void preview_display_partial_image(Preview *p);
+static void preview_display_maybe(Preview *p);
+static void preview_display_image(Preview *p);
+static void preview_save_option(Preview *p, int option, SANE_Word *save_loc, int *valid);
+static void preview_restore_option(Preview *p, int option, SANE_Word saved_value, int valid);
+static void preview_set_option_float(Preview *p, int option, float value);
+static void preview_set_option_bool(Preview *p, int option, SANE_Bool value);
+static void preview_set_option_int(Preview *p, int option, SANE_Int value);
+static int preview_increment_image_y(Preview *p);
+static void preview_read_image_data(gpointer data, gint source, GdkInputCondition cond);
+static void preview_scan_done(Preview *p);
+static void preview_scan_start(Preview *p);
+static int preview_make_image_path(Preview *p, size_t filename_size, char *filename, int level);
+static void preview_restore_image(Preview *p);
+static gint preview_expose_handler(GtkWidget *window, GdkEvent *event, gpointer data);
+static gint preview_event_handler(GtkWidget *window, GdkEvent *event, gpointer data);
+static void preview_start_button_clicked(GtkWidget *widget, gpointer data);
+static void preview_cancel_button_clicked(GtkWidget *widget, gpointer data);
+static void preview_area_correct(Preview *p);
+static void preview_save_image(Preview *p);
+static void preview_zoom_not(GtkWidget *window, gpointer data);
+static void preview_zoom_out(GtkWidget *window, gpointer data);
+static void preview_zoom_in(GtkWidget *window, gpointer data);
+static void preview_zoom_undo(GtkWidget *window, gpointer data);
+static void preview_get_color(Preview *p, int x, int y, int *red, int *green, int *blue);
+static void preview_pipette_white(GtkWidget *window, gpointer data);
+static void preview_pipette_gray(GtkWidget *window, gpointer data);
+static void preview_pipette_black(GtkWidget *window, gpointer data);
+static void preview_full_preview_area(GtkWidget *widget, gpointer call_data);
+static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data);
+
+void preview_do_gamma_correction(Preview *p);
+void preview_calculate_histogram(Preview *p,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue);
+void preview_gamma_correction(Preview *p,
+ SANE_Int *gamma_red, SANE_Int *gamma_green, SANE_Int *gamma_blue,
+ SANE_Int *gamma_red_hist, SANE_Int *gamma_green_hist, SANE_Int *gamma_blue_hist);
+void preview_area_resize(GtkWidget *widget);
+void preview_update_maximum_output_size(Preview *p);
+void preview_set_maximum_output_size(Preview *p, float width, float height);
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_order_selection(Preview *p)
+{
+ float tmp_coordinate;
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+
+ if (p->selection.active)
+ {
+ if (p->selection.coordinate[0] > p->selection.coordinate[2])
+ {
+ tmp_coordinate = p->selection.coordinate[0];
+ p->selection.coordinate[0] = p->selection.coordinate[2];
+ p->selection.coordinate[2] = tmp_coordinate;
+
+ p->selection_xedge = (p->selection_xedge + 2) & 3;
+ }
+
+ if (p->selection.coordinate[1] > p->selection.coordinate[3])
+ {
+ tmp_coordinate = p->selection.coordinate[1];
+ p->selection.coordinate[1] = p->selection.coordinate[3];
+ p->selection.coordinate[3] = tmp_coordinate;
+
+ p->selection_yedge = (p->selection_yedge + 2) & 3;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_bound_selection(Preview *p)
+{
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+
+ if (p->selection.active)
+ {
+ if (p->selection.coordinate[0] < p->scanner_surface[0])
+ {
+ p->selection.coordinate[0] = p->scanner_surface[0];
+ }
+
+ if (p->selection.coordinate[1] < p->scanner_surface[1])
+ {
+ p->selection.coordinate[1] = p->scanner_surface[1];
+ }
+
+ if (p->selection.coordinate[2] > p->scanner_surface[2])
+ {
+ p->selection.coordinate[2] = p->scanner_surface[2];
+ }
+
+ if (p->selection.coordinate[3] > p->scanner_surface[3])
+ {
+ p->selection.coordinate[3] = p->scanner_surface[3];
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_draw_rect(Preview *p, GdkWindow *win, GdkGC *gc, float coordinate[4])
+{
+ float xscale, yscale;
+ float x, y, w, h;
+ gint xi, yi, wi, hi;
+
+ x = coordinate[0];
+ y = coordinate[1];
+ w = coordinate[2] - x;
+ h = coordinate[3] - y;
+
+ if (w < 0)
+ {
+ x = coordinate[2];
+ w = -w;
+ }
+
+ if (h < 0)
+ {
+ y = coordinate[3];
+ h = -h;
+ }
+
+ preview_get_scale_device_to_preview(p, &xscale, &yscale);
+
+ x = x - p->surface[0];
+ y = y - p->surface[1];
+
+ xi = (gint) (x * xscale + 0.5);
+ yi = (gint) (y * yscale + 0.5);
+ wi = (gint) (w * xscale + 0.5);
+ hi = (gint) (h * yscale + 0.5);
+
+ gdk_draw_rectangle(win, gc, FALSE, xi, yi, wi + 1, hi + 1);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_draw_selection(Preview *p)
+{
+ if (!p->gc_selection) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ while (gtk_events_pending()) /* make sure all drawing actions are finished */
+ {
+ gtk_main_iteration();
+ }
+
+ if (p->previous_selection.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, p->previous_selection.coordinate);
+ }
+
+ if (p->selection.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, p->selection.coordinate);
+ }
+
+ p->previous_selection = p->selection;
+
+
+ if (!p->gc_selection_maximum) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ if (p->previous_selection_maximum.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection_maximum, p->previous_selection_maximum.coordinate);
+ }
+
+ if (p->selection_maximum.active)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection_maximum, p->selection_maximum.coordinate);
+ }
+
+ p->previous_selection_maximum = p->selection_maximum;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_update_selection(Preview *p)
+/* draw selection box as defined in backend */
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ int i, optnum;
+
+ p->previous_selection = p->selection;
+
+ for (i = 0; i < 4; ++i)
+ {
+ optnum = p->dialog->well_known.coord[i];
+ if (optnum > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, optnum);
+ status = sane_control_option(p->dialog->dev, optnum, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ p->selection.coordinate[i] = SANE_UNFIX(val);
+ }
+ else
+ {
+ p->selection.coordinate[i] = val;
+ }
+ }
+ else /* backend does not use scanarea options */
+ {
+ switch (i)
+ {
+ case 0:
+ case 1:
+ p->selection.coordinate[i] = 0;
+ break;
+
+ case 2:
+ p->selection.coordinate[i] = p->preview_width;
+ break;
+
+ case 3:
+ p->selection.coordinate[i] = p->preview_height;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < 2; ++i)
+ {
+ if (p->selection.coordinate[i + 2] < p->selection.coordinate[i])
+ {
+ p->selection.coordinate[i + 2] = p->selection.coordinate[i];
+ }
+ }
+
+ p->selection.active = ( (p->selection.coordinate[0] != p->selection.coordinate[2]) &&
+ (p->selection.coordinate[1] != p->selection.coordinate[3]) );
+
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_establish_selection(Preview *p)
+{
+ /* This routine only shall be called if the preview area really is changed. */
+
+ int i;
+
+ preview_order_selection(p);
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_set_option_float(p, p->dialog->well_known.coord[i], p->selection.coordinate[i]);
+ }
+
+ xsane_back_gtk_update_scan_window(p->dialog);
+
+ xsane.block_update_param = FALSE;
+
+ if (p->dialog->param_change_callback)
+ {
+ (*p->dialog->param_change_callback) (p->dialog, p->dialog->param_change_arg);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+#if 0
+static void preview_update_batch_selection(Preview *p)
+{
+ Batch_selection *batch_selection;
+
+ if (!p->gc_selection) /* window isn't mapped yet */
+ {
+ return;
+ }
+
+ batch_selection = p->batch_selection;
+
+ while (batch_selection)
+ {
+ preview_draw_rect(p, p->window->window, p->gc_selection, batch_selection->coordinate);
+
+ batch_selection = batch_selection->next;
+ }
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_device_to_image(Preview *p, float *xscalep, float *yscalep)
+{
+ float device_width, device_height;
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ device_width = fabs(p->image_surface[2] - p->image_surface[0]);
+ device_height = fabs(p->image_surface[3] - p->image_surface[1]);
+
+ if ( (device_width >0) && (device_width < INF) )
+ {
+ xscale = p->image_width / device_width;
+ }
+
+ if ( (device_height >0) && (device_height < INF) )
+ {
+ yscale = p->image_height / device_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_device_to_preview(Preview *p, float *xscalep, float *yscalep)
+{
+ float device_width, device_height;
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ device_width = fabs(p->image_surface[2] - p->image_surface[0]);
+ device_height = fabs(p->image_surface[3] - p->image_surface[1]);
+
+ if ( (device_width >0) && (device_width < INF) )
+ {
+ xscale = p->preview_width / device_width;
+ }
+
+ if ( (device_height >0) && (device_height < INF) )
+ {
+ yscale = p->preview_height / device_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_scale_preview_to_image(Preview *p, float *xscalep, float *yscalep)
+{
+ float xscale = 1.0;
+ float yscale = 1.0;
+
+ if (p->image_width > 0)
+ {
+ xscale = p->image_width / (float) p->preview_width;
+ }
+
+ if (p->image_height > 0)
+ {
+ yscale = p->image_height / (float) p->preview_height;
+ }
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ {
+ if (xscale > yscale)
+ {
+ yscale = xscale;
+ }
+ else
+ {
+ xscale = yscale;
+ }
+ }
+
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_paint_image(Preview *p)
+{
+ float xscale, yscale, src_x, src_y;
+ int dst_x, dst_y, height, x, y, old_y, src_offset;
+
+ preview_get_scale_preview_to_image(p, &xscale, &yscale);
+
+ memset(p->preview_row, 0x80, 3*p->preview_window_width);
+
+ /* don't draw last line unless it's complete: */
+ height = p->image_y;
+
+ if (p->image_x == 0 && height < p->image_height)
+ {
+ ++height;
+ }
+
+ /* for now, use simple nearest-neighbor interpolation: */
+ src_offset = 0;
+ src_x = src_y = 0.0;
+ old_y = -1;
+
+ for (dst_y = 0; dst_y < p->preview_height; ++dst_y)
+ {
+ y = (int) (src_y + 0.5);
+ if (y >= height)
+ {
+ break;
+ }
+ src_offset = y * 3 * p->image_width;
+
+ if ((p->image_data_enh) && (old_y != y))
+ {
+ old_y = y;
+ for (dst_x = 0; dst_x < p->preview_width; ++dst_x)
+ {
+ x = (int) (src_x + 0.5);
+ if (x >= p->image_width)
+ {
+ break;
+ }
+
+ p->preview_row[3*dst_x + 0] = p->image_data_enh[src_offset + 3*x + 0];
+ p->preview_row[3*dst_x + 1] = p->image_data_enh[src_offset + 3*x + 1];
+ p->preview_row[3*dst_x + 2] = p->image_data_enh[src_offset + 3*x + 2];
+ src_x += xscale;
+ }
+ }
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, dst_y, p->preview_window_width);
+ src_x = 0.0;
+ src_y += yscale;
+ }
+
+ if (dst_y >= p->preview_height-5)
+ {
+ memset(p->preview_row, 0x80, 3*p->preview_window_width);
+ for (dst_y = p->preview_height-1; dst_y < p->preview_window_height; ++dst_y)
+ {
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, dst_y, p->preview_window_width);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_partial_image(Preview *p)
+{
+ preview_paint_image(p);
+
+ if (GTK_WIDGET_DRAWABLE(p->window))
+ {
+ GtkPreview *preview = GTK_PREVIEW(p->window);
+ int src_x, src_y;
+
+ src_x = (p->window->allocation.width - preview->buffer_width)/2;
+ src_y = (p->window->allocation.height - preview->buffer_height)/2;
+ gtk_preview_put(preview, p->window->window, p->window->style->black_gc, src_x, src_y,
+ 0, 0, p->preview_width, p->preview_height);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_maybe(Preview *p)
+{
+ time_t now;
+
+ time(&now);
+
+ if (now > p->image_last_time_updated) /* wait at least one secone */
+ {
+ p->image_last_time_updated = now;
+ preview_display_partial_image(p);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_display_image(Preview *p)
+{
+ /* if image height was unknown and got larger than expected get missing memory */
+ if (p->params.lines <= 0 && p->image_y < p->image_height)
+ {
+ p->image_height = p->image_y;
+ p->image_data_raw = realloc(p->image_data_raw, 3 * p->image_width * p->image_height);
+ p->image_data_enh = realloc(p->image_data_enh, 3 * p->image_width * p->image_height);
+ assert(p->image_data_raw);
+ assert(p->image_data_enh);
+ }
+
+ memcpy(p->image_data_raw, p->image_data_enh, 3 * p->image_width * p->image_height);
+
+ preview_do_gamma_correction(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_option(Preview *p, int option, SANE_Word *save_loc, int *valid)
+{
+ SANE_Status status;
+
+ if (option <= 0)
+ {
+ *valid = 0;
+ return;
+ }
+
+ status = sane_control_option(p->dialog->dev, option, SANE_ACTION_GET_VALUE, save_loc, 0);
+ *valid = (status == SANE_STATUS_GOOD);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_restore_option(Preview *p, int option, SANE_Word saved_value, int valid)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Handle dev;
+
+ if (!valid)
+ {
+ return;
+ }
+
+ dev = p->dialog->dev;
+ status = sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &saved_value, 0);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ char buf[256];
+ opt = sane_get_option_descriptor(dev, option);
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, opt->name, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_float(Preview *p, int option, float value)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev;
+ SANE_Word word;
+
+ if (option <= 0 || value <= -INF || value >= INF)
+ {
+ return;
+ }
+
+ dev = p->dialog->dev;
+ opt = sane_get_option_descriptor(dev, option);
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ word = SANE_FIX(value) + 0.5;
+ }
+ else
+ {
+ word = value + 0.5;
+ }
+
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &word, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_bool(Preview *p, int option, SANE_Bool value)
+{
+ SANE_Handle dev;
+
+ if (option <= 0)
+ return;
+
+ dev = p->dialog->dev;
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &value, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_set_option_int(Preview *p, int option, SANE_Int value)
+{
+ SANE_Handle dev;
+
+ if (option <= 0)
+ return;
+
+ dev = p->dialog->dev;
+ sane_control_option(dev, option, SANE_ACTION_SET_VALUE, &value, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_increment_image_y(Preview *p)
+{
+ size_t extra_size, offset;
+ char buf[256];
+
+ p->image_x = 0;
+ ++p->image_y;
+ if (p->params.lines <= 0 && p->image_y >= p->image_height)
+ {
+ offset = 3 * p->image_width*p->image_height;
+ extra_size = 3 * 32 * p->image_width;
+ p->image_height += 32;
+ p->image_data_raw = realloc(p->image_data_raw, offset + extra_size);
+ p->image_data_enh = realloc(p->image_data_enh, offset + extra_size);
+ if ( (!p->image_data_enh) || (!p->image_data_raw) )
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_ALLOCATE_IMAGE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return -1;
+ }
+ memset(p->image_data_enh + offset, 0xff, extra_size);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_read_image_data(gpointer data, gint source, GdkInputCondition cond)
+{
+ SANE_Status status;
+ Preview *p = data;
+ u_char buf[8192];
+ SANE_Handle dev;
+ SANE_Int len;
+ int i, j;
+
+ dev = p->dialog->dev;
+ while (1)
+ {
+ status = sane_read(dev, buf, sizeof(buf), &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_EOF)
+ {
+ if (p->params.last_frame) /* got all preview image data */
+ {
+ preview_display_image(p); /* display preview image */
+ preview_save_image(p); /* save preview image */
+ preview_scan_done(p); /* scan is done */
+ return; /* ok, all finished */
+ }
+ else
+ {
+ preview_scan_start(p);
+ break;
+ }
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_DURING_READ, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ }
+ preview_scan_done(p);
+ return;
+ }
+
+ if (!len)
+ {
+ break; /* out of data for now */
+ }
+
+ switch (p->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (p->params.depth != 8)
+ {
+ goto bad_depth;
+ }
+
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data_enh[p->image_offset++] = buf[i];
+ if (p->image_offset%3 == 0)
+ {
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ }
+ break;
+
+ case SANE_FRAME_GRAY:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width)
+ {
+ if (preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ break; /* skip padding bits */
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ u_char gl = buf[i];
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_data_enh[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ p->image_data_enh[p->image_offset++] = gl;
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data_enh[p->image_offset] = buf[i];
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width && preview_increment_image_y(p) < 0)
+ {
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "preview_read_image_data: %s %d\n", ERR_BAD_FRAME_FORMAT, p->params.format);
+ preview_scan_done(p);
+ return;
+ }
+
+ if (p->input_tag < 0)
+ {
+ preview_display_maybe(p);
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+ }
+ }
+ preview_display_maybe(p);
+ return;
+
+bad_depth:
+ snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth);
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_scan_done(Preview *p)
+{
+ int i;
+
+ p->scanning = FALSE;
+
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove(p->input_tag);
+ p->input_tag = -1;
+ }
+
+ sane_cancel(p->dialog->dev);
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ preview_restore_option(p, p->dialog->well_known.dpi, p->saved_dpi, p->saved_dpi_valid);
+ preview_restore_option(p, p->dialog->well_known.dpi_x, p->saved_dpi_x, p->saved_dpi_x_valid);
+ preview_restore_option(p, p->dialog->well_known.dpi_y, p->saved_dpi_y, p->saved_dpi_y_valid);
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_restore_option(p, p->dialog->well_known.coord[i], p->saved_coord[i], p->saved_coord_valid[i]);
+ }
+
+ preview_restore_option(p, p->dialog->well_known.bit_depth, p->saved_bit_depth, p->saved_bit_depth_valid);
+
+ preview_set_option_bool(p, p->dialog->well_known.preview, SANE_FALSE);
+
+ gtk_widget_set_sensitive(p->cancel, FALSE);
+ xsane_set_sensitivity(TRUE);
+
+ xsane.block_update_param = FALSE;
+
+ preview_update_surface(p, 0); /* if surface was not defined it's necessary to redefine it now */
+
+ preview_update_selection(p);
+ xsane_update_histogram();
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_get_memory(Preview *p)
+{
+ char buf[256];
+
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ p->image_data_enh = malloc(3 * p->image_width * (p->image_height));
+ p->image_data_raw = malloc(3 * p->image_width * (p->image_height));
+ p->preview_row = malloc(3 * p->preview_window_width);
+
+ if ( (!p->image_data_raw) || (!p->image_data_enh) || (!p->preview_row) )
+ {
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_ALLOCATE_IMAGE, strerror(errno));
+ xsane_back_gtk_error(buf, TRUE);
+
+ return -1; /* error */
+ }
+
+ memset(p->image_data_enh, 0xff, 3*p->image_width*p->image_height); /* clean memory */
+
+ return 0; /* ok */
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_scan_start(Preview *p)
+{
+ SANE_Handle dev = p->dialog->dev;
+ SANE_Status status;
+ char buf[256];
+ int fd, y, i;
+ int gamma_gray_size = 256; /* set this values to image depth for more than 8bpp input support!!! */
+ int gamma_red_size = 256;
+ int gamma_green_size = 256;
+ int gamma_blue_size = 256;
+ int gamma_gray_max = 255; /* set this to to image depth for more than 8bpp output support */
+ int gamma_red_max = 255;
+ int gamma_green_max = 255;
+ int gamma_blue_max = 255;
+
+ for (i=0; i<4; i++)
+ {
+ p->image_surface[i] = p->surface[i];
+ }
+
+ xsane_clear_histogram(&xsane.histogram_raw);
+ xsane_clear_histogram(&xsane.histogram_enh);
+ gtk_widget_set_sensitive(p->cancel, TRUE);
+ xsane_set_sensitivity(FALSE);
+
+ /* clear old preview: */
+ memset(p->preview_row, 0xff, 3*p->preview_width);
+ for (y = 0; y < p->preview_height; ++y)
+ {
+ gtk_preview_draw_row(GTK_PREVIEW(p->window), p->preview_row, 0, y, p->preview_width);
+ }
+
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove(p->input_tag);
+ p->input_tag = -1;
+ }
+
+ if (p->dialog->well_known.gamma_vector >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ SANE_Int *gamma_data;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector);
+ gamma_gray_size = opt->size / sizeof(opt->type);
+ gamma_gray_max = opt->constraint.range->max;
+
+ gamma_data = malloc(gamma_gray_size * sizeof(SANE_Int));
+ xsane_create_gamma_curve(gamma_data, 0, 1.0, 0.0, 0.0, gamma_gray_size, gamma_gray_max);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector, gamma_data);
+ free(gamma_data);
+ }
+ }
+
+ if (p->dialog->well_known.gamma_vector_r >0)
+ {
+ const SANE_Option_Descriptor *opt;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_r);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ SANE_Int *gamma_data_red, *gamma_data_green, *gamma_data_blue;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_r);
+ gamma_red_size = opt->size / sizeof(opt->type);
+ gamma_red_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_g);
+ gamma_green_size = opt->size / sizeof(opt->type);
+ gamma_green_max = opt->constraint.range->max;
+
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.gamma_vector_b);
+ gamma_blue_size = opt->size / sizeof(opt->type);
+ gamma_blue_max = opt->constraint.range->max;
+
+ gamma_data_red = malloc(gamma_red_size * sizeof(SANE_Int));
+ gamma_data_green = malloc(gamma_green_size * sizeof(SANE_Int));
+ gamma_data_blue = malloc(gamma_blue_size * sizeof(SANE_Int));
+
+ xsane_create_gamma_curve(gamma_data_red, 0, 1.0, 0.0, 0.0, gamma_red_size, gamma_red_max);
+ xsane_create_gamma_curve(gamma_data_green, 0, 1.0, 0.0, 0.0, gamma_green_size, gamma_green_max);
+ xsane_create_gamma_curve(gamma_data_blue, 0, 1.0, 0.0, 0.0, gamma_blue_size, gamma_blue_max);
+
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_r, gamma_data_red);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_g, gamma_data_green);
+ xsane_back_gtk_update_vector(p->dialog, p->dialog->well_known.gamma_vector_b, gamma_data_blue);
+
+ free(gamma_data_red);
+ free(gamma_data_green);
+ free(gamma_data_blue);
+ }
+ }
+
+ status = sane_start(dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_START_SCANNER, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+ }
+
+ status = sane_get_parameters(dev, &p->params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_GET_PARAMS, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, TRUE);
+ preview_scan_done(p);
+ return;
+ }
+
+ p->image_offset = p->image_x = p->image_y = 0;
+
+ if (p->params.format >= SANE_FRAME_RED && p->params.format <= SANE_FRAME_BLUE)
+ {
+ p->image_offset = p->params.format - SANE_FRAME_RED;
+ }
+
+ if ( (!p->image_data_enh) || (p->params.pixels_per_line != p->image_width)
+ || ( (p->params.lines >= 0) && (p->params.lines != p->image_height) ) )
+ {
+ p->image_width = p->params.pixels_per_line;
+ p->image_height = p->params.lines;
+
+ if (p->image_height < 0)
+ {
+ p->image_height = 32; /* may have to adjust as we go... */
+ }
+
+ if (preview_get_memory(p))
+ {
+ preview_scan_done(p); /* error */
+ return;
+ }
+ }
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* THIS IS A BIT STRANGE HERE */
+ p->selection.active = FALSE;
+ p->previous_selection_maximum.active = FALSE;
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+ p->scanning = TRUE;
+
+ if (sane_set_io_mode(dev, SANE_TRUE) == SANE_STATUS_GOOD && sane_get_select_fd(dev, &fd) == SANE_STATUS_GOOD)
+ {
+ p->input_tag = gdk_input_add(fd, GDK_INPUT_READ, preview_read_image_data, p);
+ }
+ else
+ {
+ preview_read_image_data(p, -1, GDK_INPUT_READ);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_make_image_path(Preview *p, size_t filename_size, char *filename, int level)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "preview-level-%d-", level);
+ return xsane_back_gtk_make_path(filename_size, filename, 0, 0, buf, p->dialog->dev_name, ".ppm", XSANE_PATH_TMP);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static int preview_restore_image_from_file(Preview *p, FILE *in, int min_quality)
+{
+ u_int psurface_type, psurface_unit;
+ int image_width, image_height;
+ int xoffset, yoffset, width, height;
+ int quality;
+ int y;
+ float psurface[4];
+ size_t nread;
+ char *imagep;
+
+ if (!in)
+ {
+ return min_quality;
+ }
+
+ /* See whether there is a saved preview and load it if present: */
+
+ if (fscanf(in, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255\n",
+ psurface + 0, psurface + 1, psurface + 2, psurface + 3,
+ &psurface_type, &psurface_unit,
+ &image_width, &image_height) != 8)
+ {
+ return min_quality;
+ }
+
+ if ((psurface_type != p->surface_type) || (psurface_unit != p->surface_unit))
+ {
+ return min_quality;
+ }
+
+ xoffset = (p->surface[0] - psurface[0])/(psurface[2] - psurface[0]) * image_width;
+ yoffset = (p->surface[1] - psurface[1])/(psurface[3] - psurface[1]) * image_height;
+ width = (p->surface[2] - p->surface[0])/(psurface[2] - psurface[0]) * image_width;
+ height = (p->surface[3] - p->surface[1])/(psurface[3] - psurface[1]) * image_height;
+ quality = width;
+
+ if ((xoffset < 0) || (yoffset < 0) ||
+ (xoffset+width > image_width) || (yoffset+height > image_height) ||
+ (width == 0) || (height == 0))
+ {
+ return min_quality;
+ }
+
+ if (quality < min_quality)
+ {
+ return min_quality;
+ }
+
+ p->params.depth = 8;
+ p->image_width = width;
+ p->image_height = height;
+
+ if (preview_get_memory(p))
+ {
+ return min_quality; /* error allocating memory */
+ }
+
+ fseek(in, yoffset * 3 * image_width, SEEK_CUR); /* skip unused lines */
+
+ imagep = p->image_data_enh;
+
+ for (y = yoffset; y < yoffset + height; y++)
+ {
+ fseek(in, xoffset * 3, SEEK_CUR); /* skip unused pixel left of area */
+
+ nread = fread(imagep, 3, width, in);
+ imagep += width * 3;
+
+ fseek(in, (image_width - width - xoffset) * 3, SEEK_CUR); /* skip unused pixel right of area */
+ }
+
+ p->image_y = height;
+ p->image_x = width;
+
+ p->image_surface[0] = p->surface[0];
+ p->image_surface[1] = p->surface[1];
+ p->image_surface[2] = p->surface[2];
+ p->image_surface[3] = p->surface[3];
+
+ return quality;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_restore_image(Preview *p)
+{
+ char filename[PATH_MAX];
+ FILE *in;
+ int status;
+ int quality = 0;
+ int level;
+
+ /* See whether there is a saved preview and load it if present: */
+
+ for(level = 2; level >= 0; level--)
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, level);
+ if (status >= 0)
+ {
+ in = fopen(filename, "r");
+ if (in)
+ {
+ quality = preview_restore_image_from_file(p, in, quality);
+ }
+ }
+ }
+ memcpy(p->image_data_raw, p->image_data_enh, 3 * p->image_width * p->image_height);
+
+/* the following commands may be removed because they are done because a event is emmited */
+ preview_do_gamma_correction(p);
+ xsane_update_histogram();
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+/* This is executed _after_ the gtkpreview's expose routine. */
+static gint preview_expose_handler(GtkWidget *window, GdkEvent *event, gpointer data)
+{
+ Preview *p = data;
+
+ p->previous_selection.active = FALSE; /* ok, old selections are overpainted */
+ p->previous_selection_maximum.active = FALSE;
+ p->selection.active = TRUE; /* ok, old selections are overpainted */
+ p->selection_maximum.active = TRUE;
+ preview_draw_selection(p); /* draw selections again */
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static gint preview_event_handler(GtkWidget *window, GdkEvent *event, gpointer data)
+{
+ Preview *p = data;
+ GdkCursor *cursor;
+ GdkColor color;
+ GdkColormap *colormap;
+ float preview_selection[4];
+ float xscale, yscale;
+ static int event_count = 0;
+ int cursornr;
+
+ event_count++;
+
+ preview_get_scale_device_to_preview(p, &xscale, &yscale);
+
+ preview_selection[0] = xscale * (p->selection.coordinate[0] - p->surface[0]);
+ preview_selection[1] = yscale * (p->selection.coordinate[1] - p->surface[1]);
+ preview_selection[2] = xscale * (p->selection.coordinate[2] - p->surface[0]);
+ preview_selection[3] = yscale * (p->selection.coordinate[3] - p->surface[1]);
+
+ if (event->type == GDK_EXPOSE)
+ {
+ if (!p->gc_selection)
+ {
+ colormap = gdk_window_get_colormap(p->window->window);
+
+ p->gc_selection = gdk_gc_new(p->window->window);
+ gdk_gc_set_function(p->gc_selection, GDK_INVERT);
+ gdk_gc_set_line_attributes(p->gc_selection, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+
+ p->gc_selection_maximum = gdk_gc_new(p->window->window);
+ gdk_gc_set_function(p->gc_selection_maximum, GDK_XOR);
+ gdk_gc_set_line_attributes(p->gc_selection_maximum, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+ color.red = 0;
+ color.green = 65535;
+ color.blue = 30000;
+ gdk_color_alloc(colormap, &color);
+ gdk_gc_set_foreground(p->gc_selection_maximum, &color);
+
+ preview_paint_image(p);
+ }
+ else
+ {
+ while (gtk_events_pending()) /* make sure image is updated */
+ {
+ gtk_main_iteration();
+ }
+
+ p->previous_selection.active = FALSE; /* ok, old selections are overpainted */
+ p->previous_selection_maximum.active = FALSE;
+ preview_draw_selection(p); /* draw selections again */
+ }
+ }
+ else if (!p->scanning)
+ {
+ switch (event->type)
+ {
+ case GDK_UNMAP:
+ case GDK_MAP:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ switch (p->mode)
+ {
+ case MODE_PIPETTE_WHITE:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[2] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[2] = r/2.55;
+ xsane.slider_green.value[2] = g/2.55;
+ xsane.slider_blue.value[2] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[2] = xsane.slider_gray.value[2];
+ xsane.slider_green.value[2] = xsane.slider_gray.value[2];
+ xsane.slider_blue.value[2] = xsane.slider_gray.value[2];
+ }
+
+ if (xsane.slider_gray.value[2] < 2)
+ {
+ xsane.slider_gray.value[2] = 2;
+ }
+ if (xsane.slider_gray.value[1] >= xsane.slider_gray.value[2])
+ {
+ xsane.slider_gray.value[1] = xsane.slider_gray.value[2]-1;
+ if (xsane.slider_gray.value[0] >= xsane.slider_gray.value[1])
+ {
+ xsane.slider_gray.value[0] = xsane.slider_gray.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_red.value[2] < 2)
+ {
+ xsane.slider_red.value[2] = 2;
+ }
+ if (xsane.slider_red.value[1] >= xsane.slider_red.value[2])
+ {
+ xsane.slider_red.value[1] = xsane.slider_red.value[2]-1;
+ if (xsane.slider_red.value[0] >= xsane.slider_red.value[1])
+ {
+ xsane.slider_red.value[0] = xsane.slider_red.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_green.value[2] < 2)
+ {
+ xsane.slider_green.value[2] = 2;
+ }
+ if (xsane.slider_green.value[1] >= xsane.slider_green.value[2])
+ {
+ xsane.slider_green.value[1] = xsane.slider_green.value[2]-1;
+ if (xsane.slider_green.value[0] >= xsane.slider_green.value[1])
+ {
+ xsane.slider_green.value[0] = xsane.slider_green.value[1]-1;
+ }
+ }
+
+ if (xsane.slider_blue.value[2] < 2)
+ {
+ xsane.slider_blue.value[2] = 2;
+ }
+ if (xsane.slider_blue.value[1] >= xsane.slider_blue.value[2])
+ {
+ xsane.slider_blue.value[1] = xsane.slider_blue.value[2]-1;
+ if (xsane.slider_blue.value[0] >= xsane.slider_blue.value[1])
+ {
+ xsane.slider_blue.value[0] = xsane.slider_blue.value[1]-1;
+ }
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_PIPETTE_GRAY:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[1] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[1] = r/2.55;
+ xsane.slider_green.value[1] = g/2.55;
+ xsane.slider_blue.value[1] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_green.value[1] = xsane.slider_gray.value[1];
+ xsane.slider_blue.value[1] = xsane.slider_gray.value[1];
+ }
+
+ if (xsane.slider_gray.value[1] == 0)
+ {
+ xsane.slider_gray.value[1] += 1;
+ }
+ if (xsane.slider_gray.value[1] == 100)
+ {
+ xsane.slider_gray.value[1] -= 1;
+ }
+ if (xsane.slider_gray.value[1] >= xsane.slider_gray.value[2])
+ {
+ xsane.slider_gray.value[2] = xsane.slider_gray.value[1]+1;
+ }
+ if (xsane.slider_gray.value[1] <= xsane.slider_gray.value[0])
+ {
+ xsane.slider_gray.value[0] = xsane.slider_gray.value[1]-1;
+ }
+
+ if (xsane.slider_red.value[1] == 0)
+ {
+ xsane.slider_red.value[1] += 1;
+ }
+ if (xsane.slider_red.value[1] == 100)
+ {
+ xsane.slider_red.value[1] -= 1;
+ }
+ if (xsane.slider_red.value[1] >= xsane.slider_red.value[2])
+ {
+ xsane.slider_red.value[2] = xsane.slider_red.value[1]+1;
+ }
+ if (xsane.slider_red.value[1] <= xsane.slider_red.value[0])
+ {
+ xsane.slider_red.value[0] = xsane.slider_red.value[1]-1;
+ }
+
+ if (xsane.slider_green.value[1] == 0)
+ {
+ xsane.slider_green.value[1] += 1;
+ }
+ if (xsane.slider_green.value[1] == 100)
+ {
+ xsane.slider_green.value[1] -= 1;
+ }
+ if (xsane.slider_green.value[1] >= xsane.slider_green.value[2])
+ {
+ xsane.slider_green.value[2] = xsane.slider_green.value[1]+1;
+ }
+ if (xsane.slider_green.value[1] <= xsane.slider_green.value[0])
+ {
+ xsane.slider_green.value[0] = xsane.slider_green.value[1]-1;
+ }
+
+ if (xsane.slider_blue.value[1] == 0)
+ {
+ xsane.slider_blue.value[1] += 1;
+ }
+ if (xsane.slider_blue.value[1] == 100)
+ {
+ xsane.slider_blue.value[1] -= 1;
+ }
+ if (xsane.slider_blue.value[1] >= xsane.slider_blue.value[2])
+ {
+ xsane.slider_blue.value[2] = xsane.slider_blue.value[1]+1;
+ }
+ if (xsane.slider_blue.value[1] <= xsane.slider_blue.value[0])
+ {
+ xsane.slider_blue.value[0] = xsane.slider_blue.value[1]-1;
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_PIPETTE_BLACK:
+ {
+ if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) &&
+ (p->image_data_raw) ) /* left or middle button */
+ {
+ int r,g,b;
+
+ preview_get_color(p, event->button.x, event->button.y, &r, &g, &b);
+
+ xsane.slider_gray.value[0] = sqrt( (r*r+g*g+b*b) / 3)/2.55;
+
+ if ( (!xsane.enhancement_rgb_default) && (((GdkEventButton *)event)->button == 2) ) /* middle button */
+ {
+ xsane.slider_red.value[0] = r/2.55;
+ xsane.slider_green.value[0] = g/2.55;
+ xsane.slider_blue.value[0] = b/2.55;
+ }
+ else
+ {
+ xsane.slider_red.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_green.value[0] = xsane.slider_gray.value[0];
+ xsane.slider_blue.value[0] = xsane.slider_gray.value[0];
+ }
+
+ if (xsane.slider_gray.value[0] > 98)
+ {
+ xsane.slider_gray.value[0] = 98;
+ }
+ if (xsane.slider_gray.value[1] <= xsane.slider_gray.value[0])
+ {
+ xsane.slider_gray.value[1] = xsane.slider_gray.value[0]+1;
+ if (xsane.slider_gray.value[2] <= xsane.slider_gray.value[1])
+ {
+ xsane.slider_gray.value[2] = xsane.slider_gray.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_red.value[0] > 98)
+ {
+ xsane.slider_red.value[0] = 98;
+ }
+ if (xsane.slider_red.value[1] <= xsane.slider_red.value[0])
+ {
+ xsane.slider_red.value[1] = xsane.slider_red.value[0]+1;
+ if (xsane.slider_red.value[2] <= xsane.slider_red.value[1])
+ {
+ xsane.slider_red.value[2] = xsane.slider_red.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_green.value[0] > 98)
+ {
+ xsane.slider_green.value[0] = 98;
+ }
+ if (xsane.slider_green.value[1] <= xsane.slider_green.value[0])
+ {
+ xsane.slider_green.value[1] = xsane.slider_green.value[0]+1;
+ if (xsane.slider_green.value[2] <= xsane.slider_green.value[1])
+ {
+ xsane.slider_green.value[2] = xsane.slider_green.value[1]+1;
+ }
+ }
+
+ if (xsane.slider_blue.value[0] > 98)
+ {
+ xsane.slider_blue.value[0] = 98;
+ }
+ if (xsane.slider_blue.value[1] <= xsane.slider_blue.value[0])
+ {
+ xsane.slider_blue.value[1] = xsane.slider_blue.value[0]+1;
+ if (xsane.slider_blue.value[2] <= xsane.slider_blue.value[1])
+ {
+ xsane.slider_blue.value[2] = xsane.slider_blue.value[1]+1;
+ }
+ }
+
+ xsane_enhancement_by_histogram();
+ }
+
+ p->mode = MODE_NORMAL;
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW);
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+ }
+ break;
+
+ case MODE_NORMAL:
+ {
+ switch (((GdkEventButton *)event)->button)
+ {
+ case 1: /* left button */
+ p->selection_xedge = -1;
+ if ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) /* left */
+ {
+ p->selection_xedge = 0;
+ }
+ else if ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) /* right */
+ {
+ p->selection_xedge = 2;
+ }
+
+ p->selection_yedge = -1;
+ if ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) /* top */
+ {
+ p->selection_yedge = 1;
+ }
+ else if ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) /* bottom */
+ {
+ p->selection_yedge = 3;
+ }
+
+ if ( (p->selection_xedge != -1) && (p->selection_yedge != -1) ) /* move edge */
+ {
+ p->selection_drag_edge = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+ preview_draw_selection(p);
+ }
+ else /* select new area */
+ {
+ p->selection_xedge = 2;
+ p->selection_yedge = 3;
+ p->selection.coordinate[0] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[1] = p->surface[1] + event->button.y / yscale;
+ p->selection_drag = TRUE;
+
+ cursornr = GDK_CROSS;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ case 2: /* middle button */
+ case 3: /* right button */
+ if ( (preview_selection[0]-SELECTION_RANGE_OUT < event->button.x) &&
+ (preview_selection[2]+SELECTION_RANGE_OUT > event->button.x) &&
+ (preview_selection[1]-SELECTION_RANGE_OUT < event->button.y) &&
+ (preview_selection[3]+SELECTION_RANGE_OUT > event->button.y) )
+ {
+ p->selection_drag = TRUE;
+ p->selection_xpos = event->button.x;
+ p->selection_ypos = event->button.y;
+
+ cursornr = GDK_HAND2;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ switch (((GdkEventButton *)event)->button)
+ {
+ case 1: /* left button */
+ case 2: /* middle button */
+ case 3: /* right button */
+ if (p->selection_drag)
+ {
+ cursornr = XSANE_CURSOR_PREVIEW;
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+
+ if ( (p->selection_drag) || (p->selection_drag_edge) )
+ {
+
+ if (((GdkEventButton *)event)->button == 1) /* left button */
+ {
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+ }
+
+ p->selection_drag_edge = FALSE;
+ p->selection_drag = FALSE;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+ }
+ default:
+ break;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ switch (((GdkEventMotion *)event)->state)
+ {
+ case 256: /* left button */
+ if (p->selection_drag_edge)
+ {
+ p->selection.active = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->button.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->button.y / yscale;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+
+ if (p->selection_drag)
+ {
+ p->selection.active = TRUE;
+ p->selection.coordinate[p->selection_xedge] = p->surface[0] + event->motion.x / xscale;
+ p->selection.coordinate[p->selection_yedge] = p->surface[1] + event->motion.y / yscale;
+
+ preview_order_selection(p);
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+
+ cursornr = p->cursornr;
+
+ if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_RIGHT_CORNER;
+ }
+ else if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_RIGHT_CORNER;
+ }
+
+ if (cursornr != p->cursornr)
+ {
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+
+ case 512: /* middle button */
+ case 1024: /* right button */
+ if (p->selection_drag)
+ {
+ int dx, dy;
+
+ dx = p->selection_xpos - event->motion.x;
+ dy = p->selection_ypos - event->motion.y;
+
+ p->selection_xpos = event->motion.x;
+ p->selection_ypos = event->motion.y;
+
+ p->selection.active = TRUE;
+ p->selection.coordinate[0] -= dx / xscale;
+ p->selection.coordinate[1] -= dy / yscale;
+ p->selection.coordinate[2] -= dx / xscale;
+ p->selection.coordinate[3] -= dy / yscale;
+
+ preview_bound_selection(p);
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+
+ if ((preferences.gtk_update_policy == GTK_UPDATE_CONTINUOUS) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ else if ((preferences.gtk_update_policy == GTK_UPDATE_DELAYED) && (event_count == 1))
+ {
+ preview_establish_selection(p);
+ }
+ }
+ break;
+
+ default:
+ if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[1] - SELECTION_RANGE_OUT < event->button.y) && (event->button.y < preview_selection[1] + SELECTION_RANGE_IN) ) ) /* top */
+ {
+ cursornr = GDK_TOP_RIGHT_CORNER;
+ }
+ else if ( ( (preview_selection[0] - SELECTION_RANGE_OUT < event->button.x) && (event->button.x < preview_selection[0] + SELECTION_RANGE_IN) ) && /* left */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_LEFT_CORNER;
+ }
+ else if ( ( (preview_selection[2] - SELECTION_RANGE_IN < event->button.x) && (event->button.x < preview_selection[2] + SELECTION_RANGE_OUT) ) && /* right */
+ ( (preview_selection[3] - SELECTION_RANGE_IN < event->button.y) && (event->button.y < preview_selection[3] + SELECTION_RANGE_OUT) ) ) /* bottom */
+ {
+ cursornr = GDK_BOTTOM_RIGHT_CORNER;
+ }
+ else
+ {
+ cursornr = XSANE_CURSOR_PREVIEW;
+ }
+
+ if ((cursornr != p->cursornr) && (p->cursornr != -1))
+ {
+ cursor = gdk_cursor_new(cursornr); /* set curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = cursornr;
+ }
+ break;
+ }
+ break;
+
+ default:
+#if 0
+ fprintf(stderr, "preview_event_handler: unhandled event type %d\n", event->type);
+#endif
+ break;
+ }
+ }
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+
+ event_count--;
+
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_start_button_clicked(GtkWidget *widget, gpointer data)
+{
+ preview_scan(data);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_cancel_button_clicked(GtkWidget *widget, gpointer data)
+{
+ preview_scan_done(data);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+Preview *preview_new(GSGDialog *dialog)
+{
+ static int first_time = 1;
+ GtkWidget *table, *frame;
+ GtkSignalFunc signal_func;
+ GtkWidgetClass *class;
+ GtkBox *vbox, *hbox;
+ GdkCursor *cursor;
+ GtkWidget *preset_area_option_menu, *preset_area_menu, *preset_area_item;
+ Preview *p;
+ int i;
+ char buf[256];
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ {
+ return 0;
+ }
+ memset(p, 0, sizeof(*p));
+
+ p->mode = MODE_NORMAL; /* no pipette functions etc */
+ p->dialog = dialog;
+ p->input_tag = -1;
+
+ if (first_time)
+ {
+ first_time = 0;
+ gtk_preview_set_gamma(1.0);
+ gtk_preview_set_install_cmap(preferences.preview_own_cmap);
+ }
+
+ p->preset_width = INF; /* use full scanarea */
+ p->preset_height = INF; /* use full scanarea */
+
+ p->maximum_output_width = INF; /* full output with */
+ p->maximum_output_height = INF; /* full output height */
+
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_push_visual(gtk_preview_get_visual());
+#endif
+ gtk_widget_push_colormap(gtk_preview_get_cmap());
+
+ snprintf(buf, sizeof(buf), "%s %s", WINDOW_PREVIEW, device_text);
+ p->top = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(p->top), buf);
+ vbox = GTK_BOX(GTK_DIALOG(p->top)->vbox);
+ hbox = GTK_BOX(GTK_DIALOG(p->top)->action_area);
+
+ xsane_set_window_icon(p->top, 0);
+
+ /* top hbox for pipette buttons */
+ p->button_box = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(p->button_box), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), p->button_box, FALSE, FALSE, 0);
+
+ /* White, gray and black pipette button */
+ p->pipette_white = xsane_button_new_with_pixmap(p->button_box, pipette_white_xpm, DESC_PIPETTE_WHITE, (GtkSignalFunc) preview_pipette_white, p);
+ p->pipette_gray = xsane_button_new_with_pixmap(p->button_box, pipette_gray_xpm, DESC_PIPETTE_GRAY, (GtkSignalFunc) preview_pipette_gray, p);
+ p->pipette_black = xsane_button_new_with_pixmap(p->button_box, pipette_black_xpm, DESC_PIPETTE_BLACK, (GtkSignalFunc) preview_pipette_black, p);
+
+ /* Zoom not, zoom out and zoom in button */
+ p->zoom_not = xsane_button_new_with_pixmap(p->button_box, zoom_not_xpm, DESC_ZOOM_FULL, (GtkSignalFunc) preview_zoom_not, p);
+ p->zoom_out = xsane_button_new_with_pixmap(p->button_box, zoom_out_xpm, DESC_ZOOM_OUT, (GtkSignalFunc) preview_zoom_out, p);
+ p->zoom_in = xsane_button_new_with_pixmap(p->button_box, zoom_in_xpm, DESC_ZOOM_IN, (GtkSignalFunc) preview_zoom_in, p);
+ p->zoom_undo = xsane_button_new_with_pixmap(p->button_box, zoom_undo_xpm, DESC_ZOOM_UNDO, (GtkSignalFunc) preview_zoom_undo, p);
+
+ gtk_widget_set_sensitive(p->zoom_not, FALSE); /* no zoom at this point, so no zoom not */
+ gtk_widget_set_sensitive(p->zoom_out, FALSE); /* no zoom at this point, so no zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo, FALSE); /* no zoom at this point, so no zoom undo */
+
+
+
+ xsane_button_new_with_pixmap(p->button_box, full_preview_area_xpm, DESC_FULL_PREVIEW_AREA,
+ (GtkSignalFunc) preview_full_preview_area, p);
+
+ /* select maximum scanarea */
+ preset_area_menu = gtk_menu_new();
+
+ for (i = 0; i < PRESET_AREA_ITEMS; ++i)
+ {
+ preset_area_item = gtk_menu_item_new_with_label(preset_area[i].name);
+ gtk_container_add(GTK_CONTAINER(preset_area_menu), preset_area_item);
+ gtk_signal_connect(GTK_OBJECT(preset_area_item), "activate", (GtkSignalFunc) preview_preset_area_callback, p);
+ gtk_object_set_data(GTK_OBJECT(preset_area_item), "Selection", (void *) i);
+
+ gtk_widget_show(preset_area_item);
+ }
+
+ preset_area_option_menu = gtk_option_menu_new();
+ gtk_box_pack_start(GTK_BOX(p->button_box), preset_area_option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(preset_area_option_menu), preset_area_menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(preset_area_option_menu), 0); /* full area */
+/* xsane_back_gtk_set_tooltip(tooltips, preset_area_option_menu, desc); */
+
+ gtk_widget_show(preset_area_option_menu);
+ p->preset_area_option_menu = preset_area_option_menu;
+
+ gtk_widget_show(p->button_box);
+
+
+
+ /* construct the preview area (table with sliders & preview window) */
+ table = gtk_table_new(2, 2, /* homogeneous */ FALSE);
+ gtk_table_set_col_spacing(GTK_TABLE(table), 0, 1);
+ gtk_table_set_row_spacing(GTK_TABLE(table), 0, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 2);
+ gtk_box_pack_start(vbox, table, /* expand */ TRUE, /* fill */ TRUE, /* padding */ 0);
+
+ /* the empty box in the top-left corner */
+ frame = gtk_frame_new(/* label */ 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
+ gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ /* the horizontal ruler */
+ p->hruler = gtk_hruler_new();
+ gtk_table_attach(GTK_TABLE(table), p->hruler, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
+
+ /* the vertical ruler */
+ p->vruler = gtk_vruler_new();
+ gtk_table_attach(GTK_TABLE(table), p->vruler, 0, 1, 1, 2, 0, GTK_FILL, 0, 0);
+
+ /* the preview area */
+
+ p->window = gtk_preview_new(GTK_PREVIEW_COLOR);
+ gtk_preview_set_expand(GTK_PREVIEW(p->window), TRUE);
+ gtk_widget_set_events(p->window,
+ GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_signal_connect(GTK_OBJECT(p->window), "event", (GtkSignalFunc) preview_event_handler, p);
+ gtk_signal_connect_after(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_handler, p);
+ gtk_signal_connect_after(GTK_OBJECT(p->window), "size_allocate", (GtkSignalFunc) preview_area_resize, 0);
+ gtk_object_set_data(GTK_OBJECT(p->window), "PreviewPointer", p);
+
+ /* Connect the motion-notify events of the preview area with the rulers. Nifty stuff! */
+
+ class = GTK_WIDGET_CLASS(GTK_OBJECT(p->hruler)->klass);
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object(GTK_OBJECT(p->window), "motion_notify_event", signal_func, GTK_OBJECT(p->hruler));
+
+ class = GTK_WIDGET_CLASS(GTK_OBJECT(p->vruler)->klass);
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object(GTK_OBJECT(p->window), "motion_notify_event", signal_func, GTK_OBJECT(p->vruler));
+
+ p->viewport = gtk_frame_new(/* label */ 0);
+ gtk_frame_set_shadow_type(GTK_FRAME(p->viewport), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(p->viewport), p->window);
+
+ gtk_table_attach(GTK_TABLE(table), p->viewport, 1, 2, 1, 2,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
+
+ preview_update_surface(p, 0);
+
+ /* fill in action area: */
+
+ /* Start button */
+ p->start = gtk_button_new_with_label(BUTTON_PREVIEW_ACQUIRE);
+ gtk_signal_connect(GTK_OBJECT(p->start), "clicked", (GtkSignalFunc) preview_start_button_clicked, p);
+ gtk_box_pack_start(GTK_BOX(hbox), p->start, TRUE, TRUE, 0);
+
+ /* Cancel button */
+ p->cancel = gtk_button_new_with_label(BUTTON_PREVIEW_CANCEL);
+ gtk_signal_connect(GTK_OBJECT(p->cancel), "clicked", (GtkSignalFunc) preview_cancel_button_clicked, p);
+ gtk_box_pack_start(GTK_BOX(hbox), p->cancel, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(p->cancel, FALSE);
+
+ gtk_widget_show(p->cancel);
+ gtk_widget_show(p->start);
+ gtk_widget_show(p->viewport);
+ gtk_widget_show(p->window);
+ gtk_widget_show(p->hruler);
+ gtk_widget_show(p->vruler);
+ gtk_widget_show(frame);
+ gtk_widget_show(table);
+ gtk_widget_show(p->top);
+
+ cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); /* set default curosr */
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = XSANE_CURSOR_PREVIEW;
+
+ gtk_widget_pop_colormap();
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_pop_visual();
+#endif
+ return p;
+}
+
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_area_correct(Preview *p)
+{
+ float width, height, max_width, max_height;
+
+ width = p->preview_width;
+ height = p->preview_height;
+ max_width = p->preview_window_width;
+ max_height = p->preview_window_height;
+
+ width = max_width;
+ height = width / p->aspect;
+
+ if (height > max_height)
+ {
+ height = max_height;
+ width = height * p->aspect;
+ }
+
+ p->preview_width = width + 0.5;
+ p->preview_height = height + 0.5;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_update_surface(Preview *p, int surface_changed)
+{
+ float val;
+ float width, height;
+ float max_width, max_height;
+ float preset_width, preset_height;
+ const SANE_Option_Descriptor *opt;
+ int i;
+ SANE_Value_Type type;
+ SANE_Unit unit;
+ double min, max;
+
+ unit = SANE_UNIT_PIXEL;
+ type = SANE_TYPE_INT;
+
+ for (i = 0; i < 4; ++i) /* test if surface (max vals of scanarea) has changed */
+ {
+/* val = (i & 2) ? INF : -INF; */
+ val = (i & 2) ? INF : 0;
+
+ if (p->dialog->well_known.coord[i] > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.coord[i]);
+ assert(opt->unit == SANE_UNIT_PIXEL || opt->unit == SANE_UNIT_MM);
+ unit = opt->unit;
+ type = opt->type;
+
+ xsane_get_bounds(opt, &min, &max);
+
+ if (i & 2)
+ {
+ val = max;
+ }
+ else
+ {
+ val = min;
+ }
+ }
+
+ if (p->max_scanner_surface[i] != val)
+ {
+ surface_changed = 2;
+ p->max_scanner_surface[i] = val;
+ }
+ }
+
+ if (surface_changed == 2) /* redefine all surface subparts */
+ {
+ for (i = 0; i < 4; i++)
+ {
+ val = p->max_scanner_surface[i];
+ p->scanner_surface[i] = val;
+ p->surface[i] = val;
+ p->image_surface[i] = val;
+ }
+ }
+
+ max_width = p->max_scanner_surface[xsane_back_gtk_BR_X] - p->max_scanner_surface[xsane_back_gtk_TL_X];
+ max_height = p->max_scanner_surface[xsane_back_gtk_BR_Y] - p->max_scanner_surface[xsane_back_gtk_TL_Y];
+
+ width = p->scanner_surface[xsane_back_gtk_BR_X] - p->scanner_surface[xsane_back_gtk_TL_X];
+ height = p->scanner_surface[xsane_back_gtk_BR_Y] - p->scanner_surface[xsane_back_gtk_TL_Y];
+
+ preset_width = p->preset_width;
+ preset_height = p->preset_height;
+
+ if (preset_width > max_width)
+ {
+ preset_width = max_width;
+ }
+
+ if (preset_height > max_height)
+ {
+ preset_height = max_height;
+ }
+
+ if ( (width != preset_width) || (height != preset_height) )
+ {
+ p->scanner_surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+ p->surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+ p->image_surface[xsane_back_gtk_TL_X] = p->scanner_surface[xsane_back_gtk_TL_X];
+
+ p->scanner_surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+ p->surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+ p->image_surface[xsane_back_gtk_BR_X] = p->scanner_surface[xsane_back_gtk_TL_X] + preset_width;
+
+ p->scanner_surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+ p->surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+ p->image_surface[xsane_back_gtk_TL_Y] = p->scanner_surface[xsane_back_gtk_TL_Y];
+
+ p->scanner_surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+ p->surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+ p->image_surface[xsane_back_gtk_BR_Y] = p->scanner_surface[xsane_back_gtk_TL_Y] + preset_height;
+
+ surface_changed = 1;
+ }
+
+ if (p->surface_unit != unit)
+ {
+ surface_changed = 1;
+ p->surface_unit = unit;
+ }
+
+ if (p->surface_unit == SANE_UNIT_MM)
+ {
+ gtk_widget_set_sensitive(p->preset_area_option_menu, TRUE); /* enable preset area */
+ }
+ else
+ {
+ gtk_widget_set_sensitive(p->preset_area_option_menu, FALSE); /* disable preset area */
+ }
+
+ if (p->surface_type != type)
+ {
+ surface_changed = 1;
+ p->surface_type = type;
+ }
+
+ if (surface_changed)
+ {
+ /* guess the initial preview window size: */
+
+ width = p->surface[xsane_back_gtk_BR_X] - p->surface[xsane_back_gtk_TL_X];
+ height = p->surface[xsane_back_gtk_BR_Y] - p->surface[xsane_back_gtk_TL_Y];
+
+ if (p->surface_type == SANE_TYPE_INT)
+ {
+ width += 1.0;
+ height += 1.0;
+ }
+ else
+ {
+ width += SANE_UNFIX(1.0);
+ height += SANE_UNFIX(1.0);
+ }
+
+ assert(width > 0.0 && height > 0.0);
+
+ if (width >= INF || height >= INF)
+ {
+ p->aspect = 1.0;
+ }
+ else
+ {
+ p->aspect = width/height;
+ }
+ }
+ else if ( (p->image_height) && (p->image_width) )
+ {
+ p->aspect = p->image_width/(float) p->image_height;
+ }
+
+ if ( (surface_changed) && (p->preview_window_width == 0) )
+ {
+ p->preview_window_width = 0.5 * gdk_screen_width();
+ p->preview_window_height = 0.5 * gdk_screen_height();
+ }
+
+ preview_area_correct(p);
+
+ if (surface_changed)
+ {
+ gtk_widget_set_usize(GTK_WIDGET(p->window), p->preview_width, p->preview_height);
+ /* preview_area_resize is automatically called by signal handler */
+
+ preview_bound_selection(p); /* make sure selection is not larger than surface */
+ preview_restore_image(p); /* draw selected surface of the image */
+ }
+ else
+ {
+ preview_update_selection(p);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_scan(Preview *p)
+{
+ double min, max, swidth, sheight, width, height, dpi = 0;
+ const SANE_Option_Descriptor *opt;
+ gint gwidth, gheight;
+ int i;
+
+ xsane.block_update_param = TRUE; /* do not change parameters each time */
+
+ preview_save_option(p, p->dialog->well_known.dpi, &p->saved_dpi, &p->saved_dpi_valid);
+ preview_save_option(p, p->dialog->well_known.dpi_x, &p->saved_dpi_x, &p->saved_dpi_x_valid);
+ preview_save_option(p, p->dialog->well_known.dpi_y, &p->saved_dpi_y, &p->saved_dpi_y_valid);
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_save_option(p, p->dialog->well_known.coord[i], &p->saved_coord[i], p->saved_coord_valid + i);
+ }
+ preview_save_option(p, p->dialog->well_known.bit_depth, &p->saved_bit_depth, &p->saved_bit_depth_valid);
+
+ /* determine dpi, if necessary: */
+
+ if (p->dialog->well_known.dpi > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, p->dialog->well_known.dpi);
+
+ gwidth = p->preview_width;
+ gheight = p->preview_height;
+
+ height = gheight;
+ width = height * p->aspect;
+
+ if (width > gwidth)
+ {
+ width = gwidth;
+ height = width / p->aspect;
+ }
+
+ swidth = (p->surface[xsane_back_gtk_BR_X] - p->surface[xsane_back_gtk_TL_X]);
+
+ if (swidth < INF)
+ {
+ dpi = MM_PER_INCH * width/swidth;
+ }
+ else
+ {
+ sheight = (p->surface[xsane_back_gtk_BR_Y] - p->surface[xsane_back_gtk_TL_Y]);
+ if (sheight < INF)
+ {
+ dpi = MM_PER_INCH * height/sheight;
+ }
+ else
+ {
+ dpi = 18.0;
+ }
+ }
+
+ xsane_get_bounds(opt, &min, &max);
+
+ if (dpi < min)
+ {
+ dpi = min;
+ }
+
+ if (dpi > max)
+ {
+ dpi = max;
+ }
+
+ xsane_set_resolution(p->dialog->well_known.dpi, dpi); /* set resolution to dpi or next higher value that is available */
+ xsane_set_resolution(p->dialog->well_known.dpi_x, dpi); /* set resolution to dpi or next higher value that is available */
+ xsane_set_resolution(p->dialog->well_known.dpi_y, dpi); /* set resolution to dpi or next higher value that is available */
+ }
+
+ /* set the scan window (necessary since backends may default to non-maximum size): */
+
+ for (i = 0; i < 4; ++i)
+ {
+ preview_set_option_float(p, p->dialog->well_known.coord[i], p->surface[i]);
+ }
+
+ preview_set_option_bool(p, p->dialog->well_known.preview, SANE_TRUE);
+
+ if ( (p->saved_bit_depth > 8) && (p->saved_bit_depth_valid) ) /* don't scan with more than 8bpp */
+ {
+ preview_set_option_int(p, p->dialog->well_known.bit_depth, 8);
+ }
+
+ xsane.block_update_param = FALSE;
+
+ /* OK, all set to go */
+ preview_scan_start(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_image_file(Preview *p, FILE *out)
+{
+ if (out)
+ {
+ /* always save it as a PPM image: */
+ fprintf(out, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255\n",
+ p->surface[0], p->surface[1], p->surface[2], p->surface[3],
+ p->surface_type, p->surface_unit, p->image_width, p->image_height);
+
+ fwrite(p->image_data_raw, 3, p->image_width*p->image_height, out);
+ fclose(out);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_save_image(Preview *p)
+{
+ char filename[PATH_MAX];
+ FILE *out;
+ int status;
+
+ if (!p->image_data_enh)
+ {
+ return;
+ }
+
+ if ( GROSSLY_EQUAL(p->max_scanner_surface[0], p->surface[0]) && /* full device surface */
+ GROSSLY_EQUAL(p->max_scanner_surface[1], p->surface[1]) &&
+ GROSSLY_EQUAL(p->max_scanner_surface[2], p->surface[2]) &&
+ GROSSLY_EQUAL(p->max_scanner_surface[3], p->surface[3]) )
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 0);
+ }
+ else if ( GROSSLY_EQUAL(p->scanner_surface[0], p->surface[0]) && /* user defined surface */
+ GROSSLY_EQUAL(p->scanner_surface[1], p->surface[1]) &&
+ GROSSLY_EQUAL(p->scanner_surface[2], p->surface[2]) &&
+ GROSSLY_EQUAL(p->scanner_surface[3], p->surface[3]) )
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 1);
+ }
+ else /* zoom area */
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, 2);
+ }
+
+ if (status >= 0)
+ {
+ /* save preview image */
+ remove(filename); /* remove existing preview */
+ umask(0177); /* creare temporary file with "-rw-------" permissions */
+ out = fopen(filename, "w");
+ umask(XSANE_DEFAULT_UMASK); /* define new file permissions */
+
+ preview_save_image_file(p, out);
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_destroy(Preview *p)
+{
+ int level;
+ int status;
+ char filename[PATH_MAX];
+
+ if (p->scanning)
+ {
+ preview_scan_done(p); /* don't save partial window */
+ }
+ else
+ {
+ preview_save_image(p);
+ }
+
+ if (!preferences.preserve_preview)
+ {
+ for(level = 0; level <= 2; level++)
+ {
+ status = preview_make_image_path(p, sizeof(filename), filename, level);
+ if (status >= 0)
+ {
+ remove(filename); /* remove existing preview */
+ }
+ }
+ }
+
+ if (p->image_data_enh)
+ {
+ free(p->image_data_enh);
+ p->image_data_enh = 0;
+ }
+
+ if (p->image_data_raw)
+ {
+ free(p->image_data_raw);
+ p->image_data_raw = 0;
+ }
+
+ if (p->preview_row)
+ {
+ free(p->preview_row);
+ p->preview_row = 0;
+ }
+
+ if (p->gc_selection)
+ {
+ gdk_gc_destroy(p->gc_selection);
+ }
+
+ if (p->gc_selection_maximum)
+ {
+ gdk_gc_destroy(p->gc_selection_maximum);
+ }
+
+ if (p->top)
+ {
+ gtk_widget_destroy(p->top);
+ }
+ free(p);
+
+ p = 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_not(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ p->surface[i] = p->scanner_surface[i];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, FALSE); /* forbid unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, FALSE); /* forbid zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_out(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+ float delta_width = (p->surface[2] - p->surface[0]) * 0.2;
+ float delta_height = (p->surface[3] - p->surface[1]) * 0.2;
+
+ for (i=0; i<4; i++)
+ {
+ p->old_surface[i] = p->surface[i];
+ }
+
+ p->surface[0] -= delta_width;
+ p->surface[1] -= delta_height;
+ p->surface[2] += delta_width;
+ p->surface[3] += delta_height;
+
+ if (p->surface[0] < p->scanner_surface[0])
+ {
+ p->surface[0] = p->scanner_surface[0];
+ }
+
+ if (p->surface[1] < p->scanner_surface[1])
+ {
+ p->surface[1] = p->scanner_surface[1];
+ }
+
+ if (p->surface[2] > p->scanner_surface[2])
+ {
+ p->surface[2] = p->scanner_surface[2];
+ }
+
+ if (p->surface[3] > p->scanner_surface[3])
+ {
+ p->surface[3] = p->scanner_surface[3];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_in(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ int i, optnum;
+
+ for (i=0; i<4; i++)
+ {
+ p->old_surface[i] = p->surface[i];
+
+ optnum = p->dialog->well_known.coord[i];
+ if (optnum > 0)
+ {
+ opt = sane_get_option_descriptor(p->dialog->dev, optnum);
+ status = sane_control_option(p->dialog->dev, optnum, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue;
+ }
+
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ p->surface[i] = SANE_UNFIX(val);
+ }
+ else
+ {
+ p->surface[i] = val;
+ }
+ }
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, TRUE); /* allow zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo,TRUE); /* allow zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_zoom_undo(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ p->surface[i] = p->old_surface[i];
+ }
+
+ preview_update_surface(p, 1);
+ gtk_widget_set_sensitive(p->zoom_not, TRUE); /* allow unzoom */
+ gtk_widget_set_sensitive(p->zoom_out, TRUE); /* allow zoom out */
+ gtk_widget_set_sensitive(p->zoom_undo, FALSE); /* forbid zoom undo */
+
+ while (gtk_events_pending()) /* make sure all selection draw is done now */
+ {
+ gtk_main_iteration();
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_get_color(Preview *p, int x, int y, int *red, int *green, int *blue)
+{
+ int image_x, image_y;
+ float xscale_p2i, yscale_p2i;
+ int offset;
+
+ if (p->image_data_raw)
+ {
+ preview_get_scale_preview_to_image(p, &xscale_p2i, &yscale_p2i);
+
+ image_x = x * xscale_p2i;
+ image_y = y * yscale_p2i;
+
+ offset = 3 * (image_y * p->image_width + image_x);
+
+ if (!xsane.negative) /* positive */
+ {
+ *red = p->image_data_raw[offset ];
+ *green = p->image_data_raw[offset + 1];
+ *blue = p->image_data_raw[offset + 2];
+ }
+ else /* negative */
+ {
+ *red = 255 - p->image_data_raw[offset ];
+ *green = 255 - p->image_data_raw[offset + 1];
+ *blue = 255 - p->image_data_raw[offset + 2];
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_white(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_WHITE;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_white, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_gray(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_GRAY;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_gray, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_pipette_black(GtkWidget *window, gpointer data)
+{
+ Preview *p=data;
+ GdkCursor *cursor;
+ GdkColor fg;
+ GdkColor bg;
+ GdkPixmap *pixmap;
+ GdkPixmap *mask;
+
+ p->mode = MODE_PIPETTE_BLACK;
+
+ pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_black, CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+ mask = gdk_bitmap_create_from_data(p->top->window, cursor_pipette_mask , CURSOR_PIPETTE_WIDTH, CURSOR_PIPETTE_HEIGHT);
+
+ fg.red = 0;
+ fg.green = 0;
+ fg.blue = 0;
+
+ bg.red = 65535;
+ bg.green = 65535;
+ bg.blue = 65535;
+
+ cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &fg, &bg, CURSOR_PIPETTE_HOT_X, CURSOR_PIPETTE_HOT_Y);
+
+ gdk_window_set_cursor(p->window->window, cursor);
+ gdk_cursor_destroy(cursor);
+ p->cursornr = -1;
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_full_preview_area(GtkWidget *widget, gpointer call_data)
+{
+ Preview *p = call_data;
+ int i;
+
+ p->selection.active = TRUE;
+
+ for (i=0; i<4; i++)
+ {
+ p->selection.coordinate[i] = p->surface[i];
+ }
+
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data)
+{
+ Preview *p = call_data;
+ int selection;
+
+ selection = (int) gtk_object_get_data(GTK_OBJECT(widget), "Selection");
+
+ p->preset_width = preset_area[selection].width;
+ p->preset_height = preset_area[selection].height;
+
+ preview_update_surface(p, 0);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_do_gamma_correction(Preview *p)
+{
+ int x,y;
+ int offset;
+
+ if (p->image_data_raw)
+ {
+ if ((p->image_data_raw) && (p->params.depth > 1) && (preview_gamma_data_red))
+ {
+ for (y=0; y < p->image_height; y++)
+ {
+ for (x=0; x < p->image_width; x++)
+ {
+ offset = 3 * (y * p->image_width + x);
+ p->image_data_enh[offset ] = preview_gamma_data_red [p->image_data_raw[offset ]];
+ p->image_data_enh[offset + 1] = preview_gamma_data_green[p->image_data_raw[offset + 1]];
+ p->image_data_enh[offset + 2] = preview_gamma_data_blue [p->image_data_raw[offset + 2]];
+ }
+ }
+ }
+
+ preview_display_partial_image(p);
+
+ p->previous_selection.active = FALSE; /* previous selection is not drawn */
+ p->previous_selection_maximum.active = FALSE;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_calculate_histogram(Preview *p,
+ SANE_Int *count_raw, SANE_Int *count_raw_red, SANE_Int *count_raw_green, SANE_Int *count_raw_blue,
+ SANE_Int *count, SANE_Int *count_red, SANE_Int *count_green, SANE_Int *count_blue)
+{
+ int x, y;
+ int offset;
+ SANE_Int red_raw, green_raw, blue_raw;
+ SANE_Int red, green, blue;
+ SANE_Int min_x, max_x, min_y, max_y;
+ float xscale, yscale;
+
+ preview_get_scale_device_to_image(p, &xscale, &yscale);
+
+ min_x = (p->selection.coordinate[0] - p->surface[0]) * xscale;
+ min_y = (p->selection.coordinate[1] - p->surface[1]) * yscale;
+ max_x = (p->selection.coordinate[2] - p->surface[0]) * xscale;
+ max_y = (p->selection.coordinate[3] - p->surface[1]) * yscale;
+
+ if (min_x < 0)
+ {
+ min_x = 0;
+ }
+
+ if (max_x >= p->image_width)
+ {
+ max_x = p->image_width-1;
+ }
+
+ if (min_y < 0)
+ {
+ min_y = 0;
+ }
+
+ if (max_y >= p->image_height)
+ {
+ max_y = p->image_height-1;
+ }
+
+ if ((p->image_data_raw) && (p->params.depth > 1) && (preview_gamma_data_red))
+ {
+ for (y = min_y; y <= max_y; y++)
+ {
+ for (x = min_x; x <= max_x; x++)
+ {
+ offset = 3 * (y * p->image_width + x);
+ red_raw = p->image_data_raw[offset ];
+ green_raw = p->image_data_raw[offset + 1];
+ blue_raw = p->image_data_raw[offset + 2];
+
+ red = histogram_gamma_data_red [red_raw];
+ green = histogram_gamma_data_green[green_raw];
+ blue = histogram_gamma_data_blue [blue_raw];
+
+/* count_raw [(int) sqrt((red_raw*red_raw + green_raw*green_raw + blue_raw*blue_raw)/3.0)]++; */
+ count_raw [(int) ((red_raw + green_raw + blue_raw)/3)]++;
+ count_raw_red [red_raw]++;
+ count_raw_green[green_raw]++;
+ count_raw_blue [blue_raw]++;
+
+/* count [(int) sqrt((red*red + green*green + blue*blue)/3.0)]++; */
+ count [(int) ((red + green + blue)/3)]++;
+ count_red [red]++;
+ count_green[green]++;
+ count_blue [blue]++;
+ }
+ }
+ }
+ else /* no preview image => all colors = 1 */
+ {
+ int i;
+
+ for (i = 1; i <= 254; i++)
+ {
+ count_raw [i] = 0;
+ count_raw_red [i] = 0;
+ count_raw_green[i] = 0;
+ count_raw_blue [i] = 0;
+
+ count [i] = 0;
+ count_red [i] = 0;
+ count_green[i] = 0;
+ count_blue [i] = 0;
+ }
+
+ count_raw [0] = 10;
+ count_raw_red [0] = 10;
+ count_raw_green[0] = 10;
+ count_raw_blue [0] = 10;
+
+ count [0] = 10;
+ count_red [0] = 10;
+ count_green[0] = 10;
+ count_blue [0] = 10;
+
+ count_raw [255] = 10;
+ count_raw_red [255] = 10;
+ count_raw_green[255] = 10;
+ count_raw_blue [255] = 10;
+
+ count [255] = 10;
+ count_red [255] = 10;
+ count_green[255] = 10;
+ count_blue [255] = 10;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_gamma_correction(Preview *p,
+ SANE_Int *gamma_red, SANE_Int *gamma_green, SANE_Int *gamma_blue,
+ SANE_Int *gamma_red_hist, SANE_Int *gamma_green_hist, SANE_Int *gamma_blue_hist)
+{
+ preview_gamma_data_red = gamma_red;
+ preview_gamma_data_green = gamma_green;
+ preview_gamma_data_blue = gamma_blue;
+
+ histogram_gamma_data_red = gamma_red_hist;
+ histogram_gamma_data_green = gamma_green_hist;
+ histogram_gamma_data_blue = gamma_blue_hist;
+
+ preview_do_gamma_correction(p);
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_area_resize(GtkWidget *widget)
+{
+ float min_x, max_x, delta_x;
+ float min_y, max_y, delta_y;
+ float xscale, yscale, f;
+ Preview *p;
+
+ p = gtk_object_get_data(GTK_OBJECT(widget), "PreviewPointer");
+
+ p->preview_window_width = widget->allocation.width;
+ p->preview_window_height = widget->allocation.height;
+
+ p->preview_width = widget->allocation.width;
+ p->preview_height = widget->allocation.height;
+
+ preview_area_correct(p); /* set preview dimensions (with right aspect) that they fit into the window */
+
+ if (p->preview_row) /* make sure preview_row is large enough for one line of the new size */
+ {
+ p->preview_row = realloc(p->preview_row, 3 * p->preview_window_width);
+ }
+ else
+ {
+ p->preview_row = malloc(3 * p->preview_window_width);
+ }
+
+ /* set the ruler ranges: */
+
+ min_x = p->surface[xsane_back_gtk_TL_X];
+ if (min_x <= -INF)
+ {
+ min_x = 0.0;
+ }
+
+ max_x = p->surface[xsane_back_gtk_BR_X];
+ if (max_x >= INF)
+ {
+ max_x = p->image_width - 1;
+ }
+
+ min_y = p->surface[xsane_back_gtk_TL_Y];
+ if (min_y <= -INF)
+ {
+ min_y = 0.0;
+ }
+
+ max_y = p->surface[xsane_back_gtk_BR_Y];
+ if (max_y >= INF)
+ {
+ max_y = p->image_height - 1;
+ }
+
+ /* convert mm to inches if that's what the user wants: */
+
+ if (p->surface_unit == SANE_UNIT_MM)
+ {
+ double factor = 1.0/preferences.length_unit;
+
+ min_x *= factor;
+ max_x *= factor;
+ min_y *= factor;
+ max_y *= factor;
+ }
+
+ preview_get_scale_preview_to_image(p, &xscale, &yscale);
+
+ if (p->image_width > 0)
+ {
+ f = xscale * p->preview_width / p->image_width;
+ }
+ else
+ {
+ f = 1.0;
+ }
+
+ min_x *= f;
+ max_x *= f;
+ delta_x = max_x - min_x;
+
+ gtk_ruler_set_range(GTK_RULER(p->hruler), min_x, min_x + delta_x*p->preview_window_width/p->preview_width,
+ min_x, /* max_size */ 20);
+
+ if (p->image_height > 0)
+ {
+ f = yscale * p->preview_height / p->image_height;
+ }
+ else
+ {
+ f = 1.0;
+ }
+
+ min_y *= f;
+ max_y *= f;
+ delta_y = max_y - min_y;
+
+ gtk_ruler_set_range(GTK_RULER(p->vruler), min_y, min_y + delta_y*p->preview_window_height/p->preview_height,
+ min_y, /* max_size */ 20);
+
+ preview_paint_image(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_update_maximum_output_size(Preview *p)
+{
+ if ( (p->maximum_output_width >= INF) || (p->maximum_output_height >= INF) )
+ {
+ if (p->selection_maximum.active)
+ {
+ p->selection_maximum.active = FALSE;
+ }
+ }
+ else
+ {
+ p->previous_selection_maximum = p->selection_maximum;
+
+ p->selection_maximum.active = TRUE;
+ p->selection_maximum.coordinate[0] = p->selection.coordinate[0];
+ p->selection_maximum.coordinate[1] = p->selection.coordinate[1];
+ p->selection_maximum.coordinate[2] = p->selection.coordinate[0] + p->maximum_output_width;
+ p->selection_maximum.coordinate[3] = p->selection.coordinate[1] + p->maximum_output_height;
+
+ if (p->selection_maximum.coordinate[2] > p->max_scanner_surface[2])
+ {
+ p->selection_maximum.coordinate[2] = p->max_scanner_surface[2];
+ }
+
+ if (p->selection_maximum.coordinate[3] > p->max_scanner_surface[3])
+ {
+ p->selection_maximum.coordinate[3] = p->max_scanner_surface[3];
+ }
+
+ if ( (p->selection.coordinate[0] < p->selection_maximum.coordinate[0]) ||
+ (p->selection.coordinate[1] < p->selection_maximum.coordinate[1]) ||
+ (p->selection.coordinate[2] > p->selection_maximum.coordinate[2]) ||
+ (p->selection.coordinate[3] > p->selection_maximum.coordinate[3]) )
+ {
+ if (p->selection.coordinate[2] > p->selection_maximum.coordinate[2])
+ {
+ p->selection.coordinate[2] = p->selection_maximum.coordinate[2];
+ }
+
+ if (p->selection.coordinate[3] > p->selection_maximum.coordinate[3])
+ {
+ p->selection.coordinate[3] = p->selection_maximum.coordinate[3];
+ }
+ preview_draw_selection(p);
+ preview_establish_selection(p);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void preview_set_maximum_output_size(Preview *p, float width, float height)
+{
+ /* witdh and height in device units */
+
+ p->maximum_output_width = width;
+ p->maximum_output_height = height;
+
+ preview_update_maximum_output_size(p);
+ preview_draw_selection(p);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */