Path: blob/main/crates/bevy_sprite_render/src/text2d/mod.rs
6849 views
use crate::{1ExtractedSlice, ExtractedSlices, ExtractedSprite, ExtractedSpriteKind, ExtractedSprites,2};3use bevy_asset::{AssetId, Assets};4use bevy_camera::visibility::ViewVisibility;5use bevy_color::LinearRgba;6use bevy_ecs::{7entity::Entity,8system::{Commands, Query, Res, ResMut},9};10use bevy_image::prelude::*;11use bevy_math::Vec2;12use bevy_render::sync_world::TemporaryRenderEntity;13use bevy_render::Extract;14use bevy_sprite::{Anchor, Text2dShadow};15use bevy_text::{16ComputedTextBlock, PositionedGlyph, TextBackgroundColor, TextBounds, TextColor, TextLayoutInfo,17};18use bevy_transform::prelude::GlobalTransform;1920/// This system extracts the sprites from the 2D text components and adds them to the21/// "render world".22pub fn extract_text2d_sprite(23mut commands: Commands,24mut extracted_sprites: ResMut<ExtractedSprites>,25mut extracted_slices: ResMut<ExtractedSlices>,26texture_atlases: Extract<Res<Assets<TextureAtlasLayout>>>,27text2d_query: Extract<28Query<(29Entity,30&ViewVisibility,31&ComputedTextBlock,32&TextLayoutInfo,33&TextBounds,34&Anchor,35Option<&Text2dShadow>,36&GlobalTransform,37)>,38>,39text_colors: Extract<Query<&TextColor>>,40text_background_colors_query: Extract<Query<&TextBackgroundColor>>,41) {42let mut start = extracted_slices.slices.len();43let mut end = start + 1;4445for (46main_entity,47view_visibility,48computed_block,49text_layout_info,50text_bounds,51anchor,52maybe_shadow,53global_transform,54) in text2d_query.iter()55{56let scaling = GlobalTransform::from_scale(57Vec2::splat(text_layout_info.scale_factor.recip()).extend(1.),58);59if !view_visibility.get() {60continue;61}6263let size = Vec2::new(64text_bounds.width.unwrap_or(text_layout_info.size.x),65text_bounds.height.unwrap_or(text_layout_info.size.y),66);6768let top_left = (Anchor::TOP_LEFT.0 - anchor.as_vec()) * size;6970for &(section_entity, rect) in text_layout_info.section_rects.iter() {71let Ok(text_background_color) = text_background_colors_query.get(section_entity) else {72continue;73};74let render_entity = commands.spawn(TemporaryRenderEntity).id();75let offset = Vec2::new(rect.center().x, -rect.center().y);76let transform = *global_transform77* GlobalTransform::from_translation(top_left.extend(0.))78* scaling79* GlobalTransform::from_translation(offset.extend(0.));80extracted_sprites.sprites.push(ExtractedSprite {81main_entity,82render_entity,83transform,84color: text_background_color.0.into(),85image_handle_id: AssetId::default(),86flip_x: false,87flip_y: false,88kind: ExtractedSpriteKind::Single {89anchor: Vec2::ZERO,90rect: None,91scaling_mode: None,92custom_size: Some(rect.size()),93},94});95}9697if let Some(shadow) = maybe_shadow {98let shadow_transform = *global_transform99* GlobalTransform::from_translation((top_left + shadow.offset).extend(0.))100* scaling;101let color = shadow.color.into();102103for (104i,105PositionedGlyph {106position,107atlas_info,108..109},110) in text_layout_info.glyphs.iter().enumerate()111{112let rect = texture_atlases113.get(atlas_info.texture_atlas)114.unwrap()115.textures[atlas_info.location.glyph_index]116.as_rect();117extracted_slices.slices.push(ExtractedSlice {118offset: Vec2::new(position.x, -position.y),119rect,120size: rect.size(),121});122123if text_layout_info124.glyphs125.get(i + 1)126.is_none_or(|info| info.atlas_info.texture != atlas_info.texture)127{128let render_entity = commands.spawn(TemporaryRenderEntity).id();129extracted_sprites.sprites.push(ExtractedSprite {130main_entity,131render_entity,132transform: shadow_transform,133color,134image_handle_id: atlas_info.texture,135flip_x: false,136flip_y: false,137kind: ExtractedSpriteKind::Slices {138indices: start..end,139},140});141start = end;142}143144end += 1;145}146}147148let transform =149*global_transform * GlobalTransform::from_translation(top_left.extend(0.)) * scaling;150let mut color = LinearRgba::WHITE;151let mut current_span = usize::MAX;152153for (154i,155PositionedGlyph {156position,157atlas_info,158span_index,159..160},161) in text_layout_info.glyphs.iter().enumerate()162{163if *span_index != current_span {164color = text_colors165.get(166computed_block167.entities()168.get(*span_index)169.map(|t| t.entity)170.unwrap_or(Entity::PLACEHOLDER),171)172.map(|text_color| LinearRgba::from(text_color.0))173.unwrap_or_default();174current_span = *span_index;175}176let rect = texture_atlases177.get(atlas_info.texture_atlas)178.unwrap()179.textures[atlas_info.location.glyph_index]180.as_rect();181extracted_slices.slices.push(ExtractedSlice {182offset: Vec2::new(position.x, -position.y),183rect,184size: rect.size(),185});186187if text_layout_info.glyphs.get(i + 1).is_none_or(|info| {188info.span_index != current_span || info.atlas_info.texture != atlas_info.texture189}) {190let render_entity = commands.spawn(TemporaryRenderEntity).id();191extracted_sprites.sprites.push(ExtractedSprite {192main_entity,193render_entity,194transform,195color,196image_handle_id: atlas_info.texture,197flip_x: false,198flip_y: false,199kind: ExtractedSpriteKind::Slices {200indices: start..end,201},202});203start = end;204}205206end += 1;207}208}209}210211212