improve scaling of SVG images
We were not scaling images with fixed absolute dimensions.
This commit is contained in:
parent
586fb31550
commit
2d0c6b364c
@ -377,8 +377,8 @@ svg_css_length_to_pixels( RsvgLength length, double dpi )
|
|||||||
value = dpi * value / 6;
|
value = dpi * value / 6;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Probably RSVG_UNIT_PERCENT. We can't know what the pixel
|
/* Probably RSVG_UNIT_PERCENT. We can't know what the
|
||||||
* value is without more information.
|
* pixel value is without more information.
|
||||||
*/
|
*/
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
@ -397,6 +397,7 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
double height;
|
double height;
|
||||||
|
|
||||||
#if LIBRSVG_CHECK_VERSION( 2, 52, 0 )
|
#if LIBRSVG_CHECK_VERSION( 2, 52, 0 )
|
||||||
|
|
||||||
if( !rsvg_handle_get_intrinsic_size_in_pixels( svg->page,
|
if( !rsvg_handle_get_intrinsic_size_in_pixels( svg->page,
|
||||||
&width, &height ) ) {
|
&width, &height ) ) {
|
||||||
RsvgRectangle zero_rect, viewbox;
|
RsvgRectangle zero_rect, viewbox;
|
||||||
@ -412,11 +413,11 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
&has_height, &iheight,
|
&has_height, &iheight,
|
||||||
&has_viewbox, &viewbox );
|
&has_viewbox, &viewbox );
|
||||||
|
|
||||||
/* After librsvg 2.54.0, the `has_width` and `has_height` arguments
|
|
||||||
* always returns `TRUE`, since with SVG2 all documents *have* a
|
|
||||||
* default width and height of `100%`.
|
|
||||||
*/
|
|
||||||
#if LIBRSVG_CHECK_VERSION( 2, 54, 0 )
|
#if LIBRSVG_CHECK_VERSION( 2, 54, 0 )
|
||||||
|
/* After librsvg 2.54.0, the `has_width` and `has_height`
|
||||||
|
* arguments always returns `TRUE`, since with SVG2 all
|
||||||
|
* documents *have* a default width and height of `100%`.
|
||||||
|
*/
|
||||||
width = svg_css_length_to_pixels( iwidth, svg->dpi );
|
width = svg_css_length_to_pixels( iwidth, svg->dpi );
|
||||||
height = svg_css_length_to_pixels( iheight, svg->dpi );
|
height = svg_css_length_to_pixels( iheight, svg->dpi );
|
||||||
|
|
||||||
@ -424,7 +425,8 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
has_height = height > 0.0;
|
has_height = height > 0.0;
|
||||||
|
|
||||||
if( has_width && has_height ) {
|
if( has_width && has_height ) {
|
||||||
/* Success! Taking the viewbox into account is not needed.
|
/* Success! Taking the viewbox into account is not
|
||||||
|
* needed.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
else if( has_width && has_viewbox ) {
|
else if( has_width && has_viewbox ) {
|
||||||
@ -460,8 +462,8 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
|
|
||||||
if( width <= 0.0 ||
|
if( width <= 0.0 ||
|
||||||
height <= 0.0 ) {
|
height <= 0.0 ) {
|
||||||
/* We haven't found a usable set of sizes, so try working out
|
/* We haven't found a usable set of sizes, so try
|
||||||
* the visible area.
|
* working out the visible area.
|
||||||
*/
|
*/
|
||||||
rsvg_handle_get_geometry_for_layer( svg->page, NULL,
|
rsvg_handle_get_geometry_for_layer( svg->page, NULL,
|
||||||
&zero_rect, &viewbox, NULL, NULL );
|
&zero_rect, &viewbox, NULL, NULL );
|
||||||
@ -469,7 +471,9 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
height = viewbox.y + viewbox.height;
|
height = viewbox.y + viewbox.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /*!LIBRSVG_CHECK_VERSION( 2, 52, 0 )*/
|
#else /*!LIBRSVG_CHECK_VERSION( 2, 52, 0 )*/
|
||||||
|
|
||||||
{
|
{
|
||||||
RsvgDimensionData dimensions;
|
RsvgDimensionData dimensions;
|
||||||
|
|
||||||
@ -477,6 +481,7 @@ vips_foreign_load_svg_get_natural_size( VipsForeignLoadSvg *svg,
|
|||||||
width = dimensions.width;
|
width = dimensions.width;
|
||||||
height = dimensions.height;
|
height = dimensions.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /*LIBRSVG_CHECK_VERSION( 2, 52, 0 )*/
|
#endif /*LIBRSVG_CHECK_VERSION( 2, 52, 0 )*/
|
||||||
|
|
||||||
/* width or height below 0.5 can't be rounded to 1.
|
/* width or height below 0.5 can't be rounded to 1.
|
||||||
@ -499,7 +504,6 @@ vips_foreign_load_svg_get_scaled_size( VipsForeignLoadSvg *svg,
|
|||||||
{
|
{
|
||||||
double width;
|
double width;
|
||||||
double height;
|
double height;
|
||||||
double scale;
|
|
||||||
|
|
||||||
/* Get dimensions with the default dpi.
|
/* Get dimensions with the default dpi.
|
||||||
*/
|
*/
|
||||||
@ -507,36 +511,12 @@ vips_foreign_load_svg_get_scaled_size( VipsForeignLoadSvg *svg,
|
|||||||
if( vips_foreign_load_svg_get_natural_size( svg, &width, &height ) )
|
if( vips_foreign_load_svg_get_natural_size( svg, &width, &height ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* Calculate dimensions at required dpi/scale.
|
/* We scale up with cairo --- scaling with rsvg_handle_set_dpi() will
|
||||||
|
* fail for SVGs with absolute sizes.
|
||||||
*/
|
*/
|
||||||
scale = svg->scale * svg->dpi / 72.0;
|
svg->cairo_scale = svg->scale * svg->dpi / 72.0;
|
||||||
if( scale != 1.0 ) {
|
width *= svg->cairo_scale;
|
||||||
double scaled_width;
|
height *= svg->cairo_scale;
|
||||||
double scaled_height;
|
|
||||||
|
|
||||||
rsvg_handle_set_dpi( svg->page, scale * 72.0 );
|
|
||||||
if( vips_foreign_load_svg_get_natural_size( svg,
|
|
||||||
&scaled_width, &scaled_height ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if( scaled_width == width &&
|
|
||||||
scaled_height == height ) {
|
|
||||||
/* SVG without width and height always reports the same
|
|
||||||
* dimensions regardless of dpi. Apply dpi/scale using
|
|
||||||
* cairo instead.
|
|
||||||
*/
|
|
||||||
svg->cairo_scale = scale;
|
|
||||||
width *= scale;
|
|
||||||
height *= scale;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* SVG with width and height reports correctly scaled
|
|
||||||
* dimensions.
|
|
||||||
*/
|
|
||||||
width = scaled_width;
|
|
||||||
height = scaled_height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_width = VIPS_ROUND_UINT( width );
|
*out_width = VIPS_ROUND_UINT( width );
|
||||||
*out_height = VIPS_ROUND_UINT( height );
|
*out_height = VIPS_ROUND_UINT( height );
|
||||||
@ -563,7 +543,8 @@ vips_foreign_load_svg_parse( VipsForeignLoadSvg *svg, VipsImage *out )
|
|||||||
4, VIPS_FORMAT_UCHAR,
|
4, VIPS_FORMAT_UCHAR,
|
||||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
|
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
|
||||||
|
|
||||||
/* We render to a linecache, so fat strips work well.
|
/* We render to a cache with a couple of rows of tiles, so fat strips
|
||||||
|
* work well.
|
||||||
*/
|
*/
|
||||||
if( vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ) )
|
if( vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
@ -613,10 +594,14 @@ vips_foreign_load_svg_generate( VipsRegion *or,
|
|||||||
* running inside a non-threaded tilecache.
|
* running inside a non-threaded tilecache.
|
||||||
*/
|
*/
|
||||||
#if LIBRSVG_CHECK_VERSION( 2, 46, 0 )
|
#if LIBRSVG_CHECK_VERSION( 2, 46, 0 )
|
||||||
|
|
||||||
{
|
{
|
||||||
RsvgRectangle viewport;
|
RsvgRectangle viewport;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
|
/* No need to scale -- we always set the viewport to the
|
||||||
|
* whole image, and set the region to draw on the surface.
|
||||||
|
*/
|
||||||
cairo_translate( cr, -r->left, -r->top );
|
cairo_translate( cr, -r->left, -r->top );
|
||||||
viewport.x = 0;
|
viewport.x = 0;
|
||||||
viewport.y = 0;
|
viewport.y = 0;
|
||||||
@ -634,7 +619,9 @@ vips_foreign_load_svg_generate( VipsRegion *or,
|
|||||||
|
|
||||||
cairo_destroy( cr );
|
cairo_destroy( cr );
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /*!LIBRSVG_CHECK_VERSION( 2, 46, 0 )*/
|
#else /*!LIBRSVG_CHECK_VERSION( 2, 46, 0 )*/
|
||||||
|
|
||||||
cairo_scale( cr, svg->cairo_scale, svg->cairo_scale );
|
cairo_scale( cr, svg->cairo_scale, svg->cairo_scale );
|
||||||
cairo_translate( cr, -r->left / svg->cairo_scale,
|
cairo_translate( cr, -r->left / svg->cairo_scale,
|
||||||
-r->top / svg->cairo_scale );
|
-r->top / svg->cairo_scale );
|
||||||
@ -648,6 +635,7 @@ vips_foreign_load_svg_generate( VipsRegion *or,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cairo_destroy( cr );
|
cairo_destroy( cr );
|
||||||
|
|
||||||
#endif /*LIBRSVG_CHECK_VERSION( 2, 46, 0 )*/
|
#endif /*LIBRSVG_CHECK_VERSION( 2, 46, 0 )*/
|
||||||
|
|
||||||
/* Cairo makes pre-multipled BRGA -- we must byteswap and unpremultiply.
|
/* Cairo makes pre-multipled BRGA -- we must byteswap and unpremultiply.
|
||||||
|
@ -99,7 +99,7 @@ vips_g_input_stream_finalize( GObject *object )
|
|||||||
{
|
{
|
||||||
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( object );
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( object );
|
||||||
|
|
||||||
VIPS_FREEF( g_object_unref, gstream->source );
|
VIPS_UNREF( gstream->source );
|
||||||
|
|
||||||
G_OBJECT_CLASS( vips_g_input_stream_parent_class )->finalize( object );
|
G_OBJECT_CLASS( vips_g_input_stream_parent_class )->finalize( object );
|
||||||
}
|
}
|
||||||
@ -107,13 +107,13 @@ vips_g_input_stream_finalize( GObject *object )
|
|||||||
static goffset
|
static goffset
|
||||||
vips_g_input_stream_tell( GSeekable *seekable )
|
vips_g_input_stream_tell( GSeekable *seekable )
|
||||||
{
|
{
|
||||||
VipsSource *source = VIPS_G_INPUT_STREAM( seekable )->source;
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( seekable );
|
||||||
|
|
||||||
goffset pos;
|
goffset pos;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_g_input_stream_tell:\n" );
|
VIPS_DEBUG_MSG( "vips_g_input_stream_tell:\n" );
|
||||||
|
|
||||||
pos = vips_source_seek( source, 0, SEEK_CUR );
|
pos = vips_source_seek( gstream->source, 0, SEEK_CUR );
|
||||||
if( pos == -1 )
|
if( pos == -1 )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
@ -123,12 +123,12 @@ vips_g_input_stream_tell( GSeekable *seekable )
|
|||||||
static gboolean
|
static gboolean
|
||||||
vips_g_input_stream_can_seek( GSeekable *seekable )
|
vips_g_input_stream_can_seek( GSeekable *seekable )
|
||||||
{
|
{
|
||||||
VipsSource *source = VIPS_G_INPUT_STREAM( seekable )->source;
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( seekable );
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_g_input_stream_can_seek: %d\n",
|
VIPS_DEBUG_MSG( "vips_g_input_stream_can_seek: %d\n",
|
||||||
!source->is_pipe );
|
!gstream->source->is_pipe );
|
||||||
|
|
||||||
return( !source->is_pipe );
|
return( !gstream->source->is_pipe );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -149,12 +149,12 @@ static gboolean
|
|||||||
vips_g_input_stream_seek( GSeekable *seekable, goffset offset,
|
vips_g_input_stream_seek( GSeekable *seekable, goffset offset,
|
||||||
GSeekType type, GCancellable *cancellable, GError **error )
|
GSeekType type, GCancellable *cancellable, GError **error )
|
||||||
{
|
{
|
||||||
VipsSource *source = VIPS_G_INPUT_STREAM( seekable )->source;
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( seekable );
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_g_input_stream_seek: offset = %" G_GINT64_FORMAT
|
VIPS_DEBUG_MSG( "vips_g_input_stream_seek: offset = %" G_GINT64_FORMAT
|
||||||
", type = %d\n", offset, type );
|
", type = %d\n", offset, type );
|
||||||
|
|
||||||
if( vips_source_seek( source, offset,
|
if( vips_source_seek( gstream->source, offset,
|
||||||
seek_type_to_lseek( type ) ) == -1 ) {
|
seek_type_to_lseek( type ) ) == -1 ) {
|
||||||
g_set_error( error, G_IO_ERROR,
|
g_set_error( error, G_IO_ERROR,
|
||||||
G_IO_ERROR_FAILED,
|
G_IO_ERROR_FAILED,
|
||||||
@ -189,17 +189,16 @@ static gssize
|
|||||||
vips_g_input_stream_read( GInputStream *stream, void *buffer, gsize count,
|
vips_g_input_stream_read( GInputStream *stream, void *buffer, gsize count,
|
||||||
GCancellable *cancellable, GError **error )
|
GCancellable *cancellable, GError **error )
|
||||||
{
|
{
|
||||||
VipsSource *source;
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( stream );
|
||||||
gssize res;
|
|
||||||
|
|
||||||
source = VIPS_G_INPUT_STREAM( stream )->source;
|
gssize res;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_g_input_stream_read: count: %zd\n", count );
|
VIPS_DEBUG_MSG( "vips_g_input_stream_read: count: %zd\n", count );
|
||||||
|
|
||||||
if( g_cancellable_set_error_if_cancelled( cancellable, error ) )
|
if( g_cancellable_set_error_if_cancelled( cancellable, error ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
if( (res = vips_source_read( source, buffer, count )) == -1 )
|
if( (res = vips_source_read( gstream->source, buffer, count )) == -1 )
|
||||||
g_set_error( error, G_IO_ERROR,
|
g_set_error( error, G_IO_ERROR,
|
||||||
G_IO_ERROR_FAILED,
|
G_IO_ERROR_FAILED,
|
||||||
_( "Error while reading: %s" ),
|
_( "Error while reading: %s" ),
|
||||||
@ -212,17 +211,16 @@ static gssize
|
|||||||
vips_g_input_stream_skip( GInputStream *stream, gsize count,
|
vips_g_input_stream_skip( GInputStream *stream, gsize count,
|
||||||
GCancellable *cancellable, GError **error )
|
GCancellable *cancellable, GError **error )
|
||||||
{
|
{
|
||||||
VipsSource *source;
|
VipsGInputStream *gstream = VIPS_G_INPUT_STREAM( stream );
|
||||||
gssize position;
|
|
||||||
|
|
||||||
source = VIPS_G_INPUT_STREAM( stream )->source;
|
gssize position;
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_g_input_stream_skip: count: %zd\n", count );
|
VIPS_DEBUG_MSG( "vips_g_input_stream_skip: count: %zd\n", count );
|
||||||
|
|
||||||
if( g_cancellable_set_error_if_cancelled( cancellable, error ) )
|
if( g_cancellable_set_error_if_cancelled( cancellable, error ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
position = vips_source_seek( source, count, SEEK_CUR );
|
position = vips_source_seek( gstream->source, count, SEEK_CUR );
|
||||||
if( position == -1 ) {
|
if( position == -1 ) {
|
||||||
g_set_error( error, G_IO_ERROR,
|
g_set_error( error, G_IO_ERROR,
|
||||||
G_IO_ERROR_FAILED,
|
G_IO_ERROR_FAILED,
|
||||||
|
@ -742,21 +742,6 @@ vips_shutdown( void )
|
|||||||
|
|
||||||
vips_cache_drop_all();
|
vips_cache_drop_all();
|
||||||
|
|
||||||
/* In dev releases, always show leaks. But not more than once, it's
|
|
||||||
* annoying.
|
|
||||||
*/
|
|
||||||
#ifndef DEBUG_LEAK
|
|
||||||
if( vips__leak )
|
|
||||||
#endif /*DEBUG_LEAK*/
|
|
||||||
{
|
|
||||||
static gboolean done = FALSE;
|
|
||||||
|
|
||||||
if( !done ) {
|
|
||||||
done = TRUE;
|
|
||||||
vips_leak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ENABLE_DEPRECATED
|
#if ENABLE_DEPRECATED
|
||||||
im_close_plugins();
|
im_close_plugins();
|
||||||
#endif
|
#endif
|
||||||
@ -787,6 +772,21 @@ vips_shutdown( void )
|
|||||||
VIPS_FREE( vips__argv0 );
|
VIPS_FREE( vips__argv0 );
|
||||||
VIPS_FREE( vips__prgname );
|
VIPS_FREE( vips__prgname );
|
||||||
VIPS_FREEF( g_timer_destroy, vips__global_timer );
|
VIPS_FREEF( g_timer_destroy, vips__global_timer );
|
||||||
|
|
||||||
|
/* In dev releases, always show leaks. But not more than once, it's
|
||||||
|
* annoying.
|
||||||
|
*/
|
||||||
|
#ifndef DEBUG_LEAK
|
||||||
|
if( vips__leak )
|
||||||
|
#endif /*DEBUG_LEAK*/
|
||||||
|
{
|
||||||
|
static gboolean done = FALSE;
|
||||||
|
|
||||||
|
if( !done ) {
|
||||||
|
done = TRUE;
|
||||||
|
vips_leak();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
Loading…
Reference in New Issue
Block a user