mabel_aseprite/
tile.rs

1use crate::{reader::AseReader, tilemap::TileBitmaskHeader, Result};
2use std::{io::Read, ops::Index};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub(crate) struct TileId(pub u32);
6
7/// A tile is a reference to a single tile in a tilemap.
8///
9/// Note that the Aseprite file format also enables rotating or flipping tiles.
10/// But since the GUI does not yet support those (as of v1.3-beta5) we do not
11/// yet expose these attributes.
12#[derive(Debug, Clone)]
13#[allow(unused)]
14pub struct Tile {
15    pub(crate) id: TileId,
16    // These are currently (Aseprite v1.3-beta5) not supported by the GUI.
17    pub(crate) flip_x: bool,
18    pub(crate) flip_y: bool,
19    pub(crate) rotate_90cw: bool,
20}
21
22pub(crate) static EMPTY_TILE: Tile = Tile {
23    id: TileId(0),
24    flip_x: false,
25    flip_y: false,
26    rotate_90cw: false,
27};
28
29impl Tile {
30    /// The ID of the tile, i.e., the index into the corresponding tileset.
31    pub fn id(&self) -> u32 {
32        self.id.0
33    }
34
35    pub(crate) fn new(chunk: &[u8], header: &TileBitmaskHeader) -> Result<Self> {
36        AseReader::new(chunk)
37            .dword()
38            .map(|bits| Self::parse(bits, header))
39    }
40
41    fn parse(bits: u32, header: &TileBitmaskHeader) -> Self {
42        Self {
43            id: TileId(bits & header.tile_id),
44            flip_x: as_bool(bits & header.x_flip),
45            flip_y: as_bool(bits & header.y_flip),
46            rotate_90cw: as_bool(bits & header.rotate_90cw),
47        }
48    }
49}
50
51#[derive(Debug)]
52pub(crate) struct Tiles(Vec<Tile>);
53
54impl Tiles {
55    pub(crate) fn unzip<T: Read>(
56        reader: AseReader<T>,
57        expected_tile_count: usize,
58        header: &TileBitmaskHeader,
59    ) -> Result<Self> {
60        // Only 32-bit tiles supported for now
61        let expected_output_size = 4 * expected_tile_count;
62        let bytes = reader.unzip(expected_output_size)?;
63        let tiles: Result<Vec<Tile>> = bytes
64            .chunks_exact(4)
65            .map(|bytes| Tile::new(bytes, header))
66            .collect();
67        Ok(Self(tiles?))
68    }
69}
70
71impl Index<usize> for Tiles {
72    type Output = Tile;
73
74    fn index(&self, index: usize) -> &Self::Output {
75        &self.0[index]
76    }
77}
78
79fn as_bool(bitwise_and: u32) -> bool {
80    bitwise_and != 0
81}