aboutsummaryrefslogtreecommitdiff
path: root/fiv-io.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-01-22 23:17:17 +0100
committerPřemysl Eric Janouch <p@janouch.name>2022-01-23 04:12:10 +0100
commit562e140a1e9908629ce37cc1aa2cff3b5464e496 (patch)
tree08c780cfe9c07d5cf5ad0fa82e834bab3a7e901e /fiv-io.c
parentb71d5dff571f70066319a20e49a771bb9b0816b1 (diff)
downloadfiv-562e140a1e9908629ce37cc1aa2cff3b5464e496.tar.gz
fiv-562e140a1e9908629ce37cc1aa2cff3b5464e496.tar.xz
fiv-562e140a1e9908629ce37cc1aa2cff3b5464e496.zip
Add backend for accurate SVG scaling
Diffstat (limited to 'fiv-io.c')
-rw-r--r--fiv-io.c155
1 files changed, 126 insertions, 29 deletions
diff --git a/fiv-io.c b/fiv-io.c
index fa2da7a..48f3612 100644
--- a/fiv-io.c
+++ b/fiv-io.c
@@ -1596,6 +1596,65 @@ open_libraw(const gchar *data, gsize len, GError **error)
#endif // HAVE_LIBRAW ---------------------------------------------------------
#ifdef HAVE_RESVG // ----------------------------------------------------------
+typedef struct {
+ FivIoRenderClosure parent;
+ resvg_render_tree *tree; ///< Loaded resvg tree
+ double width; ///< Normal width
+ double height; ///< Normal height
+} FivIoRenderClosureResvg;
+
+static void
+load_resvg_destroy(void *closure)
+{
+ FivIoRenderClosureResvg *self = closure;
+ resvg_tree_destroy(self->tree);
+ g_free(self);
+}
+
+static cairo_surface_t *
+load_resvg_render_internal(
+ FivIoRenderClosureResvg *self, double scale, GError **error)
+{
+ double w = ceil(self->width * scale), h = ceil(self->height * scale);
+ if (w > SHRT_MAX || h > SHRT_MAX) {
+ set_error(error, "image dimensions overflow");
+ return NULL;
+ }
+
+ cairo_surface_t *surface =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
+ cairo_status_t surface_status = cairo_surface_status(surface);
+ if (surface_status != CAIRO_STATUS_SUCCESS) {
+ set_error(error, cairo_status_to_string(surface_status));
+ cairo_surface_destroy(surface);
+ return NULL;
+ }
+
+ uint32_t *pixels = (uint32_t *) cairo_image_surface_get_data(surface);
+ resvg_fit_to fit_to = {
+ scale == 1 ? RESVG_FIT_TO_TYPE_ORIGINAL : RESVG_FIT_TO_TYPE_ZOOM,
+ scale};
+ resvg_render(self->tree, fit_to, resvg_transform_identity(),
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface), (char *) pixels);
+
+ // TODO(p): Also apply colour management, we'll need to un-premultiply.
+ for (int i = 0; i < w * h; i++) {
+ uint32_t rgba = g_ntohl(pixels[i]);
+ pixels[i] = rgba << 24 | rgba >> 8;
+ }
+
+ cairo_surface_mark_dirty(surface);
+ return surface;
+}
+
+static cairo_surface_t *
+load_resvg_render(FivIoRenderClosure *closure, double scale)
+{
+ FivIoRenderClosureResvg *self = (FivIoRenderClosureResvg *) closure;
+ return load_resvg_render_internal(self, scale, NULL);
+}
+
static const char *
load_resvg_error(int err)
{
@@ -1627,6 +1686,7 @@ open_resvg(const gchar *data, gsize len, const gchar *uri, GError **error)
resvg_options *opt = resvg_options_create();
resvg_options_load_system_fonts(opt);
resvg_options_set_resources_dir(opt, g_file_peek_path(base_file));
+ // TODO(p): Acquire and set the right DPI for use.
resvg_render_tree *tree = NULL;
int err = resvg_parse_tree_from_data(data, len, opt, &tree);
resvg_options_destroy(opt);
@@ -1636,40 +1696,23 @@ open_resvg(const gchar *data, gsize len, const gchar *uri, GError **error)
return NULL;
}
- // TODO(p): Support retrieving a scaled-up/down version.
// TODO(p): See if there is a situation for resvg_get_image_viewbox().
resvg_size size = resvg_get_image_size(tree);
- double w = ceil(size.width), h = ceil(size.height);
- if (w > SHRT_MAX || h > SHRT_MAX) {
- set_error(error, "image dimensions overflow");
- resvg_tree_destroy(tree);
- return NULL;
- }
- cairo_surface_t *surface =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
- cairo_status_t surface_status = cairo_surface_status(surface);
- if (surface_status != CAIRO_STATUS_SUCCESS) {
- set_error(error, cairo_status_to_string(surface_status));
- cairo_surface_destroy(surface);
- resvg_tree_destroy(tree);
- return NULL;
- }
+ FivIoRenderClosureResvg *closure = g_malloc0(sizeof *closure);
+ closure->parent.render = load_resvg_render;
+ closure->tree = tree;
+ closure->width = size.width;
+ closure->height = size.height;
- uint32_t *pixels = (uint32_t *) cairo_image_surface_get_data(surface);
- resvg_fit_to fit_to = { RESVG_FIT_TO_TYPE_ORIGINAL, 1. };
- resvg_render(tree, fit_to, resvg_transform_identity(),
- cairo_image_surface_get_width(surface),
- cairo_image_surface_get_height(surface), (char *) pixels);
- resvg_tree_destroy(tree);
-
- // TODO(p): Also apply colour management, we'll need to un-premultiply.
- for (int i = 0; i < w * h; i++) {
- uint32_t rgba = g_ntohl(pixels[i]);
- pixels[i] = rgba << 24 | rgba >> 8;
+ cairo_surface_t *surface = load_resvg_render_internal(closure, 1., error);
+ if (!surface) {
+ load_resvg_destroy(closure);
+ return NULL;
}
- cairo_surface_mark_dirty(surface);
+ cairo_surface_set_user_data(
+ surface, &fiv_io_key_render, closure, load_resvg_destroy);
return surface;
}
@@ -1681,6 +1724,50 @@ open_resvg(const gchar *data, gsize len, const gchar *uri, GError **error)
#include <cairo/cairo-svg.h>
#endif
+typedef struct {
+ FivIoRenderClosure parent;
+ RsvgHandle *handle; ///< Loaded rsvg handle
+ double width; ///< Normal width
+ double height; ///< Normal height
+} FivIoRenderClosureLibrsvg;
+
+static void
+load_librsvg_destroy(void *closure)
+{
+ FivIoRenderClosureLibrsvg *self = closure;
+ g_object_unref(self->handle);
+ g_free(self);
+}
+
+static cairo_surface_t *
+load_librsvg_render(FivIoRenderClosure *closure, double scale)
+{
+ FivIoRenderClosureLibrsvg *self = (FivIoRenderClosureLibrsvg *) closure;
+ RsvgRectangle viewport = {.x = 0, .y = 0,
+ .width = self->width * scale, .height = self->height * scale};
+ cairo_surface_t *surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32, ceil(viewport.width), ceil(viewport.height));
+
+ GError *error = NULL;
+ cairo_t *cr = cairo_create(surface);
+ (void) rsvg_handle_render_document(self->handle, cr, &viewport, &error);
+ cairo_destroy(cr);
+ if (error) {
+ g_debug("%s", error->message);
+ g_error_free(error);
+ cairo_surface_destroy(surface);
+ return NULL;
+ }
+
+ cairo_status_t surface_status = cairo_surface_status(surface);
+ if (surface_status != CAIRO_STATUS_SUCCESS) {
+ g_debug("%s", cairo_status_to_string(surface_status));
+ cairo_surface_destroy(surface);
+ return NULL;
+ }
+ return surface;
+}
+
// FIXME: librsvg rasterizes filters, so this method isn't fully appropriate.
static cairo_surface_t *
open_librsvg(const gchar *data, gsize len, const gchar *uri, GError **error)
@@ -1744,7 +1831,14 @@ open_librsvg(const gchar *data, gsize len, const gchar *uri, GError **error)
}
cairo_destroy(cr);
- g_object_unref(handle);
+
+ FivIoRenderClosureLibrsvg *closure = g_malloc0(sizeof *closure);
+ closure->parent.render = load_librsvg_render;
+ closure->handle = handle;
+ closure->width = w;
+ closure->height = h;
+ cairo_surface_set_user_data(
+ surface, &fiv_io_key_render, closure, load_librsvg_destroy);
#ifdef FIV_RSVG_DEBUG
cairo_surface_t *svg = cairo_svg_surface_create("cairo.svg", w, h);
@@ -2438,6 +2532,7 @@ open_gdkpixbuf(
#endif // HAVE_GDKPIXBUF ------------------------------------------------------
+// TODO(p): Check that all cairo_surface_set_user_data() calls succeed.
cairo_user_data_key_t fiv_io_key_exif;
cairo_user_data_key_t fiv_io_key_orientation;
cairo_user_data_key_t fiv_io_key_icc;
@@ -2452,6 +2547,8 @@ cairo_user_data_key_t fiv_io_key_loops;
cairo_user_data_key_t fiv_io_key_page_next;
cairo_user_data_key_t fiv_io_key_page_previous;
+cairo_user_data_key_t fiv_io_key_render;
+
cairo_surface_t *
fiv_io_open(
const gchar *uri, FivIoProfile profile, gboolean enhance, GError **error)