emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
22.52k stars 1.61k forks source link

Multiple svg images render blurry #3501

Open thequver opened 1 year ago

thequver commented 1 year ago

Describe the bug When adding multiple svg images from the same source but with different size, the latter image renders blurry as if it takes the texture from the first added (smaller) image.

To Reproduce Code to reproduce:

                const IMG_SRC: egui::ImageSource = egui::include_image!("./img.svg");
                ui.add_sized(egui::Vec2{x: 20.0, y: 20.0}, egui::Image::new(IMG_SRC));
                ui.add_sized(egui::Vec2{x: 200.0, y: 200.0}, egui::Image::new(IMG_SRC));

Image i used to test: img

Expected behavior Both vector images must look sharp and render at respective for its size resolution

Screenshots Screenshot

Desktop (please complete the following information):

Additional context Reproduced on win11, android 10, eframe, egui-winit + egui-wgpu implementation, egui 0.23.0, master branch (as the time of writing)

chriscate commented 1 year ago

This appears to be due to the the caching done by the ImageLoader. If you call ctx.forget_image(IMG_SRC.uri().unwrap()); between your two ui.add_sized() calls it will work as expected based on my testing. There is also a TODO here for automatic cache eviction.

If anyone has guidance on how to implement that automatic eviction I'd be happy to take a shot at it.

YgorSouza commented 9 months ago

I was looking into this, and found that the SVG loader actually includes the size hint as part of its key:

https://github.com/emilk/egui/blob/5cf99c6308e36b5f36861accea6835f190924cce/crates/egui_extras/src/loaders/svg_loader.rs#L12-L15

Which would solve this problem, but also sounds kind of dangerous, because if you have an SVG that fills the available space, you would end up with hundreds of copies at different sizes as the UI is resized.

But in any case, it isn't working as intended, because the image loader is called by the texture loader, which does not take the size into account.

https://github.com/emilk/egui/blob/5cf99c6308e36b5f36861accea6835f190924cce/crates/egui/src/load/texture_loader.rs#L13-L24

So one way to work around this would be to copy this default texture loader to your code and change it to take the size into account like the SVG loader does, then add it to the context using add_texture_loader. Maybe this should be added to egui itself, but it seems like it would be hard to find a balance between CPU and RAM usage and image quality that would work for every use case.

molenick commented 6 months ago

I'm experiencing this as well with the SvgLoader and am trying to find a good interim solution. @YgorSouza's post has me on the right track, instead of using a TextureLoader I'm applying the same line of thinking to create a custom ImageLoader copied from egui_extras SvgLoader.

So far I've noticed the following behaviors from debugging the caching of my custom svg loader:

I don't think anything special is happening here on my end, I'm just using the painter api to paint into a rectangle. I think the next thing to wrap my head around:

molenick commented 6 months ago

Ok, I see what I was missing: TextureLoader is the abstraction level that paint uses to cache an svg.

chianti-ga commented 3 months ago

any news about this issue ?

molenick commented 3 months ago

Hi @chianti-ga I was able to work around this issue by implementing my own loaders where I changed the cache-key to represent both file uri and rendered size, so that once I rendered a svg at a given size it remains cached unless otherwise forgotten.

chianti-ga commented 3 months ago

Hi @chianti-ga I was able to work around this issue by implementing my own loaders where I changed the cache-key to represent both file uri and rendered size, so that once I rendered a svg at a given size it remains cached unless otherwise forgotten.

@molenick why not submit a pull request with this solution? I think it's a straightforward way to address the issue.

molenick commented 3 months ago

@molenick I see my solution as being custom to the need of my project, and after working with it it seems to me like the Loader API is the intended api to customize caching behavior to suit your app's need.

However it seems like this is a common use case given the popularity of this issue, so if the maintainers think this is a worthwhile addition I'd be happy to contribute.

I think the general behavior of this loader is "cache by uri and render size" instead of "cache by uri" and is pretty simple to write. I think there are a lot of people who "just want to use egui" and are surprised by the default caching behavior so this could be a good change for users that want to render svg at different size without worrying about egui's caching subsystem.

molenick commented 3 months ago

@chianti-ga Whoops, realized I responded to myself up in that last comment

barries commented 1 month ago

Yah, egui's current behavior it's clearly buggy--images should not render blurry or not based on what size you happen to "load" them at first. Image loading should be size agnostic, then image rendering should take size into account, and the resulting textures should be cached internally with size as part of the key.

Having to work around this with a lot of custom code is probably not specific to your app, @molenick, although perhaps the specific way you do it is.