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