1pub mod freetype {
2 use ndless::prelude::*;
3 use unicode_segmentation::UnicodeSegmentation;
4
5 use crate::video::Color::{RGB, RGBA};
6 use crate::{video::Color, video::Surface, video::SurfaceFlag::SWSurface};
7
8 #[derive(Debug, PartialEq, Clone)]
9 pub struct Text<'a> {
10 text: String,
11 matrix: ndless_freetype::Matrix,
12 surface: Option<Surface>,
13 up_to_date: bool,
14 height: isize,
15 font: &'a ndless_freetype::Face,
16 color: Color,
17 }
18
19 impl<'a> Text<'a> {
20 pub fn new(font: &'a ndless_freetype::Face) -> Self {
21 Self {
22 text: "".to_string(),
23 matrix: Self::radians_to_matrix(0.),
24 surface: None,
25 up_to_date: false,
26 height: 40,
27 font,
28 color: RGB(0, 0, 0),
29 }
30 }
31 pub fn text(&mut self, str: impl Into<String>) -> &mut Self {
32 let str = str.into();
33 if str != self.text {
34 self.text = str;
35 self.up_to_date = false;
36 }
37 self
38 }
39 pub fn font(&mut self, font: &'a ndless_freetype::Face) -> &mut Self {
40 if font != self.font {
41 self.font = font;
42 self.up_to_date = false;
43 }
44 self
45 }
46 pub fn color(&mut self, color: Color) -> &mut Self {
47 if color != self.color {
48 self.color = color;
49 self.up_to_date = false;
50 }
51 self
52 }
53 pub fn height(&mut self, height: isize) -> &mut Self {
54 if height != self.height {
55 self.height = height;
56 self.up_to_date = false;
57 }
58 self
59 }
60 fn radians_to_matrix(angle: f64) -> ndless_freetype::Matrix {
61 ndless_freetype::Matrix {
62 xx: (angle.cos() * f64::from(0x10000)) as ndless_freetype::FT_Fixed,
63 xy: (-angle.sin() * f64::from(0x10000)) as ndless_freetype::FT_Fixed,
64 yx: (angle.sin() * f64::from(0x10000)) as ndless_freetype::FT_Fixed,
65 yy: (angle.cos() * f64::from(0x10000)) as ndless_freetype::FT_Fixed,
66 }
67 }
68 pub fn reallocate(&mut self) {
81 self.surface = None;
82 }
83 #[allow(clippy::many_single_char_names)]
84 pub fn render(&mut self) -> &Surface {
85 if self.up_to_date && self.surface.as_ref().is_some() {
86 return self.surface.as_ref().unwrap();
87 }
88 self.font.set_char_size(self.height * 64, 0, 50, 0).unwrap();
89 let chars = self.text.graphemes(true);
90 let mut pen = ndless_freetype::Vector { x: 0, y: 0 };
91 let mut max_height = 0;
92 let mut baseline_height = 0;
93 let mut max_width = 0;
94 let mut min_height = 0;
95 for letter in chars.clone() {
96 self.font.set_transform(&mut self.matrix, &mut pen);
97 self.font
98 .load_char(
99 letter.chars().next().unwrap() as usize,
100 ndless_freetype::face::LoadFlag::RENDER,
101 )
102 .unwrap();
103 let glyph = self.font.glyph();
104 let cbox = glyph.get_glyph().unwrap().get_cbox(0);
105 if cbox.xMax / 64 > max_width {
106 max_width = cbox.xMax / 64
107 }
108 if cbox.yMin / 64 < min_height {
109 min_height = cbox.yMin / 64
110 }
111 if cbox.yMax / 64 > max_height {
112 max_height = cbox.yMax / 64
113 }
114 let y = glyph.bitmap_top() as usize;
115 pen.x += glyph.advance().x;
116 pen.y += glyph.advance().y;
117 if y > baseline_height {
118 baseline_height = y;
119 }
120 }
121 let max_height = -min_height + max_height;
122 let reassign = match &self.surface {
123 Some(surface) => {
124 surface.get_width() < max_width as u16
125 || surface.get_height() < max_height as u16
126 }
127 None => true,
128 };
129 if reassign {
130 self.surface = Some(
131 Surface::new(
132 &[SWSurface],
133 max_width as isize,
134 max_height as isize,
135 32,
136 0xFF00_0000,
137 0x00FF_0000,
138 0x0000_FF00,
139 0x0000_00FF,
140 )
141 .unwrap(),
142 );
143 } else {
144 self.surface.as_ref().unwrap().fill(RGBA(0, 0, 0, 0));
145 }
146 let scr = self.surface.as_ref().unwrap();
147 let mut pen = ndless_freetype::Vector { x: 0, y: 0 };
148 let max_width = scr.get_width();
149 let max_height = scr.get_height();
150 for letter in chars {
151 self.font.set_transform(&mut self.matrix, &mut pen);
152 self.font
153 .load_char(
154 letter.chars().next().unwrap() as usize,
155 ndless_freetype::face::LoadFlag::RENDER,
156 )
157 .unwrap();
158 let glyph = self.font.glyph();
159 let bitmap = glyph.bitmap();
160 let x = glyph.bitmap_left() as usize;
161 let y = baseline_height - glyph.bitmap_top() as usize;
162 let mut col = 0;
163 let width = bitmap.width() as usize;
164 let x_max = x + width;
165 let y_max = y + bitmap.rows() as usize;
166
167 for (row, x_scaled) in (x..x_max).enumerate() {
168 for y_scaled in y..y_max {
169 if x_scaled < max_width as usize && y_scaled < max_height as usize {
170 let alpha = bitmap.buffer()[col * width + row];
171 scr.fill_rect(
172 Some(crate::Rect {
173 x: x_scaled as i16,
174 y: y_scaled as i16,
175 w: 1,
176 h: 1,
177 }),
178 match self.color {
179 RGB(r, g, b) => RGBA(r, g, b, alpha),
180 RGBA(r, g, b, _) => RGBA(r, g, b, alpha),
181 },
182 );
183 col += 1;
184 }
185 }
186 col = 0;
187 }
188 pen.x += glyph.advance().x;
189 pen.y += glyph.advance().y;
190 }
191 scr
192 }
193 }
194}