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
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);

Wyświetl plik

@ -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;