preview: use GPixBuf and and lib function to do resize

Better to use stock functions to do image preview resizing.
preview_fixes
Ralph Little 2025-06-12 17:37:15 -07:00
rodzic b330f79c18
commit 8b7d063442
2 zmienionych plików z 190 dodań i 130 usunięć

Wyświetl plik

@ -156,14 +156,24 @@ screen_size_get_dimensions (gint *width, gint *height)
} }
static void 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; 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]; x = coord[0];
y = coord[1]; y = coord[1];
w = coord[2] - x; w = coord[2] - x;
h = coord[3] - y; h = coord[3] - y;
if (w < 0) if (w < 0)
{ {
x = coord[2]; x = coord[2];
@ -174,34 +184,32 @@ draw_rect(cairo_t *cr, double coord[4])
y = coord[3]; y = coord[3];
h = -h; h = -h;
} }
cairo_set_line_width(cr, 1.5);
/*
* 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 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_source_rgb (cr, 0, 0, 0);
cairo_set_dash (cr, dashes1, sizeof(dashes1) / sizeof(dashes1[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_rectangle (cr, x, y, w + 1, h + 1);
cairo_stroke (cr); 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}; // White dashes.
cairo_set_dash (cr, dashes2, sizeof(dashes2) / sizeof(dashes2[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_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_rectangle (cr, x, y, w + 1, h + 1); cairo_rectangle (cr, x, y, w + 1, h + 1);
cairo_stroke (cr); 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;
}
static void static void
update_selection (Preview * p) update_selection (Preview * p)
{ {
@ -211,8 +219,6 @@ update_selection (Preview * p)
SANE_Word val; SANE_Word val;
int i, optnum; int i, optnum;
p->previous_selection = p->selection;
memcpy (dev_selection, p->surface, sizeof (dev_selection)); memcpy (dev_selection, p->surface, sizeof (dev_selection));
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
{ {
@ -288,62 +294,62 @@ get_image_scale (Preview * p, float *xscalep, float *yscalep)
static void static void
paint_image (Preview * p) paint_image (Preview * p)
{ {
float xscale, yscale, src_x, src_y; // float xscale, yscale, src_x, src_y;
int dst_x, dst_y, height, x, y, src_offset; // int dst_x, dst_y, height, x, y, src_offset;
gint gwidth, gheight; // gint gwidth, gheight;
//
gwidth = p->preview_width; // gwidth = p->preview_width;
gheight = p->preview_height; // gheight = p->preview_height;
//
get_image_scale (p, &xscale, &yscale); // get_image_scale (p, &xscale, &yscale);
//
if (p->preview_row == NULL) // if (p->preview_row == NULL)
p->preview_row = malloc (3 * gwidth); // p->preview_row = malloc (3 * gwidth);
else // else
p->preview_row = realloc (p->preview_row, 3 * gwidth); // p->preview_row = realloc (p->preview_row, 3 * gwidth);
memset (p->preview_row, 0xff, 3 * gwidth); // memset (p->preview_row, 0xff, 3 * gwidth);
if (p->preview_data == NULL) //// if (p->preview_data == NULL)
p->preview_data = malloc (3 * gwidth * gheight); //// p->preview_data = malloc (3 * gwidth * gheight);
else //// else
p->preview_data = realloc (p->preview_data, 3 * gwidth * gheight); //// p->preview_data = realloc (p->preview_data, 3 * gwidth * gheight);
memset (p->preview_data, 0xff, 3 * gwidth * gheight); //// memset (p->preview_data, 0xff, 3 * gwidth * gheight);
gtk_widget_queue_draw (p->window); // gtk_widget_queue_draw (p->window);
//
/* don't draw last line unless it's complete: */ // /* don't draw last line unless it's complete: */
height = p->image_y; // height = p->image_y;
if (p->image_x == 0 && height < p->image_height) // if (p->image_x == 0 && height < p->image_height)
++height; // ++height;
//
/* for now, use simple nearest-neighbor interpolation: */ // /* for now, use simple nearest-neighbor interpolation: */
src_offset = 0; // src_offset = 0;
src_x = src_y = 0.0; // src_x = src_y = 0.0;
for (dst_y = 0; dst_y < gheight; ++dst_y) // for (dst_y = 0; dst_y < gheight; ++dst_y)
{ // {
y = (int) (src_y + 0.5); // y = (int) (src_y + 0.5);
if (y >= height) // if (y >= height)
break; // break;
src_offset = y * 3 * p->image_width; // src_offset = y * 3 * p->image_width;
//
if (p->image_data) // if (p->image_data)
for (dst_x = 0; dst_x < gwidth; ++dst_x) // for (dst_x = 0; dst_x < gwidth; ++dst_x)
{ // {
x = (int) (src_x + 0.5); // x = (int) (src_x + 0.5);
if (x >= p->image_width) // if (x >= p->image_width)
break; // break;
//
p->preview_row[3 * dst_x + 0] = // p->preview_row[3 * dst_x + 0] =
p->image_data[src_offset + 3 * x + 0]; // p->image_data[src_offset + 3 * x + 0];
p->preview_row[3 * dst_x + 1] = // p->preview_row[3 * dst_x + 1] =
p->image_data[src_offset + 3 * x + 1]; // p->image_data[src_offset + 3 * x + 1];
p->preview_row[3 * dst_x + 2] = // p->preview_row[3 * dst_x + 2] =
p->image_data[src_offset + 3 * x + 2]; // p->image_data[src_offset + 3 * x + 2];
src_x += xscale; // src_x += xscale;
} // }
memcpy(p->preview_data + (size_t) dst_y * (size_t) gwidth * 3, p->preview_row, (size_t) gwidth * 3); // memcpy(p->preview_data + (size_t) dst_y * (size_t) gwidth * 3, p->preview_row, (size_t) gwidth * 3);
src_x = 0.0; // src_x = 0.0;
src_y += yscale; // src_y += yscale;
} // }
gtk_widget_queue_draw (p->window); // gtk_widget_queue_draw (p->window);
} }
static void static void
@ -376,8 +382,8 @@ display_image (Preview * p)
p->image_data = realloc (p->image_data, p->image_data = realloc (p->image_data,
3 * p->image_width * p->image_height); 3 * p->image_width * p->image_height);
assert (p->image_data); assert (p->image_data);
p->preview_data = realloc (p->preview_data, // p->preview_data = realloc (p->preview_data,
3 * p->image_width * p->image_height); // 3 * p->image_width * p->image_height);
} }
display_partial_image (p); display_partial_image (p);
scan_done (p); scan_done (p);
@ -608,7 +614,7 @@ increment_image_y (Preview * p)
extra_size = 3 * 32 * p->image_width; extra_size = 3 * 32 * p->image_width;
p->image_height += 32; p->image_height += 32;
p->image_data = realloc (p->image_data, offset + extra_size); 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) if (!p->image_data)
{ {
snprintf (buf, sizeof (buf), snprintf (buf, sizeof (buf),
@ -619,7 +625,10 @@ increment_image_y (Preview * p)
return -1; return -1;
} }
memset (p->image_data + offset, 0xff, extra_size); 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; return 0;
} }
@ -949,12 +958,16 @@ scan_start (Preview * p)
gsg_set_sensitivity (p->dialog, FALSE); gsg_set_sensitivity (p->dialog, FALSE);
// gtk_widget_set_sensitive (p->dialog->window->parent->parent->parent, FALSE); // gtk_widget_set_sensitive (p->dialog->window->parent->parent->parent, FALSE);
if (p->preview_data == NULL) // if (p->preview_data == NULL)
p->preview_data = malloc (p->image_width * p->image_height * 3); // p->preview_data = malloc (p->image_width * p->image_height * 3);
else // else
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);
/* clear old preview: */ /* 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); gtk_widget_queue_draw (p->window);
if (p->input_tag >= 0) 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 if (!p->image_data || p->params.pixels_per_line != p->image_width
|| (p->params.lines >= 0 && p->params.lines != p->image_height)) || (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 */ /* image size changed */
if (p->image_data) if (p->image_data)
free (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_height = 32; /* may have to adjust as we go... */
p->image_data = malloc (p->image_width * p->image_height * 3); 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) if (!p->image_data)
{ {
snprintf (buf, sizeof (buf), snprintf (buf, sizeof (buf),
@ -1064,7 +1083,7 @@ scan_start (Preview * p)
if (p->selection.active) if (p->selection.active)
{ {
p->previous_selection = p->selection; // p->previous_selection = p->selection;
p->selection.active = FALSE; p->selection.active = FALSE;
gtk_widget_queue_draw (p->window); gtk_widget_queue_draw (p->window);
} }
@ -1129,23 +1148,22 @@ restore_preview_image (Preview * p)
int width, height; int width, height;
float psurface[4]; float psurface[4];
size_t nread; size_t nread;
FILE *in; FILE *in = NULL;
/* See whether there is a saved preview and load it if present: */ /* See whether there is a saved preview and load it if present: */
if (make_preview_image_path (p, sizeof (filename), filename) < 0) if (make_preview_image_path (p, sizeof (filename), filename) < 0)
return; goto finish;
in = fopen (filename, "r"); in = fopen (filename, "r");
if (!in) if (!in)
return; goto finish;
/* Be careful about consuming too many bytes after the final newline /* Be careful about consuming too many bytes after the final newline
(e.g., consider an image whose first image byte is 13 (`\r'). */ (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]", 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 + 0, psurface + 1, psurface + 2, psurface + 3,
&psurface_type, &psurface_unit, &width, &height) != 8) &psurface_type, &psurface_unit, &width, &height) != 8)
return; goto finish;
if (GROSSLY_DIFFERENT (psurface[0], p->surface[0]) if (GROSSLY_DIFFERENT (psurface[0], p->surface[0])
|| GROSSLY_DIFFERENT (psurface[1], p->surface[1]) || GROSSLY_DIFFERENT (psurface[1], p->surface[1])
@ -1153,12 +1171,12 @@ restore_preview_image (Preview * p)
|| GROSSLY_DIFFERENT (psurface[3], p->surface[3]) || GROSSLY_DIFFERENT (psurface[3], p->surface[3])
|| psurface_type != p->surface_type || psurface_unit != p->surface_unit) || psurface_type != p->surface_type || psurface_unit != p->surface_unit)
/* ignore preview image that was acquired for/with a different surface */ /* ignore preview image that was acquired for/with a different surface */
return; goto finish;
p->image_width = width; p->image_width = width;
p->image_height = height; p->image_height = height;
if ((width == 0) || (height == 0)) if ((width == 0) || (height == 0))
return; goto finish;
int data_size = 3 * width * height; int data_size = 3 * width * height;
@ -1166,25 +1184,21 @@ restore_preview_image (Preview * p)
if ((data_size / width) / height != 3) if ((data_size / width) / height != 3)
{ {
// overflow occurred. Ignore the image. The dimensions are probably corrupted. // overflow occurred. Ignore the image. The dimensions are probably corrupted.
return; goto finish;
} }
p->image_data = malloc (data_size); p->image_data = malloc (data_size);
if (!p->image_data) if (!p->image_data)
return; goto finish;
p->preview_data = malloc (data_size);
if (!p->preview_data)
{
free(p->image_data);
p->image_data = NULL;
return;
}
nread = fread (p->image_data, 3, width * height, in); nread = fread (p->image_data, 3, width * height, in);
p->image_y = nread / width; p->image_y = nread / width;
p->image_x = nread % width; p->image_x = nread % width;
finish:
if (in)
fclose(in);
} }
/* This is executed _after_ the gtkpreview's expose routine. */ /* 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; 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, if (p->image_pixbuf == NULL)
p->preview_height, p->preview_width * 3, NULL/*preview_pixbuf_data_destroy*/, NULL); {
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); 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); cairo_paint(cr);
g_object_unref(pixbuf); g_object_unref(scaled);
if (p->selection_drag == FALSE) if (p->selection_drag == FALSE)
update_selection (p); update_selection (p);
@ -1317,10 +1358,10 @@ preview_new (GSGDialog * dialog)
p->surface_unit = SANE_UNIT_MM; p->surface_unit = SANE_UNIT_MM;
p->input_tag = -1; p->input_tag = -1;
p->image_data = NULL; p->image_data = NULL;
p->preview_data = NULL;
p->preview_row = NULL; p->preview_row = NULL;
p->top = NULL; p->top = NULL;
p->scanning = FALSE; p->scanning = FALSE;
p->image_pixbuf = NULL;
if (first_time) if (first_time)
{ {
@ -1470,9 +1511,11 @@ preview_update (Preview * p)
if (surface_changed && p->image_data) if (surface_changed && p->image_data)
{ {
free (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->image_data = 0;
p->preview_data = 0;
p->image_width = 0; p->image_width = 0;
p->image_height = 0; p->image_height = 0;
} }
@ -1630,6 +1673,11 @@ preview_destroy (Preview * p)
char filename[PATH_MAX]; char filename[PATH_MAX];
FILE *out; FILE *out;
if (!p)
{
return;
}
if (p->scanning) if (p->scanning)
scan_done (p); /* don't save partial window */ scan_done (p); /* don't save partial window */
else if (preferences.preserve_preview && p->image_data else if (preferences.preserve_preview && p->image_data
@ -1648,15 +1696,29 @@ preview_destroy (Preview * p)
fclose (out); fclose (out);
} }
} }
if (p->image_data) if (p->image_data)
{
free (p->image_data); free (p->image_data);
p->image_data = NULL; p->image_data = NULL;
if (p->preview_data) }
free (p->preview_data);
p->preview_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) if (p->preview_row)
{
free (p->preview_row); free (p->preview_row);
p->preview_row = NULL; p->preview_row = NULL;
}
if (p->top) if (p->top)
gtk_widget_destroy (p->top); gtk_widget_destroy (p->top);
free (p); free (p);

Wyświetl plik

@ -62,7 +62,6 @@ typedef struct
int image_width; int image_width;
int image_height; int image_height;
u_char *image_data; /* 3 * image_width * image_height bytes */ u_char *image_data; /* 3 * image_width * image_height bytes */
u_char *preview_data; /* 3 * image_width * image_height bytes */
int selection_drag; int selection_drag;
struct struct
@ -70,7 +69,7 @@ typedef struct
int active; int active;
double coord[4]; double coord[4];
} }
selection, previous_selection; selection; // coords of selection box in the preview window
GtkWidget *top; /* top-level widget */ GtkWidget *top; /* top-level widget */
GtkWidget *hruler; GtkWidget *hruler;
@ -80,8 +79,7 @@ typedef struct
GtkWidget *cancel; /* the cancel button */ GtkWidget *cancel; /* the cancel button */
GtkWidget *preview; /* the preview button */ GtkWidget *preview; /* the preview button */
cairo_surface_t *surface_cairo; GdkPixbuf *image_pixbuf;
gboolean drawable;
} }
Preview; Preview;