HIDPI Text (#1132)
HIDPI Text * add more operator overloads to `bevy::math::Size` * render UI text at physical resolution
This commit is contained in:
		
							parent
							
								
									2754a9dde8
								
							
						
					
					
						commit
						6531fcdfd2
					
				@ -1,6 +1,6 @@
 | 
			
		||||
use bevy_reflect::Reflect;
 | 
			
		||||
use glam::Vec2;
 | 
			
		||||
use std::ops::{Add, AddAssign};
 | 
			
		||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 | 
			
		||||
 | 
			
		||||
/// A two dimensional "size" as defined by a width and height
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Debug, Reflect)]
 | 
			
		||||
@ -81,3 +81,102 @@ where
 | 
			
		||||
        self.height += rhs.y;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> Sub<Vec2> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Sub<f32, Output = T>,
 | 
			
		||||
{
 | 
			
		||||
    type Output = Size<T>;
 | 
			
		||||
 | 
			
		||||
    fn sub(self, rhs: Vec2) -> Self::Output {
 | 
			
		||||
        Self {
 | 
			
		||||
            width: self.width - rhs.x,
 | 
			
		||||
            height: self.height - rhs.y,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> SubAssign<Vec2> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: SubAssign<f32>,
 | 
			
		||||
{
 | 
			
		||||
    fn sub_assign(&mut self, rhs: Vec2) {
 | 
			
		||||
        self.width -= rhs.x;
 | 
			
		||||
        self.height -= rhs.y;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> Mul<f32> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Mul<f32, Output = T>,
 | 
			
		||||
{
 | 
			
		||||
    type Output = Size<T>;
 | 
			
		||||
 | 
			
		||||
    fn mul(self, rhs: f32) -> Self::Output {
 | 
			
		||||
        Self::Output {
 | 
			
		||||
            width: self.width * rhs,
 | 
			
		||||
            height: self.height * rhs,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> MulAssign<f32> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: MulAssign<f32>,
 | 
			
		||||
{
 | 
			
		||||
    fn mul_assign(&mut self, rhs: f32) {
 | 
			
		||||
        self.width *= rhs;
 | 
			
		||||
        self.height *= rhs;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> Div<f32> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: Div<f32, Output = T>,
 | 
			
		||||
{
 | 
			
		||||
    type Output = Size<T>;
 | 
			
		||||
 | 
			
		||||
    fn div(self, rhs: f32) -> Self::Output {
 | 
			
		||||
        Self::Output {
 | 
			
		||||
            width: self.width / rhs,
 | 
			
		||||
            height: self.height / rhs,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Reflect> DivAssign<f32> for Size<T>
 | 
			
		||||
where
 | 
			
		||||
    T: DivAssign<f32>,
 | 
			
		||||
{
 | 
			
		||||
    fn div_assign(&mut self, rhs: f32) {
 | 
			
		||||
        self.width /= rhs;
 | 
			
		||||
        self.height /= rhs;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn size_ops() {
 | 
			
		||||
        type SizeF = Size<f32>;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            SizeF::new(10., 10.) + Vec2::new(10., 10.),
 | 
			
		||||
            SizeF::new(20., 20.)
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            SizeF::new(20., 20.) - Vec2::new(10., 10.),
 | 
			
		||||
            SizeF::new(10., 10.)
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(SizeF::new(10., 10.) * 2., SizeF::new(20., 20.));
 | 
			
		||||
        assert_eq!(SizeF::new(20., 20.) / 2., SizeF::new(10., 10.));
 | 
			
		||||
 | 
			
		||||
        let mut size = SizeF::new(10., 10.);
 | 
			
		||||
 | 
			
		||||
        size += Vec2::new(10., 10.);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(size, SizeF::new(20., 20.));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,7 @@ impl Default for TextStyle {
 | 
			
		||||
pub struct DrawableText<'a> {
 | 
			
		||||
    pub render_resource_bindings: &'a mut RenderResourceBindings,
 | 
			
		||||
    pub position: Vec3,
 | 
			
		||||
    pub scale_factor: f32,
 | 
			
		||||
    pub style: &'a TextStyle,
 | 
			
		||||
    pub text_glyphs: &'a Vec<PositionedGlyph>,
 | 
			
		||||
    pub msaa: &'a Msaa,
 | 
			
		||||
@ -105,7 +106,20 @@ impl<'a> Drawable for DrawableText<'a> {
 | 
			
		||||
                color: self.style.color,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let transform = Mat4::from_translation(self.position + tv.position.extend(0.));
 | 
			
		||||
            // To get the rendering right for non-one scaling factors, we need
 | 
			
		||||
            // the sprite to be drawn in "physical" coordinates. This is because
 | 
			
		||||
            // the shader uses the size of the sprite to control the size on
 | 
			
		||||
            // screen. To accomplish this we make the sprite transform
 | 
			
		||||
            // convert from physical coordinates to logical coordinates in
 | 
			
		||||
            // addition to altering the origin. Since individual glyphs will
 | 
			
		||||
            // already be in physical coordinates, we just need to convert the
 | 
			
		||||
            // overall position to physical coordinates to get the sprites
 | 
			
		||||
            // physical position.
 | 
			
		||||
 | 
			
		||||
            let transform = Mat4::from_scale(Vec3::splat(1. / self.scale_factor))
 | 
			
		||||
                * Mat4::from_translation(
 | 
			
		||||
                    self.position * self.scale_factor + tv.position.extend(0.),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
            let transform_buffer = context.get_uniform_buffer(&transform).unwrap();
 | 
			
		||||
            let sprite_buffer = context.get_uniform_buffer(&sprite).unwrap();
 | 
			
		||||
 | 
			
		||||
@ -99,6 +99,7 @@ pub fn draw_text2d_system(
 | 
			
		||||
                msaa: &msaa,
 | 
			
		||||
                text_glyphs: &text_glyphs.glyphs,
 | 
			
		||||
                font_quad_vertex_descriptor: &vertex_buffer_descriptor,
 | 
			
		||||
                scale_factor: 1.,
 | 
			
		||||
                style: &text.style,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@ -121,8 +122,8 @@ pub fn text2d_system(
 | 
			
		||||
    mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
 | 
			
		||||
    mut text_pipeline: ResMut<DefaultTextPipeline>,
 | 
			
		||||
    mut text_queries: QuerySet<(
 | 
			
		||||
        Query<Entity, Changed<Text>>,
 | 
			
		||||
        Query<(&Text, &mut CalculatedSize)>,
 | 
			
		||||
        Query<Entity, (With<MainPass>, Changed<Text>)>,
 | 
			
		||||
        Query<(&Text, &mut CalculatedSize), With<MainPass>>,
 | 
			
		||||
    )>,
 | 
			
		||||
) {
 | 
			
		||||
    // Adds all entities where the text or the style has changed to the local queue
 | 
			
		||||
 | 
			
		||||
@ -14,31 +14,38 @@ use bevy_text::{
 | 
			
		||||
    CalculatedSize, DefaultTextPipeline, DrawableText, Font, FontAtlasSet, Text, TextError,
 | 
			
		||||
};
 | 
			
		||||
use bevy_transform::prelude::GlobalTransform;
 | 
			
		||||
use bevy_window::Windows;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default)]
 | 
			
		||||
pub struct QueuedText {
 | 
			
		||||
    entities: Vec<Entity>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn scale_value(value: f32, factor: f64) -> f32 {
 | 
			
		||||
    (value as f64 * factor) as f32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Defines how min_size, size, and max_size affects the bounds of a text
 | 
			
		||||
/// block.
 | 
			
		||||
pub fn text_constraint(min_size: Val, size: Val, max_size: Val) -> f32 {
 | 
			
		||||
pub fn text_constraint(min_size: Val, size: Val, max_size: Val, scale_factor: f64) -> f32 {
 | 
			
		||||
    // Needs support for percentages
 | 
			
		||||
    match (min_size, size, max_size) {
 | 
			
		||||
        (_, _, Val::Px(max)) => max,
 | 
			
		||||
        (Val::Px(min), _, _) => min,
 | 
			
		||||
        (Val::Undefined, Val::Px(size), Val::Undefined) => size,
 | 
			
		||||
        (Val::Auto, Val::Px(size), Val::Auto) => size,
 | 
			
		||||
        (_, _, Val::Px(max)) => scale_value(max, scale_factor),
 | 
			
		||||
        (Val::Px(min), _, _) => scale_value(min, scale_factor),
 | 
			
		||||
        (Val::Undefined, Val::Px(size), Val::Undefined) => scale_value(size, scale_factor),
 | 
			
		||||
        (Val::Auto, Val::Px(size), Val::Auto) => scale_value(size, scale_factor),
 | 
			
		||||
        _ => f32::MAX,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Computes the size of a text block and updates the TextGlyphs with the
 | 
			
		||||
/// new computed glyphs from the layout
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub fn text_system(
 | 
			
		||||
    mut queued_text: Local<QueuedText>,
 | 
			
		||||
    mut textures: ResMut<Assets<Texture>>,
 | 
			
		||||
    fonts: Res<Assets<Font>>,
 | 
			
		||||
    windows: Res<Windows>,
 | 
			
		||||
    mut texture_atlases: ResMut<Assets<TextureAtlas>>,
 | 
			
		||||
    mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
 | 
			
		||||
    mut text_pipeline: ResMut<DefaultTextPipeline>,
 | 
			
		||||
@ -47,6 +54,14 @@ pub fn text_system(
 | 
			
		||||
        Query<(&Text, &Style, &mut CalculatedSize)>,
 | 
			
		||||
    )>,
 | 
			
		||||
) {
 | 
			
		||||
    let scale_factor = if let Some(window) = windows.get_primary() {
 | 
			
		||||
        window.scale_factor()
 | 
			
		||||
    } else {
 | 
			
		||||
        1.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let inv_scale_factor = 1. / scale_factor;
 | 
			
		||||
 | 
			
		||||
    // Adds all entities where the text or the style has changed to the local queue
 | 
			
		||||
    for entity in text_queries.q0_mut().iter_mut() {
 | 
			
		||||
        queued_text.entities.push(entity);
 | 
			
		||||
@ -62,11 +77,17 @@ pub fn text_system(
 | 
			
		||||
    for entity in queued_text.entities.drain(..) {
 | 
			
		||||
        if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
 | 
			
		||||
            let node_size = Size::new(
 | 
			
		||||
                text_constraint(style.min_size.width, style.size.width, style.max_size.width),
 | 
			
		||||
                text_constraint(
 | 
			
		||||
                    style.min_size.width,
 | 
			
		||||
                    style.size.width,
 | 
			
		||||
                    style.max_size.width,
 | 
			
		||||
                    scale_factor,
 | 
			
		||||
                ),
 | 
			
		||||
                text_constraint(
 | 
			
		||||
                    style.min_size.height,
 | 
			
		||||
                    style.size.height,
 | 
			
		||||
                    style.max_size.height,
 | 
			
		||||
                    scale_factor,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
@ -75,7 +96,7 @@ pub fn text_system(
 | 
			
		||||
                text.font.clone(),
 | 
			
		||||
                &fonts,
 | 
			
		||||
                &text.value,
 | 
			
		||||
                text.style.font_size,
 | 
			
		||||
                scale_value(text.style.font_size, scale_factor),
 | 
			
		||||
                text.style.alignment,
 | 
			
		||||
                node_size,
 | 
			
		||||
                &mut *font_atlas_set_storage,
 | 
			
		||||
@ -93,7 +114,10 @@ pub fn text_system(
 | 
			
		||||
                    let text_layout_info = text_pipeline.get_glyphs(&entity).expect(
 | 
			
		||||
                        "Failed to get glyphs from the pipeline that have just been computed",
 | 
			
		||||
                    );
 | 
			
		||||
                    calculated_size.size = text_layout_info.size;
 | 
			
		||||
                    calculated_size.size = Size {
 | 
			
		||||
                        width: scale_value(text_layout_info.size.width, inv_scale_factor),
 | 
			
		||||
                        height: scale_value(text_layout_info.size.height, inv_scale_factor),
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -106,11 +130,18 @@ pub fn text_system(
 | 
			
		||||
pub fn draw_text_system(
 | 
			
		||||
    mut context: DrawContext,
 | 
			
		||||
    msaa: Res<Msaa>,
 | 
			
		||||
    windows: Res<Windows>,
 | 
			
		||||
    meshes: Res<Assets<Mesh>>,
 | 
			
		||||
    mut render_resource_bindings: ResMut<RenderResourceBindings>,
 | 
			
		||||
    text_pipeline: Res<DefaultTextPipeline>,
 | 
			
		||||
    mut query: Query<(Entity, &mut Draw, &Visible, &Text, &Node, &GlobalTransform)>,
 | 
			
		||||
) {
 | 
			
		||||
    let scale_factor = if let Some(window) = windows.get_primary() {
 | 
			
		||||
        window.scale_factor()
 | 
			
		||||
    } else {
 | 
			
		||||
        1.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let font_quad = meshes.get(&QUAD_HANDLE).unwrap();
 | 
			
		||||
    let vertex_buffer_descriptor = font_quad.get_vertex_buffer_descriptor();
 | 
			
		||||
 | 
			
		||||
@ -125,6 +156,7 @@ pub fn draw_text_system(
 | 
			
		||||
            let mut drawable_text = DrawableText {
 | 
			
		||||
                render_resource_bindings: &mut render_resource_bindings,
 | 
			
		||||
                position,
 | 
			
		||||
                scale_factor: scale_factor as f32,
 | 
			
		||||
                msaa: &msaa,
 | 
			
		||||
                text_glyphs: &text_glyphs.glyphs,
 | 
			
		||||
                font_quad_vertex_descriptor: &vertex_buffer_descriptor,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user