1use crate::{
88 Colour,
90 graphics::Graphics,
92};
93
94#[cfg(feature="colour_filter")]
95use crate::graphics::ColourFilter;
96
97mod glyph;
98pub use glyph::*;
99
100mod outline;
101pub (crate) use outline::{
102 OutlineCurve,
103 OutlineCurveBuilder,
104};
105
106pub use outline::{
107 Scale,
108};
109
110mod glyph_cache;
111pub use glyph_cache::{
112 GlyphCache,
113 RawGlyphCache,
114};
115
116mod font;
117pub use font::{
118 FontOwner,
119 FaceWrapper,
120 CachedFont,
121 Font,
122};
123
124
125use glium::{Surface,DrawError};
126
127pub use ttf_parser;
129pub use ab_glyph_rasterizer;
130
131pub struct TextBase{
135 pub position:[f32;2],
136 pub scale:Scale,
137 pub colour:Colour,
138}
139
140impl TextBase{
141 pub const fn new(position:[f32;2],scale:Scale,colour:Colour)->TextBase{
142 Self{
143 scale,
144 colour,
145 position,
146 }
147 }
148
149 pub const fn zero_position(scale:Scale,colour:Colour)->TextBase{
150 Self{
151 scale,
152 colour,
153 position:[0f32;2],
154 }
155 }
156
157 #[inline(always)]
158 pub fn set_x(&mut self,x:f32){
159 self.position[0]=x
160 }
161
162 #[inline(always)]
163 pub fn set_y(&mut self,y:f32){
164 self.position[1]=y
165 }
166
167 #[inline(always)]
168 pub fn move_to(&mut self,position:[f32;2]){
169 self.position=position
170 }
171
172 #[inline(always)]
173 pub fn shift_x(&mut self,dx:f32){
174 self.position[0]+=dx
175 }
176
177 #[inline(always)]
178 pub fn shift_y(&mut self,dy:f32){
179 self.position[1]+=dy
180 }
181
182 #[inline(always)]
183 pub fn shift(&mut self,dx:f32,dy:f32){
184 self.position[0]+=dx;
185 self.position[1]+=dy;
186 }
187
188 #[inline(always)]
189 pub fn set_alpha_channel(&mut self,alpha:f32){
190 self.colour[3]=alpha
191 }
192
193 #[inline(always)]
194 pub fn set_colour(&mut self,colour:Colour){
195 self.colour=colour
196 }
197}
198
199
200
201
202
203impl TextBase{
204 #[inline(always)]
208 pub fn draw_char<F:Font,S:Surface>(
209 &self,
210 character:char,
211 font:&F,
212 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
213 graphics:&mut Graphics<S>
214 )->Result<(),DrawError>{
215 let glyph=if let Some(glyph)=font.build_raw_glyph(character){
216 glyph
217 }
218 else{
219 if character.is_whitespace(){
220 return Ok(())
221 }
222 else{
223 font.build_raw_undefined_glyph()
224 }
225 };
226
227 let outlined=glyph.outlined_glyph(self.scale);
229
230 let position={
232 let size=outlined.size();
233 let offset=outlined.offset();
234 [
235 self.position[0],
236 self.position[1]-offset[1]-size[1] as f32,
237 ]
238 };
239
240 graphics.draw_glyph(
241 &outlined,
242 self.colour,
243 position,
244 #[cfg(feature="colour_filter")]colour_filter,
245 )
246 }
247
248 #[inline(always)]
252 pub fn draw_shift_char<F:Font,S:Surface>(
253 &self,
254 character:char,
255 shift:[f32;2],
256 font:&F,
257 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
258 graphics:&mut Graphics<S>
259 )->Result<(),DrawError>{
260 let glyph=if let Some(glyph)=font.build_raw_glyph(character){
261 glyph
262 }
263 else{
264 if character.is_whitespace(){
265 return Ok(())
266 }
267 else{
268 font.build_raw_undefined_glyph()
269 }
270 };
271
272 let outlined=glyph.outlined_glyph(self.scale);
274
275 let position={
277 let size=outlined.size();
278 let offset=outlined.offset();
279 [
280 self.position[0],
281 self.position[1]-offset[1]-size[1] as f32,
282 ]
283 };
284
285 graphics.draw_shift_glyph(
286 &outlined,
287 self.colour,
288 position,
289 shift,
290 #[cfg(feature="colour_filter")]colour_filter,
291 )
292 }
293
294 #[inline(always)]
298 pub fn draw_rotate_char<F:Font,S:Surface>(
299 &self,
300 character:char,
301 rotation_center:[f32;2],
302 angle:f32,
303 font:&F,
304 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
305 graphics:&mut Graphics<S>
306 )->Result<(),DrawError>{
307 let glyph=if let Some(glyph)=font.build_raw_glyph(character){
308 glyph
309 }
310 else{
311 if character.is_whitespace(){
312 return Ok(())
313 }
314 else{
315 font.build_raw_undefined_glyph()
316 }
317 };
318
319 let outlined=glyph.outlined_glyph(self.scale);
321
322 let position={
324 let size=outlined.size();
325 let offset=outlined.offset();
326 [
327 self.position[0],
328 self.position[1]-offset[1]-size[1] as f32,
329 ]
330 };
331
332 graphics.draw_rotate_glyph(
333 &outlined,
334 self.colour,
335 position,
336 rotation_center,
337 angle,
338 #[cfg(feature="colour_filter")]colour_filter
339 )
340 }
341
342 pub fn draw_str<F:Font,S:Surface>(
346 &self,
347 s:&str,
348 font:&F,
349 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
350 graphics:&mut Graphics<S>
351 )->Result<(),DrawError>{
352 let mut position=self.position;
353
354 let mut glyph; let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
358
359 for character in s.chars(){
360 glyph=if let Some(glyph)=font.build_raw_glyph(character){
361 glyph
362 }
363 else{
364 if character==' '{
365 position[0]+=whitespace_advance;
366 continue
367 }
368
369 font.build_raw_undefined_glyph()
370 };
371
372 let scaled=glyph.scale(self.scale);
374
375 let rect=scaled.positioned_bounding_box(position);
376
377 let advance_width=scaled.advance_width();
379
380 let outlined=scaled.outline();
382
383 graphics.draw_glyph(
384 &outlined,
385 self.colour,
386 [rect[0],rect[1]],
387 #[cfg(feature="colour_filter")]colour_filter,
388 )?;
389
390 position[0]+=advance_width;
391 }
392
393 Ok(())
394 }
395
396 pub fn draw_shift_str<F:Font,S:Surface>(
400 &self,
401 s:&str,
402 shift:[f32;2],
403 font:&F,
404 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
405 graphics:&mut Graphics<S>
406 )->Result<(),DrawError>{
407 let mut position=self.position;
408
409 let mut glyph; let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
413
414 for character in s.chars(){
415 glyph=if let Some(glyph)=font.build_raw_glyph(character){
416 glyph
417 }
418 else{
419 if character==' '{
420 position[0]+=whitespace_advance;
421 continue
422 }
423
424 font.build_raw_undefined_glyph()
425 };
426
427 let scaled=glyph.scale(self.scale);
429
430 let rect=scaled.positioned_bounding_box(position);
431
432 let advance_width=scaled.advance_width();
434
435 let outlined=scaled.outline();
437
438 graphics.draw_shift_glyph(
439 &outlined,
440 self.colour,
441 [rect[0],rect[1]],
442 shift,
443 #[cfg(feature="colour_filter")]colour_filter,
444 )?;
445
446 position[0]+=advance_width;
447 }
448
449 Ok(())
450 }
451
452 pub fn draw_rotate_str<F:Font,S:Surface>(
456 &self,
457 s:&str,
458 rotation_center:[f32;2],
459 angle:f32,
460 font:&F,
461 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
462 graphics:&mut Graphics<S>
463 )->Result<(),DrawError>{
464 let mut position=self.position;
465
466 let mut glyph; let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
470
471 for character in s.chars(){
472 glyph=if let Some(glyph)=font.build_raw_glyph(character){
473 glyph
474 }
475 else{
476 if character==' '{
477 position[0]+=whitespace_advance;
478 continue
479 }
480
481 font.build_raw_undefined_glyph()
482 };
483
484 let scaled=glyph.scale(self.scale);
486
487 let rect=scaled.positioned_bounding_box(position);
488
489 let advance_width=scaled.advance_width();
491
492 let outlined=scaled.outline();
494
495 graphics.draw_rotate_glyph(
496 &outlined,
497 self.colour,
498 [rect[0],rect[1]],
499 rotation_center,
500 angle,
501 #[cfg(feature="colour_filter")]colour_filter
502 )?;
503
504 position[0]+=advance_width;
505 }
506
507 Ok(())
508 }
509}
510
511
512
513
514
515impl TextBase{
516 #[inline(always)]
524 pub fn draw_char_glyph_cache<C:RawGlyphCache,S:Surface>(
525 &self,
526 character:char,
527 glyph_cache:&C,
528 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
529 graphics:&mut Graphics<S>
530 )->Result<(),DrawError>{
531 let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
532 glyph
533 }
534 else{
535 if character.is_whitespace(){
536 return Ok(())
537 }
538 else{
539 glyph_cache.scaled_undefined_glyph(self.scale)
540 }
541 };
542
543 let rect=glyph.positioned_bounding_box(self.position);
544
545 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
547
548 graphics.draw_glyph_cache(
549 &textured,
550 self.colour,
551 [rect[0],rect[1]],
552 #[cfg(feature="colour_filter")]colour_filter
553 )
554 }
555
556 #[inline(always)]
564 pub fn draw_shift_char_glyph_cache<C:RawGlyphCache,S:Surface>(
565 &self,
566 character:char,
567 shift:[f32;2],
568 glyph_cache:&C,
569 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
570 graphics:&mut Graphics<S>
571 )->Result<(),DrawError>{
572 let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
573 glyph
574 }
575 else{
576 if character.is_whitespace(){
577 return Ok(())
578 }
579 else{
580 glyph_cache.scaled_undefined_glyph(self.scale)
581 }
582 };
583
584 let rect=glyph.positioned_bounding_box(self.position);
585
586 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
588
589 graphics.draw_shift_glyph_cache(
590 &textured,
591 self.colour,
592 [rect[0],rect[1]],
593 shift,
594 #[cfg(feature="colour_filter")]colour_filter
595 )
596 }
597
598 #[inline(always)]
606 pub fn draw_rotate_char_glyph_cache<C:RawGlyphCache,S:Surface>(
607 &self,
608 character:char,
609 rotation_center:[f32;2],
610 angle:f32,
611 glyph_cache:&C,
612 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
613 graphics:&mut Graphics<S>
614 )->Result<(),DrawError>{
615 let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
616 glyph
617 }
618 else{
619 if character.is_whitespace(){
620 return Ok(())
621 }
622 else{
623 glyph_cache.scaled_undefined_glyph(self.scale)
624 }
625 };
626
627 let rect=glyph.positioned_bounding_box(self.position);
628
629 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
631
632 graphics.draw_rotate_glyph_cache(
633 &textured,
634 self.colour,
635 [rect[0],rect[1]],
636 rotation_center,
637 angle,
638 #[cfg(feature="colour_filter")]colour_filter
639 )
640 }
641
642 pub fn draw_str_glyph_cache<C:RawGlyphCache,S:Surface>(
650 &self,
651 s:&str,
652 glyph_cache:&C,
653 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
654 graphics:&mut Graphics<S>
655 )->Result<(),DrawError>{
656 let mut position=self.position;
657
658 let mut glyph;
659
660 for character in s.chars(){
661 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
662 glyph
663 }
664 else{
665 if character==' '{
666 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
667 continue
668 }
669
670 glyph_cache.scaled_undefined_glyph(self.scale)
671 };
672
673 let rect=glyph.positioned_bounding_box(position);
674
675 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
677
678 graphics.draw_glyph_cache(
679 &textured,
680 self.colour,
681 [rect[0],rect[1]],
682 #[cfg(feature="colour_filter")]colour_filter
683 )?;
684
685 position[0]+=glyph.advance_width();
686 }
687
688 Ok(())
689 }
690
691 pub fn draw_shift_str_glyph_cache<C:RawGlyphCache,S:Surface>(
699 &self,
700 s:&str,
701 shift:[f32;2],
702 glyph_cache:&C,
703 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
704 graphics:&mut Graphics<S>
705 )->Result<(),DrawError>{
706 let mut position=self.position;
707
708 let mut glyph; for character in s.chars(){
711 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
712 glyph
713 }
714 else{
715 if character==' '{
716 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
717 continue
718 }
719
720 glyph_cache.scaled_undefined_glyph(self.scale)
721 };
722
723 let rect=glyph.positioned_bounding_box(position);
724
725 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
727
728 graphics.draw_shift_glyph_cache(
729 &textured,
730 self.colour,
731 [rect[0],rect[1]],
732 shift,
733 #[cfg(feature="colour_filter")]colour_filter
734 )?;
735
736 position[0]+=glyph.advance_width();
737 }
738
739 Ok(())
740 }
741
742
743
744 pub fn draw_rotate_str_glyph_cache<C:RawGlyphCache,S:Surface>(
752 &self,
753 s:&str,
754 rotation_center:[f32;2],
755 angle:f32,
756 glyph_cache:&C,
757 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
758 graphics:&mut Graphics<S>
759 )->Result<(),DrawError>{
760 let mut position=self.position;
761
762 let mut glyph; for character in s.chars(){
765 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
766 glyph
767 }
768 else{
769 if character==' '{
770 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
771 continue
772 }
773
774 glyph_cache.scaled_undefined_glyph(self.scale)
775 };
776
777 let rect=glyph.positioned_bounding_box(position);
778
779 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
781
782 graphics.draw_rotate_glyph_cache(
783 &textured,
784 self.colour,
785 [rect[0],rect[1]],
786 rotation_center,
787 angle,
788 #[cfg(feature="colour_filter")]colour_filter
789 )?;
790
791 position[0]+=glyph.advance_width();
792 }
793
794 Ok(())
795 }
796
797 pub fn draw_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
807 &self,
808 s:&str,
809 chars:usize,
810 glyph_cache:&C,
811 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
812 graphics:&mut Graphics<S>
813 )->Result<bool,DrawError>{
814 let mut whole=true; let mut position=self.position;
817
818 let mut glyph; for (i,character) in s.chars().enumerate(){
821 if i==chars{
823 whole=false;
824 break
825 }
826
827 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
828 glyph
829 }
830 else{
831 if character==' '{
832 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
833 continue
834 }
835
836 glyph_cache.scaled_undefined_glyph(self.scale)
837 };
838
839 let rect=glyph.positioned_bounding_box(position);
840
841 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
843
844 graphics.draw_glyph_cache(
845 &textured,
846 self.colour,
847 [rect[0],rect[1]],
848 #[cfg(feature="colour_filter")]colour_filter
849 )?;
850
851 position[0]+=glyph.advance_width();
852 }
853
854 Ok(whole)
855 }
856
857 pub fn draw_shift_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
867 &self,
868 s:&str,
869 chars:usize,
870 shift:[f32;2],
871 glyph_cache:&C,
872 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
873 graphics:&mut Graphics<S>
874 )->Result<bool,DrawError>{
875 let mut whole=true; let mut position=self.position;
878
879 let mut glyph; for (i,character) in s.chars().enumerate(){
882 if i==chars{
884 whole=false;
885 break
886 }
887
888 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
889 glyph
890 }
891 else{
892 if character==' '{
893 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
894 continue
895 }
896
897 glyph_cache.scaled_undefined_glyph(self.scale)
898 };
899
900 let rect=glyph.positioned_bounding_box(position);
901
902 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
904
905 graphics.draw_shift_glyph_cache(
906 &textured,
907 self.colour,
908 [rect[0],rect[1]],
909 shift,
910 #[cfg(feature="colour_filter")]colour_filter
911 )?;
912
913 position[0]+=glyph.advance_width();
914 }
915
916 Ok(whole)
917 }
918
919 pub fn draw_rotate_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
929 &self,
930 s:&str,
931 chars:usize,
932 rotation_center:[f32;2],
933 angle:f32,
934 glyph_cache:&C,
935 #[cfg(feature="colour_filter")]colour_filter:ColourFilter,
936 graphics:&mut Graphics<S>
937 )->Result<bool,DrawError>{
938 let mut whole=true; let mut position=self.position;
941
942 let mut glyph;
943
944 for (i,character) in s.chars().enumerate(){
945 if i==chars{
947 whole=false;
948 break
949 }
950
951 glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
952 glyph
953 }
954 else{
955 if character==' '{
956 position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
957 continue
958 }
959
960 glyph_cache.scaled_undefined_glyph(self.scale)
961 };
962
963 let rect=glyph.positioned_bounding_box(position);
964
965 let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
967
968 graphics.draw_rotate_glyph_cache(
969 &textured,
970 self.colour,
971 [rect[0],rect[1]],
972 rotation_center,
973 angle,
974 #[cfg(feature="colour_filter")]colour_filter
975 )?;
976
977 position[0]+=glyph.advance_width();
978 }
979
980 Ok(whole)
981 }
982}