pyxel/
tilemap.rs

1use crate::canvas::{Canvas, ToIndex};
2use crate::image::SharedImage;
3use crate::utils::{f64_to_u32, parse_hex_string, simplify_string};
4
5pub type ImageTileCoord = u8;
6pub type Tile = (ImageTileCoord, ImageTileCoord);
7
8impl ToIndex for Tile {
9    fn to_index(&self) -> usize {
10        0
11    }
12}
13
14#[derive(Clone)]
15pub enum ImageSource {
16    Index(u32),
17    Image(SharedImage),
18}
19
20pub struct Tilemap {
21    pub(crate) canvas: Canvas<Tile>,
22    pub imgsrc: ImageSource,
23}
24
25pub type SharedTilemap = shared_type!(Tilemap);
26
27impl Tilemap {
28    pub fn new(width: u32, height: u32, imgsrc: ImageSource) -> SharedTilemap {
29        new_shared_type!(Self {
30            canvas: Canvas::new(width, height),
31            imgsrc,
32        })
33    }
34
35    pub const fn width(&self) -> u32 {
36        self.canvas.width()
37    }
38
39    pub const fn height(&self) -> u32 {
40        self.canvas.height()
41    }
42
43    pub fn data_ptr(&mut self) -> *mut Tile {
44        self.canvas.data_ptr()
45    }
46
47    pub fn set(&mut self, x: i32, y: i32, data_str: &[&str]) {
48        let width = simplify_string(data_str[0]).len() as u32 / 4;
49        let height = data_str.len() as u32;
50        let tilemap = Self::new(width, height, self.imgsrc.clone());
51
52        {
53            let mut tilemap = tilemap.lock();
54            for y in 0..height {
55                let src_data = simplify_string(data_str[y as usize]);
56                for x in 0..width {
57                    let index = x as usize * 4;
58                    let tile = parse_hex_string(&src_data[index..index + 4]).unwrap();
59                    tilemap.canvas.write_data(
60                        x as usize,
61                        y as usize,
62                        (
63                            ((tile >> 8) & 0xff) as ImageTileCoord,
64                            (tile & 0xff) as ImageTileCoord,
65                        ),
66                    );
67                }
68            }
69        }
70
71        self.blt(
72            x as f64,
73            y as f64,
74            tilemap,
75            0.0,
76            0.0,
77            width as f64,
78            height as f64,
79            None,
80            None,
81            None,
82        );
83    }
84
85    pub fn load(&mut self, x: i32, y: i32, filename: &str, layer_index: u32) {
86        let tilemap = Self::from_tmx(filename, layer_index);
87        let tilemap_width = tilemap.lock().width();
88        let tilemap_height = tilemap.lock().height();
89
90        self.blt(
91            x as f64,
92            y as f64,
93            tilemap,
94            0.0,
95            0.0,
96            tilemap_width as f64,
97            tilemap_height as f64,
98            None,
99            None,
100            None,
101        );
102    }
103
104    pub fn clip(&mut self, x: f64, y: f64, width: f64, height: f64) {
105        self.canvas.clip(x, y, width, height);
106    }
107
108    pub fn clip0(&mut self) {
109        self.canvas.clip0();
110    }
111
112    pub fn camera(&mut self, x: f64, y: f64) {
113        self.canvas.camera(x, y);
114    }
115
116    pub fn camera0(&mut self) {
117        self.canvas.camera0();
118    }
119
120    pub fn cls(&mut self, tile: Tile) {
121        self.canvas.cls(tile);
122    }
123
124    pub fn pget(&mut self, x: f64, y: f64) -> Tile {
125        self.canvas.pget(x, y)
126    }
127
128    pub fn pset(&mut self, x: f64, y: f64, tile: Tile) {
129        self.canvas.pset(x, y, tile);
130    }
131
132    pub fn line(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, tile: Tile) {
133        self.canvas.line(x1, y1, x2, y2, tile);
134    }
135
136    pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64, tile: Tile) {
137        self.canvas.rect(x, y, width, height, tile);
138    }
139
140    pub fn rectb(&mut self, x: f64, y: f64, width: f64, height: f64, tile: Tile) {
141        self.canvas.rectb(x, y, width, height, tile);
142    }
143
144    pub fn circ(&mut self, x: f64, y: f64, radius: f64, tile: Tile) {
145        self.canvas.circ(x, y, radius, tile);
146    }
147
148    pub fn circb(&mut self, x: f64, y: f64, radius: f64, tile: Tile) {
149        self.canvas.circb(x, y, radius, tile);
150    }
151
152    pub fn elli(&mut self, x: f64, y: f64, width: f64, height: f64, tile: Tile) {
153        self.canvas.elli(x, y, width, height, tile);
154    }
155
156    pub fn ellib(&mut self, x: f64, y: f64, width: f64, height: f64, tile: Tile) {
157        self.canvas.ellib(x, y, width, height, tile);
158    }
159
160    pub fn tri(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, tile: Tile) {
161        self.canvas.tri(x1, y1, x2, y2, x3, y3, tile);
162    }
163
164    pub fn trib(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, tile: Tile) {
165        self.canvas.trib(x1, y1, x2, y2, x3, y3, tile);
166    }
167
168    pub fn fill(&mut self, x: f64, y: f64, tile: Tile) {
169        self.canvas.fill(x, y, tile);
170    }
171
172    pub fn blt(
173        &mut self,
174        x: f64,
175        y: f64,
176        tilemap: SharedTilemap,
177        tilemap_x: f64,
178        tilemap_y: f64,
179        width: f64,
180        height: f64,
181        transparent: Option<Tile>,
182        rotate: Option<f64>,
183        scale: Option<f64>,
184    ) {
185        let rotate = rotate.unwrap_or(0.0);
186        let scale = scale.unwrap_or(1.0);
187
188        if rotate != 0.0 || scale != 1.0 {
189            self.blt_transform(
190                x,
191                y,
192                tilemap,
193                tilemap_x,
194                tilemap_y,
195                width,
196                height,
197                transparent,
198                rotate,
199                scale,
200            );
201            return;
202        }
203
204        if let Some(tilemap) = tilemap.try_lock() {
205            self.canvas.blt(
206                x,
207                y,
208                &tilemap.canvas,
209                tilemap_x,
210                tilemap_y,
211                width,
212                height,
213                transparent,
214                None,
215            );
216        } else {
217            let copy_width = f64_to_u32(width.abs());
218            let copy_height = f64_to_u32(height.abs());
219            let mut canvas = Canvas::new(copy_width, copy_height);
220
221            canvas.blt(
222                0.0,
223                0.0,
224                &self.canvas,
225                tilemap_x,
226                tilemap_y,
227                copy_width as f64,
228                copy_height as f64,
229                None,
230                None,
231            );
232
233            self.canvas
234                .blt(x, y, &canvas, 0.0, 0.0, width, height, transparent, None);
235        }
236    }
237
238    fn blt_transform(
239        &mut self,
240        x: f64,
241        y: f64,
242        tilemap: SharedTilemap,
243        tilemap_x: f64,
244        tilemap_y: f64,
245        width: f64,
246        height: f64,
247        transparent: Option<Tile>,
248        rotate: f64,
249        scale: f64,
250    ) {
251        if let Some(tilemap) = tilemap.try_lock() {
252            self.canvas.blt_transform(
253                x,
254                y,
255                &tilemap.canvas,
256                tilemap_x,
257                tilemap_y,
258                width,
259                height,
260                transparent,
261                None,
262                rotate,
263                scale,
264                false,
265            );
266        } else {
267            let copy_width = f64_to_u32(width.abs());
268            let copy_height = f64_to_u32(height.abs());
269            let mut canvas = Canvas::new(copy_width, copy_height);
270
271            canvas.blt(
272                0.0,
273                0.0,
274                &self.canvas,
275                tilemap_x,
276                tilemap_y,
277                copy_width as f64,
278                copy_height as f64,
279                None,
280                None,
281            );
282
283            self.canvas.blt_transform(
284                x,
285                y,
286                &canvas,
287                0.0,
288                0.0,
289                width,
290                height,
291                transparent,
292                None,
293                rotate,
294                scale,
295                false,
296            );
297        }
298    }
299}