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