Conversation
what am looking at? is this for 2x, 3x upscaling w/o blurring, on e.g. hires screens? |
|
Hey! Great work on this PR - we really need pixel ratio support for our terminal app (terX). I noticed that this PR doesn't include the fix for mouse selection coordinates in Here's what needs to be added to // The pixel_to_cell closure needs access to pixel_ratio
let pixel_to_cell = move |event: &web_sys::MouseEvent| -> Option<(u16, u16)> {
let x = event.offset_x() as f32;
let y = event.offset_y() as f32;
// offset_x/y are in CSS pixels, cell_width/height are in physical pixels
// Convert cell dimensions to CSS pixels for correct coordinate mapping
let css_cell_width = cell_width as f32 / pixel_ratio;
let css_cell_height = cell_height as f32 / pixel_ratio;
let col = (x / css_cell_width).floor() as u16;
let row = (y / css_cell_height).floor() as u16;
let (max_cols, max_rows) = *dimensions_ref.borrow();
if col < max_cols && row < max_rows { Some((col, row)) } else { None }
};Since we need this for our development, I'll implement the full fix (including dynamic DPR tracking for window moves between displays) in my fork. Happy to share the implementation once it's working! |
Combines: - pixel_ratio support from feat/pixel-ratio-complete (fand's PR junkdog#69) - mouse.rs fix for HiDPI coordinate conversion - selection_drag_threshold_ms for idle state fix Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
551a298 to
72e35b1
Compare
72e35b1 to
2fd2ebd
Compare
|
Hi folks, Thanks for the comments!! @junkdog @kofany |
i apologize if i came off as dismissive or short, i didn't mean it that way - but i see how it could be read as such. my bad.
https://github.com/be5invis/Iosevka looks pretty close to a pure terminal font, depending on resolution. the "problem" with bitmap fonts is that emoji still have to be rasterized from a ttf/otf, so it entails a bit more work to get it going, and would likely be processed into a nice work on the PR, i took a quick look - i'll delve deeper later today or tomorrow. a couple of questions:
|
|
Hi @junkdog ! Anyway, here are my answers to your comment. Upscaling the font
In this PR there's no distinction between dynamic/static atlas. In WebGL, the output is always calculated based on the logical canvas size ( I made a visualization to explain why we need to scale the viewport using device pixel ratio.
Say we have a font atlas baked with font size = 128px, cell size = 16px, and devicePixelRatio = 2.0. When rendered without considering pixel ratio,
This degrades quality at both steps. We can disable the bilinear filtering on the 2nd step with On the other hand, when rendered with pixel ratio, the browser renders the letter in the physical resolution, resulting in a better output quality. Non-integer pixel ratio
I'd say we should accept non-integer pixel ratio. In the current market, there are a lot of devices with non-integer pixel ratio screens. Also,
Let me know if there's any concerns 🙏 |
|
I'd mention that many other WebGL based libraries support
|
|
@kofany mouse_trim.mp4Maybe it was a problem in the commits I overwritten with force-push.. |
this can go in a different PR, but i was thinking that the dynamic font atlas could simply scale the font_size. the offscreen canvas and target canvas would need to have the pixel ratio overridden to 1.0 to render at native resolution.
but here you're downscaling from a 128x128 to a smaller target resolution; let's say the pixel ratio on some device is 1.26: a 10x16 glyph would be upscaled and rendered at 13x20, with some pretty nasty artifacts (this is true for the existing blurry version too ofc, but less noticeable)
since upscaling from lowres at non-integer scales produces artifacts (e.g. 10x16 to 13x20), my thinking was that we could maybe override the the canvas' pixel ratio to the nearest integer value. this would slightly change the terminal size but it would still look good when upscaling (although, it would snap between zoom levels). sorry for taking so long to review this - i'm pretty swamped this week, but i'll do a proper review on saturday, and then the plan is to do a new release once this PR is merged. |
junkdog
left a comment
There was a problem hiding this comment.
hi, thanks for the PR, impl looks good and addresses a real issue.
however, i'm hesitant about exposing raw pixel_ratio directly to users. for beamterm, this feels like an impl detail that should be managed internally. as it stands, users would need to:
- set up boilerplate to track
devicePixelRatiochanges (browser zoom, window moving between displays) - have a grasp of which values produce good results (integer vs fractional scaling artifacts with static atlases)
- understand the differences between static and dynamic atlases
i'd prefer a policy-based api where beamterm handles the complexity, something like:
enum PixelScaling { // (for example)
Off, // resizes to native resolution
Auto, // static: round to nears int, dynamic: scale font_size
Fixed(NonZero<u8>), // 1x, 2x, 3x
}beamterm would internally track devicePixelRatio changes (regardless of policy); the enum controls how each atlas type responds. PixelScaling::Auto would be the default (and most similar to how it works today, except w/o the blur)
i realize this is a larger scope than what you signed up for. so going forward, feel free to chime in other suggestions, but as i see it, our options are:
- if you're interested, you're more than welcome to continue working on the PR
- i can take over and build on your work (i'll credit you ofc)
- we merge a minimal version with just internal
Autoscaling
how do you want to proceed?
| .measure_performance(true) | ||
| .grid_id("container") | ||
| .enable_console_debug_api() | ||
| .enable_auto_pixel_ratio(), |
| /// Call this when `window.devicePixelRatio` changes | ||
| /// (e.g., moving window between displays with different DPI). | ||
| pub fn set_pixel_ratio(&mut self, pixel_ratio: f32) { | ||
| self.renderer.set_pixel_ratio(pixel_ratio); |
There was a problem hiding this comment.
this calls Renderer::resize but the terminal grid isn't resized (ref Terminal::resize)
have you given it any thought? |
|
Hi @junkdog ! Sorry for the late response... I didn't have time to work on this PR last week.
Yeah, that totally makes sense.
For this problem, I was leaning to fractional scaling because:
That said, that's a trade-off. And in beamterm, sticking to integer pixel ratio looks quite reasonable, given its cell-based rendering architecture. I tried the JS demo on the latest version and it's working perfectly! |



This PR adds support for high pixel ratio rendering.
It allows us to get crisper output in HiDPI environments like Retina MacBook or iPhone.
Changes
Terminal::set_pixel_ratio(),Renderer::set_pixel_ratio()BeamtermRenderer.setPixelRatio()is availableRenderer::resize()Screenshot
I edited
wave_effectexample (commit 2fd2ebd) to see the text and line edges quality.Note that we need to checkout the Ratzilla PR to run the demo properly:
ratatui/ratzilla#146
Both screenshots are taken on MacBookPro (window.devicePixelRatio = 2.0).
Before
Text, line edges and cell borders look blurry.
After
Edges are crisper.
We still see artifacts around border but it's antialias, not a bug.
Maybe we can use old-school bitmap fonts if we wanna avoid the antialias...?