1pub use {
2 std::{
3 borrow::Borrow,
4 collections::VecDeque,
5 hash::{Hash, Hasher},
6 rc::Rc,
7 cell::RefCell,
8 io::prelude::*,
9 fs::File,
10 collections::HashMap,
11 },
12 crate::{
13 makepad_platform::*,
14 cx_2d::Cx2d,
15 turtle::{Walk, Layout},
16 draw_list_2d::{ManyInstances, DrawList2d, RedrawingApi},
17 geometry::GeometryQuad2D,
18 shader::draw_trapezoid::DrawTrapezoidVector,
19 makepad_vector::font::Glyph,
20 makepad_vector::trapezoidator::Trapezoidator,
21 makepad_vector::geometry::{AffineTransformation, Transform, Vector},
22 makepad_vector::internal_iter::ExtendFromInternalIterator,
23 makepad_vector::path::PathIterator,
24 },
25 rustybuzz::{Direction, GlyphInfo, UnicodeBuffer},
26};
27
28pub struct CxFontsAtlas {
29 pub fonts: Vec<Option<CxFont >>,
30 pub path_to_font_id: HashMap<String, usize>,
31 pub texture_id: TextureId,
32 pub clear_buffer: bool,
33 pub alloc: CxFontsAtlasAlloc
34}
35
36#[derive(Default)]
37pub struct CxFontsAtlasAlloc {
38 pub texture_size: DVec2,
39 pub full: bool,
40 pub xpos: f64,
41 pub ypos: f64,
42 pub hmax: f64,
43 pub todo: Vec<CxFontsAtlasTodo>,
44}
45
46impl CxFontsAtlas {
47 pub fn new(texture_id: TextureId) -> Self {
48 Self {
49 fonts: Vec::new(),
50 path_to_font_id: HashMap::new(),
51 texture_id,
52 clear_buffer: false,
53 alloc: CxFontsAtlasAlloc {
54 full: false,
55 texture_size: DVec2 {x: 4096.0, y: 4096.0},
56 xpos: 0.0,
57 ypos: 0.0,
58 hmax: 0.0,
59 todo: Vec::new(),
60 }
61 }
62 }
63}
64impl CxFontsAtlasAlloc {
65 pub fn alloc_atlas_glyph(&mut self, w: f64, h: f64) -> CxFontAtlasGlyph {
66 if w + self.xpos >= self.texture_size.x {
67 self.xpos = 0.0;
68 self.ypos += self.hmax + 1.0;
69 self.hmax = 0.0;
70 }
71 if h + self.ypos >= self.texture_size.y {
72 self.full = true;
74 println!("FONT ATLAS FULL, TODO FIX THIS {} > {},", h + self.ypos, self.texture_size.y);
75 }
76 if h > self.hmax {
77 self.hmax = h;
78 }
79
80 let tx1 = self.xpos / self.texture_size.x;
81 let ty1 = self.ypos / self.texture_size.y;
82
83 self.xpos += w + 1.0;
84
85 CxFontAtlasGlyph {
86 t1: dvec2(tx1, ty1).into(),
87 t2: dvec2( tx1 + (w / self.texture_size.x), ty1 + (h / self.texture_size.y)).into()
88 }
89 }
90}
91
92#[derive(Clone, Live)]
93pub struct Font {
94 #[rust] pub font_id: Option<usize>,
95 #[live] pub path: LiveDependency
96}
97
98#[derive(Clone)]
99pub struct CxFontsAtlasRc(pub Rc<RefCell<CxFontsAtlas >>);
100
101impl LiveHook for Font {
102 fn after_apply(&mut self, cx: &mut Cx, _apply_from: ApplyFrom, _index: usize, _nodes: &[LiveNode]) {
103 Cx2d::lazy_construct_font_atlas(cx);
104 let atlas = cx.get_global::<CxFontsAtlasRc>().clone();
105 self.font_id = Some(atlas.0.borrow_mut().get_font_by_path(cx, self.path.as_str()));
106 }
107}
108
109impl CxFontsAtlas {
110 pub fn get_font_by_path(&mut self, cx: &Cx, path: &str) -> usize {
111 if path.len() == 0{
112 return 0
113 }
114 if let Some(item) = self.path_to_font_id.get(path) {
115 return *item;
116 }
117 let font_id = self.fonts.len();
118 self.fonts.push(None);
119 self.path_to_font_id.insert(path.to_string(), font_id);
120
121 match cx.get_dependency(path) {
122 Ok(data) => match CxFont::load_from_ttf_bytes(data) {
125 Err(_) => {
126 error!("Error loading font {} ", path);
127 }
128 Ok(cxfont) => {
129 self.fonts[font_id] = Some(cxfont);
130 }
131 }
132 Err(err) => {
133 error!("get_font_by_path - {} {}", path, err)
134 }
135 }
136 font_id
137 }
138
139 pub fn reset_fonts_atlas(&mut self) {
140 for cxfont in &mut self.fonts {
141 if let Some(cxfont) = cxfont {
142 cxfont.atlas_pages.clear();
143 }
144 }
145 self.alloc.xpos = 0.;
146 self.alloc.ypos = 0.;
147 self.alloc.hmax = 0.;
148 self.clear_buffer = true;
149 }
150
151 pub fn get_internal_font_atlas_texture_id(&self) -> TextureId {
152 self.texture_id
153 }
154}
155
156impl DrawTrapezoidVector {
157
158 fn draw_todo(&mut self, fonts_atlas: &mut CxFontsAtlas, todo: CxFontsAtlasTodo, many: &mut ManyInstances) {
160 let mut size = 1.0;
162 for i in 0..1 {
163 if i == 1 {
164 size = 0.75;
165 }
166 if i == 2 {
167 size = 0.6;
168 }
169 let trapezoids = {
170 let cxfont = fonts_atlas.fonts[todo.font_id].as_mut().unwrap();
171 let units_per_em = cxfont.ttf_font.units_per_em;
172 let atlas_page = &cxfont.atlas_pages[todo.atlas_page_id];
173 let glyph = cxfont.owned_font_face.with_ref(|face| cxfont.ttf_font.get_glyph_by_id(face, todo.glyph_id).unwrap());
174
175 let is_one_of_tab_lf_cr = ['\t', '\n', '\r'].iter().any(|&c| {
176 Some(todo.glyph_id) == cxfont.owned_font_face.with_ref(|face| face.glyph_index(c).map(|id| id.0 as usize))
177 });
178 if is_one_of_tab_lf_cr {
179 return
180 }
181
182 let glyphtc = atlas_page.atlas_glyphs.get(&todo.glyph_id).unwrap()[todo.subpixel_id].unwrap();
183 let tx = glyphtc.t1.x as f64 * fonts_atlas.alloc.texture_size.x + todo.subpixel_x_fract * atlas_page.dpi_factor;
184 let ty = 1.0 + glyphtc.t1.y as f64 * fonts_atlas.alloc.texture_size.y - todo.subpixel_y_fract * atlas_page.dpi_factor;
185
186 let font_scale_logical = atlas_page.font_size * 96.0 / (72.0 * units_per_em);
187 let font_scale_pixels = font_scale_logical * atlas_page.dpi_factor;
188 let mut trapezoids = Vec::new();
189 let trapezoidate = self.trapezoidator.trapezoidate(
191 glyph
192 .outline
193 .iter()
194 .map({
195 move | command | {
196 let cmd = command.transform(
197 &AffineTransformation::identity()
198 .translate(Vector::new(-glyph.bounds.p_min.x, -glyph.bounds.p_min.y))
199 .uniform_scale(font_scale_pixels * size)
200 .translate(Vector::new(tx, ty))
201 );
202
203 cmd
204 }
205 }).linearize(0.5),
206 );
207 if let Some(trapezoidate) = trapezoidate {
208 trapezoids.extend_from_internal_iter(
209 trapezoidate
210 );
211 }
212 trapezoids
213 };
214 for trapezoid in trapezoids {
215 self.a_xs = Vec2 {x: trapezoid.xs[0], y: trapezoid.xs[1]};
216 self.a_ys = Vec4 {x: trapezoid.ys[0], y: trapezoid.ys[1], z: trapezoid.ys[2], w: trapezoid.ys[3]};
217 self.chan = i as f32;
218 many.instances.extend_from_slice(self.draw_vars.as_slice());
219 }
220 }
221 }
222}
223
224#[derive(Clone)]
225pub struct CxDrawFontsAtlasRc(pub Rc<RefCell<CxDrawFontsAtlas >>);
226
227pub struct CxDrawFontsAtlas {
228 pub draw_trapezoid: DrawTrapezoidVector,
229 pub atlas_pass: Pass,
230 pub atlas_draw_list: DrawList2d,
231 pub atlas_texture: Texture,
232 pub counter: usize
233}
234
235impl CxDrawFontsAtlas {
236 pub fn new(cx: &mut Cx) -> Self {
237
238 let atlas_texture = Texture::new(cx);
239
240 let draw_trapezoid = DrawTrapezoidVector::new_local(cx);
243 Self {
245 counter: 0,
246 draw_trapezoid,
247 atlas_pass: Pass::new(cx),
248 atlas_draw_list: DrawList2d::new(cx),
249 atlas_texture: atlas_texture
250 }
251 }
252}
253
254impl<'a> Cx2d<'a> {
255 pub fn lazy_construct_font_atlas(cx: &mut Cx){
256 if !cx.has_global::<CxFontsAtlasRc>() {
258
259 let draw_fonts_atlas = CxDrawFontsAtlas::new(cx);
260 let texture_id = draw_fonts_atlas.atlas_texture.texture_id();
261 cx.set_global(CxDrawFontsAtlasRc(Rc::new(RefCell::new(draw_fonts_atlas))));
262
263 let fonts_atlas = CxFontsAtlas::new(texture_id);
264 cx.set_global(CxFontsAtlasRc(Rc::new(RefCell::new(fonts_atlas))));
265 }
266 }
267
268 pub fn reset_fonts_atlas(cx:&mut Cx){
269 if cx.has_global::<CxFontsAtlasRc>() {
270 let mut fonts_atlas = cx.get_global::<CxFontsAtlasRc>().0.borrow_mut();
271 fonts_atlas.reset_fonts_atlas();
272 }
273 }
274
275 pub fn draw_font_atlas(&mut self) {
276 let draw_fonts_atlas_rc = self.cx.get_global::<CxDrawFontsAtlasRc>().clone();
277 let mut draw_fonts_atlas = draw_fonts_atlas_rc.0.borrow_mut();
278 let fonts_atlas_rc = self.fonts_atlas_rc.clone();
279 let mut fonts_atlas = fonts_atlas_rc.0.borrow_mut();
280 let fonts_atlas = &mut*fonts_atlas;
281 if fonts_atlas.alloc.todo.len()>0 {
284 self.begin_pass(&draw_fonts_atlas.atlas_pass, None);
285
286 let texture_size = fonts_atlas.alloc.texture_size;
287 draw_fonts_atlas.atlas_pass.set_size(self.cx, texture_size);
288
289 let clear = if fonts_atlas.clear_buffer {
290 fonts_atlas.clear_buffer = false;
291 PassClearColor::ClearWith(Vec4::default())
292 }
293 else {
294 PassClearColor::InitWith(Vec4::default())
295 };
296
297 draw_fonts_atlas.atlas_pass.clear_color_textures(self.cx);
298 draw_fonts_atlas.atlas_pass.add_color_texture(self.cx, &draw_fonts_atlas.atlas_texture, clear);
299 draw_fonts_atlas.atlas_draw_list.begin_always(self);
300
301 let mut atlas_todo = Vec::new();
302 std::mem::swap(&mut fonts_atlas.alloc.todo, &mut atlas_todo);
303
304 if let Some(mut many) = self.begin_many_instances(&draw_fonts_atlas.draw_trapezoid.draw_vars) {
305
306 for todo in atlas_todo {
307 draw_fonts_atlas.draw_trapezoid.draw_todo(fonts_atlas, todo, &mut many);
308 }
309
310 self.end_many_instances(many);
311 }
312
313 draw_fonts_atlas.counter += 1;
314 draw_fonts_atlas.atlas_draw_list.end(self);
315 self.end_pass(&draw_fonts_atlas.atlas_pass);
316 }
317 }
319}
320
321pub struct CxFont {
322 pub ttf_font: makepad_vector::font::TTFFont,
323 pub owned_font_face: crate::owned_font_face::OwnedFace,
324 pub atlas_pages: Vec<CxFontAtlasPage>,
325 pub shape_cache: ShapeCache,
326}
327
328pub struct ShapeCache {
329 pub keys: VecDeque<(Direction, Rc<str>)>,
330 pub glyph_ids: HashMap<(Direction, Rc<str>), Vec<usize>>,
331}
332
333impl ShapeCache {
334 const MAX_SIZE: usize = 4096;
336
337 pub fn new() -> Self {
338 Self {
339 keys: VecDeque::new(),
340 glyph_ids: HashMap::new(),
341 }
342 }
343
344 pub fn get_or_compute_glyph_ids(
358 &mut self,
359 key: (Direction, &str),
360 mut rustybuzz_buffer: UnicodeBuffer,
361 owned_font_face: &crate::owned_font_face::OwnedFace
362 ) -> (&[usize], UnicodeBuffer) {
363 if !self.glyph_ids.contains_key(&key as &dyn ShapeCacheKey) {
364 if self.keys.len() == Self::MAX_SIZE {
365 for run in self.keys.drain(..Self::MAX_SIZE / 2) {
366 self.glyph_ids.remove(&run);
367 }
368 }
369
370 let (direction, string) = key;
371 rustybuzz_buffer.set_direction(direction);
372 rustybuzz_buffer.push_str(string);
373 let glyph_buffer = owned_font_face.with_ref( | face | rustybuzz::shape(face, &[], rustybuzz_buffer));
374 let glyph_ids: Vec<_> = glyph_buffer.glyph_infos().iter().map( | glyph | glyph.glyph_id as usize).collect();
375 rustybuzz_buffer = glyph_buffer.clear();
376
377 let owned_string: Rc<str> = string.into();
378 self.keys.push_back((direction, owned_string.clone()));
379 self.glyph_ids.insert((direction, owned_string), glyph_ids);
380 }
381 (&self.glyph_ids[&key as &dyn ShapeCacheKey], rustybuzz_buffer)
382 }
383}
384
385pub trait ShapeCacheKey {
402 fn direction(&self) -> Direction;
403 fn string(&self) -> &str;
404}
405
406impl<'a> Borrow<dyn ShapeCacheKey + 'a> for (Direction, Rc<str>) {
407 fn borrow(&self) -> &(dyn ShapeCacheKey + 'a) {
408 self
409 }
410}
411
412impl Eq for dyn ShapeCacheKey + '_ {}
413
414impl Hash for dyn ShapeCacheKey + '_ {
415 fn hash<H: Hasher>(&self, hasher: &mut H) {
416 self.direction().hash(hasher);
417 self.string().hash(hasher);
418 }
419}
420
421impl PartialEq for dyn ShapeCacheKey + '_ {
422 fn eq(&self, other: &Self) -> bool {
423 if self.direction() != other.direction() {
424 return false;
425 }
426 if self.string() != other.string() {
427 return false;
428 }
429 true
430 }
431}
432
433impl ShapeCacheKey for (Direction, &str) {
434 fn direction(&self) -> Direction {
435 self.0
436 }
437
438 fn string(&self) -> &str {
439 self.1
440 }
441}
442
443impl ShapeCacheKey for (Direction, Rc<str>) {
444 fn direction(&self) -> Direction {
445 self.0
446 }
447
448 fn string(&self) -> &str {
449 &self.1
450 }
451}
452
453pub const ATLAS_SUBPIXEL_SLOTS: usize = 64;
454
455#[derive(Clone)]
456pub struct CxFontAtlasPage {
457 pub dpi_factor: f64,
458 pub font_size: f64,
459 pub atlas_glyphs: HashMap<usize,[Option<CxFontAtlasGlyph>; ATLAS_SUBPIXEL_SLOTS]>
460}
461
462#[derive(Clone, Copy)]
463pub struct CxFontAtlasGlyph {
464 pub t1: Vec2,
465 pub t2: Vec2,
466}
467
468#[derive(Default, Debug)]
469pub struct CxFontsAtlasTodo {
470 pub subpixel_x_fract: f64,
471 pub subpixel_y_fract: f64,
472 pub font_id: usize,
473 pub atlas_page_id: usize,
474 pub glyph_id: usize,
475 pub subpixel_id: usize
476}
477
478impl CxFont {
479 pub fn load_from_ttf_bytes(bytes: Rc<Vec<u8>>) -> Result<Self, crate::owned_font_face::FaceParsingError> {
480 let owned_font_face = crate::owned_font_face::OwnedFace::parse(bytes, 0)?;
481 let ttf_font = owned_font_face.with_ref(|face| makepad_vector::ttf_parser::from_ttf_parser_face(face));
482 Ok(Self {
483 ttf_font,
484 owned_font_face,
485 atlas_pages: Vec::new(),
486 shape_cache: ShapeCache::new(),
487 })
488 }
489
490 pub fn get_atlas_page_id(&mut self, dpi_factor: f64, font_size: f64) -> usize {
491 for (index, sg) in self.atlas_pages.iter().enumerate() {
492 if sg.dpi_factor == dpi_factor
493 && sg.font_size == font_size {
494 return index
495 }
496 }
497 self.atlas_pages.push(CxFontAtlasPage {
498 dpi_factor: dpi_factor,
499 font_size: font_size,
500 atlas_glyphs:HashMap::new(),});
506 self.atlas_pages.len() - 1
507 }
508
509 pub fn get_glyph(&mut self, c:char)->Option<&Glyph>{
510 if c < '\u{10000}' {
511 Some(self.get_glyph_by_id(self.owned_font_face.with_ref(|face| face.glyph_index(c))?.0 as usize).unwrap())
512 } else {
513 None
514 }
515 }
516
517 pub fn get_glyph_by_id(&mut self, id: usize) -> makepad_vector::ttf_parser::Result<&Glyph> {
518 self.owned_font_face.with_ref(|face| self.ttf_font.get_glyph_by_id(face, id))
519 }
520}