use crate::{
cel::{Cel, CelId},
reader::AseReader,
tileset::TilesetsById,
user_data::UserData,
AsepriteFile, AsepriteParseError, Result,
};
use bitflags::bitflags;
use std::{io::Read, ops::Index};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayerType {
Image,
Group,
Tilemap(u32),
}
bitflags! {
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct LayerFlags: u32 {
const VISIBLE = 0x0001;
const EDITABLE = 0x0002;
const MOVEMENT_LOCKED = 0x0004;
const BACKGROUND = 0x0008;
const CONTINUOUS = 0x0010;
const COLLAPSED = 0x0020;
const REFERENCE = 0x0040;
const BACKGROUND_LAYER = Self::MOVEMENT_LOCKED.bits() | Self::BACKGROUND.bits();
}
}
#[derive(Debug)]
pub struct Layer<'a> {
pub(crate) file: &'a AsepriteFile,
pub(crate) layer_id: u32,
}
impl<'a> Layer<'a> {
fn data(&self) -> &LayerData {
&self.file.layers[self.layer_id]
}
pub fn id(&self) -> u32 {
self.layer_id
}
pub fn flags(&self) -> LayerFlags {
self.data().flags
}
pub fn name(&self) -> &str {
&self.data().name
}
pub fn blend_mode(&self) -> BlendMode {
self.data().blend_mode
}
pub fn opacity(&self) -> u8 {
self.data().opacity
}
pub fn layer_type(&self) -> LayerType {
self.data().layer_type
}
pub fn is_tilemap(&self) -> bool {
matches!(self.layer_type(), LayerType::Tilemap(_))
}
pub fn parent(&self) -> Option<Layer> {
self.file.layers.parents[self.layer_id as usize].map(|id| Layer {
file: self.file,
layer_id: id,
})
}
pub fn is_visible(&self) -> bool {
let layer_is_visible = self.data().flags.contains(LayerFlags::VISIBLE);
let parent_is_visible = self.parent().map(|p| p.is_visible()).unwrap_or(true);
layer_is_visible && parent_is_visible
}
pub fn frame(&self, frame_id: u32) -> Cel {
assert!(frame_id < self.file.num_frames());
let cel_id = CelId {
frame: frame_id as u16,
layer: self.layer_id as u16,
};
Cel {
file: self.file,
cel_id,
}
}
pub fn user_data(&self) -> Option<&UserData> {
self.data().user_data.as_ref()
}
}
#[derive(Debug)]
pub struct LayerData {
pub(crate) flags: LayerFlags,
pub(crate) name: String,
pub(crate) blend_mode: BlendMode,
pub(crate) opacity: u8,
pub(crate) layer_type: LayerType,
pub(crate) user_data: Option<UserData>,
child_level: u16,
}
impl LayerData {
pub(crate) fn is_background(&self) -> bool {
self.flags.contains(LayerFlags::BACKGROUND)
}
}
#[derive(Debug)]
pub(crate) struct LayersData {
pub(crate) layers: Vec<LayerData>,
parents: Vec<Option<u32>>,
}
impl LayersData {
pub(crate) fn validate(&self, tilesets: &TilesetsById) -> Result<()> {
for l in &self.layers {
if let LayerType::Tilemap(id) = l.layer_type {
tilesets.get(id).ok_or_else(|| {
AsepriteParseError::InvalidInput(format!(
"Tilemap layer references a missing tileset (id {}",
id
))
})?;
}
}
Ok(())
}
pub(crate) fn from_vec(layers: Vec<LayerData>) -> Result<Self> {
let parents = compute_parents(&layers);
Ok(LayersData { layers, parents })
}
}
impl Index<u32> for LayersData {
type Output = LayerData;
fn index(&self, index: u32) -> &Self::Output {
&self.layers[index as usize]
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
Addition,
Subtract,
Divide,
}
pub(crate) fn parse_chunk(data: &[u8]) -> Result<LayerData> {
let mut reader = AseReader::new(data);
let flags = reader.word()?;
let layer_type = reader.word()?;
let child_level = reader.word()?;
let _default_width = reader.word()?;
let _default_height = reader.word()?;
let blend_mode = reader.word()?;
let opacity = reader.byte()?;
let _reserved1 = reader.byte()?;
let _reserved2 = reader.word()?;
let name = reader.string()?;
let layer_type = parse_layer_type(layer_type, &mut reader)?;
let flags = LayerFlags::from_bits_truncate(flags as u32);
let blend_mode = parse_blend_mode(blend_mode)?;
Ok(LayerData {
flags,
name,
blend_mode,
opacity,
layer_type,
child_level,
user_data: None,
})
}
fn parse_layer_type<R: Read>(id: u16, reader: &mut AseReader<R>) -> Result<LayerType> {
match id {
0 => Ok(LayerType::Image),
1 => Ok(LayerType::Group),
2 => reader.dword().map(LayerType::Tilemap),
_ => Err(AsepriteParseError::InvalidInput(format!(
"Invalid layer type: {}",
id
))),
}
}
fn parse_blend_mode(id: u16) -> Result<BlendMode> {
match id {
0 => Ok(BlendMode::Normal),
1 => Ok(BlendMode::Multiply),
2 => Ok(BlendMode::Screen),
3 => Ok(BlendMode::Overlay),
4 => Ok(BlendMode::Darken),
5 => Ok(BlendMode::Lighten),
6 => Ok(BlendMode::ColorDodge),
7 => Ok(BlendMode::ColorBurn),
8 => Ok(BlendMode::HardLight),
9 => Ok(BlendMode::SoftLight),
10 => Ok(BlendMode::Difference),
11 => Ok(BlendMode::Exclusion),
12 => Ok(BlendMode::Hue),
13 => Ok(BlendMode::Saturation),
14 => Ok(BlendMode::Color),
15 => Ok(BlendMode::Luminosity),
16 => Ok(BlendMode::Addition),
17 => Ok(BlendMode::Subtract),
18 => Ok(BlendMode::Divide),
_ => Err(AsepriteParseError::InvalidInput(format!(
"Invalid/Unsupported blend mode: {}",
id
))),
}
}
fn compute_parents(layers: &[LayerData]) -> Vec<Option<u32>> {
let mut result = Vec::with_capacity(layers.len());
for id in 0..layers.len() {
let parent = {
let my_child_level = layers[id].child_level;
if my_child_level == 0 {
None
} else {
let mut parent_candidate = id - 1;
while layers[parent_candidate].child_level >= my_child_level {
assert!(parent_candidate > 0);
parent_candidate -= 1;
}
Some(parent_candidate as u32)
}
};
result.push(parent);
}
result
}