I am writing a C program to display raw images using GTK. Now that I can display an image, I'd like to increase the functionality to display multiple images back-to-back in the same window (like a media player). This program is to be used in conjunction with an image decoder that updates the image buffers as more images are decoded, and GTK will follow update the window to let us know we have decoded the images correctly.
Since gtk_main() is the last function to be called, I have opened a separate thread to setup the window and keep it running. The next step is to run a function that loads the images and changes them in the GTK window, but I am having trouble getting this to work. Here is my code to setup the window:
gtk_init(NULL, NULL);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (window), "Image Player");
g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
This will run in it's own thread. Now, I want to run this block of code to change and display the image, which is working for a single image when I run them together in one function, but I'd like to simply call a function called changeImage() which reassigns the image variable through the updated pixbuf and automatically updates it in the window.
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data ((guchar*)buf_rgb, GDK_COLORSPACE_RGB,
FALSE, 8, width, height, stride_rgb*3, NULL, NULL);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_container_add(GTK_CONTAINER (window), image);
gtk_widget_show_all(window);
How would I achieve this in GTK? I have just started learning how to use this tool last week, I believe I'm supposed to create an event using g_signal_connect, but I'm not sure how to tie it to a function call like changeImage() where I can simply update the image or call the function and the image in the display window will change.
EDIT: Test script included below:
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
FILE *x1, *y_1, *z1;
FILE *x2, *y2, *z2;
// image needs to be converted from greyscale to RGB
// using this structure works with GTK
typedef struct{
uint8_t r;
uint8_t g;
uint8_t b;
}rgb;
GtkWidget *window;
GtkWidget *image;
GdkPixbuf *pixbuf;
GtkWidget *button;
GtkWidget *hbox;
// Function to display image 8-bits
// This is the function I was using to display 1 image per window
void displayImage8_RGB(uint8_t *x, uint8_t *y, uint8_t *z, const int width, const int height){
size_t size = width * height;
size_t stride_16 = width;
size_t stride_rgb = width;
rgb *buf_rgb = (rgb*)malloc(stride_rgb*height*sizeof(rgb));
for(size_t i = 0; i < width; i++){
for(size_t j = 0; j < height; j++){
//printf("%d\t%d\n", x, y);
float y_ = x[stride_16*j+i];
float cb = y[stride_16*j+i];
float cr = z[stride_16*j+i];
cr = cr - 128;
cb = cb - 128;
y_ = (y_ * 219) + 16;
cb = (cb * 244) + 128;
cr = (cr * 224) + 128;
uint16_t r_ = (y_ + 1.402 * cr) / 255;
uint16_t g_ = (y_ - 0.344136*cb - 0.714136*cr) / 255;
uint16_t b_ = (y_ + 1.772*cb) / 255;
if (r_ > 255){
r_ = (uint8_t) 0;
}
if (g_ > 255){
g_ = (uint8_t) 0;
}
if (b_ > 255){
b_ = (uint8_t) 0;
}
buf_rgb[stride_rgb*j+i].r = r_;
buf_rgb[stride_rgb*j+i].g = g_;
buf_rgb[stride_rgb*j+i].b = b_;
//printf("x: %d\ty: %d\tz: %d\nr: %d\tg: %d\tb: %d\n", x_c, y_c, z_c, buf_rgb[stride_rgb*j+i].r, buf_rgb[stride_rgb*j+i].g, buf_rgb[stride_rgb*j+i].b);
}
}
GtkWidget* image;
gtk_init (NULL, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data ((guchar*)buf_rgb, GDK_COLORSPACE_RGB,
FALSE, 8, width, height, stride_rgb*3, NULL, NULL);
gtk_window_set_title (GTK_WINDOW (window), "Image Viewer");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_container_add(GTK_CONTAINER (window), image);
gtk_widget_show_all (window);
gtk_main ();
free(buf_rgb);
return;
}
void changeImage(uint8_t *x, uint8_t *y, uint8_t *z, const int width, const int height){
printf("displaying image\n");
size_t size = width * height;
size_t stride_16 = width;
size_t stride_rgb = width;
rgb *buf_rgb = (rgb*)malloc(stride_rgb*height*sizeof(rgb));
for(size_t i = 0; i < width; i++){
for(size_t j = 0; j < height; j++){
//printf("%d\t%d\n", x, y);
float y_ = x[stride_16*j+i];
float cb = y[stride_16*j+i];
float cr = z[stride_16*j+i];
cr = cr - 128;
cb = cb - 128;
y_ = (y_ * 219) + 16;
cb = (cb * 244) + 128;
cr = (cr * 224) + 128;
uint16_t r_ = (y_ + 1.402 * cr) / 255;
uint16_t g_ = (y_ - 0.344136*cb - 0.714136*cr) / 255;
uint16_t b_ = (y_ + 1.772*cb) / 255;
if (r_ > 255){
r_ = (uint8_t) 0;
}
if (g_ > 255){
g_ = (uint8_t) 0;
}
if (b_ > 255){
b_ = (uint8_t) 0;
}
buf_rgb[stride_rgb*j+i].r = r_;
buf_rgb[stride_rgb*j+i].g = g_;
buf_rgb[stride_rgb*j+i].b = b_;
//printf("x: %d\ty: %d\tz: %d\nr: %d\tg: %d\tb: %d\n", x_c, y_c, z_c, buf_rgb[stride_rgb*j+i].r, buf_rgb[stride_rgb*j+i].g, buf_rgb[stride_rgb*j+i].b);
}
}
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data ((guchar*)buf_rgb, GDK_COLORSPACE_RGB,
FALSE, 8, width, height, stride_rgb*3, NULL, NULL);
gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
//gtk_container_add(GTK_CONTAINER (window), image);
//gtk_widget_show_all(window);
g_object_unref(pixbuf);
free(buf_rgb);
return;
}
void* setupWindow(){
gtk_init(NULL, NULL);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (window), "Image Player");
g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
rgb *buf_rgb = (rgb*)malloc(1998*1080*sizeof(rgb));
memset(buf_rgb, 255, (1998*1080*3));
GdkPixbuf *s_pixbuf = gdk_pixbuf_new_from_data((guchar*)buf_rgb, GDK_COLORSPACE_RGB,
FALSE, 8, 1998, 1080, 1998*3, NULL, NULL);
image = gtk_image_new_from_pixbuf(s_pixbuf);
gtk_container_add(GTK_CONTAINER (window), image);
gtk_widget_show_all(window);
gtk_main();
}
int main(int argc, char *argv[]){
// for demo, pass files in this order:
// idwt_bin0_test1.raw idwt_bin1_test1.raw idwt_bin2_test1.raw idwt_bin0_test2.raw idwt_bin1_test2.raw idwt_bin2_test2.raw
// files passed to function are 3 components (.raw) for 2 uncompressed images
pthread_t win, changer;
x1 = fopen("mtest/idwt_bin0_test1.raw", "rb");
y_1 = fopen("mtest/idwt_bin1_test1.raw", "rb");
z1 = fopen("mtest/idwt_bin2_test1.raw", "rb");
x2 = fopen("mtest/idwt_bin0_test2.raw", "rb");
y2 = fopen("mtest/idwt_bin1_test2.raw", "rb");
z2 = fopen("mtest/idwt_bin2_test2.raw", "rb");
// test1 1998x1080
// busy4k 4096x2160
const int w = 1998, h = 1080;
int size = w * h; // multiply by 2 for 16-bit
uint8_t *xbuf1 = (uint8_t*)malloc(sizeof(uint8_t)*size);
uint8_t *ybuf1 = (uint8_t*)malloc(sizeof(uint8_t)*size);
uint8_t *zbuf1 = (uint8_t*)malloc(sizeof(uint8_t)*size);
uint8_t *xbuf2 = (uint8_t*)malloc(sizeof(uint8_t)*size);
uint8_t *ybuf2 = (uint8_t*)malloc(sizeof(uint8_t)*size);
uint8_t *zbuf2 = (uint8_t*)malloc(sizeof(uint8_t)*size);
// comp0 comp2 comp1
fread(xbuf1, 1, size, x1);
fread(ybuf1, 1, size, y_1);
fread(zbuf1, 1, size, z1);
fread(xbuf2, 1, size, x2);
fread(ybuf2, 1, size, y2);
fread(zbuf2, 1, size, z2);
// thread to setup main gtk window
pthread_create(&win, NULL, setupWindow, NULL);
changeImage(xbuf1, ybuf1, zbuf1, w, h); // change to image 1
sleep(3); // gtk's timer could be used instead
changeImage(xbuf2, ybuf2, zbuf2, w, h); // change to image 2
sleep(3);
fclose(x1);
fclose(x2);
fclose(y_1);
fclose(y2);
fclose(z1);
fclose(z2);
return 0;
}```
Related
I'm trying to find an example of showing an image in GTK 4.0 from an existing memory buffer, where the image is stored in the form of an array of floats of size width x height x 3. I understand I need to use GtkImage, but I don't know how to pass such an array to it.
Just for context, I wanted to implement a prototype for rendering some images, they are all rendered in memory, and as a result, I have an array of such floats, three floats for each pixel. Unfortunately, I could not find any examples for the latest GTK 4.0. There are plenty of examples of loading an image from the disk, but it seems like jumping unnecessary hoops for my case. I used to load these as OpenGL texture and show them on a quad, but it also seems like driving nails with a sledgehammer.
Thank you in advance for any help!
You can use GdkMemoryTexture for an image in memory and use a GtkPicture and set the texture using gtk_picture_set_paintable() to paint it. The following is an example that creates an opaque 8bit RGB image and displays it on the screen:
/* pixel.c
*
* Compile: cc -ggdb pixel.c -o pixel $(pkg-config --cflags --libs gtk4) -o pixel
* Run: ./pixel
*
* Author: Mohammed Sadiq <www.sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later OR CC0-1.0
*/
#include <gtk/gtk.h>
#define BYTES_PER_R8G8B8 3
#define WIDTH 400
static void
fill_row (GByteArray *array,
guint8 value,
int row_size)
{
guint i;
for (i = 0; i < row_size; i++) {
/* Fill the same values for RGB */
g_byte_array_append (array, &value, 1); /* R */
g_byte_array_append (array, &value, 1); /* G */
g_byte_array_append (array, &value, 1); /* B */
}
}
static void
add_pixel_picture (GtkPicture *picture)
{
g_autoptr(GBytes) bytes = NULL;
GdkTexture *texture;
GByteArray *pixels;
gsize height;
/* Draw something interesting */
pixels = g_byte_array_new ();
for (guint i = 0; i <= 0xff ; i++)
fill_row (pixels, i, WIDTH);
for (guint i = 0; i <= 0xff; i++)
fill_row (pixels, 0xff - i, WIDTH);
height = pixels->len / (WIDTH * BYTES_PER_R8G8B8);
bytes = g_byte_array_free_to_bytes (pixels);
texture = gdk_memory_texture_new (WIDTH, height,
GDK_MEMORY_R8G8B8,
bytes,
WIDTH * BYTES_PER_R8G8B8);
gtk_picture_set_paintable (picture, GDK_PAINTABLE (texture));
}
static void
app_activated_cb (GtkApplication *app)
{
GtkWindow *window;
GtkWidget *picture;
window = GTK_WINDOW (gtk_application_window_new (app));
g_object_set (window,
"width-request", 500,
"height-request", 400,
NULL);
picture = gtk_picture_new ();
gtk_widget_add_css_class (picture, "frame");
g_object_set (picture,
"margin-start", 96,
"margin-end", 96,
"margin-top", 96,
"margin-bottom", 96,
NULL);
gtk_window_set_child (window, picture);
add_pixel_picture (GTK_PICTURE (picture));
gtk_window_present (window);
}
int
main (int argc,
char *argv[])
{
g_autoptr(GtkApplication) app = gtk_application_new (NULL, 0);
g_signal_connect (app, "activate", G_CALLBACK (app_activated_cb), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
}
Use the GdkMemoryFormat that suites your data, or convert it to one that's supported.
This piece of SDL2 code draws some white pixels on-screen using OpenGL, then grabs the pixels field of the window's SDL_Surface and loops through it, printing out the values of the contents. Even though I just drew a white triangle, the loop shows that there's nothing but zeros in that buffer (the code just prints 0 to standard out over and over).
How can I actually get at the modified pixel buffer, in something like RGB or ARGB or RGBA format?
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <GL/glu.h>
int main()
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
const int WINDOW_WIDTH = 100;
const int WINDOW_HEIGHT = 100;
SDL_Window *window = SDL_CreateWindow("OpenGL Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLenum error = glGetError();
if( error != GL_NO_ERROR )
{
printf( "Error initializing OpenGL! %s\n", gluErrorString(error));
}
glClearColor(0, 0, 0, 1);
int quit = 0;
SDL_Event event;
while (!quit)
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
quit = 1;
break;
}
}
glBegin(GL_TRIANGLES);
glColor3f(255, 255, 255);
glVertex2f(0, 0);
glVertex2f(0, 1);
glVertex2f(1, 0);
glEnd();
SDL_GL_SwapWindow(window);
SDL_Surface *surface = SDL_GetWindowSurface(window);
int pixel_depth = SDL_BYTESPERPIXEL(surface->format->format);
char *pixels = (char*) surface->pixels;
int max_value = 0;
for (int i = 0; i < WINDOW_WIDTH * WINDOW_HEIGHT * pixel_depth; i++)
{
if (pixels[i] > max_value)
{
max_value = pixels[i];
}
}
SDL_FreeSurface(surface);
SDL_Log("%d", max_value);
}
SDL_Quit();
return 0;
}
SDL_GetWindowSurface() doesn't work with OpenGL:
You may not combine this with 3D or the rendering API on this window.
Use glReadPixels() instead.
Use PBO to read data from from the pixel buffer.
glReadBuffer(GL_COLOR_ATTACHMENT0);
writeIndex = (writeIndex + 1) % 2;
readIndex = (readIndex + 1) % 2;
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[writeIndex]);
// copy from framebuffer to PBO asynchronously. it will be ready in the NEXT frame
glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// now read other PBO which should be already in CPU memory
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo[readIndex]);
unsigned char* Data = (unsigned char *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
My problem is: I have a picture in my buffer buffer = (unsigned short *) malloc(imageSize*2);
This picture is 8 bit per pixel and 1 byte per pixel, so it is a Grayscale picture.
But to display it I need an RGB picture. Because only an RGB picture is supported.
So I need to change the picture from:
index | 1 | 2 |...
grayvalue | 128 | 135 |...
to
index | 1 | 2 |...
grayvalue | 128,128,128 | 135,135,135 |...
for | r,g,b |.
My idea is to make an array from the buffer array[grayvalue] = buffer[count], somehow add 2 grayvalues to the same pixel and change the array back into a buffer. But I can´t find how to get an array from a buffer and how to add the grayvalues to 1 pixel. I hope someone has an idea what I mean and can link me to information that I overlooked.
Edit:
#include <stdlib.h>
#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
long int imageSize = 0;
unsigned short *buffer = NULL;
const int Width = 1936, Height = 1460;
/*code to take raw image data from camera*/
// Put raw image data from the camera in the buffer.
buffer = (unsigned short *) malloc(imageSize*2);
// GTK
GtkWidget *window;
GtkWidget* image;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data ((unsigned short *) buffer, GDK_COLORSPACE_RGB,FALSE, 8,
Width, Height, Width*1, NULL, NULL);
gtk_window_set_title (GTK_WINDOW (window), "Image Viewer");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
image = gtk_image_new_from_pixbuf (pixbuf);
gtk_container_add(GTK_CONTAINER (window), image);
gtk_widget_show_all (window);
gtk_main ();
free (buffer);
return 0;
}
This is the program I use to Display my Image. The Code that I use to take a picture with the camera can´t be shared.
Your code hase 1 main problem:
You create the pixbuf with wrong rowstride value. It is in bytes, not in pixels. For RGB pixels you need 3 bytes.
After this is fixed you need to convert your greyscale pixels to RGBB pixels. This is not "combining pixels" but simply repeating the same value.
This is some sample code to convert 8bit greyscale to 24bit RGB pixels:
// build:
// gcc -o test `pkg-config --cflags gtk+-3.0` test.c `pkg-config --libs gtk+-3.0`
#include <stdlib.h>
#include <stdint.h>
#include <gtk/gtk.h>
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} rgb;
int main (int argc, char *argv[])
{
size_t Width = 1936;
size_t Height = 1280;
size_t Stride_8 = Width; // Might need rounding up
size_t Stride_rgb = Width; // Might need rounding up
//Due to lack of camera data, lets use some dummy data...
uint8_t *buffer_8 = malloc(Stride_8*Height);
for (size_t x = 0; x < Width; x++)
for (size_t y = 0; y < Height; y++)
buffer_8[Stride_8*y + x] = (x+y) & 0xFF;
/*code to take raw image data from camera*/
// Put greyscale image data from the camera into the RGB buffer.
rgb *buffer_rgb = malloc(Stride_rgb * Height * sizeof(rgb));
for (size_t x = 0; x < Width; x++)
for (size_t y = 0; y < Height; y++)
{
// Convert 1 byte greyscale in 3 byte RGB:
uint8_t grey = buffer_8[Stride_8*y + x];
buffer_rgb[Stride_rgb*y + x].r = grey;
buffer_rgb[Stride_rgb*y + x].g = grey;
buffer_rgb[Stride_rgb*y + x].b = grey;
}
// GTK
gtk_init(&argc, &argv);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (window), "test");
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
GdkPixbuf *pixbuf_rgb = gdk_pixbuf_new_from_data((guchar*)buffer_rgb, GDK_COLORSPACE_RGB,FALSE, 8,
Width, Height, Stride_rgb*3, NULL, NULL);
GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf_rgb);
gtk_container_add(GTK_CONTAINER(window), image);
gtk_widget_show_all(window);
gtk_main();
free(buffer_rgb);
free(buffer_8);
return 0;
}
I'm using gtk+2.0 and cairo. I wrote a simple program that open a window and
move a point. A simple biliard, only horizontal motion. It's just a test.
The problem is that it seems to be not so smooth, and I would ask if there
are some gtk or cairo expert here that could check for errors in the code.
Thanks.
#include <gtk/gtk.h>
#include <math.h>
gboolean timeout(gpointer data)
{
GtkWidget *widget = GTK_WIDGET(data);
if (!widget->window) return TRUE;
gtk_widget_queue_draw(widget);
}
gboolean configure(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{
return TRUE;
}
double px = 10;
double vx = **2**;
gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
cairo_t *cr = gdk_cairo_create(widget->window);
cairo_rectangle(cr, event->area.x, event->area.y, event->area.width, event->area.height);
cairo_clip(cr);
cairo_set_source_rgb(cr,1,0,0);
cairo_arc(cr, px, 100, 6, 0, 2*M_PI);
cairo_fill(cr);
cairo_set_source_rgb(cr,0,0,0);
cairo_destroy(cr);
if (px <= 3 || px >= 200-3) vx = -vx;
px += vx;
return FALSE;
}
int main(int argc, char *argv[])
{
char *title = "Test";
int sx = 200;
int sy = 200;
gtk_init(NULL,NULL);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),title);
gtk_container_set_border_width(GTK_CONTAINER (window), 2);
g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit),&window);
GtkWidget *drawing_area = gtk_drawing_area_new();
//g_signal_connect(window,"key-press-event",G_CALLBACK(on_key_press),NULL);
const GdkColor bianco = { 0, 0xffff, 0xffff, 0xffff };
const GdkColor nero = { 0, 0x0, 0x0, 0x0 };
gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &bianco);
gtk_widget_set_size_request(drawing_area, sx, sy);
g_signal_connect(drawing_area,"configure_event",G_CALLBACK(configure), NULL);
g_signal_connect(drawing_area,"expose_event",G_CALLBACK(expose),NULL);
gtk_container_add(GTK_CONTAINER(window), drawing_area);
gtk_widget_show(drawing_area);
g_timeout_add(**10**, timeout, window);
if (!GTK_WIDGET_VISIBLE (window))
gtk_widget_show_all(window);
else {
gtk_widget_destroy (window);
window = NULL;
}
gtk_main();
return 0;
}
Not so smooth ? Well, with a period of 100ms, you're drawing at best 10 frames per second, no wonder it's not smooth... You should aim for 60 fps. Furthermore, you're invalidating the whole widget by calling gtk_widget_queue_draw, so your call to cairo_clip is mostly useless, as the clipping region is the whole widget. You should call gtk_widget_queue_draw_area instead so your clipping region is useful, and determining the area by keeping a record of the animation at frame n and n-1, so you redraw both areas to avoid the previous frame not being deleted.
There are lots of interesting stuff on animation perception on Owen Tailor's blog, starting with this post and more recent:
http://blog.fishsoup.net/2009/05/28/frames-not-idles/
Give a look at all the posts with figures, it's a gold mine.
I have a GTK+ application written in C that loads a matrix of animated GIF files. These GtkImages automatically run the animation when they are loaded and then stop when the animation is completed. How would I restart the animation of each GtkImage containing the GIF and are signals generated when the animation is complete?
Thank you.
EDIT :
Would the use of gdk_pixbuf_animation_get_iter() described here make this possible?
Full code is provided below.
/*
* Compile me with:
* gcc -o reels reels.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
/* GTK */
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
/**** prototypes ****/
static void destroy (GtkWidget*, gpointer);
GdkPixbuf *create_pixbuf(const gchar * filename);
GtkWidget *SetupWindow(gchar *data, const gchar *filename);
static void destroy (GtkWidget *window, gpointer data);
void btnSpin_clicked(GtkWidget *button, gpointer data);
void btnExit_clicked(GtkWidget *button, gpointer data);
/********************/
GtkWidget *images[3][5];
static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}
void btnSpin_clicked(GtkWidget *button, gpointer data)
{
printf("Spin Button pressed.\n");
return;
}
void btnExit_clicked(GtkWidget *button, gpointer data)
{
gtk_main_quit();
return;
}
GtkWidget *SetupWindow(gchar *data, const gchar *filename)
{
GdkPixmap *background;
GdkPixbuf *pixbuf;
GdkScreen *ourscreen;
GdkColormap *colormap;
GtkStyle *style;
GdkColor fg;
GdkColor bg;
GError *error = NULL;
GdkRectangle *rect;
GtkWidget *window;
pixbuf = gdk_pixbuf_new_from_file (filename,&error);
if (error != NULL) {
if (error->domain == GDK_PIXBUF_ERROR) {
g_print ("Pixbuf Related Error:\n");
}
if (error->domain == G_FILE_ERROR) {
g_print ("File Error: Check file permissions and state:\n");
}
g_printerr ("%s\n", error[0].message);
}
gdk_pixbuf_render_pixmap_and_mask (pixbuf, &background, NULL, 0);
style = gtk_style_new ();
style->bg_pixmap[0] = background;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), data);
// gtk_window_maximize(GTK_WINDOW(window));
gtk_window_set_modal (GTK_WINDOW (window),TRUE);
gtk_window_set_default_size(GTK_WINDOW(window),628,530);
gtk_widget_set_style (GTK_WIDGET(window), GTK_STYLE(style));
gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER_ALWAYS);
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
//gtk_window_set_resizable(GTK_WINDOW(window), (gboolean) FALSE);
gtk_window_set_decorated( GTK_WINDOW(window), FALSE );
return(window);
}
int main (int argc, char *argv[])
{
GdkPixbufAnimation *animation;
GtkWidget *image;
int x,y;
GdkPixbuf *pixBuf;
GdkPixmap *pixMap;
gchar filename[20];
GtkWidget *btnSpin, *btnExit;
GtkWidget *frame; /* for absolute positionining of widgets */
GtkWidget *window;
int posx, posy;
gtk_init (&argc, &argv);
window = SetupWindow("Demo", "background.gif");
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
btnSpin = gtk_button_new_with_label("Spin");
gtk_widget_set_size_request(btnSpin, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnSpin, 229, 485);
g_signal_connect(G_OBJECT( btnSpin ), "clicked", G_CALLBACK(btnSpin_clicked), NULL );
btnExit = gtk_button_new_with_label("Exit");
gtk_widget_set_size_request(btnExit, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnExit, 320, 485);
g_signal_connect(G_OBJECT( btnExit ), "clicked", G_CALLBACK(btnExit_clicked), NULL );
/* setup animated gifs */
for( y = 0; y < 3; y++ )
{
posy = (y*120) + 20;
for( x = 0; x < 5; x++ )
{
posx = (x*120) + 20;
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new_from_file(filename);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], posx, posy);
}
}
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
After taking a look at the GtkImage source code, I am afraid there is no signal that is generated when the animation is completed. However, you should be able to restart the animation by calling gtk_image_set_from_file() or gtk_image_set_from_animation().
To completely solve your initial issue, I propose you create a subclass of GtkImage. It should behave exactly like GtkImage, except that animation_timeout should send a signal if delay < 0 around line 1315.
Information about creating a subclass of a GObject (note that GtkImage is a GObject) can be found here.
Based on user1202136's answer the following code snippets show the solution. I post this in case others find it useful.
The basic idea is to use GdkPixbufAnimation, gdk_pixbuf_animation_new_from_file and gtk_image_set_from_animation
The following code snippet(s) show how to do it.
GtkWidget *images[3][5];
GdkPixbufAnimation *animations[3][5];
/* Initial setup of animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new();
animations[y][x] = gdk_pixbuf_animation_new_from_file ( filename , &error);
gtk_image_set_from_animation (GTK_IMAGE(images[y][x]), animations[y][x]);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], (x*120) + 20, (y*120) + 20);
}
}
/* now to restart the animations use the images and animations array already stored in memory,
no need to re-read the animations from disk so this happens quickly
*/
/* restart animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
gtk_image_set_from_animation(GTK_IMAGE(images[y][x]), animations[y][x]);
}
}