RazrFalcon / resvg

An SVG rendering library.
Mozilla Public License 2.0
2.74k stars 220 forks source link

Support rendering parts/tiles of an SVG, repeatedly #354

Closed virtualritz closed 1 year ago

virtualritz commented 3 years ago

What

I.e. I only want the lower left quadrant of the image I would specify a ViewBox with a Rect::new(0.5, 0.5, 0.5, 0.5). FitTo would then pertain to the size of that region, not the total canvas.

Why

I am looking into adding support for SVG textures to a 3D renderer. During render time the renderer will request texture tiles at certain resolutions. These tiles are the aforementioned sub images.

Another use case is a viewer that e.g. allows zooming. When you zoom in 10000 times you do not want to be forced to render the whole canvas but only the visible portion.

How

It would be best if this worked by creating some sort of persistent representation of the SVG that can be queried for such 'sub' Pixmaps to keep any initial set-up/overhead of parsing etc. a one time cost.

I.e. you would create an SVG struct and call render_region() or the like on it, repeatedly.

I guess this already works efficiently when querying the same Tree/Node repeatedly?

RazrFalcon commented 3 years ago

This is not possible right now. resvg renders the whole canvas at time. We basically have to add bounds detection to see what elements would be in the required are and then render them. This change is far from trivial.

virtualritz commented 3 years ago

So what would be needed is putting all nodes in a space partitioning tree, right?

What would be not trivial if using something like the fart_aabb crate?

RazrFalcon commented 3 years ago

For example, blur filter expands the shape bbox, so we have to account that. And ideally we have to exclude shapes, not bboxes. Which means Bezier paths intersection detection. Which is also non-trivial.

Patches are welcome, since I'm not planning on working on this anytime soon.

virtualritz commented 3 years ago

I see the issue with blur but not Beziers. They have convex hull property. The worst that can happen is that you draw something outside the area of interest and those draw calls will end up not generating any pixels.

RazrFalcon commented 3 years ago

Patches are welcome.

virtualritz commented 3 years ago

On that note: if we had this, how hard would you reckon it would then be to parallelize rendering using e.g. rayon or the like?

RazrFalcon commented 3 years ago

Rendering of what and why?

virtualritz commented 3 years ago

Rendering of SVG via tiny-skia. I.e. you tile the canvas and have different cores fill different tiles. This issue is kind of a prerequisite of that. I.e. you need to know what is intersecting the resp. tile.

RazrFalcon commented 3 years ago

You want to draw on a single tiny-skia::Pixmap from multiple threads? This is not possible. And I'm not sure it will be possible ever.

virtualritz commented 3 years ago

The pixmap will be very large. A solution could be to make multiple pixmaps, one for each tile/thread and combine them, at the end.

RazrFalcon commented 3 years ago

This can be done by the caller.

Also, tiny-skia is limited to 8k x 8k images at the moment.

virtualritz commented 3 years ago

In any case – as you are not planning on adding any of this yourself, is it really important to understand my motivation?

It sprung from a thread with an engineer from Pixar. They have an SVG texture that is resolution unlimited, built-in in RenderMan. But the calculation of the texture tiles is not multi-threaded. And that is a detriment/issue. I.e. they do use a third part rasterizer and they're, in general, not made/engineered to run multi-threaded because the developers do not know about a use cases where this is a requirement.

virtualritz commented 3 years ago

This can be done by the caller.

Yeah, I guessed so.

Also, tiny-skia is limited to 8k x 8k images at the moment.

That's not an issue. Typical tiles will be more around 256x256 and they are held in a tile cache, in RAM. There is no real, final full size resolution for the case at hand. Each tile is a view into a full size texture at different resolutions. Maybe some view represents a zoom level where the full size image is a 100k pixels wide. But it will never be generated at this resolution as a whole.

RazrFalcon commented 3 years ago

I'm not questioning your motivation, I just don't have free time to work on my pet projects right now.

Anyway, the only complex task here is to determinate what should be rendered and what is not. And there is nothing in resvg right now to help you with it.

virtualritz commented 3 years ago

Hey, I didn't mean that to come across the wrong way. I do not expect any help here, just hints at obstacles as you gave me.

It's not on my plate yet but in about a month from now I will check how hard it is to sort nodes into a bounding hierarchy in resvg.

I guess the challenging bit is recursing through a node's tree and growing the bounds by the correct values for stuff like blurs etc. – what you mentioned.

RazrFalcon commented 3 years ago

A naive bbox intersection is very easy to implement. The resvg CLI already has it. But you have to properly render those nodes now. And resvg doesn't allow this right now. You have to modify render.rs first.

RazrFalcon commented 1 year ago

Out of scope.