1use std::collections::VecDeque;
5
6use crate::palette::{self, Rgb, VGA};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Color {
13 Pc(u8),
15 Xterm(u8),
17 Rgb(Rgb),
19 Ui(u8),
23}
24
25pub mod flags {
26 pub const BOLD: u8 = 1 << 0;
27 pub const BLINK: u8 = 1 << 1;
28 pub const UNDERLINE: u8 = 1 << 2;
29 pub const REVERSE: u8 = 1 << 3;
30 pub const CONCEAL: u8 = 1 << 4;
31 pub const FAINT: u8 = 1 << 5;
32 pub const DOUBLE_TOP: u8 = 1 << 6;
34 pub const DOUBLE_BOTTOM: u8 = 1 << 7;
36}
37
38pub const DEFAULT_FG: Color = Color::Pc(7);
39pub const DEFAULT_BG: Color = Color::Pc(0);
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct Cell {
43 pub ch: u8,
45 pub fg: Color,
46 pub bg: Color,
47 pub flags: u8,
48 pub tile: u32,
51}
52
53impl Default for Cell {
54 fn default() -> Self {
55 Cell { ch: b' ', fg: DEFAULT_FG, bg: DEFAULT_BG, flags: 0, tile: 0 }
56 }
57}
58
59impl Cell {
60 pub fn new(ch: u8, fg: Color, bg: Color) -> Self {
61 Cell { ch, fg, bg, flags: 0, tile: 0 }
62 }
63
64 pub fn is_ui(&self) -> bool {
66 matches!(self.fg, Color::Ui(_)) || matches!(self.bg, Color::Ui(_))
67 }
68
69 pub fn is_blank(&self) -> bool {
70 (self.ch == b' ' || self.ch == 0) && self.bg == DEFAULT_BG && self.flags & flags::REVERSE == 0 && self.tile == 0
71 }
72
73 pub fn resolve(&self, ice: bool) -> (Rgb, Rgb, bool) {
78 self.resolve_with(ice, Look::standard())
79 }
80
81 pub fn resolve_with(&self, ice: bool, look: &Look) -> (Rgb, Rgb, bool) {
83 let bold = self.flags & flags::BOLD != 0 && !look.bright_plain;
84 let blink = self.flags & flags::BLINK != 0;
85 let mut fg = match self.fg {
86 Color::Pc(i) if bold && i < 8 => look.rgb(Color::Pc(i + 8)),
87 c => look.rgb(c),
88 };
89 let mut bg = match self.bg {
90 Color::Pc(i) if blink && ice && i < 8 => look.rgb(Color::Pc(i + 8)),
91 c => look.rgb(c),
92 };
93 if self.flags & flags::REVERSE != 0 {
94 std::mem::swap(&mut fg, &mut bg);
95 }
96 if self.flags & flags::CONCEAL != 0 {
97 fg = bg;
98 }
99 (fg, bg, blink && !ice && !look.blink_steady)
100 }
101}
102
103#[derive(Clone, Debug, PartialEq, Eq)]
106pub struct Look {
107 pub palette: [Rgb; 256],
110 pub fonts: [u8; 4],
112 pub bright_font: bool,
114 pub bright_plain: bool,
116 pub blink_font: bool,
118 pub blink_steady: bool,
120 pub loaded: Vec<LoadedFont>,
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
126pub struct LoadedFont {
127 pub slot: u8,
128 pub height: u8,
129 pub data: &'static [u8],
130}
131
132pub const FIRST_LOADABLE_FONT: u8 = 43;
134
135pub const ENTRY_FONT: u8 = 255;
137
138impl Default for Look {
139 fn default() -> Look {
140 Look::standard().clone()
141 }
142}
143
144impl Look {
145 pub fn standard() -> &'static Look {
147 static STANDARD: std::sync::LazyLock<Look> = std::sync::LazyLock::new(|| Look {
148 palette: std::array::from_fn(|i| palette::xterm256(i as u8)),
149 fonts: [0; 4],
150 bright_font: false,
151 bright_plain: false,
152 blink_font: false,
153 blink_steady: false,
154 loaded: Vec::new(),
155 });
156 &STANDARD
157 }
158
159 pub fn load_font(&mut self, slot: u8, data: &[u8]) -> bool {
162 let height = match data.len() {
163 4096 => 16,
164 3584 => 14,
165 2048 => 8,
166 _ => return false,
167 };
168 if slot < FIRST_LOADABLE_FONT {
169 return false;
170 }
171 let font = LoadedFont { slot, height, data: intern(data) };
172 self.loaded.retain(|f| f.slot != slot);
173 self.loaded.push(font);
174 true
175 }
176
177 pub fn loaded_font(&self, slot: u8) -> Option<LoadedFont> {
179 self.loaded.iter().find(|f| f.slot == slot).copied()
180 }
181
182 pub fn rgb(&self, c: Color) -> Rgb {
183 match c {
184 Color::Pc(i) | Color::Ui(i) => {
186 let i = i & 15;
187 let ansi = palette::ANSI_TO_VGA.iter().position(|&v| v == i & 7).unwrap_or(0) as u8;
188 self.palette[(ansi + (i & 8)) as usize]
189 }
190 Color::Xterm(i) => self.palette[i as usize],
191 Color::Rgb(rgb) => rgb,
192 }
193 }
194
195 pub fn font_for(&self, cell: &Cell) -> u8 {
197 let bright = self.bright_font && cell.flags & flags::BOLD != 0;
198 let blink = self.blink_font && cell.flags & flags::BLINK != 0;
199 self.fonts[bright as usize | (blink as usize) << 1]
200 }
201}
202
203fn intern(data: &[u8]) -> &'static [u8] {
206 use std::collections::HashSet;
207 use std::sync::Mutex;
208 static FONTS: Mutex<Option<HashSet<&'static [u8]>>> = Mutex::new(None);
209 let mut fonts = FONTS.lock().unwrap_or_else(|e| e.into_inner());
210 let set = fonts.get_or_insert_with(HashSet::new);
211 if let Some(&have) = set.get(data) {
212 return have;
213 }
214 let kept: &'static [u8] = Box::leak(data.to_vec().into_boxed_slice());
215 set.insert(kept);
216 kept
217}
218
219pub fn color_rgb(c: Color) -> Rgb {
220 match c {
221 Color::Pc(i) | Color::Ui(i) => VGA[(i & 15) as usize],
222 Color::Xterm(i) => palette::xterm256(i),
223 Color::Rgb(rgb) => rgb,
224 }
225}
226
227#[derive(Clone, Copy, Debug)]
228struct SavedCursor {
229 x: usize,
230 y: usize,
231 pen: Cell,
232}
233
234pub struct Screen {
235 cols: usize,
236 rows: usize,
237 cells: Vec<Cell>,
238 x: usize,
239 y: usize,
240 pending_wrap: bool,
243 pub pen: Cell,
245 saved: Option<SavedCursor>,
246 scroll_top: usize,
247 scroll_bottom: usize,
248 pub autowrap: bool,
249 pub cursor_visible: bool,
250 pub ice_colors: bool,
251 pub look: Look,
253 pub base_font: u8,
255 pub base_ice: bool,
257 scrollback: VecDeque<Vec<Cell>>,
258 scrollback_limit: usize,
259 pub tiles: Tiles,
261}
262
263impl Screen {
264 pub fn new(cols: usize, rows: usize, scrollback_limit: usize) -> Self {
265 let cols = cols.max(1);
266 let rows = rows.max(1);
267 Screen {
268 cols,
269 rows,
270 cells: vec![Cell::default(); cols * rows],
271 x: 0,
272 y: 0,
273 pending_wrap: false,
274 pen: Cell::default(),
275 saved: None,
276 scroll_top: 0,
277 scroll_bottom: rows - 1,
278 autowrap: true,
279 cursor_visible: true,
280 ice_colors: false,
281 look: Look::default(),
282 base_font: 0,
283 base_ice: false,
284 scrollback: VecDeque::new(),
285 scrollback_limit,
286 tiles: Tiles::default(),
287 }
288 }
289
290 pub fn cols(&self) -> usize {
291 self.cols
292 }
293
294 pub fn rows(&self) -> usize {
295 self.rows
296 }
297
298 pub fn cursor(&self) -> (usize, usize) {
299 (self.x, self.y)
300 }
301
302 pub fn row(&self, y: usize) -> &[Cell] {
303 &self.cells[y * self.cols..(y + 1) * self.cols]
304 }
305
306 pub fn cell_mut(&mut self, x: usize, y: usize) -> Option<&mut Cell> {
307 if x < self.cols && y < self.rows { Some(&mut self.cells[y * self.cols + x]) } else { None }
308 }
309
310 pub fn scrollback_len(&self) -> usize {
311 self.scrollback.len()
312 }
313
314 pub fn scrollback_line(&self, i: usize) -> &[Cell] {
316 &self.scrollback[i]
317 }
318
319 pub fn clear_scrollback(&mut self) {
320 self.scrollback.clear();
321 }
322
323 pub fn reset(&mut self) {
325 self.pen = Cell::default();
326 self.cells.fill(Cell::default());
327 self.x = 0;
328 self.y = 0;
329 self.pending_wrap = false;
330 self.saved = None;
331 self.scroll_top = 0;
332 self.scroll_bottom = self.rows - 1;
333 self.autowrap = true;
334 self.cursor_visible = true;
335 self.ice_colors = self.base_ice;
336 let loaded = std::mem::take(&mut self.look.loaded);
338 self.look = Look { loaded, ..Look::default() };
339 self.look.fonts[0] = self.base_font;
340 }
341
342 pub fn resize(&mut self, cols: usize, rows: usize) {
343 let cols = cols.max(1);
344 let rows = rows.max(1);
345 if cols == self.cols && rows == self.rows {
346 return;
347 }
348 let mut cells = vec![Cell::default(); cols * rows];
349 for y in 0..rows.min(self.rows) {
350 let n = cols.min(self.cols);
351 cells[y * cols..y * cols + n].copy_from_slice(&self.row(y)[..n]);
352 }
353 self.cells = cells;
354 self.cols = cols;
355 self.rows = rows;
356 self.x = self.x.min(cols - 1);
357 self.y = self.y.min(rows - 1);
358 self.scroll_top = 0;
359 self.scroll_bottom = rows - 1;
360 self.pending_wrap = false;
361 }
362
363 fn blank(&self) -> Cell {
366 Cell { ch: b' ', flags: self.pen.flags & (flags::BLINK | flags::REVERSE), ..self.pen }
367 }
368
369 pub fn put(&mut self, ch: u8) {
371 if self.pending_wrap {
372 self.pending_wrap = false;
373 self.x = 0;
374 self.line_feed();
375 }
376 let idx = self.y * self.cols + self.x;
377 self.cells[idx] = Cell { ch, ..self.pen };
378 if self.x + 1 < self.cols {
379 self.x += 1;
380 } else if self.autowrap {
381 self.pending_wrap = true;
382 }
383 }
384
385 pub fn carriage_return(&mut self) {
386 self.x = 0;
387 self.pending_wrap = false;
388 }
389
390 pub fn line_feed(&mut self) {
392 self.pending_wrap = false;
393 if self.y == self.scroll_bottom {
394 self.scroll_up(1);
395 } else if self.y + 1 < self.rows {
396 self.y += 1;
397 }
398 }
399
400 pub fn reverse_index(&mut self) {
402 self.pending_wrap = false;
403 if self.y == self.scroll_top {
404 self.scroll_down(1);
405 } else if self.y > 0 {
406 self.y -= 1;
407 }
408 }
409
410 pub fn backspace(&mut self) {
411 self.pending_wrap = false;
412 self.x = self.x.saturating_sub(1);
413 }
414
415 pub fn tab(&mut self) {
416 self.pending_wrap = false;
417 self.x = ((self.x / 8 + 1) * 8).min(self.cols - 1);
418 }
419
420 pub fn back_tab(&mut self, n: usize) {
421 for _ in 0..n {
422 self.x = self.x.saturating_sub(1) / 8 * 8;
423 }
424 self.pending_wrap = false;
425 }
426
427 pub fn move_to(&mut self, x: usize, y: usize) {
429 self.x = x.min(self.cols - 1);
430 self.y = y.min(self.rows - 1);
431 self.pending_wrap = false;
432 }
433
434 pub fn move_by(&mut self, dx: isize, dy: isize) {
435 let x = (self.x as isize + dx).max(0) as usize;
436 let y = (self.y as isize + dy).max(0) as usize;
437 self.move_to(x, y);
438 }
439
440 pub fn set_scroll_region(&mut self, top: usize, bottom: usize) {
441 let bottom = bottom.min(self.rows - 1);
442 if top < bottom {
443 self.scroll_top = top;
444 self.scroll_bottom = bottom;
445 } else {
446 self.scroll_top = 0;
447 self.scroll_bottom = self.rows - 1;
448 }
449 self.move_to(0, 0);
450 }
451
452 pub fn save_cursor(&mut self) {
453 self.saved = Some(SavedCursor { x: self.x, y: self.y, pen: self.pen });
454 }
455
456 pub fn restore_cursor(&mut self) {
457 if let Some(s) = self.saved {
458 self.move_to(s.x, s.y);
459 self.pen = s.pen;
460 }
461 }
462
463 fn push_scrollback(&mut self, line: Vec<Cell>) {
464 if self.scrollback_limit == 0 {
465 return;
466 }
467 if self.scrollback.len() >= self.scrollback_limit {
468 self.scrollback.pop_front();
469 }
470 self.scrollback.push_back(line);
471 }
472
473 pub fn scroll_up(&mut self, n: usize) {
476 let (top, bottom) = (self.scroll_top, self.scroll_bottom);
477 let n = n.min(bottom - top + 1);
478 if top == 0 && bottom == self.rows - 1 {
479 for y in 0..n {
480 let line = self.row(y).to_vec();
481 self.push_scrollback(line);
482 }
483 }
484 let c = self.cols;
485 self.cells.copy_within((top + n) * c..(bottom + 1) * c, top * c);
486 let blank = self.blank();
487 self.cells[(bottom + 1 - n) * c..(bottom + 1) * c].fill(blank);
488 }
489
490 pub fn scroll_down(&mut self, n: usize) {
491 let (top, bottom) = (self.scroll_top, self.scroll_bottom);
492 let n = n.min(bottom - top + 1);
493 let c = self.cols;
494 self.cells.copy_within(top * c..(bottom + 1 - n) * c, (top + n) * c);
495 let blank = self.blank();
496 self.cells[top * c..(top + n) * c].fill(blank);
497 }
498
499 pub fn erase_display(&mut self, mode: u32) {
505 let blank = self.blank();
506 let pos = self.y * self.cols + self.x;
507 match mode {
508 0 => self.cells[pos..].fill(blank),
509 1 => self.cells[..=pos].fill(blank),
510 _ => {
511 let used = (0..self.rows).rev().find(|&y| self.row(y).iter().any(|c| !c.is_blank()));
512 if let Some(last) = used {
513 for y in 0..=last {
514 let line = self.row(y).to_vec();
515 self.push_scrollback(line);
516 }
517 }
518 self.cells.fill(blank);
519 self.move_to(0, 0);
520 }
521 }
522 self.pending_wrap = false;
523 }
524
525 pub fn erase_line(&mut self, mode: u32) {
527 let blank = self.blank();
528 let start = self.y * self.cols;
529 let (a, b) = match mode {
530 0 => (self.x, self.cols),
531 1 => (0, self.x + 1),
532 _ => (0, self.cols),
533 };
534 self.cells[start + a..start + b].fill(blank);
535 self.pending_wrap = false;
536 }
537
538 pub fn erase_chars(&mut self, n: usize) {
540 let blank = self.blank();
541 let start = self.y * self.cols;
542 let end = (self.x + n).min(self.cols);
543 self.cells[start + self.x..start + end].fill(blank);
544 }
545
546 pub fn insert_chars(&mut self, n: usize) {
548 let blank = self.blank();
549 let start = self.y * self.cols;
550 let n = n.min(self.cols - self.x);
551 let line = &mut self.cells[start..start + self.cols];
552 line.copy_within(self.x..self.cols - n, self.x + n);
553 line[self.x..self.x + n].fill(blank);
554 }
555
556 pub fn delete_chars(&mut self, n: usize) {
558 let blank = self.blank();
559 let start = self.y * self.cols;
560 let n = n.min(self.cols - self.x);
561 let line = &mut self.cells[start..start + self.cols];
562 line.copy_within(self.x + n.., self.x);
563 let cols = self.cols;
564 line[cols - n..].fill(blank);
565 }
566
567 pub fn insert_lines(&mut self, n: usize) {
569 if self.y < self.scroll_top || self.y > self.scroll_bottom {
570 return;
571 }
572 let top = self.scroll_top;
573 self.scroll_top = self.y;
574 self.scroll_down(n);
575 self.scroll_top = top;
576 self.x = 0;
577 }
578
579 pub fn delete_lines(&mut self, n: usize) {
581 if self.y < self.scroll_top || self.y > self.scroll_bottom {
582 return;
583 }
584 let (top, bottom) = (self.scroll_top, self.scroll_bottom);
585 self.scroll_top = self.y;
587 let n = n.min(bottom - self.y + 1);
588 let c = self.cols;
589 self.cells.copy_within((self.y + n) * c..(bottom + 1) * c, self.y * c);
590 let blank = self.blank();
591 self.cells[(bottom + 1 - n) * c..(bottom + 1) * c].fill(blank);
592 self.scroll_top = top;
593 self.x = 0;
594 }
595
596 pub fn draw_text(&mut self, x: usize, y: usize, text: &[u8], fg: Color, bg: Color) {
599 for (i, &ch) in text.iter().enumerate() {
600 if let Some(c) = self.cell_mut(x + i, y) {
601 *c = Cell::new(ch, fg, bg);
602 }
603 }
604 }
605
606 pub fn fill_rect(&mut self, x: usize, y: usize, w: usize, h: usize, cell: Cell) {
607 for yy in y..(y + h).min(self.rows) {
608 for xx in x..(x + w).min(self.cols) {
609 self.cells[yy * self.cols + xx] = cell;
610 }
611 }
612 }
613
614 pub fn cell_px(&self) -> (usize, usize) {
617 (8, if self.rows >= 43 { 8 } else { 16 })
618 }
619
620 pub fn size_px(&self) -> (usize, usize) {
622 let (w, h) = self.cell_px();
623 (self.cols * w, self.rows * h)
624 }
625
626 pub fn place_image(&mut self, img: &crate::sixel::Image, scrolling: bool) {
632 if img.width == 0 || img.height == 0 {
633 return;
634 }
635 let (tw, th) = self.cell_px();
636 let cx = self.x;
637 let rows = img.height.div_ceil(th);
638 let cols = img.width.div_ceil(tw);
639 if !scrolling {
640 for r in 0..rows.min(self.rows) {
641 self.tile_row(img, r, 0, r, cols);
642 }
643 return;
644 }
645 self.pending_wrap = false;
646 let last = img.height / th;
647 for r in 0..=last {
648 if r > 0 {
649 self.line_feed();
650 }
651 if r < rows {
652 let y = self.y;
653 self.tile_row(img, r, cx, y, cols);
654 }
655 }
656 self.x = cx;
657 }
658
659 fn tile_row(&mut self, img: &crate::sixel::Image, r: usize, x0: usize, y: usize, cols: usize) {
661 let (tw, th) = self.cell_px();
662 let (left, top) = ((x0 * tw) as isize, (y * th) as isize);
663 let rect = (left, top, cols * tw, th);
664 self.draw_pixels(rect, |px, py| {
665 let (ix, iy) = ((px - left) as usize, (py - top) as usize + r * th);
666 if ix < img.width && iy < img.height { img.pixels[iy * img.width + ix] } else { 0 }
667 });
668 }
669
670 pub fn draw_pixels(&mut self, rect: (isize, isize, usize, usize), pixel: impl Fn(isize, isize) -> u32) {
674 let (tw, th) = self.cell_px();
675 let (sw, sh) = self.size_px();
676 let (x0, y0) = (rect.0.max(0) as usize, rect.1.max(0) as usize);
677 let x1 = ((rect.0 + rect.2 as isize).max(0) as usize).min(sw);
678 let y1 = ((rect.1 + rect.3 as isize).max(0) as usize).min(sh);
679 if x0 >= x1 || y0 >= y1 {
680 return;
681 }
682 for cy in y0 / th..y1.div_ceil(th) {
683 for cx in x0 / tw..x1.div_ceil(tw) {
684 let idx = cy * self.cols + cx;
685 let mut px = match self.tiles.get(self.cells[idx].tile) {
686 Some(t) if (t.width, t.height) == (tw, th) => t.pixels.to_vec(),
687 _ => vec![0; tw * th],
688 };
689 let mut any = false;
690 for ty in 0..th {
691 let y = cy * th + ty;
692 if y < y0 || y >= y1 {
693 continue;
694 }
695 for tx in 0..tw {
696 let x = cx * tw + tx;
697 if x < x0 || x >= x1 {
698 continue;
699 }
700 let p = pixel(x as isize, y as isize);
701 if p != 0 {
702 px[ty * tw + tx] = p;
703 any = true;
704 }
705 }
706 }
707 if !any {
708 continue;
709 }
710 if self.tiles.full() {
711 let mut used = std::collections::HashSet::new();
712 used.extend(self.cells.iter().map(|c| c.tile));
713 used.extend(self.scrollback.iter().flatten().map(|c| c.tile));
714 self.tiles.collect(&used);
715 }
716 self.cells[idx].tile = self.tiles.add(Tile { width: tw, height: th, pixels: px.into() });
717 }
718 }
719 }
720
721 pub fn pixel_at(&self, x: usize, y: usize) -> crate::palette::Rgb {
725 let (tw, th) = self.cell_px();
726 let Some(cell) = self.cells.get((y / th) * self.cols + x / tw).filter(|_| x / tw < self.cols) else {
727 return 0;
728 };
729 if let Some(t) = self.tiles.get(cell.tile) {
730 let p = t.pixels.get((y % th) * t.width + x % tw).copied().unwrap_or(0);
731 if p != 0 {
732 return p & 0x00FF_FFFF;
733 }
734 }
735 cell.resolve_with(self.ice_colors, &self.look).1
736 }
737
738 pub fn row_text(row: &[Cell]) -> String {
740 let s: String = row.iter().map(|c| crate::cp437::to_unicode(c.ch.max(b' '))).collect();
741 s.trim_end().to_string()
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748
749 fn text(s: &Screen, y: usize) -> String {
750 Screen::row_text(s.row(y))
751 }
752
753 #[test]
754 fn delayed_wrap_at_last_column() {
755 let mut s = Screen::new(4, 3, 10);
756 for &b in b"abcd" {
757 s.put(b);
758 }
759 assert_eq!(s.cursor(), (3, 0));
760 s.carriage_return();
761 s.line_feed();
762 s.put(b'e');
763 assert_eq!(text(&s, 0), "abcd");
764 assert_eq!(text(&s, 1), "e");
765 }
766
767 #[test]
768 fn scrolling_feeds_scrollback() {
769 let mut s = Screen::new(4, 2, 10);
770 for &b in b"ab" {
771 s.put(b);
772 s.carriage_return();
773 s.line_feed();
774 }
775 assert_eq!(s.scrollback_len(), 1);
776 assert_eq!(Screen::row_text(s.scrollback_line(0)), "a");
777 assert_eq!(text(&s, 0), "b");
778 }
779
780 #[test]
781 fn clear_screen_saves_to_scrollback() {
782 let mut s = Screen::new(4, 3, 10);
783 s.put(b'x');
784 s.erase_display(2);
785 assert_eq!(s.scrollback_len(), 1);
786 assert_eq!(s.cursor(), (0, 0));
787 }
788
789 #[test]
790 fn insert_delete_chars() {
791 let mut s = Screen::new(5, 1, 0);
792 for &b in b"abcde" {
793 s.put(b);
794 }
795 s.move_to(1, 0);
796 s.delete_chars(2);
797 assert_eq!(text(&s, 0), "ade");
798 s.insert_chars(1);
799 assert_eq!(text(&s, 0), "a de");
800 }
801
802 #[test]
803 fn ice_colors_resolve() {
804 let c = Cell { ch: b'A', fg: Color::Pc(1), bg: Color::Pc(4), flags: flags::BOLD | flags::BLINK, tile: 0 };
805 assert_eq!(c.resolve(false), (VGA[9], VGA[4], true));
806 assert_eq!(c.resolve(true), (VGA[9], VGA[12], false));
807 }
808}
809
810#[derive(Clone, Debug, PartialEq, Eq)]
813pub struct Tile {
814 pub width: usize,
815 pub height: usize,
816 pub pixels: Box<[u32]>,
817}
818
819#[derive(Debug, Default)]
822pub struct Tiles {
823 store: Vec<Option<Tile>>,
824 free: Vec<u32>,
825 next_collect: usize,
827}
828
829impl Tiles {
830 pub fn get(&self, id: u32) -> Option<&Tile> {
831 id.checked_sub(1).and_then(|i| self.store.get(i as usize)).and_then(|t| t.as_ref())
832 }
833
834 fn full(&self) -> bool {
835 self.free.is_empty() && self.store.len() >= self.next_collect.max(4096)
836 }
837
838 fn collect(&mut self, used: &std::collections::HashSet<u32>) {
840 self.free.clear();
841 for (i, t) in self.store.iter_mut().enumerate() {
842 let id = i as u32 + 1;
843 if !used.contains(&id) {
844 *t = None;
845 self.free.push(id);
846 }
847 }
848 self.next_collect = (self.store.len() - self.free.len()) * 2;
849 }
850
851 fn add(&mut self, tile: Tile) -> u32 {
852 match self.free.pop() {
853 Some(id) => {
854 self.store[id as usize - 1] = Some(tile);
855 id
856 }
857 None => {
858 self.store.push(Some(tile));
859 self.store.len() as u32
860 }
861 }
862 }
863}