Start of image cache implementation... breaks image functionality.
authorMarijn van Vliet <marijn.vanvliet@med.kuleuven.be>
Sat, 6 Feb 2010 20:22:44 +0000 (20:22 +0000)
committerMarijn van Vliet <marijn.vanvliet@med.kuleuven.be>
Sat, 6 Feb 2010 20:22:44 +0000 (20:22 +0000)
git-svn-id: http://lassie.dyndns-server.com/svn/gargoyle-gtk@223 ddfedd41-794f-dd11-ae45-00112f111e67

libchimara/graphics.c
libchimara/graphics.h

index 3d941457731723cb16e1ff2fa168027f17e282ff..13048f9aa41587f81af0520c6307bcf8c0787e43 100644 (file)
@@ -8,42 +8,45 @@ extern GPrivate *glk_data_key;
 void on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info);
 void on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data);
 
-static gboolean size_determined;
 static gboolean image_loaded;
+static gboolean size_determined;
 
-glui32
-glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
+static struct image_info*
+load_image_in_cache(glui32 image, gint width, gint height)
 {
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       giblorb_result_t res;
        giblorb_err_t blorb_error = 0;
+       giblorb_result_t resource;
        GError *pixbuf_error = NULL;
-       struct image_info *info = g_new0(struct image_info, 1);
-       info->resource_number = image;
        guchar *buffer;
 
        /* Lookup the proper resource */
-       blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
+       blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &resource, giblorb_ID_Pict, image);
        if(blorb_error != giblorb_err_None) {
                WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
-               return FALSE;
+               return NULL;
        }
 
-       if(width == NULL && height == NULL) {
-               /* No size requested, don't bother loading the image */
-               giblorb_unload_chunk(glk_data->resource_map, image);
-               return TRUE;
-       }
+       struct image_info *info = g_new0(struct image_info, 1);
+       info->resource_number = image;
 
        /* Load the resource */
        GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
        g_signal_connect( loader, "size-prepared", G_CALLBACK(on_size_prepared), info ); 
-       glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
+       g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
+
+       /* Scale image if necessary */
+       if(width > 0 && height > 0) {
+               gdk_pixbuf_loader_set_size(loader, width, height);
+               info->scaled = TRUE;
+       }
+
+       glk_stream_set_position(glk_data->resource_file, resource.data.startpos, seekmode_Start);
        buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
 
        guint32 total_read = 0;
-       size_determined = FALSE;
-       while(total_read < res.length && !size_determined) {
+       image_loaded = FALSE;
+       while(total_read < resource.length && !image_loaded) {
                guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
 
                if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
@@ -51,30 +54,39 @@ glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
                        giblorb_unload_chunk(glk_data->resource_map, image);
                        gdk_pixbuf_loader_close(loader, &pixbuf_error);
                        g_free(buffer);
-                       return FALSE;
+                       return NULL;
                }
 
                total_read += num_read;
        }
-       giblorb_unload_chunk(glk_data->resource_map, image);
        gdk_pixbuf_loader_close(loader, &pixbuf_error);
+       giblorb_unload_chunk(glk_data->resource_map, resource.chunknum);
        g_free(buffer);
 
-       /* Determine the image dimensions */
+       /* Wait for the PixbufLoader to finish loading the image */
        g_mutex_lock(glk_data->resource_lock);
-       while(!size_determined) {
-               /* Wait for the PixbufLoader to finish reading the image size */
-               g_cond_wait(glk_data->resource_info_available, glk_data->resource_lock);
+       while(!image_loaded) {
+               g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
        }
        g_mutex_unlock(glk_data->resource_lock);
 
-       if(width != NULL)
-               *width = info->width;
-       if(height != NULL)
-               *height =info->height;
-       g_free(info);
-
-       return TRUE;
+       /* Store the image in the cache */
+       if( g_slist_length(glk_data->image_cache) >= IMAGE_CACHE_MAX_NUM ) {
+               printf("Cache size exceeded\n");
+               struct image_info *head = (struct image_info*) glk_data->image_cache->data;
+               gdk_pixbuf_unref(head->pixbuf);
+               g_free(head);
+               glk_data->image_cache = g_slist_remove_link(glk_data->image_cache, glk_data->image_cache);
+       }
+       printf("Loading pixbuf\n");
+       info->pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+       info->width = gdk_pixbuf_get_width(info->pixbuf);
+       info->height = gdk_pixbuf_get_height(info->pixbuf);
+       printf("Caching pixbuf\n");
+       glk_data->image_cache = g_slist_prepend(glk_data->image_cache, info);
+
+       g_object_unref(loader);
+       return info;
 }
 
 void
@@ -90,69 +102,110 @@ on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_
        g_mutex_unlock(glk_data->resource_lock);
 }
 
-glui32
-glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
+void
+on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data)
 {
-       VALID_WINDOW(win, return FALSE);
-       g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
+       gdk_threads_enter();
 
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       giblorb_result_t res;
-       giblorb_err_t blorb_error = 0;
-       GError *pixbuf_error = NULL;
-       struct image_info *info = g_new0(struct image_info, 1);
-       info->resource_number = image;
-       guchar *buffer;
-       GdkPixmap *canvas;
 
-       /* Lookup the proper resource */
-       blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
-       if(blorb_error != giblorb_err_None) {
-               WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
-               return FALSE;
-       }
+       g_mutex_lock(glk_data->resource_lock);
+       image_loaded = TRUE;
+       g_cond_broadcast(glk_data->resource_loaded);
+       g_mutex_unlock(glk_data->resource_lock);
 
-       /* Load the resource */
-       GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
-       g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
-       glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
-       buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
+       gdk_threads_leave();
+}
 
-       guint32 total_read = 0;
-       image_loaded = FALSE;
-       while(total_read < res.length) {
-               guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
 
-               if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
-                       WARNING_S("Cannot read image", pixbuf_error->message);
-                       giblorb_unload_chunk(glk_data->resource_map, image);
-                       gdk_pixbuf_loader_close(loader, &pixbuf_error);
-                       g_free(buffer);
-                       return FALSE;
-               }
+void
+clear_image_cache(struct image_info *data, gpointer user_data)
+{
+       gdk_pixbuf_unref(data->pixbuf);
+       g_free(data);
+}
 
-               total_read += num_read;
-       }
+static struct image_info*
+image_cache_find(struct image_info* to_find)
+{
+       printf("Finding image %d\n", to_find->resource_number);
+       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+       GSList *link = glk_data->image_cache;
 
-       if( !gdk_pixbuf_loader_close(loader, &pixbuf_error) ) {
-               WARNING_S("Cannot read image", pixbuf_error->message);
-               giblorb_unload_chunk(glk_data->resource_map, image);
-               g_free(buffer);
-               return FALSE;
+       /* Empty cache */
+       if(link == NULL) {
+               printf("Cache is empty\n");
+               return NULL;
        }
 
-       if(!image_loaded) {
-               /* Wait for the PixbufLoader to finish loading the image */
-               g_mutex_lock(glk_data->resource_lock);
-               while(!image_loaded) {
-                       g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
+       /* Iterate over the cache to find the correct image and size */
+       do {
+               struct image_info *info = (struct image_info*) link->data;
+               printf("Examining cache entry %d\n", info->resource_number);
+               if(info->resource_number == to_find->resource_number) {
+                       /* Check size: are we looking for a scaled version or the original one? */
+                       if(to_find->scaled) {
+                               if(info->width >= to_find->width && info->height >= to_find->height) {
+                                       return info; /* Found a good enough match */
+                               }
+                       } else {
+                               if(!info->scaled) {
+                                       printf("Cache hit\n");
+                                       return info; /* Found a match */
+                               }
+                       }
                }
-               g_mutex_unlock(glk_data->resource_lock);
+       } while( (link = g_slist_next(link)) );
+
+       return NULL; /* No match found */
+}
+
+glui32
+glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
+{
+       printf("get_info(%d)\n", image);
+       struct image_info *to_find = g_new0(struct image_info, 1);
+       struct image_info *found;
+       to_find->resource_number = image;
+       to_find->scaled = FALSE; /* we want the original image size */
+
+       if( !(found = image_cache_find(to_find)) ) {
+               printf("Cache miss for %d\n", image);
+               found = load_image_in_cache(image, 0, 0);
+               if(found == NULL)
+                       return FALSE;
+       } else {
+               printf("Cache hit for %d\n", image);
+       }
+
+       if(width != NULL)
+               *width = found->width;
+       if(width != NULL)
+               *height = found->height;
+       return TRUE;
+}
+
+glui32
+glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
+{
+       printf("image_draw(%d)\n", image);
+       VALID_WINDOW(win, return FALSE);
+       g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
+
+       struct image_info *to_find = g_new0(struct image_info, 1);
+       struct image_info *info;
+       GdkPixmap *canvas;
+
+       /* Lookup the proper resource */
+       to_find->resource_number = image;
+       to_find->scaled = FALSE; /* we want the original image size */
+
+       if( !(info = image_cache_find(to_find)) ) {
+               info = load_image_in_cache(image, 0, 0);
+               if(info == NULL)
+                       return FALSE;
        }
 
-       giblorb_unload_chunk(glk_data->resource_map, image);
-       g_free(buffer);
-       
        gdk_threads_enter();
 
        gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
@@ -161,13 +214,8 @@ glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
                return FALSE;
        }
 
-       GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
-       if(pixbuf == NULL) {
-               WARNING("Could not read image");
-               return FALSE;
-       }
-
-       gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
+       printf("image info: %d x %d, scaled=%d, pixbufaddr=%d\n", info->width, info->height, (int)info->scaled, (int)info->pixbuf);
+       gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, info->pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
 
        /* Update the screen */
        gtk_widget_queue_draw(win->widget);
@@ -177,21 +225,6 @@ glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
        return TRUE;
 }
 
-void
-on_pixbuf_closed(GdkPixbufLoader *loader, gpointer data)
-{
-       gdk_threads_enter();
-
-       ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-
-       g_mutex_lock(glk_data->resource_lock);
-       image_loaded = TRUE;
-       g_cond_broadcast(glk_data->resource_loaded);
-       g_mutex_unlock(glk_data->resource_lock);
-
-       gdk_threads_leave();
-}
-
 
 glui32
 glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
@@ -200,63 +233,21 @@ glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui3
        g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
 
        ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
-       giblorb_result_t res;
-       giblorb_err_t blorb_error = 0;
-       GError *pixbuf_error = NULL;
-       struct image_info *info = g_new0(struct image_info, 1);
-       info->resource_number = image;
-       guchar *buffer;
+       struct image_info *to_find = g_new0(struct image_info, 1);
+       struct image_info *info;
+       struct image_info *scaled_info;
        GdkPixmap *canvas;
 
        /* Lookup the proper resource */
-       blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
-       if(blorb_error != giblorb_err_None) {
-               WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
-               return FALSE;
-       }
-
-       /* Load the resource */
-       GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
-       g_signal_connect( loader, "closed", G_CALLBACK(on_pixbuf_closed), NULL ); 
-       gdk_pixbuf_loader_set_size(loader, width, height);
-       glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
-       buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
-
-       guint32 total_read = 0;
-       image_loaded = FALSE;
-       while(total_read < res.length) {
-               guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
+       to_find->resource_number = image;
+       to_find->scaled = TRUE; /* any image size equal or larger than requested will do */
 
-               if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
-                       WARNING_S("Cannot read image", pixbuf_error->message);
-                       giblorb_unload_chunk(glk_data->resource_map, image);
-                       gdk_pixbuf_loader_close(loader, &pixbuf_error);
-                       g_free(buffer);
+       if( !(info = image_cache_find(to_find)) ) {
+               info = load_image_in_cache(image, width, height);
+               if(info == NULL)
                        return FALSE;
-               }
-
-               total_read += num_read;
        }
 
-       if( !gdk_pixbuf_loader_close(loader, &pixbuf_error) ) {
-               WARNING_S("Cannot read image", pixbuf_error->message);
-               giblorb_unload_chunk(glk_data->resource_map, image);
-               g_free(buffer);
-               return FALSE;
-       }
-
-       if(!image_loaded) {
-               /* Wait for the PixbufLoader to finish loading the image */
-               g_mutex_lock(glk_data->resource_lock);
-               while(!image_loaded) {
-                       g_cond_wait(glk_data->resource_loaded, glk_data->resource_lock);
-               }
-               g_mutex_unlock(glk_data->resource_lock);
-       }
-
-       giblorb_unload_chunk(glk_data->resource_map, image);
-       g_free(buffer);
-       
        gdk_threads_enter();
 
        gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
@@ -265,13 +256,24 @@ glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui3
                return FALSE;
        }
 
-       GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
-       if(pixbuf == NULL) {
-               WARNING("Could not read image");
-               return FALSE;
+       /* Scale the image if necessary */
+       if(info->width != width || info->height != height) {
+               GdkPixbuf *scaled = gdk_pixbuf_scale_simple(info->pixbuf, width, height, GDK_INTERP_BILINEAR);
+
+               /* Add the scaled image into the image cache */
+               scaled_info = g_new0(struct image_info, 1);
+               scaled_info->resource_number = info->resource_number;
+               scaled_info->width = gdk_pixbuf_get_width(scaled);
+               scaled_info->height = gdk_pixbuf_get_width(scaled);
+               scaled_info->pixbuf = scaled;
+               scaled_info->scaled = TRUE;
+               glk_data->image_cache = g_slist_prepend(glk_data->image_cache, scaled_info);
+
+               /* Continue working with the scaled version */
+               info = scaled_info;
        }
 
-       gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
+       gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, info->pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
 
        /* Update the screen */
        gtk_widget_queue_draw(win->widget);
index 34cde77db454609e5c49b4435e8251fd730b192e..183405ee68145654e48e9520ebe5e6e7a994708b 100644 (file)
@@ -9,11 +9,17 @@
 #include "resource.h"
 #include "window.h"
 
+#define IMAGE_CACHE_MAX_NUM 10
+#define IMAGE_CACHE_MAX_SIZE 5242880
+
 struct image_info {
        guint32 resource_number;
        gint width, height;
+       GdkPixbuf* pixbuf;
+       gboolean scaled;
 };
 
 void on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t win);
+void clear_image_cache(struct image_info *data, gpointer user_data);
 
 #endif