Waiting for gtk_image_set_from_pixbuf()

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.

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

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.