aboutsummaryrefslogtreecommitdiff
path: root/libstorycode
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.me.uk>2019-02-23 11:18:20 +0100
committerThomas White <taw@bitwiz.me.uk>2019-02-23 11:18:20 +0100
commite2523695223db657fb2c348a47b318b5c2d998ce (patch)
treeaf0640749d20ebd7a81a27dfd903b7e1d24bc5cf /libstorycode
parenta716de927edbe4aaeb2025c6f11d988ca3ee05b9 (diff)
Image rendering
Diffstat (limited to 'libstorycode')
-rw-r--r--libstorycode/imagestore.c368
-rw-r--r--libstorycode/imagestore.h43
-rw-r--r--libstorycode/slide_render_cairo.c45
-rw-r--r--libstorycode/slide_render_cairo.h3
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);