Skip to content

Commit 67406cd

Browse files
authored
Update cosmic-text to 0.16 (#22308)
# Objective Update the Cosmic Text dependency to 0.16. Fixes #22191 ## Solution There aren't any breaking changes with Cosmic Text version 0.16, but it does add font-hinting support. This is controlled in Bevy with a new component `FontHinting`. The `Text` component, which is normally used with physical pixel aligned text, now requires `FontHinting::Enabled`, and `Text2d`, which normally isn't pixel aligned, now requires `FontHinting::Disabled`. Hinting needs to be set per text block, not per text span entity, so it wouldn't be appropriate to add it as a field on `TextFont`. ## Testing `testbed_ui`'s text scene in main with cosmic-text 15: <img width="1924" height="1127" alt="cosmic-15" src="https://github.com/user-attachments/assets/0ee44e38-07b0-411b-9102-b7d28de0ffcd" /> The font names are meant to be rendered in their corresponding fonts but, as you can see, some names are rendered using the incorrect font. This is due to the ascii fastpath bug in cosmic-text 15, which is fixed in 16. `testbed_ui`'s text scene on this branch with cosmic-text 16: <img width="1924" height="1127" alt="cosmic-16" src="https://github.com/user-attachments/assets/549f9a21-e24d-422d-a9e2-eac902b2e054" /> Now each font's name is rendered using the correct font.
1 parent 4189ba0 commit 67406cd

File tree

6 files changed

+55
-17
lines changed

6 files changed

+55
-17
lines changed

crates/bevy_sprite/src/text2d.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ use bevy_image::prelude::*;
2020
use bevy_math::{FloatOrd, Vec2, Vec3};
2121
use bevy_reflect::{prelude::ReflectDefault, Reflect};
2222
use bevy_text::{
23-
ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSet, LineBreak, LineHeight, SwashCache,
24-
TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextPipeline,
25-
TextReader, TextRoot, TextSpanAccess, TextWriter,
23+
ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSet, FontHinting, LineBreak, LineHeight,
24+
SwashCache, TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo,
25+
TextPipeline, TextReader, TextRoot, TextSpanAccess, TextWriter,
2626
};
2727
use bevy_transform::components::Transform;
2828
use core::any::TypeId;
@@ -88,7 +88,9 @@ use core::any::TypeId;
8888
Anchor,
8989
Visibility,
9090
VisibilityClass,
91-
Transform
91+
Transform,
92+
// Disable hinting as `Text2d` text is not always pixel-aligned
93+
FontHinting::Disabled
9294
)]
9395
#[component(on_add = visibility::add_visibility_class::<Sprite>)]
9496
pub struct Text2d(pub String);
@@ -175,6 +177,7 @@ pub fn update_text2d_layout(
175177
Ref<TextBounds>,
176178
&mut TextLayoutInfo,
177179
&mut ComputedTextBlock,
180+
Ref<FontHinting>,
178181
)>,
179182
text_font_query: Query<&TextFont>,
180183
mut text_reader: Text2dReader,
@@ -198,7 +201,7 @@ pub fn update_text2d_layout(
198201
let mut previous_scale_factor = 0.;
199202
let mut previous_mask = &RenderLayers::none();
200203

201-
for (entity, maybe_entity_mask, block, bounds, mut text_layout_info, mut computed) in
204+
for (entity, maybe_entity_mask, block, bounds, mut text_layout_info, mut computed, hinting) in
202205
&mut text_query
203206
{
204207
let entity_mask = maybe_entity_mask.unwrap_or_default();
@@ -222,6 +225,7 @@ pub fn update_text2d_layout(
222225

223226
let text_changed = scale_factor != text_layout_info.scale_factor
224227
|| block.is_changed()
228+
|| hinting.is_changed()
225229
|| computed.needs_rerender()
226230
|| (!reprocess_queue.is_empty() && reprocess_queue.remove(&entity));
227231

@@ -248,6 +252,7 @@ pub fn update_text2d_layout(
248252
scale_factor as f64,
249253
&mut computed,
250254
&mut font_system,
255+
*hinting,
251256
) {
252257
Err(TextError::NoSuchFont) => {
253258
// There was an error processing the text layout.

crates/bevy_text/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev", default-fea
3030

3131
# other
3232
wgpu-types = { version = "27", default-features = false }
33-
cosmic-text = { version = "0.15", features = ["shape-run-cache"] }
33+
cosmic-text = { version = "0.16", features = ["shape-run-cache"] }
3434
thiserror = { version = "2", default-features = false }
3535
serde = { version = "1", features = ["derive"] }
3636
smallvec = { version = "1", default-features = false }

crates/bevy_text/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ pub use text_access::*;
5959
pub mod prelude {
6060
#[doc(hidden)]
6161
pub use crate::{
62-
Font, FontWeight, Justify, LineBreak, Strikethrough, StrikethroughColor, TextColor,
63-
TextError, TextFont, TextLayout, TextSpan, Underline, UnderlineColor,
62+
Font, FontHinting, FontWeight, Justify, LineBreak, Strikethrough, StrikethroughColor,
63+
TextColor, TextError, TextFont, TextLayout, TextSpan, Underline, UnderlineColor,
6464
};
6565
}
6666

crates/bevy_text/src/pipeline.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ use bevy_math::{Rect, UVec2, Vec2};
1616
use bevy_platform::collections::HashMap;
1717
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
1818

19-
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
20-
2119
use crate::{
2220
add_glyph_to_atlas, error::TextError, get_glyph_atlas_info, ComputedTextBlock, Font,
23-
FontAtlasKey, FontAtlasSet, FontSmoothing, Justify, LineBreak, LineHeight, PositionedGlyph,
24-
TextBounds, TextEntity, TextFont, TextLayout,
21+
FontAtlasKey, FontAtlasSet, FontHinting, FontSmoothing, Justify, LineBreak, LineHeight,
22+
PositionedGlyph, TextBounds, TextEntity, TextFont, TextLayout,
2523
};
24+
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
2625

2726
/// A wrapper resource around a [`cosmic_text::FontSystem`]
2827
///
@@ -101,6 +100,7 @@ impl TextPipeline {
101100
scale_factor: f64,
102101
computed: &mut ComputedTextBlock,
103102
font_system: &mut CosmicFontSystem,
103+
hinting: FontHinting,
104104
) -> Result<(), TextError> {
105105
computed.needs_rerender = false;
106106

@@ -192,6 +192,9 @@ impl TextPipeline {
192192
// Update the buffer.
193193
let buffer = &mut computed.buffer;
194194

195+
// Set the metrics hinting strategy
196+
buffer.set_hinting(font_system, hinting.into());
197+
195198
buffer.set_wrap(
196199
font_system,
197200
match linebreak {
@@ -248,6 +251,7 @@ impl TextPipeline {
248251
layout: &TextLayout,
249252
computed: &mut ComputedTextBlock,
250253
font_system: &mut CosmicFontSystem,
254+
hinting: FontHinting,
251255
) -> Result<TextMeasureInfo, TextError> {
252256
const MIN_WIDTH_CONTENT_BOUNDS: TextBounds = TextBounds::new_horizontal(0.0);
253257

@@ -264,6 +268,7 @@ impl TextPipeline {
264268
scale_factor,
265269
computed,
266270
font_system,
271+
hinting,
267272
)?;
268273

269274
let buffer = &mut computed.buffer;

crates/bevy_text/src/text.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,25 @@ pub fn detect_text_needs_rerender<Root: Component>(
847847
}
848848
}
849849
}
850+
851+
#[derive(Component, Debug, Copy, Clone, Default, Reflect, PartialEq)]
852+
#[reflect(Component, Default, Debug, Clone, PartialEq)]
853+
/// Font hinting strategy.
854+
///
855+
/// <https://docs.rs/cosmic-text/latest/cosmic_text/enum.Hinting.html>
856+
pub enum FontHinting {
857+
#[default]
858+
/// Glyphs will have subpixel coordinates.
859+
Disabled,
860+
/// Glyphs will be snapped to integral coordinates in the X-axis during layout.
861+
Enabled,
862+
}
863+
864+
impl From<FontHinting> for cosmic_text::Hinting {
865+
fn from(value: FontHinting) -> Self {
866+
match value {
867+
FontHinting::Disabled => cosmic_text::Hinting::Disabled,
868+
FontHinting::Enabled => cosmic_text::Hinting::Enabled,
869+
}
870+
}
871+
}

crates/bevy_ui/src/widget/text.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use bevy_image::prelude::*;
1818
use bevy_math::Vec2;
1919
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
2020
use bevy_text::{
21-
ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSet, LineBreak, LineHeight, SwashCache,
22-
TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextMeasureInfo,
23-
TextPipeline, TextReader, TextRoot, TextSpanAccess, TextWriter,
21+
ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSet, FontHinting, LineBreak, LineHeight,
22+
SwashCache, TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo,
23+
TextMeasureInfo, TextPipeline, TextReader, TextRoot, TextSpanAccess, TextWriter,
2424
};
2525
use taffy::style::AvailableSpace;
2626
use tracing::error;
@@ -102,7 +102,9 @@ impl Default for TextNodeFlags {
102102
TextColor,
103103
LineHeight,
104104
TextNodeFlags,
105-
ContentSize
105+
ContentSize,
106+
// Enable hinting as UI text is normally pixel-aligned.
107+
FontHinting::Enabled
106108
)]
107109
pub struct Text(pub String);
108110

@@ -244,6 +246,7 @@ pub fn measure_text_system(
244246
&mut ComputedTextBlock,
245247
Ref<ComputedUiRenderTargetInfo>,
246248
&ComputedNode,
249+
Ref<FontHinting>,
247250
),
248251
With<Node>,
249252
>,
@@ -259,6 +262,7 @@ pub fn measure_text_system(
259262
mut computed,
260263
computed_target,
261264
computed_node,
265+
hinting,
262266
) in &mut text_query
263267
{
264268
// Note: the ComputedTextBlock::needs_rerender bool is cleared in create_text_measure().
@@ -267,7 +271,8 @@ pub fn measure_text_system(
267271
< (computed_target.scale_factor() - computed_node.inverse_scale_factor.recip()).abs()
268272
|| computed.needs_rerender()
269273
|| text_flags.needs_measure_fn
270-
|| content_size.is_added())
274+
|| content_size.is_added()
275+
|| hinting.is_changed())
271276
{
272277
continue;
273278
}
@@ -280,6 +285,7 @@ pub fn measure_text_system(
280285
&block,
281286
computed.as_mut(),
282287
&mut font_system,
288+
*hinting,
283289
) {
284290
Ok(measure) => {
285291
if block.linebreak == LineBreak::NoWrap {

0 commit comments

Comments
 (0)