Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Images are a little blurry #494

Closed
kodo-pp opened this issue Aug 24, 2020 · 6 comments
Closed

Images are a little blurry #494

kodo-pp opened this issue Aug 24, 2020 · 6 comments
Labels
bug Something isn't working rendering

Comments

@kodo-pp
Copy link

kodo-pp commented Aug 24, 2020

It looks like the Image widget draws images in such a way that some parts of the image are a little blurry (noticeable when images contain thin high-contrast lines), while other parts are displayed just fine.

Here is a minimal reproducible example:

Archive with this example: iced-blurry-images.tar.gz

use iced::*;

struct App {}
impl Application for App {
    type Message = ();
    type Executor = executor::Default;
    type Flags = ();

    fn new(_flags: ()) -> (App, Command<()>) {
        (App {}, Command::none())
    }

    fn title(&self) -> String {
        "Window Title".to_owned()
    }

    fn update(&mut self, _message: ()) -> Command<()> {
        Command::none()
    }

    // The interesting part starts here
    fn view(&mut self) -> Element<()> {
        let image = Image::new("grid.png");
        let container = Container::new(image)
            .center_x()
            .center_y()
            .width(Length::Fill)
            .height(Length::Fill);
        container.into()
    }
}

fn main() {
    App::run(Settings::default());
}

Cargo.toml dependencies section:

[dependencies]
iced = { version = "0.1", features = ["image"] }

File grid.png:
image

Result (screenshots):

  • Window size is not changed:
    image

  • Window width is changed by ~1px:
    image

(Screenshots are clickable and you should open them in the original size to clearly see what they show)

Some lines on the screenshots are 1px wide (as they should be), but other lines are drawn as if they pass right between the pixel rows (2px wide and the color is gray, not black). It looks like the image size does not matter, although I haven't tested it thoroughly.

This is reproducible even without a Container holding an Image (that is, if we directly return image.into() from draw), but I use it to see how the image on the screen changes when the window is resized (and the Image widget is moved).

At first, my guess was that the coordinates of the top-left corner of the image might be fractional (and, likewise, the coordinates of the pixels of this image), but it doesn't explain why some lines are drawn normally, while others are blurry. However, it might be one of the factors responsible for this, since when the image is centered and the window width is changed by 1px, the image position is changed by 0.5px, which causes different lines to become "blurry". Actually, in an ideal case images should probably be aligned with the pixel grid, so that they could display their content without extra "blurriness" or similar distortions.

@kodo-pp
Copy link
Author

kodo-pp commented Aug 25, 2020

I found out that applying the following patch to iced and then recompiling the same example code against the patched version seems to change the way the image is drawn back to normal, although it introduces certain artifacts around some images (see the screenshots):

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 49f1d29..6d78807 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -554,8 +554,8 @@ fn add_instance(
             (y as f32 + 0.5) / atlas::SIZE as f32,
         ],
         _size_in_atlas: [
-            (width as f32 - 1.0) / atlas::SIZE as f32,
-            (height as f32 - 1.0) / atlas::SIZE as f32,
+            width as f32 / atlas::SIZE as f32,
+            height as f32 / atlas::SIZE as f32,
         ],
         _layer: layer as u32,
     };

Please note that I wrote this patch without fully understanding what the code itself does. My edits were based purely on the intuition that the image distortions I have described are likely if the image dimensions are calculated 1px wrong. This means that this patch is obviously not ready to be merged, but it can act as a starting point and, possibly, a clue to the root cause of the problem.

I played with different images and got the following results (using the patched version of iced):

  • Original grid.png, window size unchanged:
    image
    (comment: the image seems to be not aligned with the pixel grid, but at least all the lines are blurred consistently; also there is an extra border on the right and on the bottom — the artifacts I mentioned)
  • Original grid.png, window size adjusted so that the image is aligned with the pixel grid:
    image
    (comment: everything is displayed right as it should be)
  • circle.png — a green circle with transparent background, window size unchanged:
    image
    (comment: as the window is resized, the circle's edges on the screen become sharper or less sharp, based on whether or not the image becomes aligned with the pixel grid; however, the "extra border" never appears, which is probably due to the transparent background)

circle.png is this image: circle

The artifacts suggest that this patch might break some assumptions which the internals of the renderer rely on. However, I cannot be sure for I haven't examined the renderer's source code in detail (it takes some time to really understand what the code does, and there's quite a lot of it). If I have enough time and determination, I might possibly try to investigate this issue further, but it would probably be better if someone familiar with the internals of the renderer looked at this spot of code (the one that fills _size_in_atlas with width - 1.0 and height - 1.0) — something seems to be wrong here.

P.S. I think that there still should be a way to make an image aligned with the pixel grid (so that this won't be influenced by centering or something like that), for example something like Image::new(...).pixel_grid_aligned() method. It is slightly related to the original problem, but may be worth a separate issue. I would appreciate your opinion on this point also.

@hecrj hecrj added the bug Something isn't working label Aug 27, 2020
@arctic-alpaca

This comment has been minimized.

@arctic-alpaca

This comment has been minimized.

@arctic-alpaca

This comment has been minimized.

@hecrj
Copy link
Member

hecrj commented Sep 18, 2020

Let's not conflate things here! Image and Canvas use completely different rendering pipelines.

The original problem is probably caused by the image pipeline using bilinear filtering during texture sampling when the pixels of the image are not completely aligned with the pixel grid. Transparency issues may be related to #476.

The second problem is the result of using a generic anti-aliasing strategy (MSAA) in the triangle pipeline after tessellation.

It's also important to note that iced uses logical coordinates. There is currently no consistent way to align primitives to the pixel grid from user code, as the positions could be multiplied by a non-integral DPI factor during rendering in some environments.


For the first issue, we should probably always align images to the physical pixel grid while rendering, although blurriness may still happen during magnification or minification (mipmapping may be the answer here).

For the second issue, we will eventually need a better path rasterizer! I mentioned this when I first implemented the Canvas widget in #193.

@hecrj
Copy link
Member

hecrj commented Jan 19, 2022

Closing in favor of #557.

@hecrj hecrj closed this as completed Jan 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working rendering
Projects
None yet
Development

No branches or pull requests

3 participants