1use super::*;
2use GL::font::*;
3
4pub struct Text<'r, 'a> {
5 pub pos: Vec2,
6 pub scale: f32,
7 pub color: Color,
8 pub text: &'a str,
9 pub font: &'r Font,
10}
11impl<'a> Text<'_, 'a> {
12 pub fn size(line: &str, f: &Font, scale: f32) -> Vec2 {
13 Self::size_and_len(line, f, scale).0
14 }
15 pub fn lsb(l: &str, f: &Font, scale: f32) -> f32 {
16 l.chars().next().map_or(0., |c| f.glyph(c).lsb) * scale
17 }
18 pub fn substr(line: &'a str, f: &Font, scale: f32, max_width: f32) -> (Vec2, (&'a str, &'a str)) {
19 let (x, i) = line
20 .char_indices()
21 .scan((0., None), |(x, last_c), (i, c)| {
22 let (adv, (_, _, x2, _)) = f.adv_coord(last_c, c);
23 *last_c = Some(c);
24 Some(*x + x2).filter(|x| x * scale <= max_width).map(|r| {
25 *x += adv;
26 (r, i)
27 })
28 })
29 .fuse()
30 .last()
31 .map(|(x, i)| (x, i + line[i..].utf8_slice(..1).len()))
32 .unwrap_or_default();
33
34 ((x * scale, scale), line.split_at(i))
35 }
36 pub fn adv_at(line: &str, f: &Font, scale: f32, at_glyph: usize) -> f32 {
37 line.chars()
38 .take(at_glyph)
39 .scan((0., None), |(x, last_c), c| {
40 let (adv, _) = f.adv_coord(last_c, c);
41 (*x, *last_c) = (*x + adv, Some(c));
42 Some(*x)
43 })
44 .last()
45 .map_or(0., |x| x * scale)
46 }
47 fn size_and_len(line: &str, f: &Font, scale: f32) -> (Vec2, u32) {
48 let (len, size) = line
49 .chars()
50 .scan((0., None), |(x, last_c), c| {
51 let (adv, (_, _, x2, _)) = f.adv_coord(last_c, c);
52 let x2 = *x + x2;
53 (*x, *last_c) = (*x + adv, Some(c));
54 Some(x2)
55 })
56 .enumerate()
57 .last()
58 .map(|(n, x2)| (n + 1, (x2, 1.).mul(scale)))
59 .unwrap_or((0, (0., scale)));
60 (size, u32(len))
61 }
62 pub fn compare(&self, crop: &Geom, r: &TextImpl) -> State {
63 let Self { pos, scale, color, text, font } = *self;
64 let line = *text != *r.line;
65 let xyzw = (State::XYZW | State::UV).or_def(pos != r.base.pos || scale != r.scale || *crop != r.base.crop || line);
66 let rgba = State::RGBA.or_def(color != r.base.color);
67 let ord = State::MISMATCH.or_def(line || !ptr::eq(font, r.font));
68 ord | xyzw | rgba
69 }
70 pub fn obj(self, crop: Geom) -> TextImpl {
71 let Self { pos, scale, color, text, font } = self;
72 let (size, len) = Self::size_and_len(text, font, scale);
73 TextImpl {
74 base: Base { pos, size, crop, color },
75 scale,
76 len,
77 line: text.into(),
78 font,
79 }
80 }
81}
82
83pub struct TextImpl {
84 base: Base,
85 scale: f32,
86 len: u32,
87 line: Str,
88 font: *const Font,
89}
90impl TextImpl {
91 pub fn batchable(&self, r: &Self) -> bool {
92 ptr::eq(self.font, r.font)
93 }
94}
95impl Primitive for TextImpl {
96 fn base(&self) -> &Base {
97 &self.base
98 }
99 fn write_mesh(&self, aspect: Vec2, BatchedObj { z, state, mut xyzw, mut rgba, mut uv }: BatchedObj) {
100 if self.line.is_empty() {
101 return;
102 }
103
104 let Self {
105 base: Base { pos, color, crop: (p1, p2), .. },
106 scale,
107 len,
108 ref line,
109 font,
110 } = *self;
111
112 if state.contains(State::XYZW) {
113 let font = unsafe { &*font };
114
115 let (mut x, mut last_c) = (0., None);
116 for c in line.chars() {
117 let (adv, (x1, y1, x2, y2), u) = font.adv_coord_uv(&last_c, c);
118 let is_empty = y1 == y2;
119 last_c = Some(c);
120
121 let ((x1, y1), (x2, y2), (u1, v1, u2, v2)) = <_>::to({
122 let xy1 = pos.sum((x + x1, y1).mul(scale));
123 let xy2 = pos.sum((x + x2, y2).mul(scale));
124 let uv = bound_uv((p1, p2), (xy1, xy2), u);
125 let xy1 = xy1.clmp(p1, p2).mul(aspect);
126 let xy2 = xy2.clmp(p1, p2).mul(aspect);
127 (xy1, xy2, uv).or_def(!is_empty)
128 });
129 let O = f16::ZERO;
130
131 xyzw[..16].copy_from_slice(&[x1, y1, z, O, x2, y1, z, O, x2, y2, z, O, x1, y2, z, O]);
132 xyzw = &mut xyzw[16..];
133 uv[..8].copy_from_slice(&[u1, v1, u2, v1, u2, v2, u1, v2]);
134 uv = &mut uv[8..];
135
136 x += adv;
137 }
138 }
139
140 if state.contains(State::RGBA) {
141 let (r, g, b, a) = vec4(color.mul(255).clmp(0, 255).round());
142 let col = &[r, g, b, a];
143
144 for _ in 0..4 * len {
145 rgba[..4].copy_from_slice(col);
146 rgba = &mut rgba[4..];
147 }
148 }
149 }
150 fn batch_draw(&self, b: &VaoBind<u16>, (offset, num): (u16, u16)) {
151 let s = LeakyStatic!(Shader, { Shader::pure([vs_gui__pos_col_tex, ps_gui_sdftext]) });
152
153 let t = unsafe { &*self.font }.tex().Bind(sampler());
154 let _ = Uniforms!(s, ("iTex", t));
155 b.Draw((num, offset, gl::TRIANGLES));
156 }
157
158 fn vert_count(&self) -> u32 {
159 self.len * 4
160 }
161}
162
163SHADER!(
164 ps_gui_sdftext,
165 r"in vec4 glColor;
166 in vec2 glUV;
167 layout(location = 0) out vec4 glFragColor;
168 uniform sampler2D iTex;
169
170 void main() {
171 vec2 dx = vec2(.33333333 * dFdxFine(glUV.x), 0);
172 float sz = textureSize(iTex, 0).x;
173 float dsdf = sz * dx.x * .2;
174
175 float l = texture(iTex, glUV - dx).r;
176 float c = texture(iTex, glUV).r;
177 float r = texture(iTex, glUV + dx).r;
178
179 vec3 p = smoothstep(vec3(.5 - dsdf), vec3(.5 + dsdf), vec3(l, c, r)) * 2;
180
181 vec4 correction = vec4(p.rgbg);
182 glFragColor = glColor * correction;
183 }"
184);