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

Fix for vertical text bounds and alignment #9133

Merged
merged 12 commits into from Jul 13, 2023
58 changes: 39 additions & 19 deletions 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;
Expand Down Expand Up @@ -84,20 +84,7 @@ impl GlyphBrush {
})
.collect::<Result<Vec<_>, _>>()?;

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| &sections_data[index].3);

let mut positioned_glyphs = Vec::new();
for sg in glyphs {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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<T>,
) -> 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
}
45 changes: 6 additions & 39 deletions 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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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 &section_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(&section_glyphs, |index| &scaled_fonts[index]).size();

let glyphs = self.brush.process_glyphs(
section_glyphs,
Expand Down Expand Up @@ -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(&section_glyphs, |index| &self.scaled_fonts[index]).size()
}

pub fn compute_size(&self, bounds: Vec2) -> Vec2 {
Expand Down