1use crate::palette::Palette;
2use crate::tile::TileSet;
3use crate::{FbSurface, TILE_SIZE};
4
5#[derive(Debug, Clone)]
8pub struct ScreenTileBuffer {
9 pub tiles: Vec<u8>,
10 pub width_tiles: u32,
11 pub height_tiles: u32,
12}
13
14impl ScreenTileBuffer {
15 pub fn new(width_tiles: u32, height_tiles: u32) -> Self {
16 Self {
17 tiles: vec![0x7F; (width_tiles * height_tiles) as usize],
18 width_tiles,
19 height_tiles,
20 }
21 }
22
23 #[inline]
24 pub fn get(&self, tx: u32, ty: u32) -> u8 {
25 if tx >= self.width_tiles || ty >= self.height_tiles {
26 return 0x7F;
27 }
28 self.tiles[(ty * self.width_tiles + tx) as usize]
29 }
30
31 #[inline]
32 pub fn set(&mut self, tx: u32, ty: u32, tile_id: u8) {
33 if tx < self.width_tiles && ty < self.height_tiles {
34 self.tiles[(ty * self.width_tiles + tx) as usize] = tile_id;
35 }
36 }
37
38 pub fn fill(&mut self, tile_id: u8) {
39 self.tiles.fill(tile_id);
40 }
41
42 pub fn set_row(&mut self, ty: u32, row_data: &[u8]) {
43 if ty >= self.height_tiles {
44 return;
45 }
46 let start = (ty * self.width_tiles) as usize;
47 let count = row_data.len().min(self.width_tiles as usize);
48 self.tiles[start..start + count].copy_from_slice(&row_data[..count]);
49 }
50
51 pub fn copy_from_flat(&mut self, data: &[u8]) {
52 let count = data.len().min(self.tiles.len());
53 self.tiles[..count].copy_from_slice(&data[..count]);
54 }
55
56 pub fn render(&self, fb: &mut impl FbSurface, tileset: &TileSet, palette: &Palette) {
57 let fb_w = fb.width();
58 let fb_h = fb.height();
59 for ty in 0..self.height_tiles {
60 for tx in 0..self.width_tiles {
61 let tile_id = self.get(tx, ty) as usize;
62 let tile = tileset.get(tile_id);
63 let screen_x = tx * TILE_SIZE;
64 let screen_y = ty * TILE_SIZE;
65
66 for row in 0..TILE_SIZE {
67 for col in 0..TILE_SIZE {
68 let px = screen_x + col;
69 let py = screen_y + row;
70 if px < fb_w && py < fb_h {
71 let color_idx = tile.get(row as usize, col as usize);
72 let rgba = palette.color(crate::palette::GbColor::from_u8(color_idx));
73 fb.set_pixel(px, py, rgba);
74 }
75 }
76 }
77 }
78 }
79 }
80
81 pub fn render_region(
82 &self,
83 fb: &mut impl FbSurface,
84 tileset: &TileSet,
85 palette: &Palette,
86 tx_start: u32,
87 ty_start: u32,
88 tw: u32,
89 th: u32,
90 ) {
91 let fb_w = fb.width();
92 let fb_h = fb.height();
93 let tx_end = (tx_start + tw).min(self.width_tiles);
94 let ty_end = (ty_start + th).min(self.height_tiles);
95
96 for ty in ty_start..ty_end {
97 for tx in tx_start..tx_end {
98 let tile_id = self.get(tx, ty) as usize;
99 let tile = tileset.get(tile_id);
100 let screen_x = tx * TILE_SIZE;
101 let screen_y = ty * TILE_SIZE;
102
103 for row in 0..TILE_SIZE {
104 for col in 0..TILE_SIZE {
105 let px = screen_x + col;
106 let py = screen_y + row;
107 if px < fb_w && py < fb_h {
108 let color_idx = tile.get(row as usize, col as usize);
109 let rgba = palette.color(crate::palette::GbColor::from_u8(color_idx));
110 fb.set_pixel(px, py, rgba);
111 }
112 }
113 }
114 }
115 }
116 }
117}
118
119pub fn write_tiles_at(buf: &mut ScreenTileBuffer, start_tx: u32, start_ty: u32, tile_ids: &[u8]) {
120 for (i, &tile_id) in tile_ids.iter().enumerate() {
121 let tx = start_tx + i as u32;
122 if tx >= buf.width_tiles {
123 break;
124 }
125 buf.set(tx, start_ty, tile_id);
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_screen_tile_buffer_dynamic_sizes() {
135 let buf = ScreenTileBuffer::new(20, 18);
136 assert_eq!(buf.tiles.len(), 360);
137 assert_eq!(buf.width_tiles, 20);
138 assert_eq!(buf.height_tiles, 18);
139
140 let buf2 = ScreenTileBuffer::new(30, 20);
141 assert_eq!(buf2.tiles.len(), 600);
142
143 let buf3 = ScreenTileBuffer::new(10, 10);
144 assert_eq!(buf3.tiles.len(), 100);
145 }
146
147 #[test]
148 fn test_screen_tile_buffer_set_get() {
149 let mut buf = ScreenTileBuffer::new(20, 18);
150 buf.set(3, 4, 0x42);
151 assert_eq!(buf.get(3, 4), 0x42);
152 }
153
154 #[test]
155 fn test_screen_tile_buffer_fill() {
156 let mut buf = ScreenTileBuffer::new(20, 18);
157 buf.fill(0x01);
158 for ty in 0..18 {
159 for tx in 0..20 {
160 assert_eq!(buf.get(tx, ty), 0x01);
161 }
162 }
163 }
164
165 #[test]
166 fn test_screen_tile_buffer_out_of_bounds() {
167 let buf = ScreenTileBuffer::new(10, 10);
168 assert_eq!(buf.get(10, 0), 0x7F);
169 assert_eq!(buf.get(0, 10), 0x7F);
170 }
171
172 #[test]
173 fn test_screen_tile_buffer_set_out_of_bounds() {
174 let mut buf = ScreenTileBuffer::new(10, 10);
175 buf.set(10, 0, 0xFF);
176 buf.set(0, 10, 0xFF);
177 assert_eq!(buf.get(5, 5), 0x7F);
178 }
179
180 #[test]
181 fn test_screen_tile_buffer_set_row() {
182 let mut buf = ScreenTileBuffer::new(10, 5);
183 buf.set_row(2, &[0x10, 0x20, 0x30]);
184 assert_eq!(buf.get(0, 2), 0x10);
185 assert_eq!(buf.get(1, 2), 0x20);
186 assert_eq!(buf.get(2, 2), 0x30);
187 assert_eq!(buf.get(3, 2), 0x7F);
188 }
189
190 #[test]
191 fn test_screen_tile_buffer_copy_from_flat() {
192 let mut buf = ScreenTileBuffer::new(10, 5);
193 buf.copy_from_flat(&[0xA0, 0xA1, 0xA2]);
194 assert_eq!(buf.get(0, 0), 0xA0);
195 assert_eq!(buf.get(1, 0), 0xA1);
196 assert_eq!(buf.get(2, 0), 0xA2);
197 assert_eq!(buf.get(3, 0), 0x7F);
198 }
199}