diff options
Diffstat (limited to 'src/xsane-preview.c')
-rw-r--r-- | src/xsane-preview.c | 2328 |
1 files changed, 1872 insertions, 456 deletions
diff --git a/src/xsane-preview.c b/src/xsane-preview.c index da81e3f..879bc99 100644 --- a/src/xsane-preview.c +++ b/src/xsane-preview.c @@ -3,7 +3,7 @@ xsane-preview.c Oliver Rauch <Oliver.Rauch@rauch-domain.de> - Copyright (C) 1998-2002 Oliver Rauch + Copyright (C) 1998-2005 Oliver Rauch This file is part of the XSANE package. This program is free software; you can redistribute it and/or modify @@ -76,6 +76,7 @@ /* #include <sys/param.h> */ #include "xsane-back-gtk.h" #include "xsane-front-gtk.h" +#include "xsane-batch-scan.h" #include "xsane-preview.h" #include "xsane-preferences.h" #include "xsane-gamma.h" @@ -102,6 +103,11 @@ /* ---------------------------------------------------------------------------------------------------------------------- */ +#define XSANE_ZOOM_SIZE 80 +#define XSANE_ZOOM_FACTOR 4 + +/* ---------------------------------------------------------------------------------------------------------------------- */ + static u_char *preview_gamma_data_red = 0; static u_char *preview_gamma_data_green = 0; static u_char *preview_gamma_data_blue = 0; @@ -117,6 +123,8 @@ static u_char *histogram_medium_gamma_data_blue = 0; static int preview_gamma_input_bits; +static char *ratio_string[] = { "free", " 2:1", "16:9", "15:10", " 4:3", " 1:1", " 3:4", " 9:16", "10:15", " 1:2"}; +static float ratio_value[] = { 0.0, 2.0, 16.0/9.0, 15.0/10.0, 4.0/3.0, 1.0, 0.75, 0.5625, 10.0/15.0, 0.5 }; /* ---------------------------------------------------------------------------------------------------------------------- */ /* forward declarations */ @@ -161,14 +169,18 @@ 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_delete_images(Preview *p); +static void preview_select_zoom_point(Preview *p, int preview_x, int preview_y); 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_area(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 range, int *red, int *green, int *blue); +static void preview_add_batch(GtkWidget *window, Preview *p); 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_init_autoraise_scanarea(GtkWidget *window, gpointer data); void preview_select_full_preview_area(Preview *p); static void preview_full_preview_area_callback(GtkWidget *widget, gpointer call_data); static void preview_delete_images_callback(GtkWidget *widget, gpointer call_data); @@ -178,8 +190,10 @@ static gint preview_preset_area_delete_callback(GtkWidget *widget, GtkWidget *pr static gint preview_preset_area_move_up_callback(GtkWidget *widget, GtkWidget *preset_area_widget); static gint preview_preset_area_move_down_callback(GtkWidget *widget, GtkWidget *preset_area_widget); static gint preview_preset_area_context_menu_callback(GtkWidget *widget, GdkEvent *event); -static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data); -static void preview_rotation_callback(GtkWidget *widget, gpointer call_data); +static void preview_preset_area_callback(GtkWidget *widget, gpointer data); +static void preview_rotation_callback(GtkWidget *widget, gpointer data); +static void preview_establish_ratio(Preview *p); +static void preview_ratio_callback(GtkWidget *widget, gpointer data); static void preview_autoselect_scanarea_callback(GtkWidget *window, gpointer data); void preview_do_gamma_correction(Preview *p); @@ -192,7 +206,8 @@ void preview_gamma_correction(Preview *p, int gamma_input_bits, void preview_area_resize(Preview *p); gint preview_area_resize_handler(GtkWidget *widget, GdkEvent *event, gpointer data); void preview_update_maximum_output_size(Preview *p); -void preview_set_maximum_output_size(Preview *p, float width, float height); +void preview_set_maximum_output_size(Preview *p, float width, float height, int paper_orientation); +void preview_autoraise_scanarea(Preview *p, int preview_x, int preview_y, float *autoselect_coord); void preview_autoselect_scanarea(Preview *p, float *autoselect_coord); void preview_display_valid(Preview *p); @@ -574,16 +589,17 @@ static void preview_bound_selection(Preview *p) if (p->selection.active) { -#if 0 +#if 1 xsane_bound_float(&p->selection.coordinate[0], p->scanner_surface[0], p->scanner_surface[2]); xsane_bound_float(&p->selection.coordinate[2], p->scanner_surface[0], p->scanner_surface[2]); xsane_bound_float(&p->selection.coordinate[1], p->scanner_surface[1], p->scanner_surface[3]); xsane_bound_float(&p->selection.coordinate[3], p->scanner_surface[1], p->scanner_surface[3]); -#endif +#else xsane_bound_float(&p->selection.coordinate[0], p->surface[0], p->surface[2]); xsane_bound_float(&p->selection.coordinate[2], p->surface[0], p->surface[2]); xsane_bound_float(&p->selection.coordinate[1], p->surface[1], p->surface[3]); xsane_bound_float(&p->selection.coordinate[3], p->surface[1], p->surface[3]); +#endif } } @@ -1304,7 +1320,14 @@ static void preview_restore_option(Preview *p, int option, void *saved_value, in { char buf[256]; opt = xsane_get_option_descriptor(dev, option); - snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, opt->name, XSANE_STRSTATUS(status)); + if (opt && opt->name) + { + snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, opt->name, XSANE_STRSTATUS(status)); + } + else + { + snprintf(buf, sizeof(buf), "%s %d: %s.", ERR_SET_OPTION, option, XSANE_STRSTATUS(status)); + } xsane_back_gtk_error(buf, TRUE); } } @@ -1413,8 +1436,8 @@ static int preview_increment_image_y(Preview *p) if ( (!p->image_data_enh) || (!p->image_data_raw) ) { - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %s.", ERR_FAILED_ALLOCATE_IMAGE, strerror(errno)); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return -1; } @@ -1435,8 +1458,6 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio SANE_Handle dev; SANE_Int len; int i, j; - int offset = 0; - char last = 0; DBG(DBG_proc, "preview_read_image_data\n"); @@ -1449,11 +1470,12 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio } else if (p->params.depth == 16) { - if (offset) + if (p->read_offset_16) { - buf16[0] = last; /* ATTENTION: that is wrong! */ - /* use sizeof(buf) here because sizeof(buf16) returns the size of a pointer */ - status = sane_read(dev, ((SANE_Byte *) buf16) + 1, sizeof(buf) - 1, &len); + buf[0] = p->last_offset_16_byte; + /* use buf and sizeof(buf) here because sizeof(buf16) returns the size of a pointer */ + status = sane_read(dev, buf+1, sizeof(buf) - 1, &len); + len++; } else { @@ -1463,18 +1485,18 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio if (len % 2) /* odd number of bytes */ { len--; - last = buf16[len]; - offset = 1; + p->last_offset_16_byte = buf16[len]; + p->read_offset_16 = 1; } else /* even number of bytes */ { - offset = 0; + p->read_offset_16 = 0; } } else /* bad bitdepth */ { - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return; } @@ -1526,7 +1548,19 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio if (!len) { - break; /* out of data for now */ + if (p->input_tag >= 0) /* we have a selecet fd */ + { + break; /* leave preview_read_image_data, will be called by gdk when select_fd event occurs */ + } + else + { + while (gtk_events_pending()) + { + DBG(DBG_info, "preview_read_image_data: calling gtk_main_iteration\n"); + gtk_main_iteration(); + } + continue; /* we have to keep this loop running because it will never be called again */ + } } switch (p->params.format) @@ -1581,8 +1615,8 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio break; default: - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return; } @@ -1678,8 +1712,8 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio break; default: - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return; } @@ -1705,8 +1739,8 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio u_char gl = (mask & 1) ? 0xff : 0x00; mask >>= 1; - p->image_data_raw[p->image_offset] = gl * 256; - p->image_data_enh[p->image_offset++] = gl; + p->image_data_raw[p->image_offset] = gl * 256; + 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) @@ -1756,16 +1790,16 @@ static void preview_read_image_data(gpointer data, gint source, GdkInputConditio break; default: - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %d.", ERR_PREVIEW_BAD_DEPTH, p->params.depth); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return; } break; default: - preview_scan_done(p, 0); snprintf(buf, sizeof(buf), "%s %d.", ERR_BAD_FRAME_FORMAT, p->params.format); + preview_scan_done(p, 0); xsane_back_gtk_error(buf, TRUE); return; } @@ -1839,7 +1873,7 @@ static void preview_scan_done(Preview *p, int save_image) sane_get_parameters(xsane.dev, &xsane.param); /* update xsane.param */ - if ( (preferences.preselect_scanarea) && (!p->startimage)) + if ((preferences.preselect_scanarea) && (!p->startimage) && (!xsane.medium_calibration)) { preview_autoselect_scanarea(p, p->selection.coordinate); /* get autoselection coordinates */ preview_draw_selection(p); @@ -1847,12 +1881,14 @@ static void preview_scan_done(Preview *p, int save_image) xsane_update_histogram(TRUE /* update_raw */); /* update histogram (necessary because overwritten by preview_update_surface) */ } - if (preferences.auto_correct_colors) + if ((preferences.auto_correct_colors) && (!xsane.medium_calibration)) { xsane_calculate_raw_histogram(); xsane_set_auto_enhancement(); xsane_enhancement_by_histogram(preferences.auto_enhance_gamma); } + + xsane_batch_scan_update_icon_list(); } /* ---------------------------------------------------------------------------------------------------------------------- */ @@ -1927,17 +1963,11 @@ static void preview_scan_start(Preview *p) SANE_Status status; char buf[256]; int fd, y; - 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; DBG(DBG_proc, "preview_scan_start\n"); + p->read_offset_16 = 0; + xsane.medium_changed = FALSE; preview_display_valid(p); @@ -1966,95 +1996,6 @@ static void preview_scan_start(Preview *p) p->input_tag = -1; } - if (xsane.well_known.gamma_vector >0) - { - const SANE_Option_Descriptor *opt; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector); - if (SANE_OPTION_IS_ACTIVE(opt->cap)) - { - SANE_Int *gamma_data; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.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)); - - if ((xsane.xsane_colors > 1) || (xsane.no_preview_medium_gamma)) /* color scan or medium preview gamma disabled */ - { - xsane_create_gamma_curve(gamma_data, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_gray_size, gamma_gray_max); - } - else /* grayscale scan */ - { - xsane_create_gamma_curve(gamma_data, xsane.medium_negative, 1.0, 0.0, 0.0, - xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, - gamma_gray_size, gamma_gray_max); - } - - xsane_back_gtk_update_vector(xsane.well_known.gamma_vector, gamma_data); - free(gamma_data); - } - } - - if (xsane.well_known.gamma_vector_r >0) - { - const SANE_Option_Descriptor *opt; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_r); - if (SANE_OPTION_IS_ACTIVE(opt->cap)) - { - SANE_Int *gamma_data_red, *gamma_data_green, *gamma_data_blue; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_r); - gamma_red_size = opt->size / sizeof(opt->type); - gamma_red_max = opt->constraint.range->max; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_g); - gamma_green_size = opt->size / sizeof(opt->type); - gamma_green_max = opt->constraint.range->max; - - opt = xsane_get_option_descriptor(xsane.dev, xsane.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)); - - if (xsane.no_preview_medium_gamma) /* do not use medium gamma for preview */ - { - DBG(DBG_info, "preview: not using medium gamma table\n"); - - xsane_create_gamma_curve(gamma_data_red, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_red_size, gamma_red_max); - xsane_create_gamma_curve(gamma_data_green, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_green_size, gamma_green_max); - xsane_create_gamma_curve(gamma_data_blue, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_blue_size, gamma_blue_max); - } - else /* use medium gamma for preview */ - { - DBG(DBG_info, "preview: using medium gamma table\n"); - - xsane_create_gamma_curve(gamma_data_red, xsane.medium_negative, 1.0, 0.0, 0.0, - xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, - gamma_red_size, gamma_red_max); - xsane_create_gamma_curve(gamma_data_green, xsane.medium_negative, 1.0, 0.0, 0.0, - xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, - gamma_green_size, gamma_green_max); - xsane_create_gamma_curve(gamma_data_blue, xsane.medium_negative, 1.0, 0.0, 0.0, - xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, - gamma_blue_size, gamma_blue_max); - } - - xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_r, gamma_data_red); - xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_g, gamma_data_green); - xsane_back_gtk_update_vector(xsane.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) { @@ -2110,15 +2051,11 @@ static void preview_scan_start(Preview *p) p->previous_selection_maximum.active = FALSE; #ifndef BUGGY_GDK_INPUT_EXCEPTION - /* for unix */ 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 | GDK_INPUT_EXCEPTION, preview_read_image_data, p); } else -#else - /* for win32 */ - sane_set_io_mode(dev, SANE_FALSE); #endif { preview_read_image_data(p, -1, GDK_INPUT_READ); @@ -2133,12 +2070,370 @@ static int preview_make_image_path(Preview *p, size_t filename_size, char *filen DBG(DBG_proc, "preview_make_image_path\n"); - snprintf(buf, sizeof(buf), "preview-level-%d-", level); + snprintf(buf, sizeof(buf), "xsane-preview-level-%d-", level); return xsane_back_gtk_make_path(filename_size, filename, 0, 0, buf, xsane.dev_name, ".ppm", XSANE_PATH_TMP); } /* ---------------------------------------------------------------------------------------------------------------------- */ +int preview_create_batch_icon_from_file(Preview *p, FILE *in, Batch_Scan_Parameters *parameters, int min_quality, int *min_time) +{ + u_int psurface_type, psurface_unit; + int image_width, image_height; + int xoffset, yoffset, width, height; + int max_val; + int x, y, dx, dy; + int time; + float psurface[4]; + float dsurface[4]; + char buf[255]; + float scale; + int header = 0; + int rotate16 = 16 - preview_gamma_input_bits; + int rotate8 = preview_gamma_input_bits - 8; + guint16 r,g,b; + int c; + int maximum_size; + int quality = 0; + int xx, yy; + int offset = 0; + guchar *data; + + DBG(DBG_proc, "preview_create_batch_icon_from_file\n"); + + 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" + "# time: %d\n" + "%d %d\n%d", + psurface + 0, psurface + 1, psurface + 2, psurface + 3, + &psurface_type, &psurface_unit, + &time, + &image_width, &image_height, + &max_val) != 10) + { + DBG(DBG_info, "no preview image\n"); + return min_quality; + } + + fgets(buf, sizeof(buf), in); /* skip newline character. this made a lot of problems in the past, so I skip it this way */ + + header = ftell(in); + + if (min_quality >= 0) /* read real preview */ + { + if ((psurface_type != p->surface_type) || (psurface_unit != p->surface_unit)) + { + DBG(DBG_info, "incompatible surface types %d <> %d\n", psurface_type, p->surface_type); + return min_quality; + } + + preview_rotate_previewsurface_to_devicesurface(p->rotation, p->surface, dsurface); + + + DBG(DBG_info, "stored image surface = [%3.2f %3.2f %3.2f %3.2f]\n", + psurface[0], psurface[1], psurface[2], psurface[3]); + DBG(DBG_info, "batch selection = [%3.2f %3.2f %3.2f %3.2f]\n", + parameters->tl_x, parameters->tl_y, parameters->br_x, parameters->br_y); + DBG(DBG_info, "preview device surface = [%3.2f %3.2f %3.2f %3.2f]\n", + dsurface[0], dsurface[1], dsurface[2], dsurface[3]); + + xoffset = (parameters->tl_x - psurface[0])/(psurface[2] - psurface[0]) * image_width; + yoffset = (parameters->tl_y - psurface[1])/(psurface[3] - psurface[1]) * image_height; + width = (parameters->br_x - parameters->tl_x)/(psurface[2] - psurface[0]) * image_width; + height = (parameters->br_y - parameters->tl_y)/(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)) + { + DBG(DBG_info, "image does not cover wanted surface part\n"); + return min_quality; + } + + DBG(DBG_info, "quality = %d\n", quality); + + if ( ((float) min_quality / (quality+1)) > 1.05) /* already loaded image has better quality */ + { + DBG(DBG_info, "already loaded image has higher quality\n"); + return min_quality; + } + + if ( ((float) min_quality / (quality+1)) > 0.95) /* qualities are comparable */ + { + if (*min_time > time) /* take more recent scan */ + { + DBG(DBG_info, "images have comparable quality, already loaded is more up to date\n"); + return min_quality; + } + DBG(DBG_info, "images have comparable quality, this image is more up to date\n"); + } + else + { + DBG(DBG_info, "image has best quality\n"); + } + } + else + { + xoffset = 0; + yoffset = 0; + width = image_width; + height = image_height; + } + + + { + float xscale = (float)width / parameters->gtk_preview_size; + float yscale = (float)height / parameters->gtk_preview_size; + + if (xscale > yscale) + { + scale = xscale; + } + else + { + scale = yscale; + } + } + + width = width / scale; + height = height / scale; + + if (width > parameters->gtk_preview_size) + { + width = parameters->gtk_preview_size; + } + + if (height > parameters->gtk_preview_size) + { + height = parameters->gtk_preview_size; + } + + maximum_size = parameters->gtk_preview_size -1; + + dx = (parameters->gtk_preview_size - width) / 2; + dy = (parameters->gtk_preview_size - height) / 2; + + + data = malloc(parameters->gtk_preview_size * parameters->gtk_preview_size * 3); + if (!data) + { + DBG(DBG_error, "preview_create_batch_icon_from_file: out of memory\n"); + return min_quality; + } + + /* make unused parts white */ + for (x = 0; x< parameters->gtk_preview_size * parameters->gtk_preview_size * 3; x++) + { + data[x] = 0xF0; + } + + if (max_val == 65535) + { + for (y=0; y < height; y++) + { + for (x=0; x < width; x++) + { + fseek(in, header + (xoffset + (int)(x * scale) + (yoffset + (int)(y * scale)) * image_width) * 6, SEEK_SET); + + fread(&r, 2, 1, in); /* read 16 bit value in machines byte order */ + r = preview_gamma_data_red[r >> rotate16]; + + fread(&g, 2, 1, in); + g = preview_gamma_data_green[g >> rotate16]; + + fread(&b, 2, 1, in); + b = preview_gamma_data_blue[b >> rotate16]; + + c = r * 65536 + g * 256 + b; + + switch (parameters->rotation) + { + case 0: /* 0 degree */ + xx = x + dx; + yy = y + dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 1: /* 90 degree */ + xx = maximum_size - y; + yy = x + dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 2: /* 180 degree */ + xx = maximum_size - x - dx; + yy = maximum_size - y - dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 3: /* 270 degree */ + xx = y + dy; + yy = maximum_size - x - dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 4: /* 0 degree, x-mirror */ + xx = maximum_size - x - dx; + yy = y + dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 5: /* 90 degree, x-mirror */ + xx = y + dy; + yy = x + dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 6: /* 180 degree, x-mirror */ + xx = x + dx; + yy = maximum_size - y - dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 7: /* 270 degree, x-mirror */ + xx = maximum_size - y - dy; + yy = maximum_size - x - dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + } + + data[offset + 0] = r; + data[offset + 1] = g; + data[offset + 2] = b; + } + } + + for (y = 0; y < parameters->gtk_preview_size; y++) + { + gtk_preview_draw_row(GTK_PREVIEW(parameters->gtk_preview), data + 3 * parameters->gtk_preview_size * y, + 0, y, parameters->gtk_preview_size); + } + } + else /* depth = 8 */ + { + for (y=0; y < height; y++) + { + for (x=0; x < width; x++) + { + fseek(in, header + (xoffset + (int)(x * scale) + (yoffset + (int)(y * scale)) * image_width) * 3, SEEK_SET); + + r = fgetc(in); + r = preview_gamma_data_red[r << rotate8]; + + g = fgetc(in); + g = preview_gamma_data_green[g << rotate8]; + + b = fgetc(in); + b = preview_gamma_data_blue[b << rotate8]; + switch (parameters->rotation) + { + case 0: /* 0 degree */ + xx = x + dx; + yy = y + dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 1: /* 90 degree */ + xx = maximum_size - y; + yy = x + dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 2: /* 180 degree */ + xx = maximum_size - x - dx; + yy = maximum_size - y - dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 3: /* 270 degree */ + xx = y + dy; + yy = maximum_size - x - dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 4: /* 0 degree, x-mirror */ + xx = maximum_size - x - dx; + yy = y + dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 5: /* 90 degree, x-mirror */ + xx = y + dy; + yy = x + dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 6: /* 180 degree, x-mirror */ + xx = x + dx; + yy = maximum_size - y - dy; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + + case 7: /* 270 degree, x-mirror */ + xx = maximum_size - y - dy; + yy = maximum_size - x - dx; + offset = parameters->gtk_preview_size * 3 * yy + 3*(xx); + break; + } + + data[offset + 0] = r; + data[offset + 1] = g; + data[offset + 2] = b; + } + } + + for (y = 0; y < parameters->gtk_preview_size; y++) + { + gtk_preview_draw_row(GTK_PREVIEW(parameters->gtk_preview), data + 3 * parameters->gtk_preview_size * y, + 0, y, parameters->gtk_preview_size); + } + } + + free(data); + + return quality; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +void preview_create_batch_icon(Preview *p, Batch_Scan_Parameters *parameters) +{ + FILE *in; + int quality = 0; + int time = 0; + + in = fopen(xsane.preview->filename[0], "rb"); + quality = preview_create_batch_icon_from_file(xsane.preview, in, parameters, quality, &time); + + if (quality <= 0) + { + char filename[PATH_MAX]; + + if (in) + { + fclose(in); + } + + xsane_back_gtk_make_path(sizeof(filename), filename, "xsane", 0, "xsane-startimage", 0, ".pnm", XSANE_PATH_SYSTEM); + in = fopen(filename, "rb"); /* read binary (b for win32) */ + if (in) + { + preview_create_batch_icon_from_file(xsane.preview, in, parameters, -1, &time); + } + } + fclose(in); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + static int preview_restore_image_from_file(Preview *p, FILE *in, int min_quality, int *min_time) { u_int psurface_type, psurface_unit; @@ -2202,6 +2497,7 @@ static int preview_restore_image_from_file(Preview *p, FILE *in, int min_quality yoffset = (dsurface[1] - psurface[1])/(psurface[3] - psurface[1]) * image_height; width = (dsurface[2] - dsurface[0])/(psurface[2] - psurface[0]) * image_width; height = (dsurface[3] - dsurface[1])/(psurface[3] - psurface[1]) * image_height; + quality = width; if ((xoffset < 0) || (yoffset < 0) || @@ -2397,6 +2693,284 @@ static void preview_restore_image(Preview *p) /* ---------------------------------------------------------------------------------------------------------------------- */ +static int preview_get_pixel_color(Preview *p, int x, int y, int *raw_red, int *raw_green, int *raw_blue, + int *enh_red, int *enh_green, int *enh_blue) +{ + int image_x, image_y; + int offset; + int rotate = 16 - preview_gamma_input_bits; + + DBG(DBG_proc, "preview_get_pixel_color\n"); + + if (p->image_data_raw) + { + preview_transform_coordinate_window_to_image(p, x, y, &image_x, &image_y); + + if ( (image_x >= 0) && (image_x < p->image_width) && (image_y >=0) && (image_y < p->image_height) ) + { + offset = 3 * (image_y * p->image_width + image_x); + + if (!xsane.negative) /* positive */ + { + *raw_red = (p->image_data_raw[offset ]) >> 8; + *raw_green = (p->image_data_raw[offset + 1]) >> 8; + *raw_blue = (p->image_data_raw[offset + 2]) >> 8; + } + else /* negative */ + { + *raw_red = 255 - ((p->image_data_raw[offset ]) >> 8); + *raw_green = 255 - ((p->image_data_raw[offset + 1]) >> 8); + *raw_blue = 255 - ((p->image_data_raw[offset + 2]) >> 8); + } + + /* the enhanced pixels are already inverted when negative is selected */ + /* do not use image_data_enh because the preview gamma value is applied to this */ + *enh_red = histogram_gamma_data_red [(p->image_data_raw[offset ]) >> rotate]; + *enh_green = histogram_gamma_data_green[(p->image_data_raw[offset + 1]) >> rotate]; + *enh_blue = histogram_gamma_data_blue [(p->image_data_raw[offset + 2]) >> rotate]; + + return 0; + } + } + + return -1; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void xsane_swap_int(int *a, int *b) +{ + int i; + i = *a; + *a = *b; + *b = i; +} + +static void preview_display_zoom(Preview *p, int x, int y, int zoom) +{ + int pointer_x, pointer_y; + int image_x, image_y; + int image_x_lof, image_x_rof; + int image_y_tof, image_y_bof; + int image_x_min, image_y_min; + int image_x_max, image_y_max; + int image_x_direction, image_y_direction; + int offset; + int r, g, b; + int px, py; + int i; + char *row; + + DBG(DBG_proc, "preview_display_zoom"); + + if (!p->image_data_raw) + { + return; + } + + row = calloc(XSANE_ZOOM_SIZE, 3); + + if (row) + { + preview_transform_coordinate_window_to_image(p, x, y, &image_x, &image_y); + preview_transform_coordinate_window_to_image(p, x, y, &pointer_x, &pointer_y); + + image_x_min = image_x - XSANE_ZOOM_SIZE/(zoom*2); + image_y_min = image_y - XSANE_ZOOM_SIZE/(zoom*2); + + image_x_max = image_x_min + XSANE_ZOOM_SIZE/zoom + 1; + image_y_max = image_y_min + XSANE_ZOOM_SIZE/zoom + 1; + + xsane_bound_int(&image_x_min, 0, p->image_width - 1); + xsane_bound_int(&image_x_max, 0, p->image_width - 1); + xsane_bound_int(&image_y_min, 0, p->image_height - 1); + xsane_bound_int(&image_y_max, 0, p->image_height - 1); + + if ((image_x_max - image_x_min) && (image_y_max - image_y_min)) + { + image_x_lof = image_x_min - (image_x - XSANE_ZOOM_SIZE/(zoom*2)); + image_x_rof = image_x - XSANE_ZOOM_SIZE/(zoom*2) + XSANE_ZOOM_SIZE/zoom + 1 - image_x_max; + image_y_tof = image_y_min - (image_y - XSANE_ZOOM_SIZE/(zoom*2)); + image_y_bof = image_y - XSANE_ZOOM_SIZE/(zoom*2) + XSANE_ZOOM_SIZE/zoom + 1 - image_y_max; + + image_x_direction = 1; + image_y_direction = 1; + + switch (p->rotation & 3) + { + case 0: /* do not rotate - 0 degree */ + default: + break; + + case 1: /* 90 degree */ + xsane_swap_int(&image_y_min, &image_y_max); + xsane_swap_int(&image_y_tof, &image_y_bof); + image_y_direction *= -1; + break; + + case 2: /* 180 degree */ + xsane_swap_int(&image_x_min, &image_x_max); + xsane_swap_int(&image_x_lof, &image_x_rof); + xsane_swap_int(&image_y_min, &image_y_max); + xsane_swap_int(&image_y_tof, &image_y_bof); + image_x_direction *= -1; + image_y_direction *= -1; + break; + + case 3: /* 270 degree */ + xsane_swap_int(&image_x_min, &image_x_max); + xsane_swap_int(&image_x_lof, &image_x_rof); + image_x_direction *= -1; + break; + } + + + if ((p->rotation & 1) == 0) + { + if (p->rotation & 4) /* mirror */ + { + xsane_swap_int(&image_x_min, &image_x_max); + xsane_swap_int(&image_x_lof, &image_x_rof); + image_x_direction *= -1; + } + + py = image_y_tof * zoom; + + for (i=0; i < py; i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, i, XSANE_ZOOM_SIZE); + } + + for (image_y = image_y_min; image_y != image_y_max + image_y_direction; image_y += image_y_direction) + { + px = image_x_lof * zoom; + + for (image_x = image_x_min; image_x != image_x_max + image_x_direction; image_x += image_x_direction) + { + offset = 3 * (image_y * p->image_width + image_x); + + r = (p->image_data_enh[offset ]); + g = (p->image_data_enh[offset+1]); + b = (p->image_data_enh[offset+2]); + + if ( (image_x == pointer_x) && (image_y == pointer_y) ) + { + r = g = b = (128 + (r+g+b) / 3) & 255; /* mark the cursor position */ + } + + for (i=0; (i<zoom) && (px < XSANE_ZOOM_SIZE); i++) + { + row[px*3+0] = r; + row[px*3+1] = g; + row[px*3+2] = b; + px++; + } + } + + for (i=0; (i<zoom) && (py < XSANE_ZOOM_SIZE); i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, py++, XSANE_ZOOM_SIZE); + } + } + } + else /* swap x and y */ + { + if (p->rotation & 4) /* mirror */ + { + xsane_swap_int(&image_y_min, &image_y_max); + xsane_swap_int(&image_y_tof, &image_y_bof); + image_y_direction *= -1; + } + + py = image_x_lof * zoom; + + for (i=0; i < py; i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, i, XSANE_ZOOM_SIZE); + } + + for (image_x = image_x_min; image_x != image_x_max + image_x_direction; image_x += image_x_direction) + { + px = image_y_tof * zoom; + + for (image_y = image_y_min; image_y != image_y_max + image_y_direction; image_y += image_y_direction) + { + offset = 3 * (image_y * p->image_width + image_x); + + r = (p->image_data_enh[offset ]); + g = (p->image_data_enh[offset+1]); + b = (p->image_data_enh[offset+2]); + + if ( (image_x == pointer_x) && (image_y == pointer_y) ) + { + r = g = b = (128 + (r+g+b) / 3) & 255; /* mark the cursor position */ + } + + for (i=0; (i<zoom) && (px < XSANE_ZOOM_SIZE); i++) + { + row[px*3+0] = r; + row[px*3+1] = g; + row[px*3+2] = b; + px++; + } + } + + for (i=0; (i<zoom) && (py < XSANE_ZOOM_SIZE); i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, py++, XSANE_ZOOM_SIZE); + } + } + } + + for (i=0; i < XSANE_ZOOM_SIZE; i++) + { + row[i*3+0] = 0; + row[i*3+1] = 0; + row[i*3+2] = 0; + } + + for (i=py; i < XSANE_ZOOM_SIZE; i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, i, XSANE_ZOOM_SIZE); + } + } + else + { + for (i = 0; i < XSANE_ZOOM_SIZE; i++) + { + gtk_preview_draw_row(GTK_PREVIEW(p->zoom), row, 0, i, XSANE_ZOOM_SIZE); + } + } + gtk_widget_queue_draw(p->zoom); + + free(row); + } +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void preview_display_color_components(Preview *p, int x, int y) +{ + char buffer[256]; + int raw_red, raw_green, raw_blue, enh_red, enh_green, enh_blue; + + if (! preview_get_pixel_color(p, x, y, &raw_red, &raw_green, &raw_blue, &enh_red, &enh_green, &enh_blue)) + { + snprintf(buffer, sizeof(buffer), " %03d, %03d, %03d \n" \ + " %03d, %03d, %03d ", + raw_red, raw_green, raw_blue, enh_red, enh_green, enh_blue); + } + else + { + snprintf(buffer, sizeof(buffer), " ###, ###, ### \n" \ + " ###, ###, ### "); + } + + gtk_label_set_text(GTK_LABEL(p->rgb_label), buffer); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + static gint preview_hold_event_handler(gpointer data) { Preview *p = data; @@ -2437,6 +3011,9 @@ static gint preview_motion_event_handler(GtkWidget *window, GdkEvent *event, gpo if (!p->scanning) { + preview_display_zoom(p, event->motion.x, event->motion.y, XSANE_ZOOM_FACTOR); + preview_display_color_components(p, event->motion.x, event->motion.y); + switch (((GdkEventMotion *)event)->state & GDK_Num_Lock & GDK_Caps_Lock & GDK_Shift_Lock & GDK_Scroll_Lock) /* mask all Locks */ { @@ -2447,8 +3024,159 @@ static gint preview_motion_event_handler(GtkWidget *window, GdkEvent *event, gpo if ( (p->selection_drag) || (p->selection_drag_edge) ) { p->selection.active = TRUE; - p->selection.coordinate[p->selection_xedge] = preview_x; - p->selection.coordinate[p->selection_yedge] = preview_y; + + if (preview_x < p->scanner_surface[p->index_xmin]) + { + preview_x = p->scanner_surface[p->index_xmin]; + } + else if (preview_x > p->scanner_surface[p->index_xmax]) + { + preview_x = p->scanner_surface[p->index_xmax]; + } + + if (preview_y < p->scanner_surface[p->index_ymin]) + { + preview_y = p->scanner_surface[p->index_ymin]; + } + else if (preview_y > p->scanner_surface[p->index_ymax]) + { + preview_y = p->scanner_surface[p->index_ymax]; + } + + + if (p->selection_xedge != -1) + { + p->selection.coordinate[p->selection_xedge] = preview_x; + } + + if (p->selection_yedge != -1) + { + p->selection.coordinate[p->selection_yedge] = preview_y; + } + + if (p->ratio) /* forced preview ratio ? */ + { + if ( (p->selection_xedge == p->index_xmin) && (p->selection_yedge == p->index_ymin) ) /* top left corner */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + width / p->ratio; + + if (p->selection.coordinate[p->index_ymax] > p->scanner_surface[p->index_ymax]) + { + float height; + p->selection.coordinate[p->index_ymax] = p->scanner_surface[p->index_ymax]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmax] - height * p->ratio; + } + } + else if ( (p->selection_xedge == p->index_xmax) && (p->selection_yedge == p->index_ymin) )/* top right corner */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + width / p->ratio; + + if (p->selection.coordinate[p->index_ymax] > p->scanner_surface[p->index_ymax]) + { + float height; + p->selection.coordinate[p->index_ymax] = p->scanner_surface[p->index_ymax]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + height * p->ratio; + } + } + else if ( (p->selection_xedge == p->index_xmin) && (p->selection_yedge == p->index_ymax) ) /* bottom left edge */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymax] - width / p->ratio; + + if (p->selection.coordinate[p->index_ymin] < p->scanner_surface[p->index_ymin]) + { + float height; + p->selection.coordinate[p->index_ymin] = p->scanner_surface[p->index_ymin]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmax] - height * p->ratio; + } + } + else if ( (p->selection_xedge == p->index_xmax) && (p->selection_yedge == p->index_ymax) ) /* bottom right edge */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymax] - width / p->ratio; + + if (p->selection.coordinate[p->index_ymin] < p->scanner_surface[p->index_ymin]) + { + float height; + p->selection.coordinate[p->index_ymin] = p->scanner_surface[p->index_ymin]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + height * p->ratio; + } + } + else if (p->selection_xedge == p->index_xmin) /* left edge */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + width / p->ratio; + + if (p->selection.coordinate[p->index_ymax] > p->scanner_surface[p->index_ymax]) + { + float height; + p->selection.coordinate[p->index_ymax] = p->scanner_surface[p->index_ymax]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmax] - height * p->ratio; + } + } + else if (p->selection_xedge == p->index_xmax) /* right edge */ + { + float width; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + width / p->ratio; + + if (p->selection.coordinate[p->index_ymax] > p->scanner_surface[p->index_ymax]) + { + float height; + p->selection.coordinate[p->index_ymax] = p->scanner_surface[p->index_ymax]; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + height * p->ratio; + } + } + else if (p->selection_yedge == p->index_ymin) /* top edge */ + { + float height; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + height * p->ratio; + + if (p->selection.coordinate[p->index_xmax] > p->scanner_surface[p->index_xmax]) + { + float width; + p->selection.coordinate[p->index_xmax] = p->scanner_surface[p->index_xmax]; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + p->selection.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymax] - width / p->ratio; + } + } + else if (p->selection_yedge == p->index_ymax) /* bottom edge */ + { + float height; + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + height * p->ratio; + + if (p->selection.coordinate[p->index_xmax] > p->scanner_surface[p->index_xmax]) + { + float width; + p->selection.coordinate[p->index_xmax] = p->scanner_surface[p->index_xmax]; + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + width / p->ratio; + } + } + } preview_order_selection(p); preview_bound_selection(p); @@ -2551,7 +3279,7 @@ static gint preview_motion_event_handler(GtkWidget *window, GdkEvent *event, gpo { cursor = gdk_cursor_new(cursornr); /* set curosr */ gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = cursornr; } break; @@ -2727,7 +3455,7 @@ static gint preview_motion_event_handler(GtkWidget *window, GdkEvent *event, gpo { cursor = gdk_cursor_new(cursornr); /* set curosr */ gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = cursornr; } break; @@ -2842,7 +3570,7 @@ static gint preview_button_press_event_handler(GtkWidget *window, GdkEvent *even cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = XSANE_CURSOR_PREVIEW; } break; @@ -2947,7 +3675,7 @@ static gint preview_button_press_event_handler(GtkWidget *window, GdkEvent *even cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = XSANE_CURSOR_PREVIEW; } break; @@ -3037,11 +3765,51 @@ static gint preview_button_press_event_handler(GtkWidget *window, GdkEvent *even cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); + p->cursornr = XSANE_CURSOR_PREVIEW; + } + break; + + case MODE_AUTORAISE_SCANAREA: + { + DBG(DBG_info, "autoraise scanarea mode\n"); + + if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) && + (p->image_data_raw) ) /* left or middle button */ + { + preview_autoraise_scanarea(p, event->button.x, event->button.y, p->selection.coordinate); /* raise selection area */ + } + + p->mode = MODE_NORMAL; + + cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); + gdk_window_set_cursor(p->window->window, cursor); + gdk_cursor_unref(cursor); p->cursornr = XSANE_CURSOR_PREVIEW; } break; + case MODE_ZOOM_IN: + { + DBG(DBG_info, "zoom in mode\n"); + + if ( ( (((GdkEventButton *)event)->button == 1) || (((GdkEventButton *)event)->button == 2) ) && + (p->image_data_raw) ) /* left or middle button */ + { + preview_select_zoom_point(p, event->button.x, event->button.y); /* select zoom point */ + } + else + { + p->mode = MODE_NORMAL; + + cursor = gdk_cursor_new(XSANE_CURSOR_PREVIEW); + gdk_window_set_cursor(p->window->window, cursor); + gdk_cursor_unref(cursor); + p->cursornr = XSANE_CURSOR_PREVIEW; + } + } + break; + case MODE_NORMAL: { DBG(DBG_info, "normal mode\n"); @@ -3117,7 +3885,7 @@ static gint preview_button_press_event_handler(GtkWidget *window, GdkEvent *even cursornr = GDK_CROSS; cursor = gdk_cursor_new(cursornr); /* set curosr */ gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = cursornr; } break; @@ -3139,7 +3907,7 @@ static gint preview_button_press_event_handler(GtkWidget *window, GdkEvent *even cursornr = GDK_HAND2; cursor = gdk_cursor_new(cursornr); /* set curosr */ gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = cursornr; } break; @@ -3184,7 +3952,7 @@ static gint preview_button_release_event_handler(GtkWidget *window, GdkEvent *ev cursornr = XSANE_CURSOR_PREVIEW; cursor = gdk_cursor_new(cursornr); /* set curosr */ gdk_window_set_cursor(p->window->window, cursor); - gdk_cursor_destroy(cursor); + gdk_cursor_unref(cursor); p->cursornr = cursornr; } @@ -3220,7 +3988,7 @@ static gint preview_expose_event_handler_start(GtkWidget *window, GdkEvent *even if (!p->gc_selection) { DBG(DBG_info, "defining line styles for selection and page frames\n"); - colormap = gdk_window_get_colormap(p->window->window); + colormap = gdk_drawable_get_colormap(p->window->window); p->gc_selection = gdk_gc_new(p->window->window); gdk_gc_set_function(p->gc_selection, GDK_INVERT); @@ -3265,7 +4033,7 @@ static gint preview_expose_event_handler_end(GtkWidget *window, GdkEvent *event, if (!p->gc_selection) { DBG(DBG_info, "defining line styles for selection and page frames\n"); - colormap = gdk_window_get_colormap(p->window->window); + colormap = gdk_drawable_get_colormap(p->window->window); p->gc_selection = gdk_gc_new(p->window->window); gdk_gc_set_function(p->gc_selection, GDK_INVERT); @@ -3283,8 +4051,8 @@ static gint preview_expose_event_handler_end(GtkWidget *window, GdkEvent *event, } else { - p->selection.active = expose_event_selection_active; - p->selection_maximum.active = expose_event_selection_maximum_active; + p->selection.active = expose_event_selection_active; + p->selection_maximum.active = expose_event_selection_maximum_active; preview_draw_selection(p); /* draw selections again */ } } @@ -3335,8 +4103,8 @@ static void preview_create_preset_area_menu(Preview *p, int selection) { preset_area_item = gtk_menu_item_new_with_label(preferences.preset_area[i]->name); gtk_container_add(GTK_CONTAINER(preset_area_menu), preset_area_item); - gtk_signal_connect(GTK_OBJECT(preset_area_item), "button_press_event", (GtkSignalFunc) preview_preset_area_context_menu_callback, p); - gtk_signal_connect(GTK_OBJECT(preset_area_item), "activate", (GtkSignalFunc) preview_preset_area_callback, p); + g_signal_connect(GTK_OBJECT(preset_area_item), "button_press_event", (GtkSignalFunc) preview_preset_area_context_menu_callback, p); + g_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_object_set_data(GTK_OBJECT(preset_area_item), "Preview", (void *) p); @@ -3410,16 +4178,20 @@ Preview *preview_new(void) GtkWidget *table, *frame; GtkSignalFunc signal_func; GtkWidgetClass *class; - GtkWidget *vbox, *hbox; + GtkWidget *vbox, *action_box; + GtkWidget *outer_hbox, *middle_vbox; GdkCursor *cursor; GtkWidget *preset_area_option_menu; GtkWidget *rotation_option_menu, *rotation_menu, *rotation_item; + GtkWidget *ratio_option_menu, *ratio_menu, *ratio_item; GtkWidget *delete_images; GdkBitmap *mask; GdkPixmap *pixmap = NULL; + GtkWidget *pixmapwidget; Preview *p; int i; char buf[256]; + int ratio_nr = 0; DBG(DBG_proc, "preview_new\n"); @@ -3468,12 +4240,14 @@ Preview *preview_new(void) p->maximum_output_width = INF; /* full output with */ p->maximum_output_height = INF; /* full output height */ + p->block_update_maximum_output_size_clipping = FALSE; p->preview_colors = -1; p->invalid = TRUE; /* no valid preview */ + p->ratio = 0.0; #ifndef XSERVER_WITH_BUGGY_VISUALS - gtk_widget_push_visual(gtk_preview_get_visual()); + gtk_widget_push_visual(gtk_preview_get_visual()); /* this has no function for gtk+-2.0 */ #endif gtk_widget_push_colormap(gtk_preview_get_cmap()); @@ -3481,7 +4255,8 @@ Preview *preview_new(void) p->top = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(p->top), buf); xsane_set_window_icon(p->top, 0); - gtk_accel_group_attach(xsane.accelerator_group, GTK_OBJECT(p->top)); + gtk_window_add_accel_group(GTK_WINDOW(p->top), xsane.accelerator_group); + /* set the main vbox */ vbox = gtk_vbox_new(FALSE, 0); @@ -3489,39 +4264,51 @@ Preview *preview_new(void) gtk_container_add(GTK_CONTAINER(p->top), vbox); gtk_widget_show(vbox); - /* set the main hbox */ - hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); - gtk_widget_show(hbox); - /* top hbox for icons */ + /* the button_box (hbox) */ p->button_box = gtk_hbox_new(FALSE, 1); - gtk_container_set_border_width(GTK_CONTAINER(p->button_box), 1); + gtk_container_set_border_width(GTK_CONTAINER(p->button_box), 0); gtk_box_pack_start(GTK_BOX(vbox), p->button_box, FALSE, FALSE, 0); + + /* add new selection for batch scanning */ + p->add_batch = xsane_button_new_with_pixmap(p->top->window, p->button_box, add_batch_xpm, DESC_ADD_BATCH, (GtkSignalFunc) preview_add_batch, p); + + xsane_vseparator_new(p->button_box, 3); + /* White, gray and black pipette button */ p->pipette_white = xsane_button_new_with_pixmap(p->top->window, p->button_box, pipette_white_xpm, DESC_PIPETTE_WHITE, (GtkSignalFunc) preview_pipette_white, p); p->pipette_gray = xsane_button_new_with_pixmap(p->top->window, p->button_box, pipette_gray_xpm, DESC_PIPETTE_GRAY, (GtkSignalFunc) preview_pipette_gray, p); p->pipette_black = xsane_button_new_with_pixmap(p->top->window, p->button_box, pipette_black_xpm, DESC_PIPETTE_BLACK, (GtkSignalFunc) preview_pipette_black, p); + xsane_vseparator_new(p->button_box, 3); + /* Zoom not, zoom out and zoom in button */ - p->zoom_not = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_not_xpm, DESC_ZOOM_FULL, (GtkSignalFunc) preview_zoom_not, p); - p->zoom_out = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_out_xpm, DESC_ZOOM_OUT, (GtkSignalFunc) preview_zoom_out, p); - p->zoom_in = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_in_xpm, DESC_ZOOM_IN, (GtkSignalFunc) preview_zoom_in, p); - p->zoom_undo = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_undo_xpm, DESC_ZOOM_UNDO, (GtkSignalFunc) preview_zoom_undo, p); - p->full_area = xsane_button_new_with_pixmap(p->top->window, p->button_box, auto_select_preview_area_xpm, DESC_AUTOSELECT_SCANAREA, (GtkSignalFunc) preview_autoselect_scanarea_callback, p); - p->autoselect = xsane_button_new_with_pixmap(p->top->window, p->button_box, full_preview_area_xpm, DESC_FULL_PREVIEW_AREA, (GtkSignalFunc) preview_full_preview_area_callback, p); - delete_images = xsane_button_new_with_pixmap(p->top->window, p->button_box, delete_images_xpm, DESC_DELETE_IMAGES, (GtkSignalFunc) preview_delete_images_callback, p); - - gtk_widget_add_accelerator(p->zoom_not, "clicked", xsane.accelerator_group, GDK_KP_Multiply, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_* */ - gtk_widget_add_accelerator(p->zoom_out, "clicked", xsane.accelerator_group, GDK_KP_Subtract, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_- */ - gtk_widget_add_accelerator(p->zoom_in, "clicked", xsane.accelerator_group, GDK_KP_Add, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_+ */ - gtk_widget_add_accelerator(p->zoom_undo, "clicked", xsane.accelerator_group, GDK_KP_Divide, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_/ */ - gtk_widget_add_accelerator(p->full_area, "clicked", xsane.accelerator_group, GDK_A, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_* */ - gtk_widget_add_accelerator(p->autoselect, "clicked", xsane.accelerator_group, GDK_V, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_* */ - gtk_widget_add_accelerator(delete_images, "clicked", xsane.accelerator_group, GDK_KP_Delete, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt keypad_* */ + p->zoom_not = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_not_xpm, DESC_ZOOM_FULL, (GtkSignalFunc) preview_zoom_not, p); + p->zoom_out = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_out_xpm, DESC_ZOOM_OUT, (GtkSignalFunc) preview_zoom_out, p); + p->zoom_in = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_in_xpm, DESC_ZOOM_IN, (GtkSignalFunc) preview_zoom_in, p); + p->zoom_area = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_area_xpm, DESC_ZOOM_AREA, (GtkSignalFunc) preview_zoom_area, p); + p->zoom_undo = xsane_button_new_with_pixmap(p->top->window, p->button_box, zoom_undo_xpm, DESC_ZOOM_UNDO, (GtkSignalFunc) preview_zoom_undo, p); + + xsane_vseparator_new(p->button_box, 3); + + p->full_area = xsane_button_new_with_pixmap(p->top->window, p->button_box, auto_select_preview_area_xpm, DESC_AUTOSELECT_SCANAREA, (GtkSignalFunc) preview_autoselect_scanarea_callback, p); + p->autoraise = xsane_button_new_with_pixmap(p->top->window, p->button_box, auto_raise_preview_area_xpm, DESC_AUTORAISE_SCANAREA, (GtkSignalFunc) preview_init_autoraise_scanarea, p); + p->autoselect = xsane_button_new_with_pixmap(p->top->window, p->button_box, full_preview_area_xpm, DESC_FULL_PREVIEW_AREA, (GtkSignalFunc) preview_full_preview_area_callback, p); + + xsane_vseparator_new(p->button_box, 3); + + delete_images = xsane_button_new_with_pixmap(p->top->window, p->button_box, delete_images_xpm, DESC_DELETE_IMAGES, (GtkSignalFunc) preview_delete_images_callback, p); + + gtk_widget_add_accelerator(p->zoom_not, "clicked", xsane.accelerator_group, GDK_KP_Multiply, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_* */ + gtk_widget_add_accelerator(p->zoom_out, "clicked", xsane.accelerator_group, GDK_KP_Subtract, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_- */ + gtk_widget_add_accelerator(p->zoom_in, "clicked", xsane.accelerator_group, GDK_KP_Add, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_+ */ + gtk_widget_add_accelerator(p->zoom_area, "clicked", xsane.accelerator_group, GDK_KP_Enter, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_Enter */ + gtk_widget_add_accelerator(p->zoom_undo, "clicked", xsane.accelerator_group, GDK_KP_Divide, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_/ */ + gtk_widget_add_accelerator(p->full_area, "clicked", xsane.accelerator_group, GDK_A, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_* */ + gtk_widget_add_accelerator(p->autoselect, "clicked", xsane.accelerator_group, GDK_V, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_* */ + gtk_widget_add_accelerator(delete_images, "clicked", xsane.accelerator_group, GDK_KP_Delete, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt keypad_* */ 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 */ @@ -3530,42 +4317,157 @@ Preview *preview_new(void) gtk_widget_set_sensitive(p->autoselect, FALSE); /* no selection */ + gtk_widget_show(p->button_box); + /* the button box is ready */ + + + + /* 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), 1); + gtk_box_pack_start(GTK_BOX(vbox), table, /* expand */ TRUE, /* fill */ TRUE, /* padding */ 0); + gtk_widget_show(table); + + /* 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); + gtk_widget_show(frame); + + /* the unit label */ + p->unit_label = gtk_label_new("cm"); + gtk_container_add(GTK_CONTAINER(frame), p->unit_label); + gtk_widget_show(p->unit_label); + + /* 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); + gtk_widget_show(p->hruler); + + /* 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); + gtk_widget_show(p->vruler); + + /* 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); + + /* the first expose_event is responsible to undraw the selection frame */ + g_signal_connect(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_event_handler_start, p); + g_signal_connect(GTK_OBJECT(p->window), "button_press_event", (GtkSignalFunc) preview_button_press_event_handler, p); + g_signal_connect(GTK_OBJECT(p->window), "motion_notify_event", (GtkSignalFunc) preview_motion_event_handler, p); + g_signal_connect(GTK_OBJECT(p->window), "button_release_event", (GtkSignalFunc) preview_button_release_event_handler, p); + + g_signal_connect_after(GTK_OBJECT(p->window), "size_allocate", (GtkSignalFunc) preview_area_resize_handler, p); + /* the second expose_event is responsible to redraw the selection frame */ + g_signal_connect_after(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_event_handler_end, p); + + /* Connect the motion-notify events of the preview area with the rulers. Nifty stuff! */ + +#ifdef HAVE_GTK2 + class = (GtkWidgetClass *) GTK_HSCROLLBAR_GET_CLASS(p->hruler); +#else + class = GTK_WIDGET_CLASS(GTK_OBJECT(p->hruler)->klass); +#endif + + signal_func = (GtkSignalFunc) class->motion_notify_event; + g_signal_connect_swapped(GTK_OBJECT(p->window), "motion_notify_event", signal_func, GTK_OBJECT(p->hruler)); + +#ifdef HAVE_GTK2 + class = (GtkWidgetClass *) GTK_VSCROLLBAR_GET_CLASS(p->vruler); +#else + class = GTK_WIDGET_CLASS(GTK_OBJECT(p->vruler)->klass); +#endif + + signal_func = (GtkSignalFunc) class->motion_notify_event; + g_signal_connect_swapped(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_widget_show(p->viewport); + + 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); + /* the preview area is ready */ + + + + /* the outer hbox at the bottom */ + outer_hbox = gtk_hbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(outer_hbox), 1); + gtk_box_pack_start(GTK_BOX(vbox), outer_hbox, FALSE, FALSE, 0); + gtk_widget_show(outer_hbox); + + /* the middle vbox at the bottom */ + middle_vbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(middle_vbox), 1); + gtk_box_pack_start(GTK_BOX(outer_hbox), middle_vbox, FALSE, FALSE, 0); + gtk_widget_show(middle_vbox); + + /* the menu_box (hbox) */ + p->menu_box = gtk_hbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(p->menu_box), 1); + gtk_box_pack_start(GTK_BOX(middle_vbox), p->menu_box, FALSE, FALSE, 0); + + xsane_separator_new(middle_vbox, 1); + + /* select maximum scanarea */ + pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) size_xpm); + pixmapwidget = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(p->menu_box), pixmapwidget, FALSE, FALSE, 2); + gtk_widget_show(pixmapwidget); + gdk_drawable_unref(pixmap); preset_area_option_menu = gtk_option_menu_new(); xsane_back_gtk_set_tooltip(xsane.tooltips, preset_area_option_menu, DESC_PRESET_AREA); - gtk_box_pack_start(GTK_BOX(p->button_box), preset_area_option_menu, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(p->menu_box), preset_area_option_menu, FALSE, FALSE, 0); gtk_widget_show(preset_area_option_menu); p->preset_area_option_menu = preset_area_option_menu; preview_create_preset_area_menu(p, 0); /* build menu and set default to 0=full size */ + xsane_vseparator_new(p->menu_box, 3); /* select rotation */ + pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) rotation_xpm); + pixmapwidget = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(p->menu_box), pixmapwidget, FALSE, FALSE, 2); + gtk_widget_show(pixmapwidget); + gdk_drawable_unref(pixmap); + rotation_menu = gtk_menu_new(); for (i = 0; i < 12; ++i) { - char buffer[256]; int rot; if (i<4) { - snprintf(buffer, sizeof(buffer), "%03d ", i*90); + snprintf(buf, sizeof(buf), "%03d ", i*90); rot = i; } else if (i<8) { - snprintf(buffer, sizeof(buffer), "%03d |", i*90-360); + snprintf(buf, sizeof(buf), "%03d |", i*90-360); rot = i; } else { - snprintf(buffer, sizeof(buffer), "%03d -", i*90-2*360); + snprintf(buf, sizeof(buf), "%03d -", i*90-2*360); rot = (((i & 3) + 2) & 3) + 4; } - rotation_item = gtk_menu_item_new_with_label(buffer); + rotation_item = gtk_menu_item_new_with_label(buf); gtk_container_add(GTK_CONTAINER(rotation_menu), rotation_item); - gtk_signal_connect(GTK_OBJECT(rotation_item), "activate", (GtkSignalFunc) preview_rotation_callback, p); + g_signal_connect(GTK_OBJECT(rotation_item), "activate", (GtkSignalFunc) preview_rotation_callback, p); gtk_object_set_data(GTK_OBJECT(rotation_item), "Selection", (void *) rot); gtk_widget_show(rotation_item); @@ -3573,133 +4475,143 @@ Preview *preview_new(void) rotation_option_menu = gtk_option_menu_new(); xsane_back_gtk_set_tooltip(xsane.tooltips, rotation_option_menu, DESC_ROTATION); - gtk_box_pack_start(GTK_BOX(p->button_box), rotation_option_menu, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(p->menu_box), rotation_option_menu, FALSE, FALSE, 0); gtk_option_menu_set_menu(GTK_OPTION_MENU(rotation_option_menu), rotation_menu); gtk_option_menu_set_history(GTK_OPTION_MENU(rotation_option_menu), p->rotation); /* set rotation */ -/* xsane_back_gtk_set_tooltip(tooltips, rotation_option_menu, desc); */ gtk_widget_show(rotation_option_menu); p->rotation_option_menu = rotation_option_menu; + xsane_vseparator_new(p->menu_box, 3); - gtk_widget_show(p->button_box); - + /* the preview aspect ratio menu */ + pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) aspect_ratio_xpm); + pixmapwidget = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(p->menu_box), pixmapwidget, FALSE, FALSE, 2); + gtk_widget_show(pixmapwidget); + gdk_drawable_unref(pixmap); + ratio_menu = gtk_menu_new(); - /* 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), 1); - gtk_box_pack_start(GTK_BOX(vbox), table, /* expand */ TRUE, /* fill */ TRUE, /* padding */ 0); + for (i = 0; i < sizeof(ratio_value)/sizeof(float); ++i) + { + ratio_item = gtk_menu_item_new_with_label(ratio_string[i]); + gtk_container_add(GTK_CONTAINER(ratio_menu), ratio_item); + g_signal_connect(GTK_OBJECT(ratio_item), "activate", (GtkSignalFunc) preview_ratio_callback, p); + gtk_object_set_data(GTK_OBJECT(ratio_item), "Selection", &ratio_value[i]); - /* 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); + gtk_widget_show(ratio_item); - /* the unit label */ - p->unit_label = gtk_label_new("cm"); - gtk_container_add(GTK_CONTAINER(frame), p->unit_label); - gtk_widget_show(p->unit_label); + if (ratio_value[i] == p->ratio) + { + ratio_nr = i; + } + } - /* 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); + ratio_option_menu = gtk_option_menu_new(); + xsane_back_gtk_set_tooltip(xsane.tooltips, ratio_option_menu, DESC_RATIO); + gtk_box_pack_start(GTK_BOX(p->menu_box), ratio_option_menu, FALSE, FALSE, 0); + gtk_option_menu_set_menu(GTK_OPTION_MENU(ratio_option_menu), ratio_menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(ratio_option_menu), ratio_nr); /* set ratio */ - /* 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); + gtk_widget_show(ratio_option_menu); + p->ratio_option_menu = ratio_option_menu; - /* 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); - - /* the first expose_event is responsible to undraw the selection frame */ - gtk_signal_connect(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_event_handler_start, p); - gtk_signal_connect(GTK_OBJECT(p->window), "button_press_event", (GtkSignalFunc) preview_button_press_event_handler, p); - gtk_signal_connect(GTK_OBJECT(p->window), "motion_notify_event", (GtkSignalFunc) preview_motion_event_handler, p); - gtk_signal_connect(GTK_OBJECT(p->window), "button_release_event", (GtkSignalFunc) preview_button_release_event_handler, p); - gtk_signal_connect_after(GTK_OBJECT(p->window), "size_allocate", (GtkSignalFunc) preview_area_resize_handler, p); - /* the second expose_event is responsible to redraw the selection frame */ - gtk_signal_connect_after(GTK_OBJECT(p->window), "expose_event", (GtkSignalFunc) preview_expose_event_handler_end, p); + /* the pointer zoom */ + frame = gtk_frame_new(0); + gtk_box_pack_start(GTK_BOX(outer_hbox), frame, FALSE, FALSE, 3); + gtk_container_set_border_width(GTK_CONTAINER(frame), 0); + gtk_widget_show(frame); + p->zoom = gtk_preview_new(GTK_PREVIEW_COLOR); + gtk_preview_size(GTK_PREVIEW(p->zoom), XSANE_ZOOM_SIZE, XSANE_ZOOM_SIZE); + gtk_container_add(GTK_CONTAINER(frame), p->zoom); + gtk_widget_show(p->zoom); - /* 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)); +#if 0 + /* the RGB label */ + frame = gtk_frame_new(0); + gtk_box_pack_start(GTK_BOX(p->menu_box), frame, FALSE, FALSE, 3); + gtk_widget_show(frame); + p->rgb_label = gtk_label_new(0); + gtk_container_add(GTK_CONTAINER(frame), p->rgb_label); + gtk_widget_show(p->rgb_label); + preview_display_color_components(p, -1, -1); /* display "###, ###, ###" */ +#endif + gtk_widget_show(p->menu_box); + /* the menu box is ready */ - 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); + /* set the action_hbox */ + action_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(middle_vbox), action_box, FALSE, FALSE, 2); + gtk_container_set_border_width(GTK_CONTAINER(action_box), 0); + gtk_widget_show(action_box); - /* fill in action area: */ /* the (in)valid pixmaps */ pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) valid_xpm); - p->valid_pixmap = gtk_pixmap_new(pixmap, mask); - gtk_box_pack_start(GTK_BOX(hbox), p->valid_pixmap, FALSE, FALSE, 0); + p->valid_pixmap = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(action_box), p->valid_pixmap, FALSE, FALSE, 0); gtk_widget_show(p->valid_pixmap); - gdk_pixmap_unref(pixmap); + gdk_drawable_unref(pixmap); pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) scanning_xpm); - p->scanning_pixmap = gtk_pixmap_new(pixmap, mask); - gtk_box_pack_start(GTK_BOX(hbox), p->scanning_pixmap, FALSE, FALSE, 0); + p->scanning_pixmap = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(action_box), p->scanning_pixmap, FALSE, FALSE, 0); gtk_widget_show(p->scanning_pixmap); - gdk_pixmap_unref(pixmap); + gdk_drawable_unref(pixmap); pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) incomplete_xpm); - p->incomplete_pixmap = gtk_pixmap_new(pixmap, mask); - gtk_box_pack_start(GTK_BOX(hbox), p->incomplete_pixmap, FALSE, FALSE, 0); + p->incomplete_pixmap = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(action_box), p->incomplete_pixmap, FALSE, FALSE, 0); gtk_widget_show(p->incomplete_pixmap); - gdk_pixmap_unref(pixmap); + gdk_drawable_unref(pixmap); pixmap = gdk_pixmap_create_from_xpm_d(p->top->window, &mask, xsane.bg_trans, (gchar **) invalid_xpm); - p->invalid_pixmap = gtk_pixmap_new(pixmap, mask); - gtk_box_pack_start(GTK_BOX(hbox), p->invalid_pixmap, FALSE, FALSE, 0); + p->invalid_pixmap = gtk_image_new_from_pixmap(pixmap, mask); + gtk_box_pack_start(GTK_BOX(action_box), p->invalid_pixmap, FALSE, FALSE, 0); gtk_widget_show(p->invalid_pixmap); - gdk_pixmap_unref(pixmap); + gdk_drawable_unref(pixmap); + /* Start button */ p->start = gtk_button_new_with_label(BUTTON_PREVIEW_ACQUIRE); xsane_back_gtk_set_tooltip(xsane.tooltips, p->start, DESC_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, 10); - gtk_widget_add_accelerator(p->start, "clicked", xsane.accelerator_group, GDK_P, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt P */ + g_signal_connect(GTK_OBJECT(p->start), "clicked", (GtkSignalFunc) preview_start_button_clicked, p); + gtk_box_pack_start(GTK_BOX(action_box), p->start, TRUE, TRUE, 5); + gtk_widget_add_accelerator(p->start, "clicked", xsane.accelerator_group, GDK_P, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt P */ + gtk_widget_show(p->start); /* Cancel button */ p->cancel = gtk_button_new_with_label(BUTTON_PREVIEW_CANCEL); xsane_back_gtk_set_tooltip(xsane.tooltips, p->cancel, DESC_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, 10); - gtk_widget_add_accelerator(p->cancel, "clicked", xsane.accelerator_group, GDK_Escape, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); /* Alt ESC */ + g_signal_connect(GTK_OBJECT(p->cancel), "clicked", (GtkSignalFunc) preview_cancel_button_clicked, p); + gtk_box_pack_start(GTK_BOX(action_box), p->cancel, TRUE, TRUE, 5); + gtk_widget_add_accelerator(p->cancel, "clicked", xsane.accelerator_group, GDK_Escape, GDK_MOD1_MASK, DEF_GTK_ACCEL_LOCKED); /* Alt ESC */ + gtk_widget_show(p->cancel); 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); +#if 1 + /* the RGB label */ + frame = gtk_frame_new(0); + gtk_box_pack_start(GTK_BOX(action_box), frame, FALSE, FALSE, 3); gtk_widget_show(frame); - gtk_widget_show(table); + p->rgb_label = gtk_label_new(0); + gtk_container_add(GTK_CONTAINER(frame), p->rgb_label); + gtk_widget_show(p->rgb_label); + preview_display_color_components(p, -1, -1); /* display "###, ###, ###" */ +#endif + + preview_update_surface(p, 0); + + gtk_widget_show(p->window); 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); + gdk_cursor_unref(cursor); p->cursornr = XSANE_CURSOR_PREVIEW; gtk_widget_pop_colormap(); @@ -3875,6 +4787,7 @@ void preview_update_surface(Preview *p, int surface_changed) { gtk_widget_set_sensitive(p->preset_area_option_menu, TRUE); /* enable preset area */ gtk_widget_set_sensitive(p->zoom_in, TRUE); /* zoom in is allowed at all */ + gtk_widget_set_sensitive(p->zoom_area, TRUE); /* zoom area is allowed at all */ gtk_widget_set_sensitive(p->full_area, TRUE); /* enable selection buttons */ gtk_widget_set_sensitive(p->autoselect, TRUE); } @@ -3882,6 +4795,7 @@ void preview_update_surface(Preview *p, int surface_changed) { gtk_widget_set_sensitive(p->preset_area_option_menu, FALSE); /* disable preset area */ gtk_widget_set_sensitive(p->zoom_in, FALSE); /* no zoom at all */ + gtk_widget_set_sensitive(p->zoom_area, FALSE); gtk_widget_set_sensitive(p->zoom_out, FALSE); gtk_widget_set_sensitive(p->zoom_undo, FALSE); gtk_widget_set_sensitive(p->zoom_not, FALSE); @@ -3906,7 +4820,11 @@ void preview_update_surface(Preview *p, int surface_changed) width = p->surface[p->index_xmax] - p->surface[p->index_xmin]; height = p->surface[p->index_ymax] - p->surface[p->index_ymin]; +#if 0 if ( (p->calibration) || (p->startimage) ) /* predefined image should have constant aspect */ +#else + if (p->calibration) /* predefined calibration image should have constant aspect */ +#endif { p->aspect = fabs(p->image_width/(float) p->image_height); } @@ -3942,7 +4860,7 @@ void preview_update_surface(Preview *p, int surface_changed) p->preview_window_width = 0.3 * gdk_screen_width(); p->preview_window_height = 0.5 * gdk_screen_height(); preview_area_correct(p); /* calculate preview_width and height */ - gtk_widget_set_usize(GTK_WIDGET(p->window), p->preview_width, p->preview_height); + gtk_widget_set_size_request(GTK_WIDGET(p->window), p->preview_width, p->preview_height); } else if (surface_changed) /* establish new surface */ { @@ -3967,6 +4885,7 @@ void preview_update_surface(Preview *p, int surface_changed) /* ---------------------------------------------------------------------------------------------------------------------- */ +/* preview_scan is called once when the "Preview scan" button is pressed */ void preview_scan(Preview *p) { double min, max, swidth, sheight, width, height, dpi = 0; @@ -3974,6 +4893,14 @@ void preview_scan(Preview *p) gint gwidth, gheight; int i; float dsurface[4]; + 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; DBG(DBG_proc, "preview_scan\n"); @@ -3995,10 +4922,6 @@ void preview_scan(Preview *p) preview_save_option(p, xsane.well_known.bit_depth, &p->saved_bit_depth, &p->saved_bit_depth_valid); -#if 0 - xsane_set_medium(preferences.medium[xsane.medium_nr]); /* make sure medium gamma values are up to date */ -#endif - /* determine dpi, if necessary: */ if (xsane.well_known.dpi > 0) @@ -4066,6 +4989,7 @@ void preview_scan(Preview *p) xsane_set_resolution(xsane.well_known.dpi_y, dpi); /* set resolution to dpi or next higher value that is available */ } + preview_rotate_previewsurface_to_devicesurface(p->rotation, p->surface, dsurface); for (i = 0; i < 4; ++i) @@ -4087,6 +5011,97 @@ void preview_scan(Preview *p) } #endif + + if (xsane.well_known.gamma_vector >0) + { + const SANE_Option_Descriptor *opt; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector); + if (SANE_OPTION_IS_ACTIVE(opt->cap)) + { + SANE_Int *gamma_data; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.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)); + + if ((xsane.xsane_colors > 1) || (xsane.no_preview_medium_gamma)) /* color scan or medium preview gamma disabled */ + { + xsane_create_gamma_curve(gamma_data, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_gray_size, gamma_gray_max); + } + else /* grayscale scan */ + { + xsane_create_gamma_curve(gamma_data, xsane.medium_negative, 1.0, 0.0, 0.0, + xsane.medium_shadow_gray, xsane.medium_highlight_gray, xsane.medium_gamma_gray, + gamma_gray_size, gamma_gray_max); + } + + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector, gamma_data); + free(gamma_data); + } + } + + if (xsane.well_known.gamma_vector_r >0) + { + const SANE_Option_Descriptor *opt; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_r); + if (SANE_OPTION_IS_ACTIVE(opt->cap)) + { + SANE_Int *gamma_data_red, *gamma_data_green, *gamma_data_blue; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_r); + gamma_red_size = opt->size / sizeof(opt->type); + gamma_red_max = opt->constraint.range->max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.well_known.gamma_vector_g); + gamma_green_size = opt->size / sizeof(opt->type); + gamma_green_max = opt->constraint.range->max; + + opt = xsane_get_option_descriptor(xsane.dev, xsane.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)); + + if (xsane.no_preview_medium_gamma) /* do not use medium gamma for preview */ + { + DBG(DBG_info, "preview: not using medium gamma table\n"); + + xsane_create_gamma_curve(gamma_data_red, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_red_size, gamma_red_max); + xsane_create_gamma_curve(gamma_data_green, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_green_size, gamma_green_max); + xsane_create_gamma_curve(gamma_data_blue, 0, 1.0, 0.0, 0.0, 0.0, 100.0, 1.0, gamma_blue_size, gamma_blue_max); + } + else /* use medium gamma for preview */ + { + DBG(DBG_info, "preview: using medium gamma table\n"); + + xsane_create_gamma_curve(gamma_data_red, xsane.medium_negative, 1.0, 0.0, 0.0, + xsane.medium_shadow_red, xsane.medium_highlight_red, xsane.medium_gamma_red, + gamma_red_size, gamma_red_max); + xsane_create_gamma_curve(gamma_data_green, xsane.medium_negative, 1.0, 0.0, 0.0, + xsane.medium_shadow_green, xsane.medium_highlight_green, xsane.medium_gamma_green, + gamma_green_size, gamma_green_max); + xsane_create_gamma_curve(gamma_data_blue, xsane.medium_negative, 1.0, 0.0, 0.0, + xsane.medium_shadow_blue, xsane.medium_highlight_blue, xsane.medium_gamma_blue, + gamma_blue_size, gamma_blue_max); + } + + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_r, gamma_data_red); + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_g, gamma_data_green); + xsane_back_gtk_update_vector(xsane.well_known.gamma_vector_b, gamma_data_blue); + + free(gamma_data_red); + free(gamma_data_green); + free(gamma_data_blue); + } + } + + xsane.block_update_param = FALSE; p->preview_colors = xsane.xsane_colors; p->scan_incomplete = FALSE; @@ -4189,6 +5204,8 @@ static void preview_delete_images(Preview *p) fclose(out); } preview_update_surface(p, 1); + + xsane_batch_scan_update_icon_list(); } /* ---------------------------------------------------------------------------------------------------------------------- */ @@ -4232,12 +5249,12 @@ void preview_destroy(Preview *p) if (p->gc_selection) { - gdk_gc_destroy(p->gc_selection); + gdk_gc_unref(p->gc_selection); } if (p->gc_selection_maximum) { - gdk_gc_destroy(p->gc_selection_maximum); + gdk_gc_unref(p->gc_selection_maximum); } if (p->top) @@ -4251,6 +5268,27 @@ void preview_destroy(Preview *p) /* ---------------------------------------------------------------------------------------------------------------------- */ +static void preview_zoom_area(GtkWidget *window, gpointer data) +{ + Preview *p=data; + int i; + + DBG(DBG_proc, "preview_zoom_area\n"); + + for (i=0; i<4; i++) + { + p->old_surface[i] = p->surface[i]; + p->surface[i] = p->selection.coordinate[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,TRUE); /* allow zoom undo */ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + static void preview_zoom_not(GtkWidget *window, gpointer data) { Preview *p=data; @@ -4320,19 +5358,25 @@ static void preview_zoom_out(GtkWidget *window, gpointer data) /* ---------------------------------------------------------------------------------------------------------------------- */ -static void preview_zoom_in(GtkWidget *window, gpointer data) +static void preview_select_zoom_point(Preview *p, int preview_x, int preview_y) { - Preview *p=data; int i; + float device_x, device_y; - DBG(DBG_proc, "preview_zoom_in\n"); + DBG(DBG_proc, "preview_select_zoom_point(%d, %d)\n", preview_x, preview_y); + + preview_transform_coordinate_window_to_device(p, preview_x, preview_y, &device_x, &device_y); for (i=0; i<4; i++) { p->old_surface[i] = p->surface[i]; - p->surface[i] = p->selection.coordinate[i]; } + p->surface[0] = device_x + (p->surface[0] - device_x) * 0.8; + p->surface[1] = device_y + (p->surface[1] - device_y) * 0.8; + p->surface[2] = device_x + (p->surface[2] - device_x) * 0.8; + p->surface[3] = device_y + (p->surface[3] - device_y) * 0.8; + 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 */ @@ -4375,7 +5419,7 @@ static void preview_get_color(Preview *p, int x, int y, int range, int *red, int { preview_transform_coordinate_window_to_image(p, x, y, &image_x, &image_y); - if ( (image_x < p->image_width) && (image_y < p->image_height) ) + if ( (image_x >= 0) && (image_x < p->image_width) && (image_y >=0) && (image_y < p->image_height) ) { image_x_min = image_x - range/2; image_y_min = image_y - range/2; @@ -4423,6 +5467,20 @@ static void preview_get_color(Preview *p, int x, int y, int range, int *red, int /* ---------------------------------------------------------------------------------------------------------------------- */ +static void preview_add_batch(GtkWidget *window, Preview *data) +{ + DBG(DBG_proc, "preview_add_batch\n"); + + xsane_batch_scan_add(); /* add active settings to batch list */ + +#if 0 + preview_draw_selection(p); /* read selection from backend: correct rotation */ + preview_establish_selection(p); /* read selection from backend: correct rotation */ +#endif +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + static void preview_pipette_white(GtkWidget *window, gpointer data) { Preview *p=data; @@ -4450,7 +5508,7 @@ static void preview_pipette_white(GtkWidget *window, gpointer data) 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); + gdk_cursor_unref(cursor); p->cursornr = -1; } @@ -4483,7 +5541,7 @@ static void preview_pipette_gray(GtkWidget *window, gpointer data) 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); + gdk_cursor_unref(cursor); p->cursornr = -1; } @@ -4516,12 +5574,77 @@ static void preview_pipette_black(GtkWidget *window, gpointer data) 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); + gdk_cursor_unref(cursor); + p->cursornr = -1; +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void preview_init_autoraise_scanarea(GtkWidget *window, gpointer data) +{ + Preview *p=data; + GdkCursor *cursor; + GdkColor fg; + GdkColor bg; + GdkPixmap *pixmap; + GdkPixmap *mask; + + DBG(DBG_proc, "preview_init_autoraise_scanarea\n"); + + p->mode = MODE_AUTORAISE_SCANAREA; + + pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_autoraise_scanarea, CURSOR_AUTORAISE_SCANAREA_WIDTH, CURSOR_AUTORAISE_SCANAREA_HEIGHT); + mask = gdk_bitmap_create_from_data(p->top->window, cursor_autoraise_scanarea_mask, CURSOR_AUTORAISE_SCANAREA_WIDTH, CURSOR_AUTORAISE_SCANAREA_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_AUTORAISE_SCANAREA_HOT_X, CURSOR_AUTORAISE_SCANAREA_HOT_Y); + + gdk_window_set_cursor(p->window->window, cursor); + gdk_cursor_unref(cursor); p->cursornr = -1; } /* ---------------------------------------------------------------------------------------------------------------------- */ +static void preview_zoom_in(GtkWidget *window, gpointer data) +{ + Preview *p=data; + GdkCursor *cursor; + GdkColor fg; + GdkColor bg; + GdkPixmap *pixmap; + GdkPixmap *mask; + + DBG(DBG_proc, "preview_zoom\n"); + + p->mode = MODE_ZOOM_IN; + + pixmap = gdk_bitmap_create_from_data(p->top->window, cursor_zoom, CURSOR_ZOOM_WIDTH, CURSOR_ZOOM_HEIGHT); + mask = gdk_bitmap_create_from_data(p->top->window, cursor_zoom_mask, CURSOR_ZOOM_WIDTH, CURSOR_ZOOM_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_ZOOM_HOT_X, CURSOR_ZOOM_HOT_Y); + + gdk_window_set_cursor(p->window->window, cursor); + gdk_cursor_unref(cursor); + p->cursornr = -1; +} +/* ---------------------------------------------------------------------------------------------------------------------- */ + void preview_select_full_preview_area(Preview *p) { int i; @@ -4566,32 +5689,15 @@ static void preview_delete_images_callback(GtkWidget *widget, gpointer call_data /* ---------------------------------------------------------------------------------------------------------------------- */ -int xsane_preset_area_entry_rename; - -static void xsane_preset_area_entry_rename_button_callback(GtkWidget *widget, gpointer data) -{ - DBG(DBG_proc, "xsane_preset_area_entry_rename\n"); - - xsane_preset_area_entry_rename = (int) data; -} - -/* ---------------------------------------------------------------------------------------------------------------------- */ - static gint preview_preset_area_rename_callback(GtkWidget *widget, GtkWidget *preset_area_widget) { int selection; char *oldname; char *newname; Preview *p; - GtkWidget *rename_dialog; - GtkWidget *text; - GtkWidget *button; - GtkWidget *vbox, *hbox; GtkWidget *old_preset_area_menu; - char buf[256]; int old_selection; - DBG(DBG_proc, "preview_preset_area_rename_callback\n"); selection = (int) gtk_object_get_data(GTK_OBJECT(preset_area_widget), "Selection"); @@ -4599,71 +5705,15 @@ static gint preview_preset_area_rename_callback(GtkWidget *widget, GtkWidget *pr DBG(DBG_info ,"rename %s\n", preferences.preset_area[selection]->name); + /* set menu in correct state, is a bit strange this way but I do not have a better idea */ old_preset_area_menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(p->preset_area_option_menu)); old_selection = (int) gtk_object_get_data(GTK_OBJECT(gtk_menu_get_active(GTK_MENU(old_preset_area_menu))), "Selection"); - gtk_menu_popdown(GTK_MENU(old_preset_area_menu)); - /* set menu in correct state, is a bit strange this way but I do not have a better idea */ gtk_option_menu_set_history(GTK_OPTION_MENU(p->preset_area_option_menu), old_selection); oldname = strdup(preferences.preset_area[selection]->name); - rename_dialog = gtk_window_new(GTK_WINDOW_DIALOG); - xsane_set_window_icon(rename_dialog, 0); - - /* set rename dialog */ - gtk_window_set_position(GTK_WINDOW(rename_dialog), GTK_WIN_POS_CENTER); - gtk_window_set_policy(GTK_WINDOW(rename_dialog), FALSE, FALSE, FALSE); - snprintf(buf, sizeof(buf), "%s %s", xsane.prog_name, WINDOW_PRESET_AREA_RENAME); - gtk_window_set_title(GTK_WINDOW(rename_dialog), buf); - gtk_signal_connect(GTK_OBJECT(rename_dialog), "delete_event", (GtkSignalFunc) xsane_preset_area_entry_rename_button_callback, (void *) -1); - gtk_widget_show(rename_dialog); - - /* set the main vbox */ - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 0); - gtk_container_add(GTK_CONTAINER(rename_dialog), vbox); - gtk_widget_show(vbox); - - /* set the main hbox */ - hbox = gtk_hbox_new(FALSE, 0); - xsane_separator_new(vbox, 2); - gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); - gtk_widget_show(hbox); - - text = gtk_entry_new_with_max_length(64); - xsane_back_gtk_set_tooltip(xsane.tooltips, text, DESC_PRESET_AREA_NAME); - gtk_entry_set_text(GTK_ENTRY(text), oldname); - gtk_widget_set_usize(text, 300, 0); - gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 4); - gtk_widget_show(text); - - - button = gtk_button_new_with_label("OK"); - gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_preset_area_entry_rename_button_callback, (void *) 1); - gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); - gtk_widget_show(button); - - button = gtk_button_new_with_label("Cancel"); - gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_preset_area_entry_rename_button_callback, (void *) -1); - gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); - gtk_widget_show(button); - - xsane_preset_area_entry_rename = 0; - - while (xsane_preset_area_entry_rename == 0) - { - while (gtk_events_pending()) - { - DBG(DBG_info, "preview_preset_area_rename_callback: calling gtk_main_iteration\n"); - gtk_main_iteration(); - } - } - - newname = strdup(gtk_entry_get_text(GTK_ENTRY(text))); - - if (xsane_preset_area_entry_rename == 1) /* OK button has been pressed */ + if (!xsane_front_gtk_getname_dialog(WINDOW_PRESET_AREA_RENAME, DESC_PRESET_AREA_RENAME, oldname, &newname)) { gtk_option_menu_remove_menu(GTK_OPTION_MENU(p->preset_area_option_menu)); @@ -4682,8 +5732,6 @@ static gint preview_preset_area_rename_callback(GtkWidget *widget, GtkWidget *pr free(oldname); free(newname); - gtk_widget_destroy(rename_dialog); - xsane_set_sensitivity(TRUE); return TRUE; /* event is handled */ @@ -4695,6 +5743,9 @@ static gint preview_preset_area_add_callback(GtkWidget *widget, GtkWidget *prese { int selection, i, old_selection = 0; Preview *p; + float coord[4]; + char suggested_name[256]; + char *newname; GtkWidget *old_preset_area_menu; DBG(DBG_proc, "preview_preset_area_add_callback\n"); @@ -4702,11 +5753,18 @@ static gint preview_preset_area_add_callback(GtkWidget *widget, GtkWidget *prese selection = (int) gtk_object_get_data(GTK_OBJECT(preset_area_widget), "Selection"); p = (Preview *) gtk_object_get_data(GTK_OBJECT(preset_area_widget), "Preview"); - if (selection < preferences.preset_area_definitions) - { - char buf[256]; - float coord[4]; + /* set menu in correct state, is a bit strange this way but I do not have a better idea */ + old_preset_area_menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(p->preset_area_option_menu)); + old_selection = (int) gtk_object_get_data(GTK_OBJECT(gtk_menu_get_active(GTK_MENU(old_preset_area_menu))), "Selection"); + gtk_menu_popdown(GTK_MENU(old_preset_area_menu)); + gtk_option_menu_set_history(GTK_OPTION_MENU(p->preset_area_option_menu), old_selection); + + /* sugggest name = size in mm */ + preview_rotate_previewsurface_to_devicesurface(p->rotation, p->selection.coordinate, coord); + snprintf(suggested_name, sizeof(suggested_name), "%d mm x %d mm", (int) (coord[2]-coord[0]), (int) (coord[3]-coord[1])); + if (!xsane_front_gtk_getname_dialog(WINDOW_PRESET_AREA_ADD, DESC_PRESET_AREA_ADD, suggested_name, &newname)) + { preferences.preset_area = realloc(preferences.preset_area, (preferences.preset_area_definitions+1) * sizeof(void *)); /* shift all items after selection */ @@ -4715,32 +5773,18 @@ static gint preview_preset_area_add_callback(GtkWidget *widget, GtkWidget *prese preferences.preset_area[i+1] = preferences.preset_area[i]; } - /* insert new item behind selected item, name is size in mm */ - preview_rotate_previewsurface_to_devicesurface(p->rotation, p->selection.coordinate, coord); - snprintf(buf, sizeof(buf), "%d mm x %d mm", (int) (coord[2]-coord[0]), (int) (coord[3]-coord[1])); + /* insert new item behind selected item */ preferences.preset_area[selection+1] = calloc(sizeof(Preferences_preset_area_t), 1); - preferences.preset_area[selection+1]->name = strdup(buf); + preferences.preset_area[selection+1]->name = strdup(newname); preferences.preset_area[selection+1]->xoffset = coord[0]; preferences.preset_area[selection+1]->yoffset = coord[1]; preferences.preset_area[selection+1]->width = coord[2] - coord[0]; preferences.preset_area[selection+1]->height = coord[3] - coord[1]; - DBG(DBG_proc, "added %s\n", buf); + DBG(DBG_proc, "added %s\n", newname); preferences.preset_area_definitions++; - old_preset_area_menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(p->preset_area_option_menu)); - - gtk_option_menu_remove_menu(GTK_OPTION_MENU(p->preset_area_option_menu)); - old_selection = (int) gtk_object_get_data(GTK_OBJECT(gtk_menu_get_active(GTK_MENU(old_preset_area_menu))), "Selection"); - - if (old_selection > selection) /* we are moving the selected surface */ - { - old_selection++; - } - - gtk_widget_destroy(old_preset_area_menu); - preview_create_preset_area_menu(p, old_selection); } @@ -4895,6 +5939,7 @@ static gint preview_preset_area_context_menu_callback(GtkWidget *widget, GdkEven GtkWidget *menu_item; GdkEventButton *event_button; int selection; + char buf[256]; DBG(DBG_proc, "preview_preset_area_context_menu_callback\n"); @@ -4912,42 +5957,51 @@ static gint preview_preset_area_context_menu_callback(GtkWidget *widget, GdkEven menu_item = gtk_menu_item_new_with_label(MENU_ITEM_PRESET_AREA_ADD_SEL); gtk_widget_show(menu_item); gtk_container_add(GTK_CONTAINER(menu), menu_item); - gtk_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_add_callback, widget); + g_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_add_callback, widget); + + /* add separator */ + menu_item = gtk_menu_item_new(); + gtk_widget_show(menu_item); + gtk_container_add(GTK_CONTAINER(menu), menu_item); /* rename preset area */ - menu_item = gtk_menu_item_new_with_label(MENU_ITEM_PRESET_AREA_RENAME); + snprintf(buf, sizeof(buf), "%s: %s", preferences.preset_area[selection]->name, MENU_ITEM_RENAME); + menu_item = gtk_menu_item_new_with_label(buf); gtk_widget_show(menu_item); gtk_container_add(GTK_CONTAINER(menu), menu_item); - gtk_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_rename_callback, widget); + g_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_rename_callback, widget); if (selection) /* not available for "full area" */ { /* delete preset area */ - menu_item = gtk_menu_item_new_with_label(MENU_ITEM_PRESET_AREA_DELETE); + snprintf(buf, sizeof(buf), "%s: %s", preferences.preset_area[selection]->name, MENU_ITEM_DELETE); + menu_item = gtk_menu_item_new_with_label(buf); gtk_widget_show(menu_item); gtk_container_add(GTK_CONTAINER(menu), menu_item); - gtk_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_delete_callback, widget); + g_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_delete_callback, widget); } if (selection>1) /* available from 3rd item */ { /* move up */ - menu_item = gtk_menu_item_new_with_label(MENU_OTEM_PRESET_AREA_MOVE_UP); + snprintf(buf, sizeof(buf), "%s: %s", preferences.preset_area[selection]->name, MENU_ITEM_MOVE_UP); + menu_item = gtk_menu_item_new_with_label(buf); gtk_widget_show(menu_item); gtk_container_add(GTK_CONTAINER(menu), menu_item); - gtk_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_move_up_callback, widget); + g_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_move_up_callback, widget); } if ((selection) && (selection < preferences.preset_area_definitions-1)) { /* move down */ - menu_item = gtk_menu_item_new_with_label(MENU_OTEM_PRESET_AREA_MOVE_DWN); + snprintf(buf, sizeof(buf), "%s: %s", preferences.preset_area[selection]->name, MENU_ITEM_MOVE_DWN); + menu_item = gtk_menu_item_new_with_label(buf); gtk_widget_show(menu_item); gtk_container_add(GTK_CONTAINER(menu), menu_item); - gtk_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_move_down_callback, widget); + g_signal_connect(GTK_OBJECT(menu_item), "activate", (GtkSignalFunc) preview_preset_area_move_down_callback, widget); } - gtk_widget_show(menu); +/* gtk_widget_show(menu); */ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time); return TRUE; /* event is handled */ @@ -4959,9 +6013,9 @@ static gint preview_preset_area_context_menu_callback(GtkWidget *widget, GdkEven /* ---------------------------------------------------------------------------------------------------------------------- */ -static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data) +static void preview_preset_area_callback(GtkWidget *widget, gpointer data) { - Preview *p = call_data; + Preview *p = data; int selection; DBG(DBG_proc, "preview_preset_area_callback\n"); @@ -4982,9 +6036,9 @@ static void preview_preset_area_callback(GtkWidget *widget, gpointer call_data) /* ---------------------------------------------------------------------------------------------------------------------- */ -static void preview_rotation_callback(GtkWidget *widget, gpointer call_data) +static void preview_rotation_callback(GtkWidget *widget, gpointer data) { - Preview *p = call_data; + Preview *p = data; float rotated_surface[4]; int rot; @@ -5070,8 +6124,84 @@ static void preview_rotation_callback(GtkWidget *widget, gpointer call_data) p->rotation = rot; - preview_update_selection(p); /* read selection from backend: correct rotation */ + p->block_update_maximum_output_size_clipping = TRUE; /* necessary when in copy mode */ preview_update_surface(p, 2); /* rotate surfaces */ + p->block_update_maximum_output_size_clipping = FALSE; + preview_update_selection(p); /* read selection from backend: correct rotation */ + xsane_batch_scan_update_icon_list(); /* rotate batch scan icons */ + preview_establish_ratio(p); /* make sure ratio is like selected - when selected */ +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void preview_establish_ratio(Preview *p) +{ + float width, height; + + DBG(DBG_proc, "preview_establish_ratio\n"); + + if (p->ratio == 0.0) + { + return; + } + + width = fabs(p->selection.coordinate[p->index_xmax] - p->selection.coordinate[p->index_xmin]); + height = fabs(p->selection.coordinate[p->index_ymax] - p->selection.coordinate[p->index_ymin]); + + if ( (0.99 < width / p->ratio / height) && (width / p->ratio / height < 1.01) ) + { + return; + } + + if ( (0.99 < width * p->ratio / height) && (width * p->ratio / height < 1.01) ) + { + width = height; + + if (width > p->scanner_surface[p->index_xmax] - p->scanner_surface[p->index_xmin]) + { + width = p->scanner_surface[p->index_xmax] - p->scanner_surface[p->index_xmin]; + } + } + + height = width / p->ratio; + if (height > p->scanner_surface[p->index_ymax] - p->scanner_surface[p->index_ymin]) + { + height = p->scanner_surface[p->index_ymax] - p->scanner_surface[p->index_ymin]; + width = height * p->ratio; + } + + p->selection.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + width; + if (p->selection.coordinate[p->index_xmax] > p->scanner_surface[p->index_xmax]) + { + p->selection.coordinate[p->index_xmax] = p->scanner_surface[p->index_xmax]; + p->selection.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmax] - width; + } + + p->selection.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + height; + if (p->selection.coordinate[p->index_ymax] > p->scanner_surface[p->index_ymax]) + { + p->selection.coordinate[p->index_ymax] = p->scanner_surface[p->index_ymax]; + p->selection.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymax] - height; + } + + preview_draw_selection(p); + preview_establish_selection(p); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ + +static void preview_ratio_callback(GtkWidget *widget, gpointer data) +{ + Preview *p = data; + float *ratio; + + DBG(DBG_proc, "preview_ratio_callback\n"); + + ratio = (float *) gtk_object_get_data(GTK_OBJECT(widget), "Selection"); + + p->ratio = *ratio; + + preview_establish_ratio(p); } /* ---------------------------------------------------------------------------------------------------------------------- */ @@ -5092,8 +6222,8 @@ void preview_do_gamma_correction(Preview *p) { int x,y; int offset; - u_char *image_data_enhp; - guint16 *image_data_rawp; + u_char *image_data_enhp = NULL; + guint16 *image_data_rawp = NULL; int rotate = 16 - preview_gamma_input_bits; DBG(DBG_proc, "preview_do_gamma_correction\n"); @@ -5142,7 +6272,10 @@ void preview_do_gamma_correction(Preview *p) for (x=0; x < p->image_width; x++) { - level = ((*image_data_rawp++) + (*image_data_rawp++) + (*image_data_rawp++)) / 3; + level = (*image_data_rawp++); /* red */ + level += (*image_data_rawp++); /* green */ + level += (*image_data_rawp++); /* blue */ + level /= 3; level >>= rotate; *image_data_enhp++ = preview_gamma_data_red [level]; /* use 12 bit gamma table */ *image_data_enhp++ = preview_gamma_data_green[level]; @@ -5619,11 +6752,28 @@ gint preview_area_resize_handler(GtkWidget *widget, GdkEvent *event, gpointer da } /* ---------------------------------------------------------------------------------------------------------------------- */ -#if 0 + void preview_update_maximum_output_size(Preview *p) { + float xxx = 0.0; + float yyy = 0.0; + float dxmin = 0; + float dymin = 0; + float dxmax = 0; + float dymax = 0; + + int paper_orientation = 0; + + if (p->block_update_maximum_output_size_clipping) + { + DBG(DBG_info, "preview_update_maximum_output_size: blocked\n"); + return; + } + DBG(DBG_proc, "preview_update_maximum_output_size\n"); + p->block_update_maximum_output_size_clipping = TRUE; + if ( (p->maximum_output_width >= INF) || (p->maximum_output_height >= INF) ) { if (p->selection_maximum.active) @@ -5631,127 +6781,384 @@ void preview_update_maximum_output_size(Preview *p) p->selection_maximum.active = FALSE; } } - else + else /* we have a maximum output size definition */ { p->previous_selection_maximum = p->selection_maximum; - p->selection_maximum.active = TRUE; - p->selection_maximum.coordinate[0] = (p->selection.coordinate[0] + p->selection.coordinate[2] - p->maximum_output_width )/2.0; - p->selection_maximum.coordinate[1] = (p->selection.coordinate[1] + p->selection.coordinate[3] - p->maximum_output_height)/2.0; - p->selection_maximum.coordinate[2] = (p->selection.coordinate[0] + p->selection.coordinate[2] + p->maximum_output_width )/2.0; - p->selection_maximum.coordinate[3] = (p->selection.coordinate[1] + p->selection.coordinate[3] + p->maximum_output_height)/2.0; - if (p->selection_maximum.coordinate[0] < p->max_scanner_surface[0]) + if (p->paper_orientation & 4) /* center? */ { - p->selection_maximum.coordinate[0] = p->max_scanner_surface[0]; + paper_orientation = p->paper_orientation; } - - if (p->selection_maximum.coordinate[1] < p->max_scanner_surface[1]) + else /* not in center */ { - p->selection_maximum.coordinate[1] = p->max_scanner_surface[1]; - } + switch (p->rotation) + { + default: + case 0: /* 0 degree */ + paper_orientation = p->paper_orientation & 3; + break; - if (p->selection_maximum.coordinate[2] > p->max_scanner_surface[2]) - { - p->selection_maximum.coordinate[2] = p->max_scanner_surface[2]; + case 1: /* 90 degree */ + paper_orientation = (1 - p->paper_orientation) & 3; + break; + + case 2: /* 180 degree */ + paper_orientation = (2 + p->paper_orientation) & 3; + break; + + case 3: /* 270 degree */ + paper_orientation = (3 - p->paper_orientation) & 3; + break; + + case 4: /* 0 degree, x mirror */ + paper_orientation = (1 - p->paper_orientation) & 3; + break; + + case 5: /* 90 degree, x mirror */ + paper_orientation = p->paper_orientation & 3; + break; + + case 6: /* 180 degree, x mirror */ + paper_orientation = (3 - p->paper_orientation) & 3; + break; + + case 7: /* 270 degree, x mirror */ + paper_orientation = (2 + p->paper_orientation) & 3; + break; + } } - if (p->selection_maximum.coordinate[3] > p->max_scanner_surface[3]) + switch (paper_orientation) { - p->selection_maximum.coordinate[3] = p->max_scanner_surface[3]; + default: + case 0: /* top left portrait */ + case 8: /* top left landscape */ + xxx = 0.0; + yyy = 0.0; + break; + + case 1: /* top right portrait */ + case 9: /* top right landscape */ + xxx = 1.0; + yyy = 0.0; + break; + + case 2: /* bottom right portrait */ + case 10: /* bottom right landscape */ + xxx = 1.0; + yyy = 1.0; + break; + + case 3: /* bottom left portrait */ + case 11: /* bottom left landscape */ + xxx = 0.0; + yyy = 1.0; + break; + + case 4: /* center portrait */ + case 12: /* center landscape */ + xxx = 0.5; + yyy = 0.5; + break; } - 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]) ) + p->selection_maximum.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmin] + xxx *(-p->selection.coordinate[p->index_xmin] + p->selection.coordinate[p->index_xmax]) - p->maximum_output_width * xxx - dxmin; + p->selection_maximum.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymin] + yyy *(-p->selection.coordinate[p->index_ymin] + p->selection.coordinate[p->index_ymax]) - p->maximum_output_height * yyy - dymin; + p->selection_maximum.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + xxx *(-p->selection.coordinate[p->index_xmin] + p->selection.coordinate[p->index_xmax]) + p->maximum_output_width * (1.0 - xxx) + dxmax; + p->selection_maximum.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + yyy *(-p->selection.coordinate[p->index_ymin] + p->selection.coordinate[p->index_ymax]) + p->maximum_output_height * (1.0 - yyy) + dymax; + + + if ( (p->selection.coordinate[p->index_xmin] < p->selection_maximum.coordinate[p->index_xmin]) || + (p->selection.coordinate[p->index_ymin] < p->selection_maximum.coordinate[p->index_ymin]) || + (p->selection.coordinate[p->index_xmax] > p->selection_maximum.coordinate[p->index_xmax]) || + (p->selection.coordinate[p->index_ymax] > p->selection_maximum.coordinate[p->index_ymax]) ) { - if (p->selection.coordinate[0] < p->selection_maximum.coordinate[0]) + int selection_changed = FALSE; + + if (p->selection.coordinate[p->index_xmin] < p->selection_maximum.coordinate[p->index_xmin]) { - p->selection.coordinate[0] = p->selection_maximum.coordinate[0]; + p->selection.coordinate[p->index_xmin] = p->selection_maximum.coordinate[p->index_xmin]; + selection_changed = TRUE; } - if (p->selection.coordinate[1] < p->selection_maximum.coordinate[1]) + if (p->selection.coordinate[p->index_ymin] < p->selection_maximum.coordinate[p->index_ymin]) { - p->selection.coordinate[1] = p->selection_maximum.coordinate[1]; + p->selection.coordinate[p->index_ymin] = p->selection_maximum.coordinate[p->index_ymin]; + selection_changed = TRUE; } - if (p->selection.coordinate[2] > p->selection_maximum.coordinate[2]) + if (p->selection.coordinate[p->index_xmax] > p->selection_maximum.coordinate[p->index_xmax]) { - p->selection.coordinate[2] = p->selection_maximum.coordinate[2]; + p->selection.coordinate[p->index_xmax] = p->selection_maximum.coordinate[p->index_xmax]; + selection_changed = TRUE; } - if (p->selection.coordinate[3] > p->selection_maximum.coordinate[3]) + if (p->selection.coordinate[p->index_ymax] > p->selection_maximum.coordinate[p->index_ymax]) { - p->selection.coordinate[3] = p->selection_maximum.coordinate[3]; + p->selection.coordinate[p->index_ymax] = p->selection_maximum.coordinate[p->index_ymax]; + selection_changed = TRUE; } + preview_draw_selection(p); - preview_establish_selection(p); + + if (selection_changed) + { + preview_establish_selection(p); + } } } + + p->block_update_maximum_output_size_clipping=FALSE; } -#endif +/* ---------------------------------------------------------------------------------------------------------------------- */ -void preview_update_maximum_output_size(Preview *p) +void preview_set_maximum_output_size(Preview *p, float width, float height, int paper_orientation) { - if ( (p->maximum_output_width >= INF) || (p->maximum_output_height >= INF) ) + /* witdh and height in device units */ + DBG(DBG_proc, "preview_set_maximum_output_size\n"); + + p->maximum_output_width = width; + p->maximum_output_height = height; + p->paper_orientation = paper_orientation; + + preview_update_maximum_output_size(p); + preview_draw_selection(p); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ +#define AUTORAISE_ERROR 30 +void preview_autoraise_scanarea(Preview *p, int preview_x, int preview_y, float *autoselect_coord) +{ + int x, y; + int image_x, image_y; + int offset; + float average_color_r, average_color_g, average_color_b; + int count; + float error; + int top, bottom, left, right; + int top_ok, bottom_ok, left_ok, right_ok; + float xscale, yscale; + + DBG(DBG_proc, "preview_autoraise_scanarea\n"); + + preview_transform_coordinate_window_to_image(p, preview_x, preview_y, &image_x, &image_y); + + top_ok = FALSE; + bottom_ok = FALSE; + left_ok = FALSE; + right_ok = FALSE; + + top = image_y - 5; + bottom = image_y + 5; + left = image_x - 5; + right = image_x + 5; + + + while (!(top_ok && bottom_ok && left_ok && right_ok)) { - if (p->selection_maximum.active) + /* search top */ + if (!top_ok) { - p->selection_maximum.active = FALSE; + top--; } - } - else - { - p->previous_selection_maximum = p->selection_maximum; - p->selection_maximum.active = TRUE; - p->selection_maximum.coordinate[p->index_xmin] = p->selection.coordinate[p->index_xmin]; - p->selection_maximum.coordinate[p->index_ymin] = p->selection.coordinate[p->index_ymin]; - p->selection_maximum.coordinate[p->index_xmax] = p->selection.coordinate[p->index_xmin] + p->maximum_output_width; - p->selection_maximum.coordinate[p->index_ymax] = p->selection.coordinate[p->index_ymin] + p->maximum_output_height; + top_ok = TRUE; - if (p->selection_maximum.coordinate[p->index_xmax] > p->max_scanner_surface[p->index_xmax]) + if (top <= 0) { - p->selection_maximum.coordinate[p->index_xmax] = p->max_scanner_surface[p->index_xmax]; + top = 0; + } + else + { + average_color_r = average_color_g = average_color_b = 0; + count = 0; + + for (x = left; x < right; x++) + { + offset = 3 * (top * p->image_width + x); + average_color_r += p->image_data_enh[offset + 0]; + average_color_g += p->image_data_enh[offset + 1]; + average_color_b += p->image_data_enh[offset + 2]; + count++; + } + + average_color_r /= count; + average_color_g /= count; + average_color_b /= count; + + for (x = left; x < right; x++) + { + offset = 3 * (top * p->image_width + x); + + error = fabs(p->image_data_enh[offset + 0] - average_color_r) + + fabs(p->image_data_enh[offset + 1] - average_color_g) + + fabs(p->image_data_enh[offset + 2] - average_color_b); + + if (error > AUTORAISE_ERROR) + { + top_ok = FALSE; + break; + } + } } - if (p->selection_maximum.coordinate[p->index_ymax] > p->max_scanner_surface[p->index_ymax]) + /* search bottom */ + if (!bottom_ok) { - p->selection_maximum.coordinate[p->index_ymax] = p->max_scanner_surface[p->index_ymax]; + bottom++; } - if ( (p->selection.coordinate[p->index_xmin] < p->selection_maximum.coordinate[p->index_xmin]) || - (p->selection.coordinate[p->index_ymin] < p->selection_maximum.coordinate[p->index_ymin]) || - (p->selection.coordinate[p->index_xmax] > p->selection_maximum.coordinate[p->index_xmax]) || - (p->selection.coordinate[p->index_ymax] > p->selection_maximum.coordinate[p->index_ymax]) ) + bottom_ok = TRUE; + + if (bottom >= p->image_height-1) { - if (p->selection.coordinate[p->index_xmax] > p->selection_maximum.coordinate[p->index_xmax]) + bottom = p->image_height-1; + } + else + { + average_color_r = average_color_g = average_color_b = 0; + count = 0; + + for (x = left; x < right; x++) { - p->selection.coordinate[p->index_xmax] = p->selection_maximum.coordinate[p->index_xmax]; + offset = 3 * (bottom * p->image_width + x); + average_color_r += p->image_data_enh[offset + 0]; + average_color_g += p->image_data_enh[offset + 1]; + average_color_b += p->image_data_enh[offset + 2]; + count++; } - if (p->selection.coordinate[p->index_ymax] > p->selection_maximum.coordinate[p->index_ymax]) + average_color_r /= count; + average_color_g /= count; + average_color_b /= count; + + for (x = left; x < right; x++) { - p->selection.coordinate[p->index_ymax] = p->selection_maximum.coordinate[p->index_ymax]; + offset = 3 * (bottom * p->image_width + x); + + error = fabs(p->image_data_enh[offset + 0] - average_color_r) + + fabs(p->image_data_enh[offset + 1] - average_color_g) + + fabs(p->image_data_enh[offset + 2] - average_color_b); + + if (error > AUTORAISE_ERROR) + { + bottom_ok = FALSE; + break; + } + } + } + + /* search left */ + if (!left_ok) + { + left--; + } + + left_ok = TRUE; + + if (left <= 0) + { + left = 0; + } + else + { + average_color_r = average_color_g = average_color_b = 0; + count = 0; + + for (y = top; y < bottom; y++) + { + offset = 3 * (left + y * p->image_width); + average_color_r += p->image_data_enh[offset + 0]; + average_color_g += p->image_data_enh[offset + 1]; + average_color_b += p->image_data_enh[offset + 2]; + count++; + } + + average_color_r /= count; + average_color_g /= count; + average_color_b /= count; + + for (y = top; y < bottom; y++) + { + offset = 3 * (left + y * p->image_width); + + error = fabs(p->image_data_enh[offset + 0] - average_color_r) + + fabs(p->image_data_enh[offset + 1] - average_color_g) + + fabs(p->image_data_enh[offset + 2] - average_color_b); + + if (error > AUTORAISE_ERROR) + { + left_ok = FALSE; + break; + } + } + } + + /* search right */ + if (!right_ok) + { + right++; + } + + right_ok = TRUE; + + if (right >= p->image_width-1) + { + right = p->image_width-1; + } + else + { + average_color_r = average_color_g = average_color_b = 0; + count = 0; + + for (y = top; y < bottom; y++) + { + offset = 3 * (right + y * p->image_width); + average_color_r += p->image_data_enh[offset + 0]; + average_color_g += p->image_data_enh[offset + 1]; + average_color_b += p->image_data_enh[offset + 2]; + count++; + } + + average_color_r /= count; + average_color_g /= count; + average_color_b /= count; + + for (y = top; y < bottom; y++) + { + offset = 3 * (right + y * p->image_width); + + error = fabs(p->image_data_enh[offset + 0] - average_color_r) + + fabs(p->image_data_enh[offset + 1] - average_color_g) + + fabs(p->image_data_enh[offset + 2] - average_color_b); + + if (error > AUTORAISE_ERROR) + { + right_ok = FALSE; + break; + } } - 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 */ - DBG(DBG_proc, "preview_set_maximum_output_size\n"); - p->maximum_output_width = width; - p->maximum_output_height = height; + preview_get_scale_device_to_image(p, &xscale, &yscale); - preview_update_maximum_output_size(p); - preview_draw_selection(p); + if (((p->rotation & 3) == 0) || ((p->rotation & 3) == 2)) /* 0 or 180 degree */ + { + *(autoselect_coord+0) = p->image_surface[0] + left / xscale; + *(autoselect_coord+2) = p->image_surface[0] + right / xscale; + *(autoselect_coord+1) = p->image_surface[1] + top / yscale; + *(autoselect_coord+3) = p->image_surface[1] + bottom / yscale; + } + else /* 90 or 270 degree */ + { + *(autoselect_coord+1) = p->image_surface[1] + left / xscale; + *(autoselect_coord+3) = p->image_surface[1] + right / xscale; + *(autoselect_coord+0) = p->image_surface[0] + top / yscale; + *(autoselect_coord+2) = p->image_surface[0] + bottom / yscale; + } } /* ---------------------------------------------------------------------------------------------------------------------- */ @@ -5932,6 +7339,15 @@ void preview_autoselect_scanarea(Preview *p, float *autoselect_coord) } } + if ( (top >= bottom) || (right <= left) ) /* empty selection: use complete image */ + { + DBG(DBG_info, "autoselect_scanarea: empty selection: using complete area\n"); + top = 0; + bottom = p->image_height -1; + left = 0; + right = p->image_width -1; + } + preview_get_scale_device_to_image(p, &xscale, &yscale); if (((p->rotation & 3) == 0) || ((p->rotation & 3) == 2)) /* 0 or 180 degree */ |