I’m writing a small ‘film strip’ tool in C which shows 24 300x300 pixel images. I find I have to add a delay after calling gtk_image_set_from_pixbuf() otherwise the wrong or corrupt image shows up.
Here’s a code fragment
if(image != NULL)
{
formatted = format_image_for_film_strip_data(image);
pixbuf_image = gdk_pixbuf_new_from_data
(
formatted->buffer,
GDK_COLORSPACE_RGB,
TRUE, 8, // has alpha (RGBA) RGB is 8 bits per channel
FRAME_WIDTH,
FRAME_HEIGHT,
formatted->width * 4, // row stride byte count
NULL,
NULL
);
gtk_image_set_from_pixbuf(GTK_IMAGE(image_widget), pixbuf_image);
while(gtk_events_pending())
{
**usleep(10000); // 10ms**
}
film_strip_data[frame].used = TRUE;
strcpy(film_strip_data[frame].file_name, file_name);
gtk_entry_set_alignment(GTK_ENTRY(filename_widget), 0.5); // center
gtk_entry_set_text
(
GTK_ENTRY(filename_widget),
image->name
);
After some considerable (and frustrating) time I added a 10ms delay in a gtk_events_pending() loop which ‘fixes’ the problem. This is obviously inelegant what is the proper way to wait for gtk_image_set_from_pixbuf() to finiish.
It seems this does the trick:
while(g_main_context_pending(NULL))
{
g_main_context_iteration(NULL, TRUE);
}
It allows the iteration to block and so there is no race condition.
zbrown
(Zander Brown)
September 3, 2021, 11:18pm
#3
This really shouldn’t be needed, are you free’ing ->buffer
or something?
This seems to work:
void display_full_image(FILM_STRIP *film_strip_frame)
{
GtkWidget *image_window;
GtkWidget *image_widget;
GdkPixbuf *pixbuf_image;
IMAGE *image, *scaled;
uint8_t *buffer;
double scale;
if(!film_strip_frame->used) return;
image_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable(GTK_WINDOW(image_window), FALSE);
image_widget = gtk_image_new();
// "lip_" functions are from my Library for Image Processing
image = lip_read_image
(
film_strip_frame->file_name
);
orientate_image(image);
scale = 1280.0 / image->length; // half a 4K screen height
lip_image_resample
(
image,
image->width * scale,
image->length * scale,
&scaled
);
// transfer full ownership of pixel data to the pixbuf
buffer = scaled->buffer;
// unhook the pixel data from the scaled IMAGE
scaled->buffer = NULL;
pixbuf_image = gdk_pixbuf_new_from_data
(
buffer,
GDK_COLORSPACE_RGB,
TRUE, 8, // has alpha (RGBA) RGB is 8 bits per channel
scaled->width,
scaled->length,
scaled->width * 4, // row stride byte count
NULL,
NULL
);
gtk_image_set_from_pixbuf(GTK_IMAGE(image_widget), pixbuf_image);
gtk_container_add(GTK_CONTAINER(image_window), image_widget);
gtk_widget_show_all(image_window);
// clean up
lip_delete_image(scaled);
lip_delete_image(image);
g_object_unref((gpointer)pixbuf_image);
}
zbrown
(Zander Brown)
September 4, 2021, 6:11pm
#5
Right, but now your also leaking
You probably want something closer to
static void
clear_image (guchar *pixels, gpointer data)
{
lip_delete_image (data);
}
/* … */
pixbuf_image = gdk_pixbuf_new_from_data(buffer,
GDK_COLORSPACE_RGB,
TRUE, 8, // has alpha (RGBA) RGB is 8 bits per channel
scaled->width,
scaled->length,
scaled->width * 4, // row stride byte count
clear_image, /* destroy_fn */
scaled); /* destroy_fn_data */
Hmm.
My lip_delete_image(image) cleans up all my data. The question remains what cleans up the data from gdk_pixbuf_new_from_data ?
Does the g_object_unref((gpointer)pixbuf_image); clean up everything it has ? Both the buffer data it has been given and whatever wrapper structure a pix_buf has ? Do I need to add g_object_unref((gpointer)buffer); too ?
The provided pixel buffer must not be freed because if I attempt to unref it the code seg faults. However a straight free() deletes the data without any trouble.
In your destroy function what does guchar *pixels refer to ? Since it isn’t used…
Here is my fixed up code with a PixbufDestroyNotify wrapper function:
void PixbufDestroyNotify
(
__attribute__((unused))guchar *pixels,
gpointer image
)
{
lip_delete_image((IMAGE *)image);
}
void display_full_image(FILM_STRIP *film_strip_frame)
{
GtkWidget *image_window;
GtkWidget *image_widget;
GdkPixbuf *pixbuf_image;
IMAGE *image, *scaled;
double scale;
if(!film_strip_frame->used) return;
image_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
image_widget = gtk_image_new();
gtk_window_set_resizable(GTK_WINDOW(image_window), FALSE);
gtk_container_add(GTK_CONTAINER(image_window), image_widget);
gtk_window_set_title(GTK_WINDOW(image_window), film_strip_frame->file_name);
// "lip_" functions are from my Library for Image Processing
image = lip_read_image
(
film_strip_frame->file_name
);
orientate_image(image);
// ~two thirds of the screen height
scale = (workarea.height * 0.666) / image->length;
lip_image_resample
(
image,
image->width * scale,
image->length * scale,
&scaled
);
pixbuf_image = gdk_pixbuf_new_from_data
(
scaled->buffer,
GDK_COLORSPACE_RGB,
TRUE, 8, // has alpha (RGBA) RGB is 8 bits per channel
scaled->width,
scaled->length,
scaled->width * 4, // row stride byte count
PixbufDestroyNotify,
(gpointer)scaled
);
gtk_image_set_from_pixbuf(GTK_IMAGE(image_widget), pixbuf_image);
gtk_widget_show_all(image_window);
// clean up
//lip_delete_image(scaled); // already deleted via void PixbufDestroyNotify
lip_delete_image(image);
g_object_unref((gpointer)pixbuf_image);
}
Thanks for your help. My contact-sheet / film-strip is working well.
system
(system)
Closed
October 5, 2021, 3:08pm
#11
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.