From 0a40b16bd410aa9ac882c4c2ffe2cb50acdcd0ff Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 1 Apr 2024 15:22:47 +0200 Subject: [PATCH] Fix blurry rendering in some browsers (#4299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Closes https://github.com/emilk/egui/issues/4241 I would love some more testers of this. I'm not sure if we really need the round-to-even code, but I'm hesitant to out-right revert https://github.com/emilk/egui/pull/151 when I cannot reproduce its problem. Keeping it seems quite safe though. --- # Testing Checkout the branch and run: * `./scripts/start_server.sh` * `./scripts/build_demo_web.sh` and then open `http://localhost:8888/index.html#Rendering` * `./scripts/build_demo_web.sh --wgpu` and then open `http://localhost:8888/index.html#Rendering` Check the "Rendering test" that the squares in the pixel alignment test are perfectly sharp, like this: Screenshot 2024-04-01 at 13 27 20 If it looks something like this, something is WRONG: Screenshot 2024-04-01 at 13 29 07 Please try it on different zoom levels in different browsers, and if possible on different monitors with different native dpi scaling. Report back the results! ### Mac I have tested on a high-DPI Mac: * Chromium (Brave): ✅ Can reproduce problem on `master`, and it's now fixed * Firefox: ✅ Can reproduce problem on `master`, and it's now fixed * Safari: ❌ Can't get it to work; giving up for now --- crates/eframe/src/web/mod.rs | 63 +++++++++++++++++------------------- web_demo/index.html | 7 +++- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index a322a530..566c9b03 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -130,51 +130,46 @@ fn resize_canvas_to_screen_size( ) -> Option<()> { let parent = canvas.parent_element()?; - // Prefer the client width and height so that if the parent - // element is resized that the egui canvas resizes appropriately. - let width = parent.client_width(); - let height = parent.client_height(); - - let canvas_real_size = Vec2 { - x: width as f32, - y: height as f32, - }; - - if width <= 0 || height <= 0 { - log::error!("egui canvas parent size is {}x{}. Try adding `html, body {{ height: 100%; width: 100% }}` to your CSS!", width, height); - } - + // In this function we use "pixel" to mean physical pixel, + // and "point" to mean "logical CSS pixel". let pixels_per_point = native_pixels_per_point(); - let max_size_pixels = pixels_per_point * max_size_points; + // Prefer the client width and height so that if the parent + // element is resized that the egui canvas resizes appropriately. + let parent_size_points = Vec2 { + x: parent.client_width() as f32, + y: parent.client_height() as f32, + }; - let canvas_size_pixels = pixels_per_point * canvas_real_size; - let canvas_size_pixels = canvas_size_pixels.min(max_size_pixels); - let canvas_size_points = canvas_size_pixels / pixels_per_point; - - // Make sure that the height and width are always even numbers. - // otherwise, the page renders blurry on some platforms. - // See https://github.com/emilk/egui/issues/103 - fn round_to_even(v: f32) -> f32 { - (v / 2.0).round() * 2.0 + if parent_size_points.x <= 0.0 || parent_size_points.y <= 0.0 { + log::error!("The parent element of the egui canvas is {}x{}. Try adding `html, body {{ height: 100%; width: 100% }}` to your CSS!", parent_size_points.x, parent_size_points.y); } + // We take great care here to ensure the rendered canvas aligns + // perfectly to the physical pixel grid, lest we get blurry text. + // At the time of writing, we get pixel perfection on Chromium and Firefox on Mac, + // but Desktop Safari will be blurry on most zoom levels. + // See https://github.com/emilk/egui/issues/4241 for more. + + let canvas_size_pixels = pixels_per_point * parent_size_points.min(max_size_points); + + // Make sure that the size is always an even number of pixels, + // otherwise, the page renders blurry on some platforms. + // See https://github.com/emilk/egui/issues/103 + let canvas_size_pixels = (canvas_size_pixels / 2.0).round() * 2.0; + + let canvas_size_points = canvas_size_pixels / pixels_per_point; + canvas .style() - .set_property( - "width", - &format!("{}px", round_to_even(canvas_size_points.x)), - ) + .set_property("width", &format!("{}px", canvas_size_points.x)) .ok()?; canvas .style() - .set_property( - "height", - &format!("{}px", round_to_even(canvas_size_points.y)), - ) + .set_property("height", &format!("{}px", canvas_size_points.y)) .ok()?; - canvas.set_width(round_to_even(canvas_size_pixels.x) as u32); - canvas.set_height(round_to_even(canvas_size_pixels.y) as u32); + canvas.set_width(canvas_size_pixels.x as u32); + canvas.set_height(canvas_size_pixels.y as u32); Some(()) } diff --git a/web_demo/index.html b/web_demo/index.html index afe4cc64..142355b4 100644 --- a/web_demo/index.html +++ b/web_demo/index.html @@ -37,7 +37,12 @@ width: 100%; } - /* Position canvas in center-top: */ + /* Position canvas in center-top. + This is rather arbitrarily chosen. + In particular, it seems like both Chromium and Firefox will still align + the canvas on the physical pixel grid, which is required to get + pixel-perfect (non-blurry) rendering in egui. + See https://github.com/emilk/egui/issues/4241 for more */ canvas { margin-right: auto; margin-left: auto;