Add preliminary support for resvg
It claims better SVG support, but it sucks for a plethora of reasons.
This commit is contained in:
parent
45238d78cd
commit
4ba1d85363
94
fiv-io.c
94
fiv-io.c
|
@ -43,6 +43,9 @@
|
||||||
#ifdef HAVE_LIBRAW
|
#ifdef HAVE_LIBRAW
|
||||||
#include <libraw.h>
|
#include <libraw.h>
|
||||||
#endif // HAVE_LIBRAW
|
#endif // HAVE_LIBRAW
|
||||||
|
#ifdef HAVE_RESVG
|
||||||
|
#include <resvg.h>
|
||||||
|
#endif // HAVE_RESVG
|
||||||
#ifdef HAVE_LIBRSVG
|
#ifdef HAVE_LIBRSVG
|
||||||
#include <librsvg/rsvg.h>
|
#include <librsvg/rsvg.h>
|
||||||
#endif // HAVE_LIBRSVG
|
#endif // HAVE_LIBRSVG
|
||||||
|
@ -92,9 +95,9 @@ const char *fiv_io_supported_media_types[] = {
|
||||||
#ifdef HAVE_LIBRAW
|
#ifdef HAVE_LIBRAW
|
||||||
"image/x-dcraw",
|
"image/x-dcraw",
|
||||||
#endif // HAVE_LIBRAW
|
#endif // HAVE_LIBRAW
|
||||||
#ifdef HAVE_LIBRSVG
|
#if defined HAVE_RESVG || defined HAVE_LIBRSVG
|
||||||
"image/svg+xml",
|
"image/svg+xml",
|
||||||
#endif // HAVE_LIBRSVG
|
#endif // HAVE_RESVG || HAVE_LIBRSVG
|
||||||
#ifdef HAVE_XCURSOR
|
#ifdef HAVE_XCURSOR
|
||||||
"image/x-xcursor",
|
"image/x-xcursor",
|
||||||
#endif // HAVE_XCURSOR
|
#endif // HAVE_XCURSOR
|
||||||
|
@ -1320,6 +1323,85 @@ open_libraw(const gchar *data, gsize len, GError **error)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBRAW ---------------------------------------------------------
|
#endif // HAVE_LIBRAW ---------------------------------------------------------
|
||||||
|
#ifdef HAVE_RESVG // ----------------------------------------------------------
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
load_resvg_error(int err)
|
||||||
|
{
|
||||||
|
switch (err) {
|
||||||
|
case RESVG_ERROR_NOT_AN_UTF8_STR:
|
||||||
|
return "not a UTF-8 string";
|
||||||
|
case RESVG_ERROR_FILE_OPEN_FAILED:
|
||||||
|
return "I/O failure";
|
||||||
|
case RESVG_ERROR_MALFORMED_GZIP:
|
||||||
|
return "malformed gzip";
|
||||||
|
case RESVG_ERROR_ELEMENTS_LIMIT_REACHED:
|
||||||
|
return "element limit reached";
|
||||||
|
case RESVG_ERROR_INVALID_SIZE:
|
||||||
|
return "invalid or unspecified image size";
|
||||||
|
case RESVG_ERROR_PARSING_FAILED:
|
||||||
|
return "parsing failed";
|
||||||
|
default:
|
||||||
|
return "general failure";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cairo_surface_t *
|
||||||
|
open_resvg(const gchar *data, gsize len, const gchar *uri, GError **error)
|
||||||
|
{
|
||||||
|
GFile *file = g_file_new_for_uri(uri);
|
||||||
|
GFile *base_file = g_file_get_parent(file);
|
||||||
|
g_object_unref(file);
|
||||||
|
|
||||||
|
resvg_options *opt = resvg_options_create();
|
||||||
|
resvg_options_load_system_fonts(opt);
|
||||||
|
resvg_options_set_resources_dir(opt, g_file_peek_path(base_file));
|
||||||
|
resvg_render_tree *tree = NULL;
|
||||||
|
int err = resvg_parse_tree_from_data(data, len, opt, &tree);
|
||||||
|
resvg_options_destroy(opt);
|
||||||
|
g_object_unref(base_file);
|
||||||
|
if (err != RESVG_OK) {
|
||||||
|
set_error(error, load_resvg_error(err));
|
||||||
|
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);
|
||||||
|
int 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);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_mark_dirty(surface);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_RESVG ----------------------------------------------------------
|
||||||
#ifdef HAVE_LIBRSVG // --------------------------------------------------------
|
#ifdef HAVE_LIBRSVG // --------------------------------------------------------
|
||||||
|
|
||||||
#ifdef FIV_RSVG_DEBUG
|
#ifdef FIV_RSVG_DEBUG
|
||||||
|
@ -2391,6 +2473,14 @@ fiv_io_open_from_data(const char *data, size_t len, const gchar *uri,
|
||||||
g_clear_error(error);
|
g_clear_error(error);
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBRAW ---------------------------------------------------------
|
#endif // HAVE_LIBRAW ---------------------------------------------------------
|
||||||
|
#ifdef HAVE_RESVG // ----------------------------------------------------------
|
||||||
|
if ((surface = open_resvg(data, len, uri, error)))
|
||||||
|
break;
|
||||||
|
if (error) {
|
||||||
|
g_debug("%s", (*error)->message);
|
||||||
|
g_clear_error(error);
|
||||||
|
}
|
||||||
|
#endif // HAVE_RESVG ----------------------------------------------------------
|
||||||
#ifdef HAVE_LIBRSVG // --------------------------------------------------------
|
#ifdef HAVE_LIBRSVG // --------------------------------------------------------
|
||||||
if ((surface = open_librsvg(data, len, uri, error)))
|
if ((surface = open_librsvg(data, len, uri, error)))
|
||||||
break;
|
break;
|
||||||
|
|
28
meson.build
28
meson.build
|
@ -2,17 +2,16 @@
|
||||||
project('fiv', 'c',
|
project('fiv', 'c',
|
||||||
default_options : ['c_std=gnu99', 'warning_level=2'],
|
default_options : ['c_std=gnu99', 'warning_level=2'],
|
||||||
version : '0.1.0')
|
version : '0.1.0')
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
meson.get_compiler('c').get_supported_arguments('-Wno-cast-function-type'),
|
cc.get_supported_arguments('-Wno-cast-function-type'), language : 'c')
|
||||||
language : 'c',
|
|
||||||
)
|
|
||||||
|
|
||||||
# This, annoyingly, enables the leak sanitizer by default,
|
# This, annoyingly, enables the leak sanitizer by default,
|
||||||
# which requires some tuning to not stand in the way.
|
# which requires some tuning to not stand in the way.
|
||||||
# Use -Db_sanitize=address,undefined and adjust LSAN_OPTIONS yourself.
|
# Use -Db_sanitize=address,undefined and adjust LSAN_OPTIONS yourself.
|
||||||
#if get_option('buildtype').startswith('debug')
|
#if get_option('buildtype').startswith('debug')
|
||||||
# flags = meson.get_compiler('c').get_supported_arguments(
|
# flags = cc.get_supported_arguments('-fsanitize=address,undefined')
|
||||||
# '-fsanitize=address,undefined')
|
|
||||||
# add_project_arguments(flags, language : ['c'])
|
# add_project_arguments(flags, language : ['c'])
|
||||||
# add_project_link_arguments(flags, language : ['c'])
|
# add_project_link_arguments(flags, language : ['c'])
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,6 +22,19 @@ libjpegqs = dependency('libjpegqs',
|
||||||
allow_fallback : true,
|
allow_fallback : true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# As of writing, the API is unstable, and no pkg-config file is produced.
|
||||||
|
# Trying to wrap Cargo in Meson is a recipe for pain, so no version pinning.
|
||||||
|
resvg = disabler()
|
||||||
|
if not get_option('resvg').disabled()
|
||||||
|
resvg = dependency('resvg', required : false)
|
||||||
|
if not resvg.found()
|
||||||
|
resvg = cc.find_library('libresvg', required : get_option('resvg'))
|
||||||
|
if resvg.found() and not cc.has_header('resvg.h')
|
||||||
|
error('resvg.h not found')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
lcms2 = dependency('lcms2', required : get_option('lcms2'))
|
lcms2 = dependency('lcms2', required : get_option('lcms2'))
|
||||||
# Note that only libraw_r is thread-safe, but we'll just run it out-of process.
|
# Note that only libraw_r is thread-safe, but we'll just run it out-of process.
|
||||||
libraw = dependency('libraw', required : get_option('libraw'))
|
libraw = dependency('libraw', required : get_option('libraw'))
|
||||||
|
@ -35,6 +47,7 @@ dependencies = [
|
||||||
dependency('gtk+-3.0'),
|
dependency('gtk+-3.0'),
|
||||||
dependency('pixman-1'),
|
dependency('pixman-1'),
|
||||||
|
|
||||||
|
# Wuffs is included as a submodule.
|
||||||
dependency('libturbojpeg'),
|
dependency('libturbojpeg'),
|
||||||
dependency('libwebp'),
|
dependency('libwebp'),
|
||||||
dependency('libwebpdemux'),
|
dependency('libwebpdemux'),
|
||||||
|
@ -49,12 +62,14 @@ dependencies = [
|
||||||
lcms2,
|
lcms2,
|
||||||
libjpegqs,
|
libjpegqs,
|
||||||
libraw,
|
libraw,
|
||||||
|
resvg,
|
||||||
librsvg,
|
librsvg,
|
||||||
xcursor,
|
xcursor,
|
||||||
libheif,
|
libheif,
|
||||||
libtiff,
|
libtiff,
|
||||||
gdkpixbuf,
|
gdkpixbuf,
|
||||||
meson.get_compiler('c').find_library('m', required : false),
|
|
||||||
|
cc.find_library('m', required : false),
|
||||||
]
|
]
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
|
@ -63,6 +78,7 @@ conf.set_quoted('PROJECT_VERSION', meson.project_version())
|
||||||
conf.set('HAVE_JPEG_QS', libjpegqs.found())
|
conf.set('HAVE_JPEG_QS', libjpegqs.found())
|
||||||
conf.set('HAVE_LCMS2', lcms2.found())
|
conf.set('HAVE_LCMS2', lcms2.found())
|
||||||
conf.set('HAVE_LIBRAW', libraw.found())
|
conf.set('HAVE_LIBRAW', libraw.found())
|
||||||
|
conf.set('HAVE_RESVG', resvg.found())
|
||||||
conf.set('HAVE_LIBRSVG', librsvg.found())
|
conf.set('HAVE_LIBRSVG', librsvg.found())
|
||||||
conf.set('HAVE_XCURSOR', xcursor.found())
|
conf.set('HAVE_XCURSOR', xcursor.found())
|
||||||
conf.set('HAVE_LIBHEIF', libheif.found())
|
conf.set('HAVE_LIBHEIF', libheif.found())
|
||||||
|
|
|
@ -4,6 +4,8 @@ option('libjpegqs', type : 'feature', value : 'auto',
|
||||||
description : 'Build with JPEG Quant Smooth integration')
|
description : 'Build with JPEG Quant Smooth integration')
|
||||||
option('libraw', type : 'feature', value : 'auto',
|
option('libraw', type : 'feature', value : 'auto',
|
||||||
description : 'Build with raw photo support, requires LibRaw')
|
description : 'Build with raw photo support, requires LibRaw')
|
||||||
|
option('resvg', type : 'feature', value : 'disabled',
|
||||||
|
description : 'Build with SVG support via resvg (pre-1.0 unstable API)')
|
||||||
option('librsvg', type : 'feature', value : 'auto',
|
option('librsvg', type : 'feature', value : 'auto',
|
||||||
description : 'Build with SVG support, requires librsvg')
|
description : 'Build with SVG support, requires librsvg')
|
||||||
option('xcursor', type : 'feature', value : 'auto',
|
option('xcursor', type : 'feature', value : 'auto',
|
||||||
|
|
Loading…
Reference in New Issue