use std::io::Read;
use image::RgbaImage;
use crate::{
cel::CelContent,
reader::AseReader,
tile::{self, Tile, EMPTY_TILE},
AsepriteParseError, Cel, Result, Tileset,
};
pub struct Tilemap<'a> {
pub(crate) cel: Cel<'a>,
pub(crate) tileset: &'a Tileset,
pub(crate) logical_size: (u16, u16),
}
impl<'a> Tilemap<'a> {
fn tilemap(&self) -> &TilemapData {
if let CelContent::Tilemap(ref tilemap_data) = self.cel.raw_cel().unwrap().content {
tilemap_data
} else {
panic!("Tilemap cel does not contain a tilemap")
}
}
pub fn width(&self) -> u32 {
self.logical_size.0 as u32
}
pub fn height(&self) -> u32 {
self.logical_size.1 as u32
}
pub fn tile_size(&self) -> (u32, u32) {
let sz = self.tileset.tile_size();
(sz.width() as u32, sz.height() as u32)
}
pub fn tileset(&self) -> &Tileset {
self.tileset
}
pub fn image(&self) -> RgbaImage {
self.cel.image()
}
pub fn tile(&self, x: u32, y: u32) -> &Tile {
let (ofs_x, ofs_y) = self.tile_offsets();
let x = x as i32 - ofs_x;
let y = y as i32 - ofs_y;
let w = self.tilemap().width() as i32;
let h = self.tilemap().height() as i32;
if x < 0 || y < 0 || x >= w || y >= h {
return &EMPTY_TILE;
}
let index = (y as usize * w as usize) + x as usize;
&self.tilemap().tiles[index]
}
pub fn tile_offsets(&self) -> (i32, i32) {
let (x, y) = self.pixel_offsets();
let size = self.tileset().tile_size();
(x / size.width() as i32, y / size.height() as i32)
}
pub fn pixel_offsets(&self) -> (i32, i32) {
self.cel.top_left()
}
}
#[allow(unused)]
#[derive(Debug)]
pub struct TilemapData {
width: u16,
height: u16,
tiles: tile::Tiles,
bits_per_tile: u16,
bitmask_header: TileBitmaskHeader,
}
impl TilemapData {
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn tile(&self, x: u16, y: u16) -> Option<&Tile> {
if x >= self.width || y >= self.height {
return None;
}
let index = (y as usize * self.width as usize) + x as usize;
Some(&self.tiles[index])
}
pub(crate) fn parse_chunk<R: Read>(mut reader: AseReader<R>) -> Result<Self> {
let width = reader.word()?;
let height = reader.word()?;
let bits_per_tile = reader.word()?;
if bits_per_tile != 32 {
return Err(AsepriteParseError::UnsupportedFeature(format!(
"Asefile only supports 32 bits per tile, got input with {} bits per tile",
bits_per_tile
)));
}
let bitmask_header = TileBitmaskHeader::parse(&mut reader)?;
reader.skip_reserved(10)?;
let expected_tile_count = width as usize * height as usize;
let tiles = tile::Tiles::unzip(reader, expected_tile_count, &bitmask_header)?;
Ok(Self {
width,
height,
tiles,
bits_per_tile,
bitmask_header,
})
}
}
#[derive(Debug)]
pub(crate) struct TileBitmaskHeader {
pub tile_id: u32,
pub x_flip: u32,
pub y_flip: u32,
pub rotate_90cw: u32,
}
impl TileBitmaskHeader {
pub(crate) fn parse<R: Read>(reader: &mut AseReader<R>) -> Result<Self> {
let tile_id = reader.dword()?;
let x_flip = reader.dword()?;
let y_flip = reader.dword()?;
let rotate_90cw = reader.dword()?;
Ok(Self {
tile_id,
x_flip,
y_flip,
rotate_90cw,
})
}
}