mabel_aseprite/
layer.rs

1use crate::{
2    cel::{Cel, CelId},
3    reader::AseReader,
4    tileset::TilesetsById,
5    user_data::UserData,
6    AsepriteFile, AsepriteParseError, Result,
7};
8use bitflags::bitflags;
9use std::{io::Read, ops::Index};
10
11/// Types of layer.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum LayerType {
14    /// A regular image layer. This is the normal layer type.
15    Image,
16    /// A layer that groups other layers and does not contain any image data.
17    /// In Aseprite these are represented by a folder icon.
18    Group,
19    /// A tilemap layer. Contains the index of the tileset used for the tiles.
20    ///
21    /// In Aseprite these are represented by a grid icon.
22    Tilemap(u32),
23}
24
25bitflags! {
26    /// Various layer attributes.
27    ///
28    /// For checking whether a layer is visible prefer to use [Layer::is_visible]
29    /// as that also takes into account any parent layer's visibility.
30    #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
31    pub struct LayerFlags: u32 {
32        /// Layer is visible (eye icon is enabled).
33        const VISIBLE = 0x0001;
34        /// Layer can be modified (lock icon is disabled).
35        const EDITABLE = 0x0002;
36        /// Layer cannot be moved.
37        const MOVEMENT_LOCKED = 0x0004;
38        /// Layer is background (stack order cannot be changed).
39        const BACKGROUND = 0x0008;
40        /// Prefer to link cels when the user copies them.
41        const CONTINUOUS = 0x0010;
42        /// Prefer to show this group layer collapsed.
43        const COLLAPSED = 0x0020;
44        /// This is a reference layer.
45        const REFERENCE = 0x0040;
46
47        /// The is a background layer.
48        const BACKGROUND_LAYER = Self::MOVEMENT_LOCKED.bits() | Self::BACKGROUND.bits();
49    }
50}
51
52/// A reference to a single layer.
53#[derive(Debug)]
54pub struct Layer<'a> {
55    pub(crate) file: &'a AsepriteFile,
56    pub(crate) layer_id: u32,
57}
58
59impl<'a> Layer<'a> {
60    fn data(&self) -> &LayerData {
61        &self.file.layers[self.layer_id]
62    }
63
64    /// This layer's ID.
65    pub fn id(&self) -> u32 {
66        self.layer_id
67    }
68
69    /// Layer's flags
70    pub fn flags(&self) -> LayerFlags {
71        self.data().flags
72    }
73
74    /// Name of the layer
75    pub fn name(&self) -> &str {
76        &self.data().name
77    }
78
79    /// Blend mode of the layer. Describes how this layer is combined with the
80    /// layers underneath it. See [BlendMode] for details.
81    pub fn blend_mode(&self) -> BlendMode {
82        self.data().blend_mode
83    }
84
85    /// Layer opacity describes
86    pub fn opacity(&self) -> u8 {
87        self.data().opacity
88    }
89
90    /// Describes whether this is a regular, group, or tilemap layer.
91    pub fn layer_type(&self) -> LayerType {
92        self.data().layer_type
93    }
94
95    /// Is this a tilemap layer?
96    pub fn is_tilemap(&self) -> bool {
97        matches!(self.layer_type(), LayerType::Tilemap(_))
98    }
99
100    /// The parent of this layer, if any. For layers that are part of a group
101    /// this returns the parent layer.
102    ///
103    /// Does not indicate the blend order of layers (i.e., which layers are
104    /// above or below).
105    pub fn parent(&self) -> Option<Layer> {
106        self.file.layers.parents[self.layer_id as usize].map(|id| Layer {
107            file: self.file,
108            layer_id: id,
109        })
110    }
111
112    /// Returns if this layer is visible. This requires that this layer and all
113    /// of its parent layers are visible.
114    pub fn is_visible(&self) -> bool {
115        let layer_is_visible = self.data().flags.contains(LayerFlags::VISIBLE);
116        let parent_is_visible = self.parent().map(|p| p.is_visible()).unwrap_or(true);
117        layer_is_visible && parent_is_visible
118    }
119
120    /// Get a reference to the Cel for this frame in the layer.
121    pub fn frame(&self, frame_id: u32) -> Cel {
122        assert!(frame_id < self.file.num_frames());
123        let cel_id = CelId {
124            frame: frame_id as u16,
125            layer: self.layer_id as u16,
126        };
127        Cel {
128            file: self.file,
129            cel_id,
130        }
131    }
132
133    /// Returns a reference to the layer's [UserData], if any exists.
134    pub fn user_data(&self) -> Option<&UserData> {
135        self.data().user_data.as_ref()
136    }
137}
138
139#[derive(Debug)]
140pub struct LayerData {
141    pub(crate) flags: LayerFlags,
142    pub(crate) name: String,
143    pub(crate) blend_mode: BlendMode,
144    pub(crate) opacity: u8,
145    pub(crate) layer_type: LayerType,
146    pub(crate) user_data: Option<UserData>,
147    child_level: u16,
148}
149
150impl LayerData {
151    pub(crate) fn is_background(&self) -> bool {
152        self.flags.contains(LayerFlags::BACKGROUND)
153    }
154}
155
156#[derive(Debug)]
157pub(crate) struct LayersData {
158    // Sorted back to front (or bottom to top in the GUI, but groups occur
159    // before their children, i.e., lower index)
160    pub(crate) layers: Vec<LayerData>,
161    parents: Vec<Option<u32>>,
162}
163
164impl LayersData {
165    pub(crate) fn validate(&self, tilesets: &TilesetsById) -> Result<()> {
166        for l in &self.layers {
167            if let LayerType::Tilemap(id) = l.layer_type {
168                // Validate that all Tilemap layers reference an existing Tileset.
169                tilesets.get(id).ok_or_else(|| {
170                    AsepriteParseError::InvalidInput(format!(
171                        "Tilemap layer references a missing tileset (id {}",
172                        id
173                    ))
174                })?;
175            }
176        }
177        Ok(())
178    }
179
180    pub(crate) fn from_vec(layers: Vec<LayerData>) -> Result<Self> {
181        // TODO: Validate some properties
182        let parents = compute_parents(&layers);
183        Ok(LayersData { layers, parents })
184    }
185}
186
187impl Index<u32> for LayersData {
188    type Output = LayerData;
189
190    fn index(&self, index: u32) -> &Self::Output {
191        &self.layers[index as usize]
192    }
193}
194
195/// Describes how the pixels from two layers are combined.
196/// See also [Blend modes (Wikipedia)](https://en.wikipedia.org/wiki/Blend_modes)
197///
198/// Blend modes use Aseprite's "new layer blending method", i.e., we assume that
199/// the source Aseprite has a checkmark under "Edit > Preferences > Experimental >
200/// New Layer Blending Method (#1096)". This is the default as of Aseprite 1.2.25.
201#[allow(missing_docs)]
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum BlendMode {
204    Normal,
205    Multiply,
206    Screen,
207    Overlay,
208    Darken,
209    Lighten,
210    ColorDodge,
211    ColorBurn,
212    HardLight,
213    SoftLight,
214    Difference,
215    Exclusion,
216    Hue,
217    Saturation,
218    Color,
219    Luminosity,
220    Addition,
221    Subtract,
222    Divide,
223}
224
225pub(crate) fn parse_chunk(data: &[u8]) -> Result<LayerData> {
226    let mut reader = AseReader::new(data);
227
228    let flags = reader.word()?;
229    let layer_type = reader.word()?;
230    let child_level = reader.word()?;
231    let _default_width = reader.word()?;
232    let _default_height = reader.word()?;
233    let blend_mode = reader.word()?;
234    let opacity = reader.byte()?;
235    let _reserved1 = reader.byte()?;
236    let _reserved2 = reader.word()?;
237    let name = reader.string()?;
238    let layer_type = parse_layer_type(layer_type, &mut reader)?;
239
240    let flags = LayerFlags::from_bits_truncate(flags as u32);
241
242    let blend_mode = parse_blend_mode(blend_mode)?;
243
244    // println!(
245    //     "Layer {}: flags={:?} type={:?} blend_mode={:?}, opacity={}",
246    //     name, flags, layer_type, blend_mode, opacity
247    // );
248
249    Ok(LayerData {
250        flags,
251        name,
252        blend_mode,
253        opacity,
254        layer_type,
255        child_level,
256        user_data: None,
257    })
258}
259
260fn parse_layer_type<R: Read>(id: u16, reader: &mut AseReader<R>) -> Result<LayerType> {
261    match id {
262        0 => Ok(LayerType::Image),
263        1 => Ok(LayerType::Group),
264        2 => reader.dword().map(LayerType::Tilemap),
265        _ => Err(AsepriteParseError::InvalidInput(format!(
266            "Invalid layer type: {}",
267            id
268        ))),
269    }
270}
271
272fn parse_blend_mode(id: u16) -> Result<BlendMode> {
273    match id {
274        0 => Ok(BlendMode::Normal),
275        1 => Ok(BlendMode::Multiply),
276        2 => Ok(BlendMode::Screen),
277        3 => Ok(BlendMode::Overlay),
278        4 => Ok(BlendMode::Darken),
279        5 => Ok(BlendMode::Lighten),
280        6 => Ok(BlendMode::ColorDodge),
281        7 => Ok(BlendMode::ColorBurn),
282        8 => Ok(BlendMode::HardLight),
283        9 => Ok(BlendMode::SoftLight),
284        10 => Ok(BlendMode::Difference),
285        11 => Ok(BlendMode::Exclusion),
286        12 => Ok(BlendMode::Hue),
287        13 => Ok(BlendMode::Saturation),
288        14 => Ok(BlendMode::Color),
289        15 => Ok(BlendMode::Luminosity),
290        16 => Ok(BlendMode::Addition),
291        17 => Ok(BlendMode::Subtract),
292        18 => Ok(BlendMode::Divide),
293        _ => Err(AsepriteParseError::InvalidInput(format!(
294            "Invalid/Unsupported blend mode: {}",
295            id
296        ))),
297    }
298}
299
300fn compute_parents(layers: &[LayerData]) -> Vec<Option<u32>> {
301    let mut result = Vec::with_capacity(layers.len());
302
303    for id in 0..layers.len() {
304        let parent = {
305            let my_child_level = layers[id].child_level;
306            if my_child_level == 0 {
307                None
308            } else {
309                // Find first layer with a lower id and a lower child_level.
310                let mut parent_candidate = id - 1;
311                while layers[parent_candidate].child_level >= my_child_level {
312                    assert!(parent_candidate > 0);
313                    parent_candidate -= 1;
314                }
315                Some(parent_candidate as u32)
316            }
317        };
318        result.push(parent);
319    }
320    result
321}