/* Compile with: gcc -o gif2png-gdk-pixbuf gif2png-gdk-pixbuf.c `pkg-config cairo gdk-2.0 gdk-pixbuf-2.0 --cflags --libs` */ #include #include #include #include #include #include #define TARGET_WIDTH 2000 #define TARGET_HEIGHT 2000 #define SMALL_SIZE 1024 #define BUFFER_SIZE 4096 #ifndef MIN # define MIN(a,b) ((a)<(b)?(a):(b)) #endif #ifndef MAX # define MAX(a,b) ((a)>(b)?(a):(b)) #endif typedef struct{ GdkPixbufAnimation *animation; }image_t; typedef struct{ image_t *image; GdkPixbuf *pixbuf; }frame_t; image_t * testcase_load(const char *filename){ image_t *image = (image_t*)malloc(sizeof(image_t)); FILE* f; GdkPixbufLoader *loader; f = fopen(filename, "r"); if(!f) return NULL; loader = gdk_pixbuf_loader_new_with_mime_type ("image/gif", NULL); if(!loader) return NULL; for(;;){ guchar buffer[BUFFER_SIZE]; ssize_t read_bytes; read_bytes = fread(buffer, 1, BUFFER_SIZE, f); if (read_bytes < 0) return NULL; if(0 == read_bytes) break; if(!gdk_pixbuf_loader_write(loader, buffer, read_bytes, NULL)) return NULL; } gdk_pixbuf_loader_close(loader, NULL); fclose(f); image->animation = gdk_pixbuf_loader_get_animation(loader); return image; } frame_t * testcase_frame(image_t *image,int nr){ frame_t *frame = (frame_t*)malloc(sizeof(frame_t)); GdkPixbufAnimationIter *iter; frame->image = image; if(gdk_pixbuf_animation_is_static_image(image->animation)){ frame->pixbuf = gdk_pixbuf_animation_get_static_image(image->animation); }else{ iter = gdk_pixbuf_animation_get_iter(image->animation, NULL); while(nr-->0){ /* Here the frame number could be advanced but that is too complicated to bother with here. */ } frame->pixbuf = gdk_pixbuf_animation_iter_get_pixbuf(iter); } return frame; } cairo_surface_t * testcase_prepare_draw(frame_t *frame,cairo_t *cr){ cairo_surface_t *surface; int width; int height; double scale; /* Calculate scale */ width = gdk_pixbuf_animation_get_width(frame->image->animation); height = gdk_pixbuf_animation_get_height(frame->image->animation); scale = MIN((double)TARGET_WIDTH/width,(double)TARGET_HEIGHT/height); surface = cairo_surface_create_similar_image(cairo_get_target(cr), CAIRO_FORMAT_ARGB32, scale * width, scale * height); cr = cairo_create(surface); cairo_scale(cr, scale, scale); gdk_cairo_set_source_pixbuf(cr, frame->pixbuf, 0, 0); cairo_paint(cr); cairo_destroy(cr); return surface; } void testcase_finish_draw(cairo_surface_t *surface, cairo_t *cr){ int width; int height; double x_offset; double y_offset; /* Calculate offset */ width = cairo_image_surface_get_width(surface); height = cairo_image_surface_get_height(surface); x_offset = (TARGET_WIDTH - width)/2; y_offset = (TARGET_HEIGHT - height)/2; cairo_translate(cr, x_offset, y_offset); cairo_set_source_surface(cr, surface, 0, 0); cairo_paint(cr); } static clock_t start; static void measuretime_start(){ start = clock(); } static double measuretime_point(){ clock_t now = clock(); return (double) (now - start) / CLOCKS_PER_SEC; } int main(int argc,char **argv){ cairo_surface_t *surface; cairo_t *cr; image_t *image; frame_t *frame; cairo_surface_t *tmp_surface; if(argc<2){ fprintf(stderr,"Usage: %s []\n",argv[0]); return 1; } /* Create a surface to render the image on */ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,TARGET_WIDTH,TARGET_HEIGHT); cr = cairo_create(surface); /* Load the image */ measuretime_start(); image = testcase_load(argv[1]); if(!image){ fprintf(stderr,"Failed to load GIF image %s\n",argv[1]); return 1; } printf("Loading the image took %f seconds\n",measuretime_point()); /* Get a frame */ measuretime_start(); frame = testcase_frame(image,0); printf("Getting a frame took %f seconds\n",measuretime_point()); /* Render the frame in two steps */ measuretime_start(); tmp_surface = testcase_prepare_draw(frame,cr); printf("Rendering the frame, prepare step took %f seconds\n",measuretime_point()); measuretime_start(); testcase_finish_draw(tmp_surface,cr); printf("Rendering the frame, finish step took %f seconds\n",measuretime_point()); cairo_surface_destroy(tmp_surface); /* Optionally save the result */ if(argc>=3) cairo_surface_write_to_png(surface,argv[2]); return 0; }