diff --git a/src/preview.c b/src/preview.c index a7749d6..9116ec3 100644 --- a/src/preview.c +++ b/src/preview.c @@ -156,14 +156,24 @@ screen_size_get_dimensions (gint *width, gint *height) } static void -draw_rect(cairo_t *cr, double coord[4]) +draw_selection (Preview *p, cairo_t *cr) { + if (!p->selection.active) + return; + + double *coord = p->selection.coord; double x, y, w, h; + /* + * Make sure that we account for a selection dragged in reverse + * from what we expect, e.g leftwards or upwards or both. + * + */ x = coord[0]; y = coord[1]; w = coord[2] - x; h = coord[3] - y; + if (w < 0) { x = coord[2]; @@ -174,32 +184,30 @@ draw_rect(cairo_t *cr, double coord[4]) y = coord[3]; h = -h; } - cairo_set_line_width(cr, 1.5); - const double dashes1[2] = {4.0, 4.0}; + + /* + * Draw selection rectangle. One black dash, then another, filling in the + * black dash gaps with white. This means that regardless of the picture, the + * dash pattern is visible. + * + */ + const double dashes1[2] = { 4.0, 4.0 }; + const double dashes2[4] = { 0.0, 4.0, 4.0, 0.0 }; + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_line_width (cr, 1.5); + + // Black dashes. cairo_set_source_rgb (cr, 0, 0, 0); - cairo_set_dash(cr, dashes1, sizeof(dashes1)/sizeof(dashes1[0]), 0); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_rectangle(cr, x, y, w + 1, h + 1); - cairo_stroke(cr); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - const double dashes2[4] = {0.0, 4.0, 4.0, 0.0}; - cairo_set_dash(cr, dashes2, sizeof(dashes2)/sizeof(dashes2[0]), 0); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_rectangle(cr, x, y, w + 1, h + 1); - cairo_stroke(cr); -} + cairo_set_dash (cr, dashes1, sizeof(dashes1) / sizeof(dashes1[0]), 0); + cairo_rectangle (cr, x, y, w + 1, h + 1); + cairo_stroke (cr); -static void -draw_selection (Preview * p, cairo_t *cr) -{ - if (p->previous_selection.active) - draw_rect (cr, p->previous_selection.coord); - - if (p->selection.active) - draw_rect (cr, p->selection.coord); - - p->previous_selection = p->selection; + // White dashes. + cairo_set_dash (cr, dashes2, sizeof(dashes2) / sizeof(dashes2[0]), 0); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_rectangle (cr, x, y, w + 1, h + 1); + cairo_stroke (cr); } static void @@ -211,8 +219,6 @@ update_selection (Preview * p) SANE_Word val; int i, optnum; - p->previous_selection = p->selection; - memcpy (dev_selection, p->surface, sizeof (dev_selection)); for (i = 0; i < 4; ++i) { @@ -288,62 +294,62 @@ get_image_scale (Preview * p, float *xscalep, float *yscalep) static void paint_image (Preview * p) { - float xscale, yscale, src_x, src_y; - int dst_x, dst_y, height, x, y, src_offset; - gint gwidth, gheight; - - gwidth = p->preview_width; - gheight = p->preview_height; - - get_image_scale (p, &xscale, &yscale); - - if (p->preview_row == NULL) - p->preview_row = malloc (3 * gwidth); - else - p->preview_row = realloc (p->preview_row, 3 * gwidth); - memset (p->preview_row, 0xff, 3 * gwidth); - if (p->preview_data == NULL) - p->preview_data = malloc (3 * gwidth * gheight); - else - p->preview_data = realloc (p->preview_data, 3 * gwidth * gheight); - memset (p->preview_data, 0xff, 3 * gwidth * gheight); - gtk_widget_queue_draw (p->window); - - /* don't draw last line unless it's complete: */ - height = p->image_y; - if (p->image_x == 0 && height < p->image_height) - ++height; - - /* for now, use simple nearest-neighbor interpolation: */ - src_offset = 0; - src_x = src_y = 0.0; - for (dst_y = 0; dst_y < gheight; ++dst_y) - { - y = (int) (src_y + 0.5); - if (y >= height) - break; - src_offset = y * 3 * p->image_width; - - if (p->image_data) - for (dst_x = 0; dst_x < gwidth; ++dst_x) - { - x = (int) (src_x + 0.5); - if (x >= p->image_width) - break; - - p->preview_row[3 * dst_x + 0] = - p->image_data[src_offset + 3 * x + 0]; - p->preview_row[3 * dst_x + 1] = - p->image_data[src_offset + 3 * x + 1]; - p->preview_row[3 * dst_x + 2] = - p->image_data[src_offset + 3 * x + 2]; - src_x += xscale; - } - memcpy(p->preview_data + (size_t) dst_y * (size_t) gwidth * 3, p->preview_row, (size_t) gwidth * 3); - src_x = 0.0; - src_y += yscale; - } - gtk_widget_queue_draw (p->window); +// float xscale, yscale, src_x, src_y; +// int dst_x, dst_y, height, x, y, src_offset; +// gint gwidth, gheight; +// +// gwidth = p->preview_width; +// gheight = p->preview_height; +// +// get_image_scale (p, &xscale, &yscale); +// +// if (p->preview_row == NULL) +// p->preview_row = malloc (3 * gwidth); +// else +// p->preview_row = realloc (p->preview_row, 3 * gwidth); +// memset (p->preview_row, 0xff, 3 * gwidth); +//// if (p->preview_data == NULL) +//// p->preview_data = malloc (3 * gwidth * gheight); +//// else +//// p->preview_data = realloc (p->preview_data, 3 * gwidth * gheight); +//// memset (p->preview_data, 0xff, 3 * gwidth * gheight); +// gtk_widget_queue_draw (p->window); +// +// /* don't draw last line unless it's complete: */ +// height = p->image_y; +// if (p->image_x == 0 && height < p->image_height) +// ++height; +// +// /* for now, use simple nearest-neighbor interpolation: */ +// src_offset = 0; +// src_x = src_y = 0.0; +// for (dst_y = 0; dst_y < gheight; ++dst_y) +// { +// y = (int) (src_y + 0.5); +// if (y >= height) +// break; +// src_offset = y * 3 * p->image_width; +// +// if (p->image_data) +// for (dst_x = 0; dst_x < gwidth; ++dst_x) +// { +// x = (int) (src_x + 0.5); +// if (x >= p->image_width) +// break; +// +// p->preview_row[3 * dst_x + 0] = +// p->image_data[src_offset + 3 * x + 0]; +// p->preview_row[3 * dst_x + 1] = +// p->image_data[src_offset + 3 * x + 1]; +// p->preview_row[3 * dst_x + 2] = +// p->image_data[src_offset + 3 * x + 2]; +// src_x += xscale; +// } +// memcpy(p->preview_data + (size_t) dst_y * (size_t) gwidth * 3, p->preview_row, (size_t) gwidth * 3); +// src_x = 0.0; +// src_y += yscale; +// } +// gtk_widget_queue_draw (p->window); } static void @@ -376,8 +382,8 @@ display_image (Preview * p) p->image_data = realloc (p->image_data, 3 * p->image_width * p->image_height); assert (p->image_data); - p->preview_data = realloc (p->preview_data, - 3 * p->image_width * p->image_height); +// p->preview_data = realloc (p->preview_data, +// 3 * p->image_width * p->image_height); } display_partial_image (p); scan_done (p); @@ -608,7 +614,7 @@ increment_image_y (Preview * p) extra_size = 3 * 32 * p->image_width; p->image_height += 32; p->image_data = realloc (p->image_data, offset + extra_size); - p->preview_data = realloc (p->preview_data, offset + extra_size); +// p->preview_data = realloc (p->preview_data, offset + extra_size); if (!p->image_data) { snprintf (buf, sizeof (buf), @@ -619,7 +625,10 @@ increment_image_y (Preview * p) return -1; } memset (p->image_data + offset, 0xff, extra_size); - memset (p->preview_data + offset, 0xff, extra_size); +// memset (p->preview_data + offset, 0xff, extra_size); + + g_object_unref(p->image_pixbuf); + p->image_pixbuf = NULL; } return 0; } @@ -949,12 +958,16 @@ scan_start (Preview * p) gsg_set_sensitivity (p->dialog, FALSE); // gtk_widget_set_sensitive (p->dialog->window->parent->parent->parent, FALSE); - if (p->preview_data == NULL) - p->preview_data = malloc (p->image_width * p->image_height * 3); - else - p->preview_data = realloc (p->preview_data, 3 * p->image_width * p->image_height); +// if (p->preview_data == NULL) +// p->preview_data = malloc (p->image_width * p->image_height * 3); +// else +// p->preview_data = realloc (p->preview_data, 3 * p->image_width * p->image_height); /* clear old preview: */ - memset (p->preview_data, 0xff, 3 * p->image_width * p->image_height); +// memset (p->preview_data, 0xff, 3 * p->image_width * p->image_height); + + g_object_unref(p->image_pixbuf); + p->image_pixbuf = NULL; + gtk_widget_queue_draw (p->window); if (p->input_tag >= 0) @@ -1040,6 +1053,12 @@ scan_start (Preview * p) if (!p->image_data || p->params.pixels_per_line != p->image_width || (p->params.lines >= 0 && p->params.lines != p->image_height)) { + if (p->image_pixbuf) + { + g_object_unref(p->image_pixbuf); + p->image_pixbuf = NULL; + } + /* image size changed */ if (p->image_data) free (p->image_data); @@ -1050,7 +1069,7 @@ scan_start (Preview * p) p->image_height = 32; /* may have to adjust as we go... */ p->image_data = malloc (p->image_width * p->image_height * 3); - p->preview_data = malloc (p->preview_width * p->image_height * 3); +// p->preview_data = malloc (p->preview_width * p->image_height * 3); if (!p->image_data) { snprintf (buf, sizeof (buf), @@ -1064,7 +1083,7 @@ scan_start (Preview * p) if (p->selection.active) { - p->previous_selection = p->selection; +// p->previous_selection = p->selection; p->selection.active = FALSE; gtk_widget_queue_draw (p->window); } @@ -1129,23 +1148,22 @@ restore_preview_image (Preview * p) int width, height; float psurface[4]; size_t nread; - FILE *in; + FILE *in = NULL; /* See whether there is a saved preview and load it if present: */ - if (make_preview_image_path (p, sizeof (filename), filename) < 0) - return; + goto finish; in = fopen (filename, "r"); if (!in) - return; + goto finish; /* Be careful about consuming too many bytes after the final newline (e.g., consider an image whose first image byte is 13 (`\r'). */ if (fscanf (in, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255%*[\n]", psurface + 0, psurface + 1, psurface + 2, psurface + 3, &psurface_type, &psurface_unit, &width, &height) != 8) - return; + goto finish; if (GROSSLY_DIFFERENT (psurface[0], p->surface[0]) || GROSSLY_DIFFERENT (psurface[1], p->surface[1]) @@ -1153,12 +1171,12 @@ restore_preview_image (Preview * p) || GROSSLY_DIFFERENT (psurface[3], p->surface[3]) || psurface_type != p->surface_type || psurface_unit != p->surface_unit) /* ignore preview image that was acquired for/with a different surface */ - return; + goto finish; p->image_width = width; p->image_height = height; if ((width == 0) || (height == 0)) - return; + goto finish; int data_size = 3 * width * height; @@ -1166,25 +1184,21 @@ restore_preview_image (Preview * p) if ((data_size / width) / height != 3) { // overflow occurred. Ignore the image. The dimensions are probably corrupted. - return; + goto finish; } p->image_data = malloc (data_size); if (!p->image_data) - return; - - p->preview_data = malloc (data_size); - if (!p->preview_data) - { - free(p->image_data); - p->image_data = NULL; - return; - } + goto finish; nread = fread (p->image_data, 3, width * height, in); p->image_y = nread / width; p->image_x = nread % width; + +finish: + if (in) + fclose(in); } /* This is executed _after_ the gtkpreview's expose routine. */ @@ -1193,13 +1207,40 @@ expose_handler (GtkWidget * window, cairo_t *cr, gpointer data) { Preview *p = data; - if (p->preview_data == NULL) return FALSE; + if (p->image_data == NULL) + return FALSE; - GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(p->preview_data, GDK_COLORSPACE_RGB, FALSE, 8, p->preview_width, - p->preview_height, p->preview_width * 3, NULL/*preview_pixbuf_data_destroy*/, NULL); - gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); + if (p->image_pixbuf == NULL) + { + p->image_pixbuf = gdk_pixbuf_new_from_data (p->image_data, + GDK_COLORSPACE_RGB, FALSE, 8, + p->image_width, + p->image_height, + p->image_width * 3, NULL, + NULL); + + } + + gint width = gtk_widget_get_allocated_width (window); + gint height = gtk_widget_get_allocated_height (window); + + gint img_width = gdk_pixbuf_get_width (p->image_pixbuf); + gint img_height = gdk_pixbuf_get_height (p->image_pixbuf); + double scale = MIN((double )width / img_width, (double )height / img_height); + gint scaled_width = img_width * scale; + gint scaled_height = img_height * scale; + + /* + * TODO: we could store this scaled pixbuf in the Preview and only regenerate it + * when the scaled image dimensions change. [RL] + */ + GdkPixbuf *scaled = gdk_pixbuf_scale_simple (p->image_pixbuf, scaled_width, + scaled_height, + GDK_INTERP_BILINEAR); + + gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0); cairo_paint(cr); - g_object_unref(pixbuf); + g_object_unref(scaled); if (p->selection_drag == FALSE) update_selection (p); @@ -1317,10 +1358,10 @@ preview_new (GSGDialog * dialog) p->surface_unit = SANE_UNIT_MM; p->input_tag = -1; p->image_data = NULL; - p->preview_data = NULL; p->preview_row = NULL; p->top = NULL; p->scanning = FALSE; + p->image_pixbuf = NULL; if (first_time) { @@ -1470,9 +1511,11 @@ preview_update (Preview * p) if (surface_changed && p->image_data) { free (p->image_data); - free (p->preview_data); + + g_object_unref(p->image_pixbuf); + p->image_pixbuf = NULL; + p->image_data = 0; - p->preview_data = 0; p->image_width = 0; p->image_height = 0; } @@ -1630,6 +1673,11 @@ preview_destroy (Preview * p) char filename[PATH_MAX]; FILE *out; + if (!p) + { + return; + } + if (p->scanning) scan_done (p); /* don't save partial window */ else if (preferences.preserve_preview && p->image_data @@ -1648,15 +1696,29 @@ preview_destroy (Preview * p) fclose (out); } } + if (p->image_data) - free (p->image_data); - p->image_data = NULL; - if (p->preview_data) - free (p->preview_data); - p->preview_data = NULL; + { + free (p->image_data); + p->image_data = NULL; + } + +// if (p->preview_data) +// free (p->preview_data); +// p->preview_data = NULL; + + if (p->image_pixbuf) + { + g_object_unref(p->image_pixbuf); + p->image_pixbuf = NULL; + } + if (p->preview_row) - free (p->preview_row); - p->preview_row = NULL; + { + free (p->preview_row); + p->preview_row = NULL; + } + if (p->top) gtk_widget_destroy (p->top); free (p); diff --git a/src/preview.h b/src/preview.h index e843693..492d9dd 100644 --- a/src/preview.h +++ b/src/preview.h @@ -62,7 +62,6 @@ typedef struct int image_width; int image_height; u_char *image_data; /* 3 * image_width * image_height bytes */ - u_char *preview_data; /* 3 * image_width * image_height bytes */ int selection_drag; struct @@ -70,7 +69,7 @@ typedef struct int active; double coord[4]; } - selection, previous_selection; + selection; // coords of selection box in the preview window GtkWidget *top; /* top-level widget */ GtkWidget *hruler; @@ -80,8 +79,7 @@ typedef struct GtkWidget *cancel; /* the cancel button */ GtkWidget *preview; /* the preview button */ - cairo_surface_t *surface_cairo; - gboolean drawable; + GdkPixbuf *image_pixbuf; } Preview;