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