
# Replace ab_glyph with the more capable cosmic-text Fixes #7616. Cosmic-text is a more mature text-rendering library that handles scripts and ligatures better than ab_glyph, it can also handle system fonts which can be implemented in bevy in the future Rebase of https://github.com/bevyengine/bevy/pull/8808 ## Changelog Replaces text renderer ab_glyph with cosmic-text The definition of the font size has changed with the migration to cosmic text. The behavior is now consistent with other platforms (e.g. the web), where the font size in pixels measures the height of the font (the distance between the top of the highest ascender and the bottom of the lowest descender). Font sizes in your app need to be rescaled to approximately 1.2x smaller; for example, if you were using a font size of 60.0, you should now use a font size of 50.0. ## Migration guide - `Text2dBounds` has been replaced with `TextBounds`, and it now accepts `Option`s to the bounds, instead of using `f32::INFINITY` to inidicate lack of bounds - Textsizes should be changed, dividing the current size with 1.2 will result in the same size as before. - `TextSettings` struct is removed - Feature `subpixel_alignment` has been removed since cosmic-text already does this automatically - TextBundles and things rendering texts requires the `CosmicBuffer` Component on them as well ## Suggested followups: - TextPipeline: reconstruct byte indices for keeping track of eventual cursors in text input - TextPipeline: (future work) split text entities into section entities - TextPipeline: (future work) text editing - Support line height as an option. Unitless `1.2` is the default used in browsers (1.2x font size). - Support System Fonts and font families - Example showing of animated text styles. Eg. throbbing hyperlinks --------- Co-authored-by: tigregalis <anak.harimau@gmail.com> Co-authored-by: Nico Burns <nico@nicoburns.com> Co-authored-by: sam edelsten <samedelsten1@gmail.com> Co-authored-by: Dimchikkk <velo.app1@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Rob Parrett <robparrett@gmail.com>
168 lines
6.2 KiB
Rust
168 lines
6.2 KiB
Rust
use bevy_asset::{Asset, AssetId, Assets, Handle};
|
|
use bevy_ecs::component::Component;
|
|
use bevy_math::{URect, UVec2};
|
|
use bevy_reflect::Reflect;
|
|
use bevy_render::texture::Image;
|
|
use bevy_utils::HashMap;
|
|
|
|
/// Stores a map used to lookup the position of a texture in a [`TextureAtlas`].
|
|
/// This can be used to either use and look up a specific section of a texture, or animate frame-by-frame as a sprite sheet.
|
|
///
|
|
/// Optionally it can store a mapping from sub texture handles to the related area index (see
|
|
/// [`TextureAtlasBuilder`]).
|
|
///
|
|
/// [Example usage animating sprite.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
|
|
/// [Example usage animating sprite in response to an event.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_animation.rs)
|
|
/// [Example usage loading sprite sheet.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
|
|
///
|
|
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
|
|
#[derive(Asset, Reflect, Debug, Clone)]
|
|
#[reflect(Debug)]
|
|
pub struct TextureAtlasLayout {
|
|
// TODO: add support to Uniforms derive to write dimensions and sprites to the same buffer
|
|
pub size: UVec2,
|
|
/// The specific areas of the atlas where each texture can be found
|
|
pub textures: Vec<URect>,
|
|
/// Maps from a specific image handle to the index in `textures` where they can be found.
|
|
///
|
|
/// This field is set by [`TextureAtlasBuilder`].
|
|
///
|
|
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
|
|
pub(crate) texture_handles: Option<HashMap<AssetId<Image>, usize>>,
|
|
}
|
|
|
|
impl TextureAtlasLayout {
|
|
/// Create a new empty layout with custom `dimensions`
|
|
pub fn new_empty(dimensions: UVec2) -> Self {
|
|
Self {
|
|
size: dimensions,
|
|
texture_handles: None,
|
|
textures: Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// Generate a [`TextureAtlasLayout`] as a grid where each
|
|
/// `tile_size` by `tile_size` grid-cell is one of the *section* in the
|
|
/// atlas. Grid cells are separated by some `padding`, and the grid starts
|
|
/// at `offset` pixels from the top left corner. Resulting layout is
|
|
/// indexed left to right, top to bottom.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `tile_size` - Each layout grid cell size
|
|
/// * `columns` - Grid column count
|
|
/// * `rows` - Grid row count
|
|
/// * `padding` - Optional padding between cells
|
|
/// * `offset` - Optional global grid offset
|
|
pub fn from_grid(
|
|
tile_size: UVec2,
|
|
columns: u32,
|
|
rows: u32,
|
|
padding: Option<UVec2>,
|
|
offset: Option<UVec2>,
|
|
) -> Self {
|
|
let padding = padding.unwrap_or_default();
|
|
let offset = offset.unwrap_or_default();
|
|
let mut sprites = Vec::new();
|
|
let mut current_padding = UVec2::ZERO;
|
|
|
|
for y in 0..rows {
|
|
if y > 0 {
|
|
current_padding.y = padding.y;
|
|
}
|
|
for x in 0..columns {
|
|
if x > 0 {
|
|
current_padding.x = padding.x;
|
|
}
|
|
|
|
let cell = UVec2::new(x, y);
|
|
let rect_min = (tile_size + current_padding) * cell + offset;
|
|
|
|
sprites.push(URect {
|
|
min: rect_min,
|
|
max: rect_min + tile_size,
|
|
});
|
|
}
|
|
}
|
|
|
|
let grid_size = UVec2::new(columns, rows);
|
|
|
|
Self {
|
|
size: ((tile_size + current_padding) * grid_size) - current_padding,
|
|
textures: sprites,
|
|
texture_handles: None,
|
|
}
|
|
}
|
|
|
|
/// Add a *section* to the list in the layout and returns its index
|
|
/// which can be used with [`TextureAtlas`]
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `rect` - The section of the texture to be added
|
|
///
|
|
/// [`TextureAtlas`]: crate::TextureAtlas
|
|
pub fn add_texture(&mut self, rect: URect) -> usize {
|
|
self.textures.push(rect);
|
|
self.textures.len() - 1
|
|
}
|
|
|
|
/// The number of textures in the [`TextureAtlasLayout`]
|
|
pub fn len(&self) -> usize {
|
|
self.textures.len()
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.textures.is_empty()
|
|
}
|
|
|
|
/// Retrieves the texture *section* index of the given `texture` handle.
|
|
///
|
|
/// This requires the layout to have been built using a [`TextureAtlasBuilder`]
|
|
///
|
|
/// [`TextureAtlasBuilder`]: crate::TextureAtlasBuilder
|
|
pub fn get_texture_index(&self, texture: impl Into<AssetId<Image>>) -> Option<usize> {
|
|
let id = texture.into();
|
|
self.texture_handles
|
|
.as_ref()
|
|
.and_then(|texture_handles| texture_handles.get(&id).cloned())
|
|
}
|
|
}
|
|
|
|
/// Component used to draw a specific section of a texture.
|
|
///
|
|
/// It stores a handle to [`TextureAtlasLayout`] and the index of the current section of the atlas.
|
|
/// The texture atlas contains various *sections* of a given texture, allowing users to have a single
|
|
/// image file for either sprite animation or global mapping.
|
|
/// You can change the texture [`index`](Self::index) of the atlas to animate the sprite or display only a *section* of the texture
|
|
/// for efficient rendering of related game objects.
|
|
///
|
|
/// Check the following examples for usage:
|
|
/// - [`animated sprite sheet example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
|
|
/// - [`sprite animation event example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_animation.rs)
|
|
/// - [`texture atlas example`](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
|
|
#[derive(Component, Default, Debug, Clone, Reflect)]
|
|
pub struct TextureAtlas {
|
|
/// Texture atlas layout handle
|
|
pub layout: Handle<TextureAtlasLayout>,
|
|
/// Texture atlas section index
|
|
pub index: usize,
|
|
}
|
|
|
|
impl TextureAtlas {
|
|
/// Retrieves the current texture [`URect`] of the sprite sheet according to the section `index`
|
|
pub fn texture_rect(&self, texture_atlases: &Assets<TextureAtlasLayout>) -> Option<URect> {
|
|
let atlas = texture_atlases.get(&self.layout)?;
|
|
atlas.textures.get(self.index).copied()
|
|
}
|
|
}
|
|
|
|
impl From<Handle<TextureAtlasLayout>> for TextureAtlas {
|
|
fn from(texture_atlas: Handle<TextureAtlasLayout>) -> Self {
|
|
Self {
|
|
layout: texture_atlas,
|
|
index: 0,
|
|
}
|
|
}
|
|
}
|