1#![allow(mismatched_lifetime_syntaxes)]
2
3mod counter;
13pub mod embedded_font;
14mod graph;
15mod table;
16#[cfg(feature = "wgpu")]
17pub mod wgpu;
18#[cfg(feature = "wgpu-core")]
19pub mod wgpu_core;
20#[cfg(any(feature = "wgpu", feature = "wgpu-core"))]
21mod wgpu_common;
22
23use bytemuck::{Pod, Zeroable};
24use embedded_font::*;
25
26pub use counter::*;
27pub use graph::*;
28pub use table::*;
29
30pub const BACKGROUND_LAYER: Layer = 0;
31pub const FRONT_LAYER: Layer = 1;
32
33#[derive(Copy, Clone, Debug, PartialEq)]
35pub struct Point {
36 pub x: i32,
37 pub y: i32,
38}
39
40impl From<(f32, f32)> for Point {
41 fn from(val: (f32, f32)) -> Self {
42 Point {
43 x: val.0 as i32,
44 y: val.1 as i32,
45 }
46 }
47}
48
49impl From<(i32, i32)> for Point {
50 fn from(val: (i32, i32)) -> Self {
51 Point { x: val.0, y: val.1 }
52 }
53}
54
55#[derive(Copy, Clone, Debug, PartialEq)]
57pub struct PointF {
58 pub x: f32,
59 pub y: f32,
60}
61
62pub type Color = (u8, u8, u8, u8);
64pub type Layer = usize;
66
67fn color_to_u32(color: Color) -> u32 {
68 (color.0 as u32) << 24 | (color.1 as u32) << 16 | (color.2 as u32) << 8 | color.3 as u32
69}
70
71#[repr(C)]
72#[derive(Copy, Clone, Debug)]
73pub struct Vertex {
74 pub x: f32,
75 pub y: f32,
76 pub uv: u32,
77 pub color: u32,
78}
79
80unsafe impl Pod for Vertex {}
81unsafe impl Zeroable for Vertex {}
82
83pub(crate) struct LayerGeometry {
84 pub indices: Vec<u16>,
85}
86
87pub struct OverlayGeometry {
88 vertices: Vec<Vertex>,
89 layers: Vec<LayerGeometry>,
90}
91
92impl OverlayGeometry {
93 pub fn new(layer_count: usize) -> Self {
94 let mut layers = Vec::new();
95 for _ in 0..layer_count {
96 layers.push(LayerGeometry {
97 indices: Vec::new(),
98 });
99 }
100 OverlayGeometry {
101 vertices: Vec::new(),
102 layers,
103 }
104 }
105
106 pub fn begin_frame(&mut self) {
107 self.vertices.clear();
108 for layer in &mut self.layers {
109 layer.indices.clear();
110 }
111 }
112
113 pub fn push_text(
114 &mut self,
115 layer: Layer,
116 text: &str,
117 mut position: Point,
118 color: Color,
119 ) -> (Point, Point) {
120 let color = color_to_u32(color);
121 let mut min = position;
122 let mut max = min;
123
124 for c in text.chars() {
125 if c == '\n' {
126 position.x = min.x;
127 position.y += FONT_HEIGHT as i32;
128 continue;
129 }
130
131 let idx = c as usize - FIRST_CHAR as usize;
132 if idx >= GLYPH_INFO.len() {
133 continue;
134 }
135 let glyph = &GLYPH_INFO[idx];
136
137 let uv0x = (glyph.uv0.0 as u32) << 16;
138 let uv0y = glyph.uv0.1 as u32;
139 let uv1x = (glyph.uv1.0 as u32) << 16;
140 let uv1y = glyph.uv1.1 as u32;
141
142 let x0 = position.x + glyph.offset.0 as i32;
143 let y0 = position.y + glyph.offset.1 as i32;
144 let x1 = x0 + (glyph.uv1.0 - glyph.uv0.0) as i32;
145 let y1 = y0 + (glyph.uv1.1 - glyph.uv0.1) as i32;
146
147 let offset = self.vertices.len() as u16;
148 self.vertices.push(Vertex {
149 x: x0 as f32,
150 y: y0 as f32,
151 uv: uv0x | uv0y,
152 color,
153 });
154 self.vertices.push(Vertex {
155 x: x1 as f32,
156 y: y0 as f32,
157 uv: uv1x | uv0y,
158 color,
159 });
160 self.vertices.push(Vertex {
161 x: x1 as f32,
162 y: y1 as f32,
163 uv: uv1x | uv1y,
164 color,
165 });
166 self.vertices.push(Vertex {
167 x: x0 as f32,
168 y: y1 as f32,
169 uv: uv0x | uv1y,
170 color,
171 });
172 let layer = &mut self.layers[layer];
173 for i in [0u16, 1, 2, 0, 2, 3] {
174 layer.indices.push(offset + i);
175 }
176
177 position.x += glyph.x_advance as i32;
178
179 min.x = min.x.min(x0);
180 min.y = min.y.min(y0);
181 max.x = max.x.max(x1);
182 max.y = max.y.max(y1);
183 }
184
185 (min, max)
186 }
187
188 pub fn push_rectangle(
189 &mut self,
190 layer: Layer,
191 rect: &(Point, Point),
192 color0: Color,
193 color1: Color,
194 ) {
195 let uv = (OPAQUE_PIXEL.0 as u32) << 16 | OPAQUE_PIXEL.1 as u32;
196 let x0 = rect.0.x;
197 let y0 = rect.0.y;
198 let x1 = rect.1.x;
199 let y1 = rect.1.y;
200 let color0 = color_to_u32(color0);
201 let color1 = color_to_u32(color1);
202
203 let offset = self.vertices.len() as u16;
204 self.vertices.push(Vertex {
205 x: x0 as f32,
206 y: y0 as f32,
207 uv,
208 color: color0,
209 });
210 self.vertices.push(Vertex {
211 x: x1 as f32,
212 y: y0 as f32,
213 uv,
214 color: color0,
215 });
216 self.vertices.push(Vertex {
217 x: x1 as f32,
218 y: y1 as f32,
219 uv,
220 color: color1,
221 });
222 self.vertices.push(Vertex {
223 x: x0 as f32,
224 y: y1 as f32,
225 uv,
226 color: color1,
227 });
228 let layer = &mut self.layers[layer];
229 for i in [0u16, 1, 2, 0, 2, 3] {
230 layer.indices.push(offset + i);
231 }
232 }
233
234 pub fn push_mesh(&mut self, layer: Layer, vertices: &[PointF], indices: &[u16], color: Color) {
235 let uv = (OPAQUE_PIXEL.0 as u32) << 16 | OPAQUE_PIXEL.1 as u32;
236 let layer = &mut self.layers[layer];
237 self.vertices.reserve(vertices.len());
238 layer.indices.reserve(indices.len());
239 let offset = self.vertices.len() as u16;
240 let color = color_to_u32(color);
241 for vertex in vertices {
242 self.vertices.push(Vertex {
243 x: vertex.x,
244 y: vertex.y,
245 uv,
246 color,
247 });
248 }
249 for idx in indices {
250 layer.indices.push(offset + *idx);
251 }
252 }
253}
254
255pub struct Overlay {
256 pub geometry: OverlayGeometry,
257 pub style: Style,
258 pub cursor: Point,
259 pub item_flow: Orientation,
260 pub group_flow: Orientation,
261 pub string_buffer: String,
262 group_area: (Point, Point),
263 in_group: bool,
264 max_x: i32,
265 max_y: i32,
266}
267
268impl Overlay {
269 pub fn new() -> Self {
270 let style = Style::default();
271 let cursor = Point {
272 x: style.margin,
273 y: style.margin,
274 };
275 Overlay {
276 geometry: OverlayGeometry::new(2),
277 style,
278 cursor,
279 item_flow: Orientation::Horizontal,
280 group_flow: Orientation::Vertical,
281 string_buffer: String::with_capacity(128),
282 group_area: (cursor, cursor),
283 in_group: false,
284 max_x: 0,
285 max_y: 0,
286 }
287 }
288
289 pub fn begin_frame(&mut self) {
290 self.geometry.begin_frame();
291
292 self.cursor = Point {
293 x: self.style.margin,
294 y: self.style.margin,
295 };
296 self.group_area = (self.cursor, self.cursor);
297 self.max_x = 0;
298 self.max_y = 0;
299 self.in_group = false;
300 }
301
302 pub fn current_group_width(&self) -> i32 {
303 self.group_area.1.x - self.group_area.0.x
304 }
305
306 pub fn current_group_height(&self) -> i32 {
307 self.group_area.1.y - self.group_area.0.y
308 }
309
310 pub fn draw_item(&mut self, item: &dyn OverlayItem) {
311 let first = !self.in_group;
312 if !self.in_group {
313 self.begin_group();
314 }
315
316 let margin = if first { 0 } else { self.style.margin };
317 self.cursor = match self.item_flow {
318 Orientation::Vertical => Point {
319 x: self.group_area.0.x,
320 y: self.group_area.1.y + margin,
321 },
322 Orientation::Horizontal => Point {
323 x: self.group_area.1.x + margin,
324 y: self.group_area.0.y,
325 },
326 };
327
328 let rect = item.draw(self.cursor, self);
329
330 self.group_area.0.x = self.group_area.0.x.min(rect.0.x);
331 self.group_area.0.y = self.group_area.0.y.min(rect.0.y);
332 self.group_area.1.x = self.group_area.1.x.max(rect.1.x);
333 self.group_area.1.y = self.group_area.1.y.max(rect.1.y);
334 }
335
336 pub fn push_separator(&mut self) {
337 if !self.in_group {
338 return;
339 }
340
341 match self.item_flow {
342 Orientation::Vertical => {
343 self.cursor.y += self.style.margin * 3;
344 }
345 Orientation::Horizontal => {
346 self.cursor.x += self.style.margin * 3;
347 }
348 }
349 }
350
351 pub fn push_column(&mut self) {
352 if self.in_group {
353 self.end_group();
354 }
355
356 let p = Point {
357 x: self.max_x + self.style.margin * 3,
358 y: self.style.margin,
359 };
360
361 self.group_area = (p, p);
362 }
363
364 fn begin_group(&mut self) {
365 match self.group_flow {
366 Orientation::Vertical => {
367 let margin = if self.group_area.1.y > self.style.margin {
368 self.style.margin * 3
369 } else {
370 0
371 };
372 self.cursor.x = self.group_area.0.x;
373 self.cursor.y = self.group_area.1.y + margin;
374 }
375 Orientation::Horizontal => {
376 let margin = if self.group_area.1.x > self.style.margin {
377 self.style.margin * 3
378 } else {
379 0
380 };
381 self.cursor.x = self.group_area.1.x + margin;
382 self.cursor.y = self.group_area.0.y;
383 }
384 }
385
386 self.group_area = (self.cursor, self.cursor);
387 self.in_group = true;
388 }
389
390 pub fn end_group(&mut self) {
391 self.in_group = false;
392 if self.group_area.0.x >= self.group_area.1.x || self.group_area.0.y >= self.group_area.1.y
393 {
394 return;
395 }
396
397 self.group_area.1.x = self
398 .group_area
399 .1
400 .x
401 .max(self.group_area.0.x + self.style.min_group_width);
402 self.group_area.1.y = self
403 .group_area
404 .1
405 .y
406 .max(self.group_area.0.y + self.style.min_group_height);
407
408 self.max_x = self.max_x.max(self.group_area.1.x);
409 self.max_y = self.max_y.max(self.group_area.1.y);
410
411 let margin = self.style.margin;
412 let mut bg = self.group_area;
413 bg.0.x -= margin;
414 bg.0.y -= margin;
415 bg.1.x += margin;
416 bg.1.y += margin;
417
418 self.geometry.push_rectangle(
419 BACKGROUND_LAYER,
420 &bg,
421 self.style.background[0],
422 self.style.background[1],
423 );
424 }
425
426 pub fn finish(&mut self) {
427 if self.in_group {
428 self.end_group();
429 }
430 }
431}
432
433pub trait OverlayItem {
434 fn draw(&self, position: Point, output: &mut Overlay) -> (Point, Point);
435}
436
437impl<'a> OverlayItem for &'a str {
438 fn draw(&self, position: Point, output: &mut Overlay) -> (Point, Point) {
439 let p = Point {
440 x: position.x,
441 y: position.y + FONT_HEIGHT as i32,
442 };
443
444 output
445 .geometry
446 .push_text(FRONT_LAYER, self, p, output.style.text_color[0])
447 }
448}
449
450#[derive(Copy, Clone, Debug, PartialEq)]
451pub struct Style {
452 pub margin: i32,
453 pub line_spacing: i32,
454 pub min_group_width: i32,
455 pub min_group_height: i32,
456 pub column_spacing: i32,
457 pub background: [Color; 2],
458 pub text_color: [Color; 2],
459 pub title_color: Color,
460 pub highlight_color: Color,
461}
462
463impl Default for Style {
464 fn default() -> Self {
465 Style {
466 margin: 10,
467 line_spacing: 2,
468 min_group_width: 0,
469 min_group_height: 0,
470 column_spacing: 20,
471 background: [(0, 0, 0, 255), (0, 0, 0, 200)],
472 text_color: [(255, 255, 255, 255), (200, 200, 200, 255)],
473 title_color: (120, 150, 255, 255),
474 highlight_color: (255, 100, 100, 255),
475 }
476 }
477}