use crate::{
Colour,
graphics::Graphics,
};
#[cfg(feature="colour_filter")]
use crate::graphics::ColourFilter;
mod glyph;
pub use glyph::*;
mod outline;
pub (crate) use outline::{
OutlineCurve,
OutlineCurveBuilder,
};
pub use outline::{
Scale,
};
mod glyph_cache;
pub use glyph_cache::{
GlyphCache,
RawGlyphCache,
};
mod font;
pub use font::{
FontOwner,
FaceWrapper,
CachedFont,
Font,
};
use glium::{Surface,DrawError};
pub use ttf_parser;
pub use ab_glyph_rasterizer;
pub struct TextBase{
pub position:[f32;2],
pub scale:Scale,
pub colour:Colour,
}
impl TextBase{
pub const fn new(position:[f32;2],scale:Scale,colour:Colour)->TextBase{
Self{
scale,
colour,
position,
}
}
pub const fn zero_position(scale:Scale,colour:Colour)->TextBase{
Self{
scale,
colour,
position:[0f32;2],
}
}
#[inline(always)]
pub fn set_x(&mut self,x:f32){
self.position[0]=x
}
#[inline(always)]
pub fn set_y(&mut self,y:f32){
self.position[1]=y
}
#[inline(always)]
pub fn move_to(&mut self,position:[f32;2]){
self.position=position
}
#[inline(always)]
pub fn shift_x(&mut self,dx:f32){
self.position[0]+=dx
}
#[inline(always)]
pub fn shift_y(&mut self,dy:f32){
self.position[1]+=dy
}
#[inline(always)]
pub fn shift(&mut self,dx:f32,dy:f32){
self.position[0]+=dx;
self.position[1]+=dy;
}
#[inline(always)]
pub fn set_alpha_channel(&mut self,alpha:f32){
self.colour[3]=alpha
}
#[inline(always)]
pub fn set_colour(&mut self,colour:Colour){
self.colour=colour
}
}
impl TextBase{
#[inline(always)]
pub fn draw_char<F:Font,S:Surface>(
&self,
character:char,
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
font.build_raw_undefined_glyph()
}
};
let outlined=glyph.outlined_glyph(self.scale);
let position={
let size=outlined.size();
let offset=outlined.offset();
[
self.position[0],
self.position[1]-offset[1]-size[1] as f32,
]
};
graphics.draw_glyph(
&outlined,
self.colour,
position,
#[cfg(feature="colour_filter")]colour_filter,
)
}
#[inline(always)]
pub fn draw_shift_char<F:Font,S:Surface>(
&self,
character:char,
shift:[f32;2],
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
font.build_raw_undefined_glyph()
}
};
let outlined=glyph.outlined_glyph(self.scale);
let position={
let size=outlined.size();
let offset=outlined.offset();
[
self.position[0],
self.position[1]-offset[1]-size[1] as f32,
]
};
graphics.draw_shift_glyph(
&outlined,
self.colour,
position,
shift,
#[cfg(feature="colour_filter")]colour_filter,
)
}
#[inline(always)]
pub fn draw_rotate_char<F:Font,S:Surface>(
&self,
character:char,
rotation_center:[f32;2],
angle:f32,
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
font.build_raw_undefined_glyph()
}
};
let outlined=glyph.outlined_glyph(self.scale);
let position={
let size=outlined.size();
let offset=outlined.offset();
[
self.position[0],
self.position[1]-offset[1]-size[1] as f32,
]
};
graphics.draw_rotate_glyph(
&outlined,
self.colour,
position,
rotation_center,
angle,
#[cfg(feature="colour_filter")]colour_filter
)
}
pub fn draw_str<F:Font,S:Surface>(
&self,
s:&str,
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
for character in s.chars(){
glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character==' '{
position[0]+=whitespace_advance;
continue
}
font.build_raw_undefined_glyph()
};
let scaled=glyph.scale(self.scale);
let rect=scaled.positioned_bounding_box(position);
let advance_width=scaled.advance_width();
let outlined=scaled.outline();
graphics.draw_glyph(
&outlined,
self.colour,
[rect[0],rect[1]],
#[cfg(feature="colour_filter")]colour_filter,
)?;
position[0]+=advance_width;
}
Ok(())
}
pub fn draw_shift_str<F:Font,S:Surface>(
&self,
s:&str,
shift:[f32;2],
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
for character in s.chars(){
glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character==' '{
position[0]+=whitespace_advance;
continue
}
font.build_raw_undefined_glyph()
};
let scaled=glyph.scale(self.scale);
let rect=scaled.positioned_bounding_box(position);
let advance_width=scaled.advance_width();
let outlined=scaled.outline();
graphics.draw_shift_glyph(
&outlined,
self.colour,
[rect[0],rect[1]],
shift,
#[cfg(feature="colour_filter")]colour_filter,
)?;
position[0]+=advance_width;
}
Ok(())
}
pub fn draw_rotate_str<F:Font,S:Surface>(
&self,
s:&str,
rotation_center:[f32;2],
angle:f32,
font:&F,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
let whitespace_advance=font.whitespace_advance_width(self.scale.horizontal);
for character in s.chars(){
glyph=if let Some(glyph)=font.build_raw_glyph(character){
glyph
}
else{
if character==' '{
position[0]+=whitespace_advance;
continue
}
font.build_raw_undefined_glyph()
};
let scaled=glyph.scale(self.scale);
let rect=scaled.positioned_bounding_box(position);
let advance_width=scaled.advance_width();
let outlined=scaled.outline();
graphics.draw_rotate_glyph(
&outlined,
self.colour,
[rect[0],rect[1]],
rotation_center,
angle,
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=advance_width;
}
Ok(())
}
}
impl TextBase{
#[inline(always)]
pub fn draw_char_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
character:char,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
glyph_cache.scaled_undefined_glyph(self.scale)
}
};
let rect=glyph.positioned_bounding_box(self.position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
#[cfg(feature="colour_filter")]colour_filter
)
}
#[inline(always)]
pub fn draw_shift_char_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
character:char,
shift:[f32;2],
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
glyph_cache.scaled_undefined_glyph(self.scale)
}
};
let rect=glyph.positioned_bounding_box(self.position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_shift_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
shift,
#[cfg(feature="colour_filter")]colour_filter
)
}
#[inline(always)]
pub fn draw_rotate_char_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
character:char,
rotation_center:[f32;2],
angle:f32,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character.is_whitespace(){
return Ok(())
}
else{
glyph_cache.scaled_undefined_glyph(self.scale)
}
};
let rect=glyph.positioned_bounding_box(self.position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_rotate_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
rotation_center,
angle,
#[cfg(feature="colour_filter")]colour_filter
)
}
pub fn draw_str_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
for character in s.chars(){
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(())
}
pub fn draw_shift_str_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
shift:[f32;2],
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
for character in s.chars(){
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_shift_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
shift,
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(())
}
pub fn draw_rotate_str_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
rotation_center:[f32;2],
angle:f32,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<(),DrawError>{
let mut position=self.position;
let mut glyph;
for character in s.chars(){
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_rotate_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
rotation_center,
angle,
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(())
}
pub fn draw_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
chars:usize,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<bool,DrawError>{
let mut whole=true;
let mut position=self.position;
let mut glyph;
for (i,character) in s.chars().enumerate(){
if i==chars{
whole=false;
break
}
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(whole)
}
pub fn draw_shift_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
chars:usize,
shift:[f32;2],
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<bool,DrawError>{
let mut whole=true;
let mut position=self.position;
let mut glyph;
for (i,character) in s.chars().enumerate(){
if i==chars{
whole=false;
break
}
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_shift_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
shift,
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(whole)
}
pub fn draw_rotate_str_part_glyph_cache<C:RawGlyphCache,S:Surface>(
&self,
s:&str,
chars:usize,
rotation_center:[f32;2],
angle:f32,
glyph_cache:&C,
#[cfg(feature="colour_filter")]colour_filter:ColourFilter,
graphics:&mut Graphics<S>
)->Result<bool,DrawError>{
let mut whole=true;
let mut position=self.position;
let mut glyph;
for (i,character) in s.chars().enumerate(){
if i==chars{
whole=false;
break
}
glyph=if let Some(glyph)=glyph_cache.scaled_glyph(character,self.scale){
glyph
}
else{
if character==' '{
position[0]+=glyph_cache.whitespace_advance_width(self.scale.horizontal);
continue
}
glyph_cache.scaled_undefined_glyph(self.scale)
};
let rect=glyph.positioned_bounding_box(position);
let textured=TexturedGlyph::raw(glyph.data(),[rect[2],rect[3]]);
graphics.draw_rotate_glyph_cache(
&textured,
self.colour,
[rect[0],rect[1]],
rotation_center,
angle,
#[cfg(feature="colour_filter")]colour_filter
)?;
position[0]+=glyph.advance_width();
}
Ok(whole)
}
}