aseprite_loader/binary/chunks/
cel.rs

1use nom::bytes::complete::take;
2use strum::FromRepr;
3
4use crate::binary::{
5    errors::ParseResult,
6    image::Image,
7    scalars::{byte, dword, short, word, Byte, Dword, Short, Word},
8};
9
10#[derive(Debug)]
11pub struct CelChunk<'a> {
12    /// Layer index (see NOTE.2)
13    pub layer_index: Word,
14    /// X position
15    pub x: Short,
16    /// Y position
17    pub y: Short,
18    /// Opacity level
19    pub opacity: Byte,
20    /// Z-Index
21    pub z_index: Short,
22    /// Cel Data
23    pub content: CelContent<'a>,
24}
25
26#[derive(Debug, FromRepr)]
27
28enum CelType {
29    /// 0 - Raw Image Data (unused, compressed image is preferred)
30    RawImageData,
31    /// 1 - Linked Cel
32    LinkedCel,
33    /// 2 - Compressed Image
34    CompressedImage,
35    /// 3 - Compressed Tilemap
36    CompressedTilemap,
37    Unknown(Word),
38}
39
40impl From<Word> for CelType {
41    fn from(word: Word) -> Self {
42        CelType::from_repr(word.into()).unwrap_or(Self::Unknown(word))
43    }
44}
45
46#[derive(Debug)]
47pub enum CelContent<'a> {
48    Image(Image<'a>),
49    LinkedCel {
50        /// Frame position to link with
51        frame_position: Word,
52    },
53    CompressedTilemap {
54        /// Width in number of tiles
55        width: Word,
56        /// Height in number of tiles
57        height: Word,
58        /// Bits per tile (at the moment it's always 32-bit per tile)
59        bits_per_tile: Word,
60        /// Bitmask for tile ID (e.g. 0x1fffffff for 32-bit tiles)
61        bitmask_tile_id: Dword,
62        /// Bitmask for X flip
63        bitmask_x_flip: Dword,
64        /// Bitmask for Y-Flip
65        bitmask_y_flip: Dword,
66        /// Bitmask for diagonal flip (swap X/Y axis)
67        bitmask_diagonal_flip: Dword,
68        /// Row by row, from top to bottom tile by tile
69        /// compressed with ZLIB method (see NOTE.3)
70        data: &'a [u8],
71    },
72    Unknown {
73        cel_type: Word,
74        data: &'a [u8],
75    },
76}
77
78pub fn parse_cel_chunk(input: &[u8]) -> ParseResult<'_, CelChunk<'_>> {
79    let (input, layer_index) = word(input)?;
80    let (input, x) = short(input)?;
81    let (input, y) = short(input)?;
82    let (input, opacity) = byte(input)?;
83    let (input, cel_type) = word(input)?;
84    let cel_type = CelType::from(cel_type);
85    let (input, z_index) = short(input)?;
86    let (input, _) = take(5usize)(input)?;
87    let content = match cel_type {
88        CelType::RawImageData => {
89            let (input, width) = word(input)?;
90            let (input, height) = word(input)?;
91            CelContent::Image(Image {
92                width,
93                height,
94                data: input,
95                compressed: false,
96            })
97        }
98        CelType::LinkedCel => {
99            let (_, frame_position) = word(input)?;
100            CelContent::LinkedCel { frame_position }
101        }
102        CelType::CompressedImage => {
103            let (input, width) = word(input)?;
104            let (input, height) = word(input)?;
105            CelContent::Image(Image {
106                width,
107                height,
108                data: input,
109                compressed: true,
110            })
111        }
112        CelType::CompressedTilemap => {
113            let (input, width) = word(input)?;
114            let (input, height) = word(input)?;
115            let (input, bits_per_tile) = word(input)?;
116            let (input, bitmask_tile_id) = dword(input)?;
117            let (input, bitmask_y_flip) = dword(input)?;
118            let (input, bitmask_x_flip) = dword(input)?;
119            let (input, bitmask_diagonal_flip) = dword(input)?;
120            let (input, _) = take(10usize)(input)?;
121            CelContent::CompressedTilemap {
122                width,
123                height,
124                bits_per_tile,
125                bitmask_tile_id,
126                bitmask_x_flip,
127                bitmask_y_flip,
128                bitmask_diagonal_flip,
129                data: input,
130            }
131        }
132        CelType::Unknown(cel_type) => CelContent::Unknown {
133            cel_type,
134            data: input,
135        },
136    };
137    Ok((
138        &input[input.len()..],
139        CelChunk {
140            layer_index,
141            x,
142            y,
143            opacity,
144            z_index,
145            content,
146        },
147    ))
148}