From e2523695223db657fb2c348a47b318b5c2d998ce Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 23 Feb 2019 11:18:20 +0100 Subject: Image rendering --- libstorycode/imagestore.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 libstorycode/imagestore.c (limited to 'libstorycode/imagestore.c') diff --git a/libstorycode/imagestore.c b/libstorycode/imagestore.c new file mode 100644 index 0000000..5eb50fb --- /dev/null +++ b/libstorycode/imagestore.c @@ -0,0 +1,368 @@ +/* + * imagestore.c + * + * Copyright © 2013-2018 Thomas White + * + * This file is part of Colloquium. + * + * Colloquium is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imagestore.h" + +#define MAX_SIZES (32) + +struct image_record +{ + char *filename; + cairo_surface_t *surf[MAX_SIZES]; + int w[MAX_SIZES]; + int last_used[MAX_SIZES]; + int n_sizes; + int h_last_used; + int full_width; + int full_height; +}; + + +struct _imagestore +{ + int n_images; + struct image_record *images; + int max_images; + GFile *pparent; + GFile *iparent; + const char *storename; +}; + + +static int alloc_images(ImageStore *is, int new_max_images) +{ + struct image_record *images_new; + + images_new = realloc(is->images, + sizeof(struct image_record)*new_max_images); + if ( images_new == NULL ) return 1; + + is->images = images_new; + is->max_images = new_max_images; + return 0; +} + + +ImageStore *imagestore_new(const char *storename) +{ + ImageStore *is; + + is = calloc(1, sizeof(ImageStore)); + if ( is == NULL ) return NULL; + + is->images = NULL; + is->pparent = NULL; + if ( storename != NULL ) { + is->iparent = g_file_new_for_uri(storename); + } else { + is->iparent = NULL; + } + is->n_images = 0; + is->max_images = 0; + if ( alloc_images(is, 32) ) { + free(is); + return NULL; + } + + return is; +} + + +void imagestore_set_parent(ImageStore *is, GFile *parent) +{ + if ( is->pparent != NULL ) { + g_object_unref(is->pparent); + } + is->pparent = parent; +} + + +void imagestore_destroy(ImageStore *is) +{ + int i; + + for ( i=0; in_images; i++ ) { + int j; + free(is->images[i].filename); + for ( j=0; jimages[i].n_sizes; j++ ) { + if ( is->images[i].surf[j] != NULL ) { + g_object_unref(is->images[i].surf[j]); + } + } + } + g_object_unref(is->pparent); + g_object_unref(is->iparent); + free(is->images); + free(is); +} + + +static cairo_surface_t *pixbuf_to_surface(GdkPixbuf *t) +{ + cairo_surface_t *surf; + cairo_t *cr; + int w, h; + + if ( t == NULL ) return NULL; + + w = gdk_pixbuf_get_width(t); + h = gdk_pixbuf_get_height(t); + + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + if ( surf == NULL ) return NULL; + + cr = cairo_create(surf); + + gdk_cairo_set_source_pixbuf(cr, t, 0, 0); + cairo_pattern_t *patt = cairo_get_source(cr); + cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD); + cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); + cairo_paint(cr); + + cairo_destroy(cr); + + return surf; +} + + +static int try_load(GFile *file, GdkPixbuf **pixbuf, gint w, gint h) +{ + GFileInputStream *stream; + GError *error = NULL; + + stream = g_file_read(file, NULL, &error); + if ( stream != NULL ) { + GError *pberr = NULL; + *pixbuf = gdk_pixbuf_new_from_stream_at_scale(G_INPUT_STREAM(stream), + w, h, TRUE, + NULL, &pberr); + g_object_unref(stream); + g_object_unref(file); + return 1; + } + + return 0; +} + + +static GdkPixbuf *load_image(const char *uri, GFile *pparent, GFile *iparent, + gint w, gint h) +{ + GFile *file; + GdkPixbuf *pixbuf; + + /* Literal pathname */ + file = g_file_new_for_path(uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + + /* Try the file prefixed with the directory the presentation is in */ + if ( pparent != NULL ) { + file = g_file_get_child(pparent, uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + } + + /* Try prefixing with imagestore folder */ + if ( iparent != NULL ) { + file = g_file_get_child(iparent, uri); + if ( try_load(file, &pixbuf, w, h) ) return pixbuf; + } + + return NULL; +} + + +static struct image_record *add_image_record(ImageStore *is, + const char *filename) +{ + int idx; + + if ( is->n_images == is->max_images ) { + if ( alloc_images(is, is->max_images+32) ) { + fprintf(stderr, "Couldn't allocate memory for image " + "records.\n"); + return NULL; + } + } + + idx = is->n_images++; + + is->images[idx].n_sizes = 0; + is->images[idx].filename = strdup(filename); + is->images[idx].h_last_used = 0; + + return &is->images[idx]; +} + + + +int imagestore_get_size(ImageStore *is, const char *filename, + int *w, int *h) +{ + GdkPixbuf *pixbuf; + struct image_record *ir; + int i; + + for ( i=0; in_images; i++ ) { + if ( strcmp(is->images[i].filename, filename) == 0 ) { + *w = is->images[i].full_width; + *h = is->images[i].full_height; + return 0; + } + } + + pixbuf = load_image(filename, is->pparent, is->iparent, -1, -1); + if ( pixbuf == NULL ) return 1; + + + *w = gdk_pixbuf_get_width(pixbuf); + *h = gdk_pixbuf_get_height(pixbuf); + + ir = add_image_record(is, filename); + if ( ir != NULL ) { + ir->full_width = *w; + ir->full_height = *h; + } /* otherwise can't cache, too bad */ + + g_object_unref(pixbuf); + + return 0; +} + + +static int find_earliest(struct image_record *im) +{ + int i, earliest, l; + + earliest = im->h_last_used; + l = im->n_sizes; + + for ( i=0; in_sizes; i++ ) { + if ( im->last_used[i] < earliest ) { + earliest = im->last_used[i]; + l = i; + } + } + + assert(l != im->n_sizes); + + cairo_surface_destroy(im->surf[l]); + + return l; +} + + +static cairo_surface_t *add_image_size(struct image_record *im, + const char *filename, + GFile *pparent, GFile *iparent, + int w) +{ + cairo_surface_t *surf; + GdkPixbuf *t; + int pos; + + t = load_image(filename, pparent, iparent, w, -1); + if ( t == NULL ) return NULL; + surf = pixbuf_to_surface(t); + g_object_unref(t); + + /* Add surface to list */ + if ( im->n_sizes == MAX_SIZES ) { + pos = find_earliest(im); + } else { + pos = im->n_sizes++; + } + + im->w[pos] = w; + im->surf[pos] = surf; + im->last_used[pos] = im->h_last_used++; + + return surf; +} + + +static cairo_surface_t *add_new_image(ImageStore *is, const char *filename, int w) +{ + struct image_record *ir; + ir = add_image_record(is, filename); + if ( ir == NULL ) return NULL; + return add_image_size(ir, filename, is->pparent, is->iparent, w); +} + + +void show_imagestore(ImageStore *is) +{ + int i; + + printf("Store %p contains %i records.\n", is, is->n_images); + + for ( i=0; in_images; i++ ) { + + printf("%s :\n", is->images[i].filename); + printf("\n"); + + } +} + + +cairo_surface_t *lookup_image(ImageStore *is, const char *filename, int w) +{ + int i, j; + int found = 0; + cairo_surface_t *surf; + + for ( i=0; in_images; i++ ) { + if ( strcmp(is->images[i].filename, filename) == 0 ) { + found = 1; + break; + } + } + if ( !found ) { + return add_new_image(is, filename, w); + } + + for ( j=0; jimages[i].n_sizes; j++ ) { + if ( is->images[i].w[j] == w ) { + is->images[i].last_used[j] = is->images[i].h_last_used++; + return is->images[i].surf[j]; + } + } + + /* We don't have this size yet */ + surf = add_image_size(&is->images[i], filename, is->pparent, + is->iparent, w); + return surf; +} -- cgit v1.2.3