1
1
mirror of https://gitlab.gnome.org/GNOME/gimp.git synced 2025-10-06 01:12:40 +02:00

libgimp, plug-ins: implement loading SVG breaking aspect ratio.

Previous implementation was always keeping ratio. If the width/height
argument were of a different aspect, librsvg simply fits the image with
original aspect within the box. Now we will appropriately scale
differently on both dimensions to have the image fill the whole box.

Note that this doesn't work with librsvg before 2.46.0 (not that it's
not possible, just that I haven't looked at how it should be implemented
there (which would be a different implementation anyway since there is
no cairo step).

Finally I add a build warning for when we'll prepare for GIMP 4, as the
pixel density arg needs to be split in 2 (each dimension can have its
own PPI), even though this is not really needed right now.
This commit is contained in:
Jehan
2025-08-28 22:10:02 +02:00
parent d8712a525b
commit 9519901150
2 changed files with 79 additions and 24 deletions

View File

@@ -132,6 +132,10 @@ gimp_vector_load_procedure_constructed (GObject *object)
FALSE,
G_PARAM_READWRITE);
#if GIMP_CHECK_VERSION(3, 99, 0)
#warning Make per-coordinate x/y pixel density. Simplify plug-ins/common/file-svg.c code when not keeping ratio.
#endif
/* Note: the "pixel-density" is saved in pixels per inch. "physical-unit"
* property is only there for display.
*/

View File

@@ -42,6 +42,8 @@
#define SVG_DEFAULT_SIZE 500
#define SVG_PREVIEW_SIZE 128
#define EPSILON 1e-6
typedef struct
{
@@ -113,6 +115,8 @@ static GimpPDBStatusType load_dialog (GFile *file,
GimpProcedureConfig *config,
GimpVectorLoadData extracted_data,
GError **error);
static gboolean svg_extract_dimensions (RsvgHandle *handle,
GimpVectorLoadData *extracted_dimensions);
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
static GimpUnit * svg_rsvg_to_gimp_unit (RsvgUnit unit);
@@ -212,18 +216,7 @@ svg_extract (GimpProcedure *procedure,
gpointer extract_data,
GError **error)
{
RsvgHandle *handle;
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
gboolean out_has_width;
RsvgLength out_width;
gboolean out_has_height;
RsvgLength out_height;
gboolean out_has_viewbox;
RsvgRectangle out_viewbox;
#endif
#if ! LIBRSVG_CHECK_VERSION(2, 52, 0)
RsvgDimensionData dim;
#endif
RsvgHandle *handle;
g_return_val_if_fail (extracted_dimensions != NULL, FALSE);
g_return_val_if_fail (data_for_run_destroy != NULL && *data_for_run_destroy == NULL, FALSE);
@@ -319,14 +312,42 @@ svg_extract (GimpProcedure *procedure,
return FALSE;
}
if (! svg_extract_dimensions (handle, extracted_dimensions))
{
g_object_unref (handle);
return FALSE;
}
*data_for_run = handle;
*data_for_run_destroy = g_object_unref;
return TRUE;
}
static gboolean
svg_extract_dimensions (RsvgHandle *handle,
GimpVectorLoadData *extracted_dimensions)
{
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
gboolean out_has_width;
RsvgLength out_width;
gboolean out_has_height;
RsvgLength out_height;
gboolean out_has_viewbox;
RsvgRectangle out_viewbox;
#endif
#if ! LIBRSVG_CHECK_VERSION(2, 52, 0)
RsvgDimensionData dim;
#endif
/* SVG has no pixel density information */
extracted_dimensions->exact_density = TRUE;
extracted_dimensions->correct_ratio = TRUE;
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
rsvg_handle_get_intrinsic_dimensions (handle,
&out_has_width, &out_width,
&out_has_height, &out_height,
&out_has_width, &out_width,
&out_has_height, &out_height,
&out_has_viewbox, &out_viewbox);
if (out_has_width && out_has_height)
@@ -445,9 +466,6 @@ svg_extract (GimpProcedure *procedure,
extracted_dimensions->correct_ratio = TRUE;
#endif
*data_for_run = handle;
*data_for_run_destroy = g_object_unref;
return TRUE;
}
@@ -637,11 +655,17 @@ load_rsvg_pixbuf (RsvgHandle *handle,
GdkPixbuf *pixbuf = NULL;
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
cairo_surface_t *surface = NULL;
cairo_t *cr = NULL;
RsvgRectangle viewport = { 0, };
guchar *src;
gint y;
cairo_surface_t *surface = NULL;
cairo_t *cr = NULL;
RsvgRectangle viewport = { 0, };
guchar *src;
gint y;
/* Aspect-ratio breaking is only implemented with newer librsvg API. */
GimpVectorLoadData dimensions;
gdouble x_scale = 1.0;
gdouble y_scale = 1.0;
gdouble aspect_width = width;
gdouble aspect_height = height;
#else
SvgLoadVals vals;
#endif
@@ -650,11 +674,38 @@ load_rsvg_pixbuf (RsvgHandle *handle,
rsvg_handle_set_dpi (handle, resolution);
#if LIBRSVG_CHECK_VERSION(2, 46, 0)
if (svg_extract_dimensions (handle, &dimensions))
{
if (fabs ((gdouble) dimensions.width / dimensions.height - (gdouble) width / height) < EPSILON)
{
rsvg_handle_set_dpi (handle, resolution);
}
else if ((gdouble) dimensions.width / dimensions.height > (gdouble) width / height)
{
aspect_height = (gdouble) width * dimensions.height / dimensions.width;
/* XXX The X/Y resolution doesn't change a thing regarding
* rendering unfortunately so we still have to scale when the
* aspect ratio is not kept. I still set these, just in case.
*/
rsvg_handle_set_dpi_x_y (handle, resolution, aspect_height / height * resolution);
y_scale = height / aspect_height;
}
else
{
aspect_width = (gdouble) height * dimensions.width / dimensions.height;
rsvg_handle_set_dpi_x_y (handle, aspect_width / width * resolution, resolution);
x_scale = width / aspect_width;
}
}
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
cr = cairo_create (surface);
viewport.width = width;
viewport.height = height;
viewport.width = (double) aspect_width;
viewport.height = (double) aspect_height;
if (x_scale != 1.0 || y_scale != 1.0)
cairo_scale (cr, x_scale, y_scale);
rsvg_handle_render_document (handle, cr, &viewport, NULL);
pixbuf = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (surface),