linebender / resvg

An SVG rendering library.
Apache License 2.0
2.84k stars 229 forks source link

Understanding viewbox flattening - missing C API call? #822

Closed jermy closed 1 month ago

jermy commented 2 months ago

Hi,

I've been doing an update to move from resvg 0.41 to 0.43 in some render code. There are two parts of our resvg usage:

  1. Generate a SVG with some text
    • We don't know how large the text will be, so the easiest thing to do is generate a SVG and find the bounding box via resvg_get_image_bbox in the C API. It's typical for some fonts to return negative values for the viewbox x & y.
    • We write this bounding box back into the SVG as the viewbox, so the SVG can be rendered will all the text visible.
    • There's a bug where the bounding box doesn't include the edges of some text, mostly relating to curvier fonts. I'll be looking into this and will probably file a separate bug about it.
  2. Render a SVG to a canvas/onto a block of memory

For the second case it's useful to know how the viewbox of the SVG relates to the area being rendered: For efficiency we might want to render the image onto a block of memory that's a different aspect ratio from the SVG with an updated transform to that new size, but only if we can be sure that there isn't content that is outside the viewbox which might then get accidentally rendered. We currently work this out by making sure the viewbox is at least the same size as the bounding box.

As far as I can tell, resvg_get_image_bbox is continuing to return the absolute position of the bounding box so we can continue to do (1) , but now that resvg_get_image_viewbox doesn't exist, we don't have any way to work out what the coordinates of this bounding box are in the space we are rendering, which means we might have to revert to copy data for (2).

Should we have some way in the C API to get the transformation done from the original position (which I think is what you refer to as absolute/canvas position) of the bounding box, to the rendered position (again, relative/image position) of that box? That could either be multiple similar functions for resvg_get_image_bbox named appropriately, or just one with an optional argument for absolute/relative, or a way to get the root transform?

RazrFalcon commented 2 months ago

Sorry, I'm not really sure what are you trying to achieve. No API is missing, since as a user you should not care/know about viewBox attribute at all.

resvg_get_image_bbox returns a calculated, tight bounding box of all SVG elements, while resvg_get_image_size returns... well, it's complicated. It's either width+height from SVG, or viewBox from SVG, or the same as resvg_get_image_bbox when both are missing.

If you're trying to get a bbox of a generated SVG you don't know the size of beforehand then resvg_get_image_bbox is what you're looking for. No idea what clipping issues are you having and why.

jermy commented 2 months ago

I guess we no longer know which parts of the image could contain any image or not. Effectively resvg_get_image_bbox and resvg_get_image_size are in two different coordinate systems (pre/post flattening), and we could previously use the viewbox to translate between the two. With the flattening changes, we no longer have that data.

The rust API has separate tree.0.root().abs_bounding_box() and tree.0.root().bounding_box() depending on which coordinate system you want, but only one of those is exposed via the C API.

Here's not quite what we're doing, but feels like a perfectly reasonable use-case: lets say you wanted to render a SVG with resvg to a canvas, then draw a rectangle on top of that of the image bounding box, how would you do that?

I'll post a separate issue about the font clipping, although it might be slightly related since it's ultimately a bounding box thing.

RazrFalcon commented 2 months ago

I guess we no longer know which parts of the image could contain any image or not.

Can you clarify what you mean hear? Nothing have changed.

Effectively resvg_get_image_bbox and resvg_get_image_size are in two different coordinate systems

No, there is only one coordinate system.

and we could previously use the viewbox to translate between the two

viewBox has nothing to do with coordinate systems. It's just an extra transform.

With the flattening changes, we no longer have that data.

You do not, but I still do not understand how it can be useful. Can you provide a detailed example?

The rust API has separate tree.0.root().abs_bounding_box() and tree.0.root().bounding_box() depending on which coordinate system you want, but only one of those is exposed via the C API.

You must not use them either. It has nothing to do with C API.

Here's not quite what we're doing, but feels like a perfectly reasonable use-case: lets say you wanted to render a SVG with resvg to a canvas, then draw a rectangle on top of that of the image bounding box, how would you do that?

You draw SVG onto a Pixmap and then render this Pixmap onto your canvas. And the Pixmap size is your bbox. resvg API isn't designed to render SVG onto a larger canvas, if that's what you're trying to do.

RazrFalcon commented 2 months ago

See how crates/c-api/examples/cairo/example.c works. You create a Pixmap with resvg_get_image_size size and then render onto it. That's it. Everything else is wrong.