mabel_aseprite/
tilemap.rs1use std::io::Read;
2
3use image::RgbaImage;
4
5use crate::{
6 cel::CelContent,
7 reader::AseReader,
8 tile::{self, Tile, EMPTY_TILE},
9 AsepriteParseError, Cel, Result, Tileset,
10};
11
12pub struct Tilemap<'a> {
18 pub(crate) cel: Cel<'a>,
19 pub(crate) tileset: &'a Tileset,
20 pub(crate) logical_size: (u16, u16),
21}
22
23impl<'a> Tilemap<'a> {
24 fn tilemap(&self) -> &TilemapData {
25 if let CelContent::Tilemap(ref tilemap_data) = self.cel.raw_cel().unwrap().content {
26 tilemap_data
27 } else {
28 panic!("Tilemap cel does not contain a tilemap")
29 }
30 }
31
32 pub fn width(&self) -> u32 {
34 self.logical_size.0 as u32
35 }
36
37 pub fn height(&self) -> u32 {
39 self.logical_size.1 as u32
40 }
41
42 pub fn tile_size(&self) -> (u32, u32) {
44 let sz = self.tileset.tile_size();
45 (sz.width() as u32, sz.height() as u32)
46 }
47
48 pub fn tileset(&self) -> &Tileset {
50 self.tileset
51 }
52
53 pub fn image(&self) -> RgbaImage {
55 self.cel.image()
56 }
57
58 pub fn tile(&self, x: u32, y: u32) -> &Tile {
66 let (ofs_x, ofs_y) = self.tile_offsets();
67 let x = x as i32 - ofs_x;
68 let y = y as i32 - ofs_y;
69 let w = self.tilemap().width() as i32;
72 let h = self.tilemap().height() as i32;
73 if x < 0 || y < 0 || x >= w || y >= h {
74 return &EMPTY_TILE;
75 }
76 let index = (y as usize * w as usize) + x as usize;
77 &self.tilemap().tiles[index]
78 }
79
80 pub fn tile_offsets(&self) -> (i32, i32) {
82 let (x, y) = self.pixel_offsets();
83 let size = self.tileset().tile_size();
84 (x / size.width() as i32, y / size.height() as i32)
85 }
86
87 pub fn pixel_offsets(&self) -> (i32, i32) {
89 self.cel.top_left()
90 }
91}
92
93#[allow(unused)]
94#[derive(Debug)]
95pub struct TilemapData {
96 width: u16,
97 height: u16,
98 tiles: tile::Tiles,
100 bits_per_tile: u16,
101 bitmask_header: TileBitmaskHeader,
102}
103
104impl TilemapData {
105 pub fn width(&self) -> u16 {
107 self.width
108 }
109
110 pub fn height(&self) -> u16 {
112 self.height
113 }
114
115 pub fn tile(&self, x: u16, y: u16) -> Option<&Tile> {
116 if x >= self.width || y >= self.height {
117 return None;
118 }
119 let index = (y as usize * self.width as usize) + x as usize;
120 Some(&self.tiles[index])
121 }
122
123 pub(crate) fn parse_chunk<R: Read>(mut reader: AseReader<R>) -> Result<Self> {
124 let width = reader.word()?;
125 let height = reader.word()?;
126 let bits_per_tile = reader.word()?;
127 if bits_per_tile != 32 {
128 return Err(AsepriteParseError::UnsupportedFeature(format!(
129 "Asefile only supports 32 bits per tile, got input with {} bits per tile",
130 bits_per_tile
131 )));
132 }
133 let bitmask_header = TileBitmaskHeader::parse(&mut reader)?;
134 reader.skip_reserved(10)?;
135 let expected_tile_count = width as usize * height as usize;
136 let tiles = tile::Tiles::unzip(reader, expected_tile_count, &bitmask_header)?;
137 Ok(Self {
138 width,
139 height,
140 tiles,
141 bits_per_tile,
142 bitmask_header,
143 })
144 }
145}
146
147#[derive(Debug)]
148pub(crate) struct TileBitmaskHeader {
149 pub tile_id: u32,
150 pub x_flip: u32,
151 pub y_flip: u32,
152 pub rotate_90cw: u32,
153}
154
155impl TileBitmaskHeader {
156 pub(crate) fn parse<R: Read>(reader: &mut AseReader<R>) -> Result<Self> {
157 let tile_id = reader.dword()?;
158 let x_flip = reader.dword()?;
159 let y_flip = reader.dword()?;
160 let rotate_90cw = reader.dword()?;
161 Ok(Self {
162 tile_id,
163 x_flip,
164 y_flip,
165 rotate_90cw,
166 })
167 }
168}