diff options
Diffstat (limited to 'libstorycode')
-rw-r--r-- | libstorycode/imagestore.c | 368 | ||||
-rw-r--r-- | libstorycode/imagestore.h | 43 | ||||
-rw-r--r-- | libstorycode/slide_render_cairo.c | 45 | ||||
-rw-r--r-- | libstorycode/slide_render_cairo.h | 3 |
4 files changed, 447 insertions, 12 deletions
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 <taw@bitwiz.org.uk> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <libgen.h> +#include <cairo.h> +#include <glib.h> +#include <gio/gio.h> +#include <gdk/gdk.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#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; i<is->n_images; i++ ) { + int j; + free(is->images[i].filename); + for ( j=0; j<is->images[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; i<is->n_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; i<im->n_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; i<is->n_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; i<is->n_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; j<is->images[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; +} diff --git a/libstorycode/imagestore.h b/libstorycode/imagestore.h new file mode 100644 index 0000000..0d4df24 --- /dev/null +++ b/libstorycode/imagestore.h @@ -0,0 +1,43 @@ +/* + * imagestore.h + * + * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef IMAGESTORE_H +#define IMAGESTORE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <cairo.h> +#include <gio/gio.h> + +typedef struct _imagestore ImageStore; + +extern ImageStore *imagestore_new(const char *storename); +extern void imagestore_destroy(ImageStore *is); +extern void imagestore_set_parent(ImageStore *is, GFile *parent); +extern void show_imagestore(ImageStore *is); + +extern cairo_surface_t *lookup_image(ImageStore *is, const char *uri, int w); +extern int imagestore_get_size(ImageStore *is, const char *uri, int *w, int *h); + +#endif /* IMAGESTORE_H */ diff --git a/libstorycode/slide_render_cairo.c b/libstorycode/slide_render_cairo.c index 8e1eeba..b3c8a8f 100644 --- a/libstorycode/slide_render_cairo.c +++ b/libstorycode/slide_render_cairo.c @@ -31,11 +31,13 @@ #include <assert.h> #include <string.h> #include <stdlib.h> +#include <math.h> #include "presentation.h" #include "slide.h" #include "narrative.h" #include "stylesheet.h" +#include "imagestore.h" #include "slide_priv.h" @@ -117,21 +119,41 @@ static void render_text(struct slide_item *item, cairo_t *cr, PangoContext *pc, static void render_image(struct slide_item *item, cairo_t *cr, - double parent_w, double parent_h) + ImageStore *is, double parent_w, double parent_h) { - cairo_rectangle(cr, lcalc(item->geom.x, parent_w), - lcalc(item->geom.y, parent_h), - lcalc(item->geom.w, parent_w), - lcalc(item->geom.h, parent_h)); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke_preserve(cr); - cairo_set_source_rgba(cr, 0.0, 0.5, 0.0, 0.5); + double x, y, w, h; + double wd, hd; + cairo_surface_t *surf; + + x = lcalc(item->geom.x, parent_w); + y = lcalc(item->geom.y, parent_h); + w = lcalc(item->geom.w, parent_w); + h = lcalc(item->geom.h, parent_h); + + wd = w; hd = h; + cairo_user_to_device_distance(cr, &wd, &hd); + surf = lookup_image(is, item->filename, wd); + if ( surf == NULL ) return; + + cairo_user_to_device(cr, &x, &y); + x = rint(x); y = rint(y); + cairo_device_to_user(cr, &x, &y); + + cairo_save(cr); + cairo_new_path(cr); + cairo_rectangle(cr, x, y, w, h); + cairo_translate(cr, x, y); + cairo_scale(cr, w/wd, h/hd); + cairo_set_source_surface(cr, surf, 0.0, 0.0); + cairo_pattern_t *patt = cairo_get_source(cr); + cairo_pattern_set_extend(patt, CAIRO_EXTEND_NONE); + cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST); cairo_fill(cr); + cairo_restore(cr); } -int slide_render_cairo(Slide *s, cairo_t *cr, Stylesheet *stylesheet, +int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *stylesheet, int slide_number, PangoLanguage *lang, PangoContext *pc) { int i; @@ -151,7 +173,8 @@ int slide_render_cairo(Slide *s, cairo_t *cr, Stylesheet *stylesheet, break; case SLIDE_ITEM_IMAGE : - render_image(&s->items[i], cr, s->logical_w, s->logical_h); + render_image(&s->items[i], cr, is, + s->logical_w, s->logical_h); break; default : diff --git a/libstorycode/slide_render_cairo.h b/libstorycode/slide_render_cairo.h index afbdec3..4f8fd68 100644 --- a/libstorycode/slide_render_cairo.h +++ b/libstorycode/slide_render_cairo.h @@ -28,8 +28,9 @@ #endif #include "presentation.h" +#include "imagestore.h" -extern int slide_render_cairo(Slide *s, cairo_t *cr, Stylesheet *stylesheet, +extern int slide_render_cairo(Slide *s, cairo_t *cr, ImageStore *is, Stylesheet *stylesheet, int slide_number, PangoLanguage *lang, PangoContext *pc); |