diff options
| author | Přemysl Eric Janouch <p@janouch.name> | 2022-01-21 09:04:01 +0100 | 
|---|---|---|
| committer | Přemysl Eric Janouch <p@janouch.name> | 2022-01-21 09:14:19 +0100 | 
| commit | 4ba1d85363a3b32d24b487cec68555853a3c6735 (patch) | |
| tree | ba5e30a4dc772851acd13adb6c5af858ba3de7be | |
| parent | 45238d78cd9535b33bfbeeaaac4645af1bd93834 (diff) | |
| download | fiv-4ba1d85363a3b32d24b487cec68555853a3c6735.tar.gz fiv-4ba1d85363a3b32d24b487cec68555853a3c6735.tar.xz fiv-4ba1d85363a3b32d24b487cec68555853a3c6735.zip  | |
Add preliminary support for resvg
It claims better SVG support, but it sucks for a plethora of reasons.
| -rw-r--r-- | fiv-io.c | 94 | ||||
| -rw-r--r-- | meson.build | 28 | ||||
| -rw-r--r-- | meson_options.txt | 2 | 
3 files changed, 116 insertions, 8 deletions
@@ -43,6 +43,9 @@  #ifdef HAVE_LIBRAW  #include <libraw.h>  #endif  // HAVE_LIBRAW +#ifdef HAVE_RESVG +#include <resvg.h> +#endif  // HAVE_RESVG  #ifdef HAVE_LIBRSVG  #include <librsvg/rsvg.h>  #endif  // HAVE_LIBRSVG @@ -92,9 +95,9 @@ const char *fiv_io_supported_media_types[] = {  #ifdef HAVE_LIBRAW  	"image/x-dcraw",  #endif  // HAVE_LIBRAW -#ifdef HAVE_LIBRSVG +#if defined HAVE_RESVG || defined HAVE_LIBRSVG  	"image/svg+xml", -#endif  // HAVE_LIBRSVG +#endif  // HAVE_RESVG || HAVE_LIBRSVG  #ifdef HAVE_XCURSOR  	"image/x-xcursor",  #endif  // HAVE_XCURSOR @@ -1320,6 +1323,85 @@ open_libraw(const gchar *data, gsize len, GError **error)  }  #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 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);  		}  #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  // --------------------------------------------------------  		if ((surface = open_librsvg(data, len, uri, error)))  			break; diff --git a/meson.build b/meson.build index 0ccc8cd..af7a39b 100644 --- a/meson.build +++ b/meson.build @@ -2,17 +2,16 @@  project('fiv', 'c',  	default_options : ['c_std=gnu99', 'warning_level=2'],  	version : '0.1.0') + +cc = meson.get_compiler('c')  add_project_arguments( -	meson.get_compiler('c').get_supported_arguments('-Wno-cast-function-type'), -	language : 'c', -) +	cc.get_supported_arguments('-Wno-cast-function-type'), language : 'c')  # This, annoyingly, enables the leak sanitizer by default,  # which requires some tuning to not stand in the way.  # Use -Db_sanitize=address,undefined and adjust LSAN_OPTIONS yourself.  #if get_option('buildtype').startswith('debug') -#	flags = meson.get_compiler('c').get_supported_arguments( -#		'-fsanitize=address,undefined') +#	flags = cc.get_supported_arguments('-fsanitize=address,undefined')  #	add_project_arguments(flags, language : ['c'])  #	add_project_link_arguments(flags, language : ['c'])  #endif @@ -23,6 +22,19 @@ libjpegqs = dependency('libjpegqs',  	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'))  # Note that only libraw_r is thread-safe, but we'll just run it out-of process.  libraw = dependency('libraw', required : get_option('libraw')) @@ -35,6 +47,7 @@ dependencies = [  	dependency('gtk+-3.0'),  	dependency('pixman-1'), +	# Wuffs is included as a submodule.  	dependency('libturbojpeg'),  	dependency('libwebp'),  	dependency('libwebpdemux'), @@ -49,12 +62,14 @@ dependencies = [  	lcms2,  	libjpegqs,  	libraw, +	resvg,  	librsvg,  	xcursor,  	libheif,  	libtiff,  	gdkpixbuf, -	meson.get_compiler('c').find_library('m', required : false), + +	cc.find_library('m', required : false),  ]  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_LCMS2', lcms2.found())  conf.set('HAVE_LIBRAW', libraw.found()) +conf.set('HAVE_RESVG', resvg.found())  conf.set('HAVE_LIBRSVG', librsvg.found())  conf.set('HAVE_XCURSOR', xcursor.found())  conf.set('HAVE_LIBHEIF', libheif.found()) diff --git a/meson_options.txt b/meson_options.txt index d59a568..fb44094 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,6 +4,8 @@ option('libjpegqs', type : 'feature', value : 'auto',  	description : 'Build with JPEG Quant Smooth integration')  option('libraw', type : 'feature', value : 'auto',  	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',  	description : 'Build with SVG support, requires librsvg')  option('xcursor', type : 'feature', value : 'auto',  | 
