mabel_aseprite/
slice.rs

1use std::io::Read;
2
3use crate::{reader::AseReader, user_data::UserData, Result};
4
5/// A slice is a region of the sprite with some attributes.
6///
7/// They are created using the slice tool and can be animated over frames. See
8/// the [official docs on slices](https://www.aseprite.org/docs/slices/) for
9/// details.
10#[derive(Debug, Clone)]
11pub struct Slice {
12    /// The name of the slice. Not guaranteed to be unique.
13    pub name: String,
14    /// A sequence of [SliceKey]s. Together, these describe the shape and
15    /// position of a slice during animation.
16    pub keys: Vec<SliceKey>,
17    /// User data associated with this slice.
18    pub user_data: Option<UserData>,
19}
20
21/// A devision of a [Slice] into nine regions for 9-slice scaling.
22#[derive(Debug, Clone)]
23pub struct Slice9 {
24    /// X position of the center area (relative to slice bounds).
25    pub center_x: i32,
26    /// Y position of the center area (relative to slice bounds).
27    pub center_y: i32,
28    /// Width of the center area.
29    pub center_width: u32,
30    /// Height of the center area.
31    pub center_height: u32,
32}
33
34impl Slice9 {
35    fn read<R: Read>(reader: &mut AseReader<R>) -> Result<Self> {
36        let center_x = reader.long()?;
37        let center_y = reader.long()?;
38        let center_width = reader.dword()?;
39        let center_height = reader.dword()?;
40        Ok(Self {
41            center_x,
42            center_y,
43            center_width,
44            center_height,
45        })
46    }
47}
48
49/// The position and shape of a [Slice], starting at a given frame.
50#[derive(Debug, Clone)]
51pub struct SliceKey {
52    /// Starting frame number for this slice key. This slice is valid from this
53    /// frame to the end of the animation or the next slice key.
54    pub from_frame: u32,
55    /// Origin of the slice.
56    pub origin: (i32, i32),
57    /// Size of the slice.
58    pub size: (u32, u32),
59    /// 9-slicing information.
60    pub slice9: Option<Slice9>,
61    /// Pivot information. Relative to the origin.
62    pub pivot: Option<(i32, i32)>,
63}
64
65impl SliceKey {
66    fn read<R: Read>(reader: &mut AseReader<R>, flags: u32) -> Result<Self> {
67        let from_frame = reader.dword()?;
68        let origin_x = reader.long()?;
69        let origin_y = reader.long()?;
70        let origin = (origin_x, origin_y);
71        let slice_width = reader.dword()?;
72        let slice_height = reader.dword()?;
73        let size = (slice_width, slice_height);
74        let slice9 = if flags & 1 != 0 {
75            Some(Slice9::read(reader)?)
76        } else {
77            None
78        };
79        let pivot = if flags & 2 != 0 {
80            let x = reader.long()?;
81            let y = reader.long()?;
82            Some((x, y))
83        } else {
84            None
85        };
86
87        Ok(Self {
88            from_frame,
89            origin,
90            size,
91            slice9,
92            pivot,
93        })
94    }
95}
96
97pub(crate) fn parse_chunk(data: &[u8]) -> Result<Slice> {
98    let mut reader = AseReader::new(data);
99
100    let num_slice_keys = reader.dword()?;
101    let flags = reader.dword()?;
102    let _reserved = reader.dword()?;
103    let name = reader.string()?;
104    let slice_keys: Result<Vec<SliceKey>> = (0..num_slice_keys)
105        .map(|_id| SliceKey::read(&mut reader, flags))
106        .collect();
107
108    Ok(Slice {
109        name,
110        keys: slice_keys?,
111        user_data: None,
112    })
113}