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

Improved Text Rendering #10537

Merged
merged 7 commits into from
Nov 14, 2023
Merged

Conversation

ickshonpe
Copy link
Contributor

@ickshonpe ickshonpe commented Nov 13, 2023

Objective

The quality of Bevy's text rendering can vary wildly depending on the font, font size, pixel alignment and scale factor.

But this situation can be improved dramatically with some small adjustments.

Solution

  • Text node positions are rounded to the nearest physical pixel before rendering.
  • Each glyph texture has a 1-pixel wide transparent border added along its edges.

This means font atlases will use more memory because of the extra pixel of padding for each glyph but it's more than worth it I think (although glyph size is increased by 2 pixels on both axes, the net increase is 1 pixel as the font texture atlas's padding has been removed).

Results

Screenshots are from the 'ui' example with a scale factor of 1.5.

Things can get much uglier with the right font and worst scale factortm.

before

list-bad-text

after

good_list_text

Changelog

  • Font texture atlases are no longer padded.
  • Each glyph texture has a 1-pixel wide padding added along its edges.
  • Text node positions are rounded to the nearest physical pixel before rendering.

* Each glyph texture has a 1 pixel wide padding added along its edges.
* Text node positions are rounded to the nearest physical pixel before rendering.
Copy link
Contributor

@nicopap nicopap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a regression. I tried to run game_menu on 0.11.0 and it seems to have crisp fonts, while on 0.12.0 it looks ugly. This PR makes the font as crisp as in 0.11.0

crates/bevy_ui/src/render/mod.rs Outdated Show resolved Hide resolved
crates/bevy_text/src/font.rs Show resolved Hide resolved
crates/bevy_ui/src/render/mod.rs Outdated Show resolved Hide resolved
@nicopap nicopap added this to the 0.12.1 milestone Nov 13, 2023
@nicopap nicopap added A-UI Graphical user interfaces, styles, layouts, and widgets P-Regression Functionality that used to work but no longer does. Add a test for this! C-Bug An unexpected or incorrect behavior A-Text Rendering and layout for characters labels Nov 13, 2023
@ickshonpe
Copy link
Contributor Author

This fixes a regression. I tried to run game_menu on 0.11.0 and it seems to have crisp fonts, while on 0.12.0 it looks ugly. This PR makes the font as crisp as in 0.11.0

The difference is because the rounding changes in 0.12 caused the text's position to shift slightly so it was no longer pixel-aligned.
You should be able to get the same ugly text in 0.11.0 by playing around with the scale factor.

@nicoburns
Copy link
Contributor

Why is the padding change required? Does the rounding / pixel-grid fitting not fix this issue by itself?

@ickshonpe
Copy link
Contributor Author

ickshonpe commented Nov 13, 2023

Why is the padding change required? Does the rounding / pixel-grid fitting not fix this issue by itself?

Pixel alignment makes things look more consistent and clean, while the padding change fixes how some of the glyphs are cut-off with hard edges. It's particularly clear if you compare the r or m glyphs between the screenshots.

@nicopap nicopap added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Nov 13, 2023
@rparrett
Copy link
Contributor

Does this interact with the subpixel_glyph_atlas feature? I'd assume that text positions should not be rounded if that feature is enabled but I'm not actually sure how that works.

@alice-i-cecile alice-i-cecile removed the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Nov 14, 2023
@alice-i-cecile
Copy link
Member

Waiting until those two questions are answered before merging.

@ickshonpe
Copy link
Contributor Author

ickshonpe commented Nov 14, 2023

Does this interact with the subpixel_glyph_atlas feature? I'd assume that text positions should not be rounded if that feature is enabled but I'm not actually sure how that works.

This PR (accidentally) fixes the subpixel_glyph_atlas feature too.

The reason that text looks so bad is because bevy_text already implements pixel alignment for individual glyphs (unless subpixel adjustment is enabled). But Bevy's text rendering for both the UI and Text2d doesn't pixel-align the position where it draws the text. All the glyphs will be aligned with that position, so sometimes with a scale factor of 1 and integer coordinates it will be okay, but a lot of time it's a mess.

The subpixel_glyph_atlas feature also assumes that the text will be drawn pixel-aligned, and without that pixel-alignment you get blurred text.

Subpixel adjustment at a misaligned position:

list-misaligned-sp

Subpixel adjustment at a pixel-aligned position:

scrolling_list-sp

Comment on lines +625 to +627
.map(|window| window.scale_factor())
.unwrap_or(1.)
* ui_scale.0) as f32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would have worked. tbh re-using the same bind name would also be fine.

let window = windows.get_single();
let window_scale = window.map_or(1., |w| w.resolution.scale_factor());
let scale_factor = (window_scale * ui_scale.0) as f32;

@nicoburns nicoburns added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Nov 14, 2023
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Nov 14, 2023
@rparrett
Copy link
Contributor

The subpixel_glyph_atlas feature also assumes that the text will be drawn pixel-aligned, and without that pixel-alignment you get blurred text.

Okay, the implementation details of that feature are surprising to me but that all seems to make sense after staring at that PR a bit. The way that all works seems quite janky. It seems like this is a good fix for now.

Thanks for looking into that.

Merged via the queue into bevyengine:main with commit 0b0ef58 Nov 14, 2023
22 checks passed
@nicoburns
Copy link
Contributor

nicoburns commented Nov 14, 2023

Text is looking really good now!

cart pushed a commit that referenced this pull request Nov 30, 2023
# Objective

The quality of Bevy's text rendering can vary wildly depending on the
font, font size, pixel alignment and scale factor.

But this situation can be improved dramatically with some small
adjustments.

## Solution

* Text node positions are rounded to the nearest physical pixel before
rendering.
* Each glyph texture has a 1-pixel wide transparent border added along
its edges.

This means font atlases will use more memory because of the extra pixel
of padding for each glyph but it's more than worth it I think (although
glyph size is increased by 2 pixels on both axes, the net increase is 1
pixel as the font texture atlas's padding has been removed).

## Results

Screenshots are from the 'ui' example with a scale factor of 1.5. 

Things can get much uglier with the right font and worst scale
factor<sup>tm</sup>.

### before 
<img width="300" alt="list-bad-text"
src="https://github.com/bevyengine/bevy/assets/27962798/482b384d-8743-4bae-9a65-468ff1b4c301">

### after
<img width="300" alt="good_list_text"
src="https://github.com/bevyengine/bevy/assets/27962798/34323b0a-f714-47ba-9728-a59804987bc8">
 
---

## Changelog
* Font texture atlases are no longer padded.
* Each glyph texture has a 1-pixel wide padding added along its edges.
* Text node positions are rounded to the nearest physical pixel before
rendering.
rdrpenguin04 pushed a commit to rdrpenguin04/bevy that referenced this pull request Jan 9, 2024
# Objective

The quality of Bevy's text rendering can vary wildly depending on the
font, font size, pixel alignment and scale factor.

But this situation can be improved dramatically with some small
adjustments.

## Solution

* Text node positions are rounded to the nearest physical pixel before
rendering.
* Each glyph texture has a 1-pixel wide transparent border added along
its edges.

This means font atlases will use more memory because of the extra pixel
of padding for each glyph but it's more than worth it I think (although
glyph size is increased by 2 pixels on both axes, the net increase is 1
pixel as the font texture atlas's padding has been removed).

## Results

Screenshots are from the 'ui' example with a scale factor of 1.5. 

Things can get much uglier with the right font and worst scale
factor<sup>tm</sup>.

### before 
<img width="300" alt="list-bad-text"
src="https://github.com/bevyengine/bevy/assets/27962798/482b384d-8743-4bae-9a65-468ff1b4c301">

### after
<img width="300" alt="good_list_text"
src="https://github.com/bevyengine/bevy/assets/27962798/34323b0a-f714-47ba-9728-a59804987bc8">
 
---

## Changelog
* Font texture atlases are no longer padded.
* Each glyph texture has a 1-pixel wide padding added along its edges.
* Text node positions are rounded to the nearest physical pixel before
rendering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Text Rendering and layout for characters A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior P-Regression Functionality that used to work but no longer does. Add a test for this! S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants