1use crate::cx::*;
2use makepad_trapezoidator::Trapezoidator;
3use makepad_geometry::{AffineTransformation, Transform, Vector};
4use makepad_internal_iter::*;
5use makepad_path::PathIterator;
6
7impl Cx {
8 pub fn reset_font_atlas_and_redraw(&mut self){
14 for font in &mut self.fonts{
15 font.atlas_pages.truncate(0);
16 }
17 self.fonts_atlas.alloc_xpos = 0.;
18 self.fonts_atlas.alloc_ypos = 0.;
19 self.fonts_atlas.alloc_hmax = 0.;
20 self.fonts_atlas.clear_buffer = true;
21 self.redraw_child_area(Area::All);
22 }
23
24 pub fn load_font(&mut self, path: &str) -> Font {
25 let found = self.fonts.iter().position( | v | v.path == path);
26 if let Some(font_id) = found {
27 return Font {
28 font_id: Some(font_id),
29 }
30 }
31
32 let font_id = self.fonts.len();
33 self.fonts.push(CxFont {
34 path: path.to_string(),
35 ..Default::default()
36 });
37
38 return Font {
39 font_id: Some(font_id)
40 }
41 }
42}
43
44#[derive(Copy, Clone, Default)]
45pub struct Font{
46 pub font_id: Option<usize>
47}
48
49pub struct TrapezoidText {
50 shader: Shader,
51 trapezoidator: Trapezoidator
52}
53
54impl TrapezoidText {
55 pub fn style(cx: &mut Cx) -> Self {
56 Self {
57 shader: cx.add_shader(Self::def_trapezoid_shader(), "TrapezoidShader"),
58 trapezoidator: Trapezoidator::default(),
59 }
60 }
61
62 fn instance_a_xs()->InstanceVec2{uid!()}
63 fn instance_a_ys()->InstanceVec4{uid!()}
64 fn instance_chan()->InstanceFloat{uid!()}
65
66 pub fn def_trapezoid_shader() -> ShaderGen {
67 let mut sg = ShaderGen::new();
68 sg.geometry_vertices = vec![0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
69 sg.geometry_indices = vec![0, 1, 2, 2, 3, 0];
70
71 sg.compose(shader_ast!({
72
73 let geom: vec2<Geometry>;
74
75 let a_xs: Self::instance_a_xs();
76 let a_ys: Self::instance_a_ys();
77 let chan: Self::instance_chan();
78
79 let v_p0: vec2<Varying>;
80 let v_p1: vec2<Varying>;
81 let v_p2: vec2<Varying>;
82 let v_p3: vec2<Varying>;
83 let v_pixel: vec2<Varying>;
84
85 fn intersect_line_segment_with_vertical_line(p0: vec2, p1: vec2, x: float) -> vec2 {
86 return vec2(
87 x,
88 mix(p0.y, p1.y, (x - p0.x) / (p1.x - p0.x))
89 );
90 }
91
92 fn intersect_line_segment_with_horizontal_line(p0: vec2, p1: vec2, y: float) -> vec2 {
93 return vec2(
94 mix(p0.x, p1.x, (y - p0.y) / (p1.y - p0.y)),
95 y
96 );
97 }
98
99 fn compute_clamped_right_trapezoid_area(p0: vec2, p1: vec2, p_min: vec2, p_max: vec2) -> float {
100 let x0 = clamp(p0.x, p_min.x, p_max.x);
101 let x1 = clamp(p1.x, p_min.x, p_max.x);
102 if (p0.x < p_min.x && p_min.x < p1.x) {
103 p0 = intersect_line_segment_with_vertical_line(p0, p1, p_min.x);
104 }
105 if (p0.x < p_max.x && p_max.x < p1.x) {
106 p1 = intersect_line_segment_with_vertical_line(p0, p1, p_max.x);
107 }
108 if (p0.y < p_min.y && p_min.y < p1.y) {
109 p0 = intersect_line_segment_with_horizontal_line(p0, p1, p_min.y);
110 }
111 if (p1.y < p_min.y && p_min.y < p0.y) {
112 p1 = intersect_line_segment_with_horizontal_line(p1, p0, p_min.y);
113 }
114 if (p0.y < p_max.y && p_max.y < p1.y) {
115 p1 = intersect_line_segment_with_horizontal_line(p0, p1, p_max.y);
116 }
117 if (p1.y < p_max.y && p_max.y < p0.y) {
118 p0 = intersect_line_segment_with_horizontal_line(p1, p0, p_max.y);
119 }
120 p0 = clamp(p0, p_min, p_max);
121 p1 = clamp(p1, p_min, p_max);
122 let h0 = p_max.y - p0.y;
123 let h1 = p_max.y - p1.y;
124 let a0 = (p0.x - x0) * h0;
125 let a1 = (p1.x - p0.x) * (h0 + h1) * 0.5;
126 let a2 = (x1 - p1.x) * h1;
127 return a0 + a1 + a2;
128 }
129
130 fn compute_clamped_trapezoid_area(p_min: vec2, p_max: vec2) -> float {
131 let a0 = compute_clamped_right_trapezoid_area(v_p0, v_p1, p_min, p_max);
132 let a1 = compute_clamped_right_trapezoid_area(v_p2, v_p3, p_min, p_max);
133 return a0 - a1;
134 }
135
136 fn pixel() -> vec4 {
137 let p_min = v_pixel.xy - 0.5;
142 let p_max = v_pixel.xy + 0.5;
143 let t_area = compute_clamped_trapezoid_area(p_min, p_max);
144 if chan < 0.5{
145 return vec4(t_area,0.,0.,0.);
146 }
147 if chan < 1.5{
148 return vec4(0., t_area, 0.,0.);
149 }
150 if chan < 2.5{
151 return vec4(0., 0., t_area,0.);
152 }
153 return vec4(t_area, t_area, t_area,0.);
154
155 }
166
167 fn vertex() -> vec4 {
168 let pos_min = vec2(a_xs.x, min(a_ys.x, a_ys.y));
169 let pos_max = vec2(a_xs.y, max(a_ys.z, a_ys.w));
170 let pos = mix(pos_min - 1.0, pos_max + 1.0, geom);
171
172 v_p0 = vec2(a_xs.x, a_ys.x);
174 v_p1 = vec2(a_xs.y, a_ys.y);
175 v_p2 = vec2(a_xs.x, a_ys.z);
176 v_p3 = vec2(a_xs.y, a_ys.w);
177 v_pixel = pos;
178 return camera_projection * vec4(pos, 0.0, 1.0);
180 }
181 }))
182 }
183
184 pub fn draw_char(&mut self, cx: &mut Cx, c:char, font_id:usize, font_size:f32) {
186 let inst = cx.new_instance(&self.shader, 1);
188 if inst.need_uniforms_now(cx) {
189 }
190
191 let trapezoids = {
192 let cxfont = &cx.fonts[font_id];
193 let font = cxfont.font_loaded.as_ref().unwrap();
194
195 let slot = if c < '\u{10000}' {
196 cx.fonts[font_id].font_loaded.as_ref().unwrap().char_code_to_glyph_index_map[c as usize]
197 } else {
198 0
199 };
200
201 if slot == 0 {
202 return
203 }
204 let glyph = &cx.fonts[font_id].font_loaded.as_ref().unwrap().glyphs[slot];
205 let dpi_factor = cx.current_dpi_factor;
206 let pos = cx.get_turtle_pos();
207 let font_scale_logical = font_size * 96.0 / (72.0 * font.units_per_em);
208 let font_scale_pixels = font_scale_logical * dpi_factor;
209
210 let mut trapezoids = Vec::new();
211 trapezoids.extend_from_internal_iter(
212 self.trapezoidator.trapezoidate(
213 glyph
214 .outline
215 .commands()
216 .map({
217 move | command | {
218 command.transform(
219 &AffineTransformation::identity()
220 .translate(Vector::new(-glyph.bounds.p_min.x, -glyph.bounds.p_min.y))
221 .uniform_scale(font_scale_pixels)
222 .translate(Vector::new(pos.x, pos.y))
223 )
224 }
225 }).linearize(0.5),
226 ),
227 );
228 trapezoids
229 };
230 for trapezoid in trapezoids {
231 let data = [
232 trapezoid.xs[0],
233 trapezoid.xs[1],
234 trapezoid.ys[0],
235 trapezoid.ys[1],
236 trapezoid.ys[2],
237 trapezoid.ys[3],
238 3.0
239 ];
240 inst.push_slice(cx, &data);
241 }
242 }
243
244 pub fn draw_todo(&mut self, cx: &mut Cx, todo: CxFontsAtlasTodo) {
246 let inst = cx.new_instance(&self.shader, 1);
247 if inst.need_uniforms_now(cx) {
248 }
249
250 let mut size = 1.0;
251 for i in 0..3{
252 if i == 1{
253 size = 0.75;
254 }
255 if i == 2{
256 size = 0.6;
257 }
258 let trapezoids = {
259 let cxfont = &cx.fonts[todo.font_id];
260 let font = cxfont.font_loaded.as_ref().unwrap();
261 let atlas_page = &cxfont.atlas_pages[todo.atlas_page_id];
262 let glyph = &font.glyphs[todo.glyph_id];
263
264 if todo.glyph_id == font.char_code_to_glyph_index_map[10] ||
265 todo.glyph_id == font.char_code_to_glyph_index_map[9] ||
266 todo.glyph_id == font.char_code_to_glyph_index_map[13] {
267 return
268 }
269
270 let glyphtc = atlas_page.atlas_glyphs[todo.glyph_id][todo.subpixel_id].unwrap();
271 let tx = glyphtc.tx1 * cx.fonts_atlas.texture_size.x + todo.subpixel_x_fract* atlas_page.dpi_factor;
272 let ty = 1.0 + glyphtc.ty1 * cx.fonts_atlas.texture_size.y - todo.subpixel_y_fract* atlas_page.dpi_factor;
273
274 let font_scale_logical = atlas_page.font_size * 96.0 / (72.0 * font.units_per_em);
275 let font_scale_pixels = font_scale_logical * atlas_page.dpi_factor;
276 let mut trapezoids = Vec::new();
277 trapezoids.extend_from_internal_iter(
278 self.trapezoidator.trapezoidate(
279 glyph
280 .outline
281 .commands()
282 .map({
283 move | command | {
284 command.transform(
285 &AffineTransformation::identity()
286 .translate(Vector::new(-glyph.bounds.p_min.x, -glyph.bounds.p_min.y))
287 .uniform_scale(font_scale_pixels * size)
288 .translate(Vector::new(tx, ty))
289 )
290 }
291 }).linearize(0.5),
292 ),
293 );
294 trapezoids
295 };
296 for trapezoid in trapezoids {
297 let data = [
298 trapezoid.xs[0],
299 trapezoid.xs[1],
300 trapezoid.ys[0],
301 trapezoid.ys[1],
302 trapezoid.ys[2],
303 trapezoid.ys[3],
304 i as f32
305 ];
306 inst.push_slice(cx, &data);
307 }
308 }
309 }
310}
311
312pub struct CxAfterDraw {
313 pub trapezoid_text: TrapezoidText,
314 pub atlas_pass: Pass,
315 pub atlas_view: View,
316 pub atlas_texture: Texture
317}
318
319impl CxAfterDraw {
320 pub fn new(cx: &mut Cx) -> Self {
321 cx.fonts_atlas.texture_size = Vec2 {x: 2048.0, y: 2048.0};
322 let mut atlas_texture = Texture::default();
323 atlas_texture.set_desc(cx, None);
324 cx.fonts_atlas.texture_id = atlas_texture.texture_id.unwrap();
325
326 Self {
327 trapezoid_text: TrapezoidText::style(cx),
328 atlas_pass: Pass::default(),
329 atlas_view: View {
330 always_redraw: true,
331 ..View::new(cx)
332 },
333 atlas_texture: atlas_texture
334 }
335 }
336
337 pub fn after_draw(&mut self, cx: &mut Cx) {
338 if cx.fonts_atlas.atlas_todo.len()>0 {
342 self.atlas_pass.begin_pass(cx);
343 self.atlas_pass.set_size(cx, cx.fonts_atlas.texture_size);
344 let clear = if cx.fonts_atlas.clear_buffer{
345 cx.fonts_atlas.clear_buffer = false;
346 ClearColor::ClearWith(Color::default())
347 }
348 else{
349 ClearColor::InitWith(Color::default())
350 };
351 self.atlas_pass.add_color_texture(cx, &mut self.atlas_texture, clear);
352 let _ = self.atlas_view.begin_view(cx, Layout::default());
353 let mut atlas_todo = Vec::new();
354 std::mem::swap(&mut cx.fonts_atlas.atlas_todo, &mut atlas_todo);
355 for todo in atlas_todo {
356 self.trapezoid_text.draw_todo(cx, todo);
357 }
360 self.atlas_view.end_view(cx);
361 self.atlas_pass.end_pass(cx);
362 }
363 }
365}
366
367#[derive(Default)]
368pub struct CxFont {
369 pub path: String,
370 pub font_loaded: Option<makepad_font::Font>,
371 pub atlas_pages: Vec<CxFontAtlasPage>,
372}
373
374pub const ATLAS_SUBPIXEL_SLOTS: usize = 64;
375
376pub struct CxFontAtlasPage {
377 pub dpi_factor: f32,
378 pub font_size: f32,
379 pub atlas_glyphs: Vec<[Option<CxFontAtlasGlyph>; ATLAS_SUBPIXEL_SLOTS]>
380}
381
382#[derive(Clone, Copy)]
383pub struct CxFontAtlasGlyph {
384 pub tx1: f32,
385 pub ty1: f32,
386 pub tx2: f32,
387 pub ty2: f32,
388}
389
390#[derive(Default)]
391pub struct CxFontsAtlasTodo {
392 pub subpixel_x_fract: f32,
393 pub subpixel_y_fract: f32,
394 pub font_id: usize,
395 pub atlas_page_id: usize,
396 pub glyph_id: usize,
397 pub subpixel_id: usize
398}
399
400#[derive(Default)]
401pub struct CxFontsAtlas {
402 pub texture_id: usize,
403 pub texture_size: Vec2,
404 pub clear_buffer: bool,
405 pub alloc_xpos: f32,
406 pub alloc_ypos: f32,
407 pub alloc_hmax: f32,
408 pub atlas_todo: Vec<CxFontsAtlasTodo>,
409}
410
411impl CxFontsAtlas {
412 pub fn alloc_atlas_glyph(&mut self, path: &str, w: f32, h: f32) -> CxFontAtlasGlyph {
413 if w + self.alloc_xpos >= self.texture_size.x {
414 self.alloc_xpos = 0.0;
415 self.alloc_ypos += self.alloc_hmax + 1.0;
416 self.alloc_hmax = 0.0;
417 }
418 if h + self.alloc_ypos >= self.texture_size.y {
419 println!("FONT ATLAS FULL {}, TODO FIX THIS", path);
420 }
421 if h > self.alloc_hmax {
422 self.alloc_hmax = h;
423 }
424
425 let tx1 = self.alloc_xpos / self.texture_size.x;
426 let ty1 = self.alloc_ypos / self.texture_size.y;
427
428 self.alloc_xpos += w + 1.0;
429
430 if h > self.alloc_hmax {
431 self.alloc_hmax = h;
432 }
433
434 CxFontAtlasGlyph {
435 tx1: tx1,
436 ty1: ty1,
437 tx2: tx1 + (w / self.texture_size.x),
438 ty2: ty1 + (h / self.texture_size.y)
439 }
440 }
441}
442
443impl CxFont {
444 pub fn load_from_ttf_bytes(&mut self, bytes: &[u8]) -> makepad_ttf_parser::Result<()> {
445 let font = makepad_ttf_parser::parse_ttf(bytes) ?;
446 self.font_loaded = Some(font);
447 Ok(())
448 }
449
450 pub fn get_atlas_page_id(&mut self, dpi_factor: f32, font_size: f32) -> usize {
451 for (index, sg) in self.atlas_pages.iter().enumerate() {
452 if sg.dpi_factor == dpi_factor
453 && sg.font_size == font_size {
454 return index
455 }
456 }
457 if let Some(font) = &self.font_loaded {
458 self.atlas_pages.push(CxFontAtlasPage {
459 dpi_factor: dpi_factor,
460 font_size: font_size,
461 atlas_glyphs: {
462 let mut v = Vec::new();
463 v.resize(font.glyphs.len(), [None; ATLAS_SUBPIXEL_SLOTS]);
464 v
465 }
466 });
467 self.atlas_pages.len() - 1
468 }
469 else {
470 panic!("Font not loaded {}", self.path);
471 }
472 }
473}