diff --git a/crates/bevy_text/src/glyph_brush.rs b/crates/bevy_text/src/glyph_brush.rs index 9b17f09a7b..70fea30322 100644 --- a/crates/bevy_text/src/glyph_brush.rs +++ b/crates/bevy_text/src/glyph_brush.rs @@ -1,6 +1,6 @@ -use ab_glyph::{Font as _, FontArc, Glyph, ScaleFont as _}; +use ab_glyph::{Font as _, FontArc, Glyph, PxScaleFont, ScaleFont as _}; use bevy_asset::{Assets, Handle}; -use bevy_math::Vec2; +use bevy_math::{Rect, Vec2}; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; use bevy_utils::tracing::warn; @@ -84,20 +84,7 @@ impl GlyphBrush { }) .collect::, _>>()?; - let mut min_x = std::f32::MAX; - let mut min_y = std::f32::MAX; - let mut max_y = std::f32::MIN; - for sg in &glyphs { - let glyph = &sg.glyph; - - let scaled_font = sections_data[sg.section_index].3; - min_x = min_x.min(glyph.position.x); - min_y = min_y.min(glyph.position.y - scaled_font.ascent()); - max_y = max_y.max(glyph.position.y - scaled_font.descent()); - } - min_x = min_x.floor(); - min_y = min_y.floor(); - max_y = max_y.floor(); + let text_bounds = compute_text_bounds(&glyphs, |index| §ions_data[index].3); let mut positioned_glyphs = Vec::new(); for sg in glyphs { @@ -136,11 +123,15 @@ impl GlyphBrush { let glyph_rect = texture_atlas.textures[atlas_info.glyph_index]; let size = Vec2::new(glyph_rect.width(), glyph_rect.height()); - let x = bounds.min.x + size.x / 2.0 - min_x; + let x = bounds.min.x + size.x / 2.0 - text_bounds.min.x; let y = match y_axis_orientation { - YAxisOrientation::BottomToTop => max_y - bounds.max.y + size.y / 2.0, - YAxisOrientation::TopToBottom => bounds.min.y + size.y / 2.0 - min_y, + YAxisOrientation::BottomToTop => { + text_bounds.max.y - bounds.max.y + size.y / 2.0 + } + YAxisOrientation::TopToBottom => { + bounds.min.y + size.y / 2.0 - text_bounds.min.y + } }; let position = adjust.position(Vec2::new(x, y)); @@ -209,3 +200,32 @@ impl GlyphPlacementAdjuster { Vec2::new(self.0, 0.) + v } } + +/// Computes the minimal bounding rectangle for a block of text. +/// Ignores empty trailing lines. +pub(crate) fn compute_text_bounds<'a, T>( + section_glyphs: &[SectionGlyph], + get_scaled_font: impl Fn(usize) -> &'a PxScaleFont, +) -> bevy_math::Rect +where + T: ab_glyph::Font + 'a, +{ + let mut text_bounds = Rect { + min: Vec2::splat(std::f32::MAX), + max: Vec2::splat(std::f32::MIN), + }; + + for sg in section_glyphs { + let scaled_font = get_scaled_font(sg.section_index); + let glyph = &sg.glyph; + text_bounds = text_bounds.union(Rect { + min: Vec2::new(glyph.position.x, 0.), + max: Vec2::new( + glyph.position.x + scaled_font.h_advance(glyph.id), + glyph.position.y - scaled_font.descent(), + ), + }); + } + + text_bounds +} diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index e71e49c030..2d16ce03c3 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -1,4 +1,4 @@ -use ab_glyph::{PxScale, ScaleFont}; +use ab_glyph::PxScale; use bevy_asset::{Assets, Handle, HandleId}; use bevy_ecs::component::Component; use bevy_ecs::system::Resource; @@ -10,8 +10,9 @@ use bevy_utils::HashMap; use glyph_brush_layout::{FontId, GlyphPositioner, SectionGeometry, SectionText}; use crate::{ - error::TextError, glyph_brush::GlyphBrush, scale_value, BreakLineOn, Font, FontAtlasSet, - FontAtlasWarning, PositionedGlyph, TextAlignment, TextSection, TextSettings, YAxisOrientation, + compute_text_bounds, error::TextError, glyph_brush::GlyphBrush, scale_value, BreakLineOn, Font, + FontAtlasSet, FontAtlasWarning, PositionedGlyph, TextAlignment, TextSection, TextSettings, + YAxisOrientation, }; #[derive(Default, Resource)] @@ -84,24 +85,7 @@ impl TextPipeline { return Ok(TextLayoutInfo::default()); } - let mut min_x: f32 = std::f32::MAX; - let mut min_y: f32 = std::f32::MAX; - let mut max_x: f32 = std::f32::MIN; - let mut max_y: f32 = std::f32::MIN; - - for sg in §ion_glyphs { - let scaled_font = scaled_fonts[sg.section_index]; - let glyph = &sg.glyph; - // The fonts use a coordinate system increasing upwards so ascent is a positive value - // and descent is negative, but Bevy UI uses a downwards increasing coordinate system, - // so we have to subtract from the baseline position to get the minimum and maximum values. - min_x = min_x.min(glyph.position.x); - min_y = min_y.min(glyph.position.y - scaled_font.ascent()); - max_x = max_x.max(glyph.position.x + scaled_font.h_advance(glyph.id)); - max_y = max_y.max(glyph.position.y - scaled_font.descent()); - } - - let size = Vec2::new(max_x - min_x, max_y - min_y); + let size = compute_text_bounds(§ion_glyphs, |index| &scaled_fonts[index]).size(); let glyphs = self.brush.process_glyphs( section_glyphs, @@ -229,24 +213,7 @@ impl TextMeasureInfo { .line_breaker(self.linebreak_behaviour) .calculate_glyphs(&self.fonts, &geom, sections); - let mut min_x: f32 = std::f32::MAX; - let mut min_y: f32 = std::f32::MAX; - let mut max_x: f32 = std::f32::MIN; - let mut max_y: f32 = std::f32::MIN; - - for sg in section_glyphs { - let scaled_font = &self.scaled_fonts[sg.section_index]; - let glyph = &sg.glyph; - // The fonts use a coordinate system increasing upwards so ascent is a positive value - // and descent is negative, but Bevy UI uses a downwards increasing coordinate system, - // so we have to subtract from the baseline position to get the minimum and maximum values. - min_x = min_x.min(glyph.position.x); - min_y = min_y.min(glyph.position.y - scaled_font.ascent()); - max_x = max_x.max(glyph.position.x + scaled_font.h_advance(glyph.id)); - max_y = max_y.max(glyph.position.y - scaled_font.descent()); - } - - Vec2::new(max_x - min_x, max_y - min_y) + compute_text_bounds(§ion_glyphs, |index| &self.scaled_fonts[index]).size() } pub fn compute_size(&self, bounds: Vec2) -> Vec2 {