/* Compile with: gcc -o gif2png-gdk-pixbuf gif2png-gdk-pixbuf.c `pkg-config cairo gdk-2.0 gdk-pixbuf-2.0 --cflags --libs` -lm */ #include #include #include #include #include #include #include #define TARGET_WIDTH 2000 #define TARGET_HEIGHT 2000 #define MAX_SIZE 1024 #define BUFFER_SIZE 4096 #ifndef MIN # define MIN(a,b) ((a)<(b)?(a):(b)) #endif typedef struct{ GdkPixbuf *pixbuf; }pyramid_level_t; typedef struct{ int width; int height; pyramid_level_t *level; int levels; }lazy_pyramid_t; typedef struct{ GdkPixbufAnimation *animation; int maxsize; }image_t; typedef struct{ image_t *image; lazy_pyramid_t pyramid; }frame_t; void lazy_pyramid_ensure_level(lazy_pyramid_t *pyramid, int level){ if(!pyramid->level[level].pixbuf){ double scale = 1.0/(1<level[level].pixbuf = gdk_pixbuf_scale_simple(pyramid->level[0].pixbuf, scale * pyramid->width, scale * pyramid->height, GDK_INTERP_BILINEAR); } } void lazy_pyramid_init(lazy_pyramid_t *pyramid, GdkPixbuf *pixbuf, int maxsize){ int level_size; int i; pyramid->width = gdk_pixbuf_get_width(pixbuf); pyramid->height = gdk_pixbuf_get_height(pixbuf); level_size = MAX(pyramid->width,pyramid->height); pyramid->levels = 1; while(level_size>maxsize){ level_size /= 2; ++pyramid->levels; } pyramid->level = (pyramid_level_t*)malloc(pyramid->levels*sizeof(pyramid_level_t)); pyramid->level[0].pixbuf = pixbuf; for(i=1;ilevels;++i) pyramid->level[i].pixbuf = NULL; /* only create the topmost level at first */ lazy_pyramid_ensure_level(pyramid,pyramid->levels-1); } void lazy_pyramid_flush(lazy_pyramid_t *pyramid){ int i; for(i=1;ilevels-1;++i){ g_object_unref(pyramid->level[i].pixbuf); pyramid->level[i].pixbuf = NULL; } } int lazy_pyramid_level_for_scale(lazy_pyramid_t *pyramid, double scale){ int level = 0; int i; for(i=0;ilevels;++i){ if(scale >= 1.0/(1<level[level].pixbuf, 0, 0); cairo_paint(cr); cairo_restore(cr); } image_t * testcase_load(const char *filename, int maxsize){ image_t *image = (image_t*)malloc(sizeof(image_t)); GdkPixbufLoader *loader; loader = gdk_pixbuf_loader_new_with_mime_type ("image/gif", NULL); if(!loader) return NULL; #if 1 { GMappedFile *f = g_mapped_file_new(filename, FALSE, NULL); if(!f) return NULL; if(!gdk_pixbuf_loader_write(loader, (const unsigned char*)g_mapped_file_get_contents(f), g_mapped_file_get_length(f), NULL)) return NULL; g_mapped_file_unref(f); } #else { FILE* f; f = fopen(filename, "r"); if(!f) return NULL; for(;;){ guchar buffer[BUFFER_SIZE]; ssize_t read_bytes; static size_t total; read_bytes = fread(buffer, 1, BUFFER_SIZE, f); if (read_bytes < 0) return NULL; if(0 == read_bytes) break; total += read_bytes; printf("read %d bytes (%d total)\n",(int)read_bytes,(int)total); if(!gdk_pixbuf_loader_write(loader, buffer, read_bytes, NULL)) return NULL; } fclose(f); } #endif gdk_pixbuf_loader_close(loader, NULL); image->animation = gdk_pixbuf_loader_get_animation(loader); image->maxsize = maxsize; 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)){ lazy_pyramid_init(&frame->pyramid, gdk_pixbuf_animation_get_static_image(image->animation), image->maxsize); }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. */ } lazy_pyramid_init(&frame->pyramid, gdk_pixbuf_animation_iter_get_pixbuf(iter), image->maxsize); } return frame; } void setup_scale(cairo_t *cr, frame_t *frame, int maxwidth, int maxheight){ int width; int height; double x_offset; double y_offset; 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)maxwidth/width,(double)maxheight/height); x_offset = (maxwidth - scale*width)/2; y_offset = (maxheight - scale*height)/2; cairo_translate(cr, x_offset, y_offset); cairo_scale(cr, scale, scale); } int testcase_draw_quick(frame_t *frame, cairo_t *cr){ int good_quality = 1; int level = lazy_pyramid_level_from_cairo(&frame->pyramid, cr); for(;!frame->pyramid.level[level].pixbuf && levelpyramid.levels;++level) good_quality = 0; lazy_pyramid_draw_level(&frame->pyramid, level, cr); return good_quality; } void testcase_draw(frame_t *frame,cairo_t *cr){ int level = lazy_pyramid_level_from_cairo(&frame->pyramid, cr); lazy_pyramid_ensure_level(&frame->pyramid, level); lazy_pyramid_draw_level(&frame->pyramid, level, cr); } void testcase_prepare(frame_t *frame,cairo_t *cr){ int level = lazy_pyramid_level_from_cairo(&frame->pyramid, cr); lazy_pyramid_ensure_level(&frame->pyramid, level); } 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; int good_quality; 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],MAX_SIZE); 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()); setup_scale(cr, frame, TARGET_WIDTH, TARGET_HEIGHT); /* Render the frame (scaled) */ measuretime_start(); good_quality = testcase_draw_quick(frame,cr); printf("Rendering the frame quickly took %f seconds\n",measuretime_point()); if(!good_quality){ measuretime_start(); testcase_prepare(frame,cr); printf("Preparing the frame took %f seconds\n",measuretime_point()); measuretime_start(); testcase_draw(frame,cr); printf("Rendering the frame in full quality took %f seconds\n",measuretime_point()); } cairo_destroy(cr); /* Optionally save the result */ if(argc>=3) cairo_surface_write_to_png(surface,argv[2]); return 0; }