@@ -17,7 +17,7 @@ use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
1717
1818use crate :: {
1919 error:: TextError , BreakLineOn , CosmicBuffer , Font , FontAtlasSets , FontSmoothing , JustifyText ,
20- PositionedGlyph , TextBounds , TextSection , YAxisOrientation ,
20+ PositionedGlyph , TextBounds , TextSection , TextStyle , YAxisOrientation ,
2121} ;
2222
2323/// A wrapper resource around a [`cosmic_text::FontSystem`]
@@ -51,28 +51,37 @@ impl Default for SwashCache {
5151 }
5252}
5353
54+ /// Information about a font collected as part of preparing for text layout.
55+ #[ derive( Clone ) ]
56+ struct FontFaceInfo {
57+ stretch : cosmic_text:: fontdb:: Stretch ,
58+ style : cosmic_text:: fontdb:: Style ,
59+ weight : cosmic_text:: fontdb:: Weight ,
60+ family_name : Arc < str > ,
61+ }
62+
5463/// The `TextPipeline` is used to layout and render [`Text`](crate::Text).
5564///
5665/// See the [crate-level documentation](crate) for more information.
5766#[ derive( Default , Resource ) ]
5867pub struct TextPipeline {
5968 /// Identifies a font [`ID`](cosmic_text::fontdb::ID) by its [`Font`] [`Asset`](bevy_asset::Asset).
60- map_handle_to_font_id : HashMap < AssetId < Font > , ( cosmic_text:: fontdb:: ID , String ) > ,
69+ map_handle_to_font_id : HashMap < AssetId < Font > , ( cosmic_text:: fontdb:: ID , Arc < str > ) > ,
6170 /// Buffered vec for collecting spans.
6271 ///
6372 /// See [this dark magic](https://users.rust-lang.org/t/how-to-cache-a-vectors-capacity/94478/10).
64- spans_buffer : Vec < ( & ' static str , Attrs < ' static > ) > ,
73+ spans_buffer : Vec < ( usize , & ' static str , & ' static TextStyle , FontFaceInfo ) > ,
6574}
6675
6776impl TextPipeline {
6877 /// Utilizes [`cosmic_text::Buffer`] to shape and layout text
6978 ///
7079 /// Negative or 0.0 font sizes will not be laid out.
7180 #[ allow( clippy:: too_many_arguments) ]
72- pub fn update_buffer (
81+ pub fn update_buffer < ' a > (
7382 & mut self ,
7483 fonts : & Assets < Font > ,
75- sections : & [ TextSection ] ,
84+ text_spans : impl Iterator < Item = ( & ' a str , & ' a TextStyle ) > ,
7685 linebreak_behavior : BreakLineOn ,
7786 bounds : TextBounds ,
7887 scale_factor : f64 ,
@@ -82,16 +91,45 @@ impl TextPipeline {
8291 ) -> Result < ( ) , TextError > {
8392 let font_system = & mut font_system. 0 ;
8493
85- // return early if the fonts are not loaded yet
86- let mut font_size = 0. ;
87- for section in sections {
88- if section. style . font_size > font_size {
89- font_size = section. style . font_size ;
94+ // Collect span information into a vec. This is necessary because font loading requires mut access
95+ // to FontSystem, which the cosmic-text Buffer also needs.
96+ let mut font_size: f32 = 0. ;
97+ let mut spans: Vec < ( usize , & str , & TextStyle , FontFaceInfo ) > =
98+ core:: mem:: take ( & mut self . spans_buffer )
99+ . into_iter ( )
100+ . map ( |_| -> ( usize , & str , & TextStyle , FontFaceInfo ) { unreachable ! ( ) } )
101+ . collect ( ) ;
102+
103+ for ( span_index, ( span, style) ) in text_spans. enumerate ( ) {
104+ // Return early if a font is not loaded yet.
105+ if !fonts. contains ( style. font . id ( ) ) {
106+ spans. clear ( ) ;
107+ self . spans_buffer = spans
108+ . into_iter ( )
109+ . map (
110+ |_| -> ( usize , & ' static str , & ' static TextStyle , FontFaceInfo ) {
111+ unreachable ! ( )
112+ } ,
113+ )
114+ . collect ( ) ;
115+
116+ return Err ( TextError :: NoSuchFont ) ;
117+ }
118+
119+ // Get max font size for use in cosmic Metrics.
120+ font_size = font_size. max ( style. font_size ) ;
121+
122+ // Load Bevy fonts into cosmic-text's font system.
123+ let face_info =
124+ load_font_to_fontdb ( style, font_system, & mut self . map_handle_to_font_id , fonts) ;
125+
126+ // Save spans that aren't zero-sized.
127+ if scale_factor <= 0.0 || style. font_size <= 0.0 {
128+ continue ;
90129 }
91- fonts
92- . get ( section. style . font . id ( ) )
93- . ok_or ( TextError :: NoSuchFont ) ?;
130+ spans. push ( ( span_index, span, style, face_info) ) ;
94131 }
132+
95133 let line_height = font_size * 1.2 ;
96134 let mut metrics = Metrics :: new ( font_size, line_height) . scale ( scale_factor as f32 ) ;
97135 // Metrics of 0.0 cause `Buffer::set_metrics` to panic. We hack around this by 'falling
@@ -100,45 +138,20 @@ impl TextPipeline {
100138 metrics. font_size = metrics. font_size . max ( 0.000001 ) ;
101139 metrics. line_height = metrics. line_height . max ( 0.000001 ) ;
102140
103- // Load Bevy fonts into cosmic-text's font system.
104- // This is done as as separate pre-pass to avoid borrow checker issues
105- for section in sections. iter ( ) {
106- load_font_to_fontdb ( section, font_system, & mut self . map_handle_to_font_id , fonts) ;
107- }
108-
109141 // Map text sections to cosmic-text spans, and ignore sections with negative or zero fontsizes,
110142 // since they cannot be rendered by cosmic-text.
111143 //
112144 // The section index is stored in the metadata of the spans, and could be used
113145 // to look up the section the span came from and is not used internally
114146 // in cosmic-text.
115- let mut spans: Vec < ( & str , Attrs ) > = core:: mem:: take ( & mut self . spans_buffer )
116- . into_iter ( )
117- . map ( |_| -> ( & str , Attrs ) { unreachable ! ( ) } )
118- . collect ( ) ;
119- // `metrics.font_size` hack continued: ignore all spans when scale_factor is zero.
120- if scale_factor > 0.0 {
121- spans. extend (
122- sections
123- . iter ( )
124- . enumerate ( )
125- . filter ( |( _section_index, section) | section. style . font_size > 0.0 )
126- . map ( |( section_index, section) | {
127- (
128- & section. value [ ..] ,
129- get_attrs (
130- section,
131- section_index,
132- font_system,
133- & self . map_handle_to_font_id ,
134- scale_factor,
135- ) ,
136- )
137- } ) ,
138- ) ;
139- }
140- let spans_iter = spans. iter ( ) . copied ( ) ;
147+ let spans_iter = spans. iter ( ) . map ( |( span_index, span, style, font_info) | {
148+ (
149+ * span,
150+ get_attrs ( * span_index, * style, font_info, scale_factor) ,
151+ )
152+ } ) ;
141153
154+ // Update the buffer.
142155 buffer. set_metrics ( font_system, metrics) ;
143156 buffer. set_size ( font_system, bounds. width , bounds. height ) ;
144157
@@ -165,7 +178,7 @@ impl TextPipeline {
165178 spans. clear ( ) ;
166179 self . spans_buffer = spans
167180 . into_iter ( )
168- . map ( |_| -> ( & ' static str , Attrs < ' static > ) { unreachable ! ( ) } )
181+ . map ( |_| -> ( usize , & ' static str , & ' static TextStyle , FontFaceInfo ) { unreachable ! ( ) } )
169182 . collect ( ) ;
170183
171184 Ok ( ( ) )
@@ -203,7 +216,9 @@ impl TextPipeline {
203216
204217 self . update_buffer (
205218 fonts,
206- sections,
219+ sections
220+ . iter ( )
221+ . map ( |section| ( section. value . as_str ( ) , & section. style ) ) ,
207222 linebreak_behavior,
208223 bounds,
209224 scale_factor,
@@ -310,7 +325,9 @@ impl TextPipeline {
310325
311326 self . update_buffer (
312327 fonts,
313- sections,
328+ sections
329+ . iter ( )
330+ . map ( |section| ( section. value . as_str ( ) , & section. style ) ) ,
314331 linebreak_behavior,
315332 MIN_WIDTH_CONTENT_BOUNDS ,
316333 scale_factor,
@@ -384,13 +401,13 @@ impl TextMeasureInfo {
384401}
385402
386403fn load_font_to_fontdb (
387- section : & TextSection ,
404+ style : & TextStyle ,
388405 font_system : & mut cosmic_text:: FontSystem ,
389- map_handle_to_font_id : & mut HashMap < AssetId < Font > , ( cosmic_text:: fontdb:: ID , String ) > ,
406+ map_handle_to_font_id : & mut HashMap < AssetId < Font > , ( cosmic_text:: fontdb:: ID , Arc < str > ) > ,
390407 fonts : & Assets < Font > ,
391- ) {
392- let font_handle = section . style . font . clone ( ) ;
393- map_handle_to_font_id
408+ ) -> FontFaceInfo {
409+ let font_handle = style. font . clone ( ) ;
410+ let ( face_id , family_name ) = map_handle_to_font_id
394411 . entry ( font_handle. id ( ) )
395412 . or_insert_with ( || {
396413 let font = fonts. get ( font_handle. id ( ) ) . expect (
@@ -404,34 +421,35 @@ fn load_font_to_fontdb(
404421 // TODO: it is assumed this is the right font face
405422 let face_id = * ids. last ( ) . unwrap ( ) ;
406423 let face = font_system. db ( ) . face ( face_id) . unwrap ( ) ;
407- let family_name = face. families [ 0 ] . 0 . to_owned ( ) ;
424+ let family_name = Arc :: from ( face. families [ 0 ] . 0 . as_str ( ) ) ;
408425
409426 ( face_id, family_name)
410427 } ) ;
428+ let face = font_system. db ( ) . face ( * face_id) . unwrap ( ) ;
429+
430+ FontFaceInfo {
431+ stretch : face. stretch ,
432+ style : face. style ,
433+ weight : face. weight ,
434+ family_name : family_name. clone ( ) ,
435+ }
411436}
412437
413- /// Translates [`TextSection`] to [`Attrs`],
414- /// loading fonts into the [`Database`](cosmic_text::fontdb::Database) if required.
438+ /// Translates [`TextStyle`] to [`Attrs`].
415439fn get_attrs < ' a > (
416- section : & TextSection ,
417- section_index : usize ,
418- font_system : & mut cosmic_text:: FontSystem ,
419- map_handle_to_font_id : & ' a HashMap < AssetId < Font > , ( cosmic_text:: fontdb:: ID , String ) > ,
440+ span_index : usize ,
441+ style : & TextStyle ,
442+ face_info : & ' a FontFaceInfo ,
420443 scale_factor : f64 ,
421444) -> Attrs < ' a > {
422- let ( face_id, family_name) = map_handle_to_font_id
423- . get ( & section. style . font . id ( ) )
424- . expect ( "Already loaded with load_font_to_fontdb" ) ;
425- let face = font_system. db ( ) . face ( * face_id) . unwrap ( ) ;
426-
427445 let attrs = Attrs :: new ( )
428- . metadata ( section_index )
429- . family ( Family :: Name ( family_name) )
430- . stretch ( face . stretch )
431- . style ( face . style )
432- . weight ( face . weight )
433- . metrics ( Metrics :: relative ( section . style . font_size , 1.2 ) . scale ( scale_factor as f32 ) )
434- . color ( cosmic_text:: Color ( section . style . color . to_linear ( ) . as_u32 ( ) ) ) ;
446+ . metadata ( span_index )
447+ . family ( Family :: Name ( & * face_info . family_name ) )
448+ . stretch ( face_info . stretch )
449+ . style ( face_info . style )
450+ . weight ( face_info . weight )
451+ . metrics ( Metrics :: relative ( style. font_size , 1.2 ) . scale ( scale_factor as f32 ) )
452+ . color ( cosmic_text:: Color ( style. color . to_linear ( ) . as_u32 ( ) ) ) ;
435453 attrs
436454}
437455
0 commit comments