Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add resvg #2

Merged
merged 12 commits into from
Jan 24, 2023
6 changes: 4 additions & 2 deletions libvips/foreign/foreign.c
Original file line number Diff line number Diff line change
Expand Up @@ -3006,11 +3006,13 @@ vips_foreign_operation_init( void )
vips_foreign_load_pdf_source_get_type();
#endif /*HAVE_PDFIUM*/

#ifdef HAVE_RSVG
#if (defined(HAVE_RSVG) || defined(HAVE_RESVG)) && !defined(RESVG_MODULE)
vips_foreign_load_svg_file_get_type();
vips_foreign_load_svg_buffer_get_type();
#ifdef HAVE_RSVG
vips_foreign_load_svg_source_get_type();
#endif /*HAVE_RSVG*/
#endif
#endif /*defined(HAVE_RSVG) || defined(HAVE_RESVG)*/

#if defined(HAVE_LIBJXL) && !defined(LIBJXL_MODULE)
vips_foreign_load_jxl_file_get_type();
Expand Down
9 changes: 8 additions & 1 deletion libvips/foreign/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ foreign_sources = files(
'rawsave.c',
'spngload.c',
'spngsave.c',
'svgload.c',
'tiff2vips.c',
'tiff.c',
'tiffload.c',
Expand Down Expand Up @@ -123,6 +122,14 @@ endif

libvips_sources += foreign_sources

resvg_module_sources = files(
'svgload.c',
)

if not resvg_module
foreign_sources += resvg_module_sources
endif

foreign_lib = static_library('foreign',
foreign_sources,
foreign_headers,
Expand Down
158 changes: 138 additions & 20 deletions libvips/foreign/svgload.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,38 @@
#include <cairo.h>
#include <librsvg/rsvg.h>

/* Render SVGs with tiles this size. They need to be pretty big to limit
* overcomputation.
/* A handy #define for we-will-handle-svgz.
*/
#define TILE_SIZE (2000)
#if LIBRSVG_CHECK_FEATURE(SVGZ)
#define HANDLE_SVGZ
#endif

/* The <svg tag must appear within this many bytes of the start of the file.
*/
#define SVG_HEADER_SIZE (1000)
#elif defined(HAVE_RESVG)

/* A handy #define for we-will-handle-svgz.
*/
#if LIBRSVG_CHECK_FEATURE(SVGZ) && defined(HAVE_ZLIB)
#include <resvg.h>
#define HANDLE_SVGZ

#endif

#if defined(HAVE_RSVG) || defined(HAVE_RESVG)

#ifndef HAVE_ZLIB
#undef HANDLE_SVGZ
#endif

#ifdef HANDLE_SVGZ
#include <zlib.h>
#endif

/* Render SVGs with tiles this size. They need to be pretty big to limit
* overcomputation.
*/
#define TILE_SIZE (2000)

/* The <svg tag must appear within this many bytes of the start of the file.
*/
#define SVG_HEADER_SIZE (1000)

typedef struct _VipsForeignLoadSvg {
VipsForeignLoad parent_object;

Expand All @@ -119,7 +132,12 @@ typedef struct _VipsForeignLoadSvg {
*/
gboolean unlimited;

#ifdef HAVE_RSVG
RsvgHandle *page;
#else
resvg_options *options;
resvg_render_tree *tree;
#endif

} VipsForeignLoadSvg;

Expand Down Expand Up @@ -308,7 +326,12 @@ vips_foreign_load_svg_dispose( GObject *gobject )
{
VipsForeignLoadSvg *svg = (VipsForeignLoadSvg *) gobject;

#ifdef HAVE_RSVG
VIPS_UNREF( svg->page );
#else
resvg_options_destroy( svg->options );
VIPS_FREEF( resvg_tree_destroy, svg->tree );
#endif

G_OBJECT_CLASS( vips_foreign_load_svg_parent_class )->
dispose( gobject );
Expand All @@ -328,6 +351,7 @@ vips_foreign_load_svg_get_flags( VipsForeignLoad *load )
return( VIPS_FOREIGN_PARTIAL );
}

#ifdef HAVE_RSVG
#if LIBRSVG_CHECK_VERSION( 2, 52, 0 )
/* Derived from `CssLength::to_user` in librsvg.
* https://gitlab.gnome.org/GNOME/librsvg/-/blob/e6607c9ae8d8409d4efff6b12993717400b3356e/src/length.rs#L368
Expand Down Expand Up @@ -386,6 +410,7 @@ svg_css_length_to_pixels( RsvgLength length, double dpi )
return value;
}
#endif
#endif

static int
vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
Expand All @@ -396,6 +421,7 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
double width;
double height;

#ifdef HAVE_RSVG
#if LIBRSVG_CHECK_VERSION( 2, 52, 0 )

if( !rsvg_handle_get_intrinsic_size_in_pixels( svg->page,
Expand Down Expand Up @@ -483,6 +509,11 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
}

#endif /*LIBRSVG_CHECK_VERSION( 2, 52, 0 )*/
#else /* HAVE_RSVG */
resvg_size size = resvg_get_image_size( svg->tree );
width = size.width;
height = size.height;
#endif

/* width or height below 0.5 can't be rounded to 1.
*/
Expand All @@ -505,9 +536,11 @@ vips_foreign_load_svg_get_scaled_size( VipsForeignLoadSvg *svg,
double width;
double height;

#ifdef HAVE_RSVG
/* Get dimensions with the default dpi.
*/
rsvg_handle_set_dpi( svg->page, 72.0 );
#endif
if( vips_foreign_load_svg_get_natural_size( svg, &width, &height ) )
return( -1 );

Expand Down Expand Up @@ -565,23 +598,25 @@ vips_foreign_load_svg_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
const VipsForeignLoadSvg *svg = (VipsForeignLoadSvg *) a;
const VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( svg );
const VipsRect *r = &or->valid;

cairo_surface_t *surface;
cairo_t *cr;
int y;

#ifdef DEBUG
printf( "vips_foreign_load_svg_generate:\n "
"left = %d, top = %d, width = %d, height = %d\n",
r->left, r->top, r->width, r->height );
#endif /*DEBUG*/

/* rsvg won't always paint the background.
/* SVG won't always paint the background.
*/
vips_region_black( or );

#ifdef HAVE_RSVG
const VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( svg );

cairo_surface_t *surface;
cairo_t *cr;
int y;

surface = cairo_image_surface_create_for_data(
VIPS_REGION_ADDR( or, r->left, r->top ),
CAIRO_FORMAT_ARGB32,
Expand Down Expand Up @@ -644,6 +679,42 @@ vips_foreign_load_svg_generate( VipsRegion *or,
vips__premultiplied_bgra2rgba(
(guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ),
r->width );
#else

VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top );

resvg_render(
svg->tree,
(resvg_fit_to) { .type = RESVG_FIT_TO_TYPE_ORIGINAL },
(resvg_transform) {
.a = svg->cairo_scale,
.d = svg->cairo_scale,
.e = -r->left,
.f = -r->top,
},
r->width,
r->height,
(char *) q
);

/* Just unpremultiply.
*/
for( int i = 0; i < r->width * r->height; i++ ) {
VipsPel * restrict p = &q[i * 4];
VipsPel x = p[3];

/* Skip transparent and fully opaque pixels.
*/
if( x == 0 || x == 255 )
continue;

/* Any compiler will unroll it.
*/
for( int j = 0; j < 3; j++ )
p[j] = 255 * p[j] / x;
}

#endif

return( 0 );
}
Expand Down Expand Up @@ -731,8 +802,16 @@ vips_foreign_load_svg_init( VipsForeignLoadSvg *svg )
svg->dpi = 72.0;
svg->scale = 1.0;
svg->cairo_scale = 1.0;
#ifdef HAVE_RESVG
svg->options = resvg_options_create();

/* Get dimensions with the default dpi.
*/
resvg_options_set_dpi( svg->options, 72.0 );
#endif
}

#ifdef HAVE_RSVG
typedef struct _VipsForeignLoadSvgSource {
VipsForeignLoadSvg parent_object;

Expand Down Expand Up @@ -783,7 +862,6 @@ vips_foreign_load_svg_source_header( VipsForeignLoad *load )
return( -1 );
}
g_object_unref( gstream );

return( vips_foreign_load_svg_header( load ) );
}

Expand Down Expand Up @@ -833,6 +911,7 @@ static void
vips_foreign_load_svg_source_init( VipsForeignLoadSvgSource *source )
{
}
#endif

typedef struct _VipsForeignLoadSvgFile {
VipsForeignLoadSvg parent_object;
Expand All @@ -859,11 +938,34 @@ vips_foreign_load_svg_file_is_a( const char *filename )
vips_foreign_load_svg_is_a( buf, bytes ) );
}

#ifdef HAVE_RESVG
static const char *
resvg_error_msg( resvg_error e ) {
switch( e ) {
case RESVG_ERROR_NOT_AN_UTF8_STR:
return "only UTF-8 content is supported";
case RESVG_ERROR_FILE_OPEN_FAILED:
return "failed to open the provided file";
case RESVG_ERROR_MALFORMED_GZIP:
return "compressed SVG must use the GZip algorithm";
case RESVG_ERROR_ELEMENTS_LIMIT_REACHED:
return "we do not allow SVG with more than 1_000_000 elements for security reasons";
case RESVG_ERROR_INVALID_SIZE:
return "SVG doesn't have a valid size";
case RESVG_ERROR_PARSING_FAILED:
return "failed to parse SVG data";
default:
return "unknown error";
}
}
#endif

static int
vips_foreign_load_svg_file_header( VipsForeignLoad *load )
{
VipsForeignLoadSvg *svg = (VipsForeignLoadSvg *) load;
VipsForeignLoadSvgFile *file = (VipsForeignLoadSvgFile *) load;
#ifdef HAVE_RSVG
RsvgHandleFlags flags = svg->unlimited ? RSVG_HANDLE_FLAG_UNLIMITED : 0;

GError *error = NULL;
Expand All @@ -878,7 +980,15 @@ vips_foreign_load_svg_file_header( VipsForeignLoad *load )
return( -1 );
}
g_object_unref( gfile );

#else
resvg_error error = resvg_parse_tree_from_file(
file->filename, svg->options, &svg->tree );
if( error != RESVG_OK ) {
vips_error( VIPS_OBJECT_GET_CLASS( svg )->nickname,
"%s", resvg_error_msg( error ) );
return( -1 );
}
#endif
VIPS_SETSTR( load->out->filename, file->filename );

return( vips_foreign_load_svg_header( load ) );
Expand All @@ -888,7 +998,7 @@ static const char *vips_foreign_svg_suffs[] = {
".svg",
/* librsvg supports svgz directly, no need to check for zlib here.
*/
#if LIBRSVG_CHECK_FEATURE(SVGZ)
#ifdef HANDLE_SVGZ
".svgz",
".svg.gz",
#endif
Expand Down Expand Up @@ -948,6 +1058,7 @@ vips_foreign_load_svg_buffer_header( VipsForeignLoad *load )
VipsForeignLoadSvg *svg = (VipsForeignLoadSvg *) load;
VipsForeignLoadSvgBuffer *buffer =
(VipsForeignLoadSvgBuffer *) load;
#ifdef HAVE_RSVG
RsvgHandleFlags flags = svg->unlimited ? RSVG_HANDLE_FLAG_UNLIMITED : 0;

GError *error = NULL;
Expand All @@ -963,7 +1074,15 @@ vips_foreign_load_svg_buffer_header( VipsForeignLoad *load )
return( -1 );
}
g_object_unref( gstream );

#else
resvg_error error = resvg_parse_tree_from_data(
buffer->buf->data, buffer->buf->length, svg->options, &svg->tree );
if( error != RESVG_OK ) {
vips_error( VIPS_OBJECT_GET_CLASS( svg )->nickname,
"%s", resvg_error_msg( error ) );
return( -1 );
}
#endif
return( vips_foreign_load_svg_header( load ) );
}

Expand Down Expand Up @@ -1146,4 +1265,3 @@ vips_svgload_source( VipsSource *source, VipsImage **out, ... )

return( result );
}

15 changes: 15 additions & 0 deletions libvips/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,18 @@ if openslide_module
install_dir: module_dir
)
endif

if resvg_module
shared_module('vips-resvg',
'module/resvg.c',
resvg_module_sources,
c_args: module_c_args,
link_args: module_link_args,
name_prefix: '',
name_suffix: module_suffix,
dependencies: [module_dep, resvg_dep],
gnu_symbol_visibility: 'hidden',
install: true,
install_dir: module_dir
)
endif
Loading