1use glyph_brush::ab_glyph::{point, FontVec as ABFontVec};
2use glyph_brush::{BrushAction, BrushError, FontId};
3use solstice::image::{Image, Settings};
4use solstice::mesh::IndexedMesh;
5use solstice::quad_batch::*;
6use solstice::texture::*;
7use solstice::Context;
8
9pub struct FontVec(ABFontVec);
10
11impl std::ops::Deref for FontVec {
12 type Target = ABFontVec;
13
14 fn deref(&self) -> &Self::Target {
15 &self.0
16 }
17}
18
19impl std::convert::TryFrom<Vec<u8>> for FontVec {
20 type Error = glyph_brush::ab_glyph::InvalidFont;
21
22 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
23 Ok(Self(ABFontVec::try_from_vec(value)?))
24 }
25}
26
27pub struct Text {
28 quad_batch: QuadBatch<super::Vertex2D>,
29 font_texture: Image,
30 glyph_brush: glyph_brush::GlyphBrush<Quad<super::Vertex2D>, glyph_brush::Extra, ABFontVec>,
31}
32
33pub const DEFAULT_VERT: &str = r#"
34vec4 pos(mat4 transform_projection, vec4 vertex_position) {
35 return transform_projection * vertex_position;
36}
37"#;
38
39pub const DEFAULT_FRAG: &str = r#"
40vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
41 float a = Texel(texture, texture_coords).a;
42 color.a *= a;
43 return color;
44}
45"#;
46
47impl Text {
48 pub fn new(ctx: &mut Context) -> Result<Self, crate::GraphicsError> {
49 let glyph_brush = glyph_brush::GlyphBrushBuilder::using_fonts(vec![]).build();
50
51 let font_texture = {
52 let (width, height) = glyph_brush.texture_dimensions();
53 Image::new(
54 ctx,
55 TextureType::Tex2D,
56 solstice::PixelFormat::Alpha,
57 width,
58 height,
59 Settings {
60 mipmaps: false,
61 ..Settings::default()
62 },
63 )?
64 };
65 ctx.set_texture_data(
67 font_texture.get_texture_key(),
68 font_texture.get_texture_info(),
69 font_texture.get_texture_type(),
70 None,
71 );
72
73 let quad_batch = QuadBatch::new(ctx, 1000)?;
74
75 Ok(Self {
76 quad_batch,
77 font_texture,
78 glyph_brush,
79 })
80 }
81
82 pub fn add_font(&mut self, font_data: FontVec) -> FontId {
83 self.glyph_brush.add_font(font_data.0)
84 }
85
86 pub fn set_text(
87 &mut self,
88 text: glyph_brush::Text,
89 bounds: super::Rectangle,
90 layout: glyph_brush::Layout<glyph_brush::BuiltInLineBreaker>,
91 ctx: &mut Context,
92 ) {
93 self.glyph_brush.queue(glyph_brush::Section {
94 text: vec![text],
95 screen_position: (bounds.x, bounds.y),
96 bounds: (bounds.width, bounds.height),
97 layout,
98 });
99 self.update(ctx);
100 }
101
102 pub fn texture(&self) -> &solstice::image::Image {
103 &self.font_texture
104 }
105
106 pub fn geometry(
107 &mut self,
108 ctx: &mut Context,
109 ) -> solstice::Geometry<&IndexedMesh<super::Vertex2D, u16>> {
110 self.quad_batch.unmap(ctx)
111 }
112
113 fn update(&mut self, ctx: &mut Context) {
114 let Self {
115 quad_batch,
116 font_texture,
117 glyph_brush,
118 ..
119 } = self;
120
121 let to_vertex = |glyph_vertex: glyph_brush::GlyphVertex| {
122 let glyph_brush::GlyphVertex {
123 mut tex_coords,
124 pixel_coords,
125 bounds,
126 extra,
127 } = glyph_vertex;
128 let mut gl_rect = glyph_brush::ab_glyph::Rect {
129 min: point(pixel_coords.min.x as f32, pixel_coords.min.y as f32),
130 max: point(pixel_coords.max.x as f32, pixel_coords.max.y as f32),
131 };
132
133 if gl_rect.max.x > bounds.max.x {
135 let old_width = gl_rect.width();
136 gl_rect.max.x = bounds.max.x;
137 tex_coords.max.x =
138 tex_coords.min.x + tex_coords.width() * gl_rect.width() / old_width;
139 }
140 if gl_rect.min.x < bounds.min.x {
141 let old_width = gl_rect.width();
142 gl_rect.min.x = bounds.min.x;
143 tex_coords.min.x =
144 tex_coords.max.x - tex_coords.width() * gl_rect.width() / old_width;
145 }
146 if gl_rect.max.y > bounds.max.y {
147 let old_height = gl_rect.height();
148 gl_rect.max.y = bounds.max.y;
149 tex_coords.max.y =
150 tex_coords.min.y + tex_coords.height() * gl_rect.height() / old_height;
151 }
152 if gl_rect.min.y < bounds.min.y {
153 let old_height = gl_rect.height();
154 gl_rect.min.y = bounds.min.y;
155 tex_coords.min.y =
156 tex_coords.max.y - tex_coords.height() * gl_rect.height() / old_height;
157 }
158
159 Quad {
160 vertices: [
161 super::Vertex2D {
162 position: [gl_rect.min.x as f32, gl_rect.min.y as f32],
163 uv: [tex_coords.min.x, tex_coords.min.y],
164 color: extra.color,
165 },
166 super::Vertex2D {
167 position: [gl_rect.max.x as f32, gl_rect.min.y as f32],
168 uv: [tex_coords.max.x, tex_coords.min.y],
169 color: extra.color,
170 },
171 super::Vertex2D {
172 position: [gl_rect.max.x as f32, gl_rect.max.y as f32],
173 uv: [tex_coords.max.x, tex_coords.max.y],
174 color: extra.color,
175 },
176 super::Vertex2D {
177 position: [gl_rect.min.x as f32, gl_rect.max.y as f32],
178 uv: [tex_coords.min.x, tex_coords.max.y],
179 color: extra.color,
180 },
181 ],
182 }
183 };
184
185 loop {
186 let update_texture = |rect: glyph_brush::Rectangle<u32>, data: &[u8]| {
187 let mut info = font_texture.get_texture_info();
188 info.set_width(rect.width());
189 info.set_height(rect.height());
190 ctx.set_texture_sub_data(
191 font_texture.get_texture_key(),
192 info,
193 font_texture.get_texture_type(),
194 data,
195 rect.min[0],
196 rect.min[1],
197 );
198 };
199 match glyph_brush.process_queued(update_texture, to_vertex) {
200 Ok(action) => match action {
201 BrushAction::Draw(quads) => {
202 quad_batch.clear();
203 for quad in quads {
204 quad_batch.push(quad);
205 }
206 break;
207 }
208 BrushAction::ReDraw => {
209 break;
210 }
211 },
212 Err(error) => match error {
213 BrushError::TextureTooSmall { suggested: (w, h) } => {
214 let mut info = font_texture.get_texture_info();
215 info.set_width(w);
216 info.set_height(h);
217 font_texture.set_texture_info(info);
218 ctx.set_texture_data(
219 font_texture.get_texture_key(),
220 font_texture.get_texture_info(),
221 font_texture.get_texture_type(),
222 None,
223 );
224 glyph_brush.resize_texture(w, h);
225 }
226 },
227 }
228 }
229 }
230}