use std::collections::BTreeMap;
use std::ops::RangeInclusive;
use crate::error::AsepriteError;
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ColorMode {
Rgba,
Grayscale,
Indexed,
}
impl ColorMode {
pub fn bytes_per_pixel(self) -> usize {
match self {
Self::Rgba => 4,
Self::Grayscale => 2,
Self::Indexed => 1,
}
}
pub(crate) fn from_depth(depth: u16) -> Result<Self, AsepriteError> {
match depth {
32 => Ok(Self::Rgba),
16 => Ok(Self::Grayscale),
8 => Ok(Self::Indexed),
d => Err(AsepriteError::UnsupportedColorDepth(d)),
}
}
pub(crate) fn to_depth(self) -> u16 {
match self {
Self::Rgba => 32,
Self::Grayscale => 16,
Self::Indexed => 8,
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LayerKind {
Normal,
Group,
Tilemap {
tileset_index: u32,
},
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BlendMode {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
Addition,
Subtract,
Divide,
}
impl BlendMode {
pub(crate) fn from_u16(v: u16) -> Self {
match v {
0 => Self::Normal,
1 => Self::Multiply,
2 => Self::Screen,
3 => Self::Overlay,
4 => Self::Darken,
5 => Self::Lighten,
6 => Self::ColorDodge,
7 => Self::ColorBurn,
8 => Self::HardLight,
9 => Self::SoftLight,
10 => Self::Difference,
11 => Self::Exclusion,
12 => Self::Hue,
13 => Self::Saturation,
14 => Self::Color,
15 => Self::Luminosity,
16 => Self::Addition,
17 => Self::Subtract,
18 => Self::Divide,
_ => Self::Normal,
}
}
pub(crate) fn to_u16(self) -> u16 {
match self {
Self::Normal => 0,
Self::Multiply => 1,
Self::Screen => 2,
Self::Overlay => 3,
Self::Darken => 4,
Self::Lighten => 5,
Self::ColorDodge => 6,
Self::ColorBurn => 7,
Self::HardLight => 8,
Self::SoftLight => 9,
Self::Difference => 10,
Self::Exclusion => 11,
Self::Hue => 12,
Self::Saturation => 13,
Self::Color => 14,
Self::Luminosity => 15,
Self::Addition => 16,
Self::Subtract => 17,
Self::Divide => 18,
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LoopDirection {
Forward,
Reverse,
PingPong,
PingPongReverse,
}
impl LoopDirection {
pub(crate) fn from_u8(v: u8) -> Self {
match v {
0 => Self::Forward,
1 => Self::Reverse,
2 => Self::PingPong,
3 => Self::PingPongReverse,
_ => Self::Forward,
}
}
pub(crate) fn to_u8(self) -> u8 {
match self {
Self::Forward => 0,
Self::Reverse => 1,
Self::PingPong => 2,
Self::PingPongReverse => 3,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum ColorProfile {
None,
SRgb {
flags: u16,
gamma: u32,
},
Icc {
flags: u16,
gamma: u32,
data: Vec<u8>,
},
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct LayerRef(pub(crate) usize);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct GroupRef(pub(crate) usize);
impl LayerRef {
pub fn index(&self) -> usize {
self.0
}
}
impl GroupRef {
pub fn index(&self) -> usize {
self.0
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Pixels {
pub data: Vec<u8>,
pub width: u16,
pub height: u16,
}
impl Pixels {
pub fn new(
data: Vec<u8>,
width: u16,
height: u16,
color_mode: ColorMode,
) -> Result<Self, AsepriteError> {
let expected = width as usize * height as usize * color_mode.bytes_per_pixel();
if data.len() != expected {
return Err(AsepriteError::PixelSizeMismatch {
expected,
actual: data.len(),
});
}
Ok(Self {
data,
width,
height,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
pub name: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GridInfo {
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
}
impl Default for GridInfo {
fn default() -> Self {
Self {
x: 0,
y: 0,
width: 16,
height: 16,
}
}
}
#[derive(Clone)]
pub(crate) struct UnknownChunk {
pub frame_index: usize,
pub chunk_type: u16,
pub data: Vec<u8>,
}
#[derive(Clone, Debug)]
pub(crate) struct ChunkOrderEntry {
pub frame_index: usize,
pub chunk_type: u16,
pub layer_index: Option<usize>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Layer {
pub name: String,
pub kind: LayerKind,
pub parent: Option<usize>,
pub opacity: u8,
pub blend_mode: BlendMode,
pub visible: bool,
pub editable: bool,
pub lock_movement: bool,
pub background: bool,
pub prefer_linked_cels: bool,
pub collapsed: bool,
pub reference_layer: bool,
pub user_data: Option<UserData>,
}
pub struct LayerOptions {
pub opacity: u8,
pub blend_mode: BlendMode,
pub visible: bool,
pub editable: bool,
pub lock_movement: bool,
pub background: bool,
pub collapsed: bool,
pub prefer_linked_cels: bool,
pub reference_layer: bool,
}
impl Default for LayerOptions {
fn default() -> Self {
Self {
opacity: 255,
blend_mode: BlendMode::Normal,
visible: true,
editable: true,
lock_movement: false,
background: false,
collapsed: false,
prefer_linked_cels: false,
reference_layer: false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Frame {
pub duration_ms: u16,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Tag {
pub name: String,
pub from_frame: usize,
pub to_frame: usize,
pub direction: LoopDirection,
pub repeat: u16,
pub user_data: Option<UserData>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Cel {
pub kind: CelKind,
pub opacity: u8,
pub z_index: i16,
pub user_data: Option<UserData>,
pub extra: Option<CelExtra>,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum CelKind {
Raw { pixels: Pixels, x: i16, y: i16 },
Compressed {
pixels: Pixels,
x: i16,
y: i16,
original_compressed: Option<Vec<u8>>,
},
Linked { source_frame: usize, x: i16, y: i16 },
Tilemap {
width: u16,
height: u16,
bits_per_tile: u16,
tile_id_bitmask: u32,
x_flip_bitmask: u32,
y_flip_bitmask: u32,
d_flip_bitmask: u32,
tiles: Vec<u32>,
x: i16,
y: i16,
original_compressed: Option<Vec<u8>>,
},
}
pub struct CelOptions {
pub pixels: Pixels,
pub x: i16,
pub y: i16,
pub opacity: u8,
pub z_index: i16,
}
impl Default for CelOptions {
fn default() -> Self {
Self {
pixels: Pixels {
data: vec![],
width: 0,
height: 0,
},
x: 0,
y: 0,
opacity: 255,
z_index: 0,
}
}
}
pub struct LinkedCelOptions {
pub x: i16,
pub y: i16,
pub opacity: u8,
pub z_index: i16,
pub user_data: Option<UserData>,
pub extra: Option<CelExtra>,
}
impl Default for LinkedCelOptions {
fn default() -> Self {
Self {
x: 0,
y: 0,
opacity: 255,
z_index: 0,
user_data: None,
extra: None,
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct UserData {
pub text: Option<String>,
pub color: Option<Color>,
pub properties: Vec<PropertiesMap>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PropertiesMap {
pub key: u32,
pub entries: Vec<(String, PropertyValue)>,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyValue {
Bool(bool),
Int8(i8),
UInt8(u8),
Int16(i16),
UInt16(u16),
Int32(i32),
UInt32(u32),
Int64(i64),
UInt64(u64),
Fixed(u32),
Float(f32),
Double(f64),
String(String),
Point(i32, i32),
Size(i32, i32),
Rect(i32, i32, i32, i32),
Vector(Vec<PropertyValue>),
Properties(Vec<(String, PropertyValue)>),
Uuid([u8; 16]),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Slice {
pub name: String,
pub keys: Vec<SliceKey>,
pub has_nine_patch: bool,
pub has_pivot: bool,
pub user_data: Option<UserData>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SliceKey {
pub frame: u32,
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
pub nine_patch: Option<NinePatch>,
pub pivot: Option<(i32, i32)>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct NinePatch {
pub center_x: i32,
pub center_y: i32,
pub center_width: u32,
pub center_height: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CelExtra {
pub precise_x: u32,
pub precise_y: u32,
pub width: u32,
pub height: u32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TilesetFlags(pub u32);
impl TilesetFlags {
pub fn has_external_link(self) -> bool {
self.0 & 1 != 0
}
pub fn has_embedded_tiles(self) -> bool {
self.0 & 2 != 0
}
pub fn empty_tile_is_zero(self) -> bool {
self.0 & 4 != 0
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Tileset {
pub id: u32,
pub flags: TilesetFlags,
pub name: String,
pub tile_count: u32,
pub tile_width: u16,
pub tile_height: u16,
pub base_index: i16,
pub data: TilesetData,
pub user_data: Option<UserData>,
pub tile_user_data: Vec<Option<UserData>>,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum TilesetData {
Embedded {
pixels: Vec<u8>,
original_compressed: Option<Vec<u8>>,
},
External {
external_file_id: u32,
tileset_id_in_external: u32,
},
Empty,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ExternalFile {
pub id: u32,
pub file_type: ExternalFileType,
pub name: String,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExternalFileType {
Palette,
Tileset,
ExtensionProps,
ExtensionTileMgmt,
}
impl ExternalFileType {
pub(crate) fn from_u8(v: u8) -> Self {
match v {
0 => Self::Palette,
1 => Self::Tileset,
2 => Self::ExtensionProps,
3 => Self::ExtensionTileMgmt,
_ => Self::Palette,
}
}
pub(crate) fn to_u8(self) -> u8 {
match self {
Self::Palette => 0,
Self::Tileset => 1,
Self::ExtensionProps => 2,
Self::ExtensionTileMgmt => 3,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LegacyMask {
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
pub name: String,
pub bitmap: Vec<u8>,
}
#[derive(Clone)]
pub struct AsepriteFile {
width: u16,
height: u16,
color_mode: ColorMode,
flags: u32,
deprecated_speed: u16,
num_colors: u16,
transparent_index: u8,
pixel_ratio: (u8, u8),
grid: GridInfo,
color_profile: Option<ColorProfile>,
palette: Vec<Color>,
layers: Vec<Layer>,
frames: Vec<Frame>,
tags: Vec<Tag>,
slices: Vec<Slice>,
sprite_user_data: Option<UserData>,
cels: BTreeMap<(usize, usize), Cel>,
tilesets: Vec<Tileset>,
external_files: Vec<ExternalFile>,
legacy_masks: Vec<LegacyMask>,
pub(crate) unknown_chunks: Vec<UnknownChunk>,
pub(crate) chunk_order: Vec<ChunkOrderEntry>,
}
impl std::fmt::Debug for AsepriteFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsepriteFile")
.field("width", &self.width)
.field("height", &self.height)
.field("color_mode", &self.color_mode)
.field("layers", &self.layers.len())
.field("frames", &self.frames.len())
.field("tags", &self.tags.len())
.field("cels", &self.cels.len())
.finish()
}
}
impl AsepriteFile {
pub fn new(width: u16, height: u16, color_mode: ColorMode) -> Self {
Self {
width,
height,
color_mode,
flags: 1,
deprecated_speed: 0,
num_colors: 0,
transparent_index: 0,
pixel_ratio: (1, 1),
grid: GridInfo::default(),
color_profile: None,
palette: Vec::new(),
layers: Vec::new(),
frames: Vec::new(),
tags: Vec::new(),
slices: Vec::new(),
sprite_user_data: None,
cels: BTreeMap::new(),
tilesets: Vec::new(),
external_files: Vec::new(),
legacy_masks: Vec::new(),
unknown_chunks: Vec::new(),
chunk_order: Vec::new(),
}
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn color_mode(&self) -> ColorMode {
self.color_mode
}
pub fn flags(&self) -> u32 {
self.flags
}
pub(crate) fn deprecated_speed(&self) -> u16 {
self.deprecated_speed
}
pub(crate) fn num_colors(&self) -> u16 {
self.num_colors
}
pub fn pixel_ratio(&self) -> (u8, u8) {
self.pixel_ratio
}
pub fn grid(&self) -> &GridInfo {
&self.grid
}
pub fn color_profile(&self) -> &Option<ColorProfile> {
&self.color_profile
}
pub fn palette(&self) -> &[Color] {
&self.palette
}
pub fn layers(&self) -> &[Layer] {
&self.layers
}
pub fn frames(&self) -> &[Frame] {
&self.frames
}
pub fn tags(&self) -> &[Tag] {
&self.tags
}
pub fn slices(&self) -> &[Slice] {
&self.slices
}
pub fn sprite_user_data(&self) -> &Option<UserData> {
&self.sprite_user_data
}
pub fn transparent_index(&self) -> u8 {
self.transparent_index
}
pub fn legacy_masks(&self) -> &[LegacyMask] {
&self.legacy_masks
}
pub fn tilesets(&self) -> &[Tileset] {
&self.tilesets
}
pub fn external_files(&self) -> &[ExternalFile] {
&self.external_files
}
pub fn cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
self.cels.get(&(layer.0, frame))
}
pub fn resolve_cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
let cel = self.cels.get(&(layer.0, frame))?;
match &cel.kind {
CelKind::Linked { source_frame, .. } => self.cels.get(&(layer.0, *source_frame)),
_ => Some(cel),
}
}
pub fn layer_ref(&self, index: usize) -> Option<LayerRef> {
self.layers.get(index).and_then(|l| match l.kind {
LayerKind::Normal | LayerKind::Tilemap { .. } => Some(LayerRef(index)),
LayerKind::Group => None,
})
}
pub fn group_ref(&self, index: usize) -> Option<GroupRef> {
self.layers.get(index).and_then(|l| {
if l.kind == LayerKind::Group {
Some(GroupRef(index))
} else {
None
}
})
}
fn push_layer(
&mut self,
name: &str,
kind: LayerKind,
parent: Option<usize>,
opts: &LayerOptions,
) -> usize {
let index = self.layers.len();
self.layers.push(Layer {
name: name.to_string(),
kind,
parent,
opacity: opts.opacity,
blend_mode: opts.blend_mode,
visible: opts.visible,
editable: opts.editable,
lock_movement: opts.lock_movement,
background: opts.background,
prefer_linked_cels: opts.prefer_linked_cels,
collapsed: opts.collapsed,
reference_layer: opts.reference_layer,
user_data: None,
});
index
}
pub fn add_layer(&mut self, name: &str) -> LayerRef {
LayerRef(self.push_layer(name, LayerKind::Normal, None, &LayerOptions::default()))
}
pub fn add_layer_with(&mut self, name: &str, opts: LayerOptions) -> LayerRef {
LayerRef(self.push_layer(name, LayerKind::Normal, None, &opts))
}
pub fn add_group(&mut self, name: &str) -> GroupRef {
GroupRef(self.push_layer(name, LayerKind::Group, None, &LayerOptions::default()))
}
pub fn add_group_with(&mut self, name: &str, opts: LayerOptions) -> GroupRef {
GroupRef(self.push_layer(name, LayerKind::Group, None, &opts))
}
pub fn add_layer_in(&mut self, name: &str, parent: GroupRef) -> LayerRef {
LayerRef(self.push_layer(
name,
LayerKind::Normal,
Some(parent.0),
&LayerOptions::default(),
))
}
pub fn add_layer_in_with(
&mut self,
name: &str,
parent: GroupRef,
opts: LayerOptions,
) -> LayerRef {
LayerRef(self.push_layer(name, LayerKind::Normal, Some(parent.0), &opts))
}
pub fn add_group_in(&mut self, name: &str, parent: GroupRef) -> GroupRef {
GroupRef(self.push_layer(
name,
LayerKind::Group,
Some(parent.0),
&LayerOptions::default(),
))
}
pub fn add_group_in_with(
&mut self,
name: &str,
parent: GroupRef,
opts: LayerOptions,
) -> GroupRef {
GroupRef(self.push_layer(name, LayerKind::Group, Some(parent.0), &opts))
}
pub fn add_tilemap_layer(&mut self, name: &str, tileset_index: u32) -> LayerRef {
let index = self.layers.len();
self.layers.push(Layer {
name: name.to_string(),
kind: LayerKind::Tilemap { tileset_index },
parent: None,
opacity: 255,
blend_mode: BlendMode::Normal,
visible: true,
editable: true,
lock_movement: false,
background: false,
prefer_linked_cels: false,
collapsed: false,
reference_layer: false,
user_data: None,
});
LayerRef(index)
}
#[allow(clippy::too_many_arguments)]
pub fn set_tilemap_cel(
&mut self,
layer: LayerRef,
frame: usize,
tiles: Vec<u32>,
width: u16,
height: u16,
x: i16,
y: i16,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Tilemap {
width,
height,
bits_per_tile: 32,
tile_id_bitmask: 0x1fff_ffff,
x_flip_bitmask: 0x2000_0000,
y_flip_bitmask: 0x4000_0000,
d_flip_bitmask: 0x8000_0000,
tiles,
x,
y,
original_compressed: None,
},
opacity: 255,
z_index: 0,
user_data: None,
extra: None,
},
);
Ok(())
}
pub fn add_frame(&mut self, duration_ms: u16) -> usize {
let index = self.frames.len();
self.frames.push(Frame { duration_ms });
index
}
pub fn set_cel(
&mut self,
layer: LayerRef,
frame: usize,
pixels: Pixels,
x: i16,
y: i16,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Compressed {
pixels,
x,
y,
original_compressed: None,
},
opacity: 255,
z_index: 0,
user_data: None,
extra: None,
},
);
Ok(())
}
pub fn set_cel_with(
&mut self,
layer: LayerRef,
frame: usize,
opts: CelOptions,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Compressed {
pixels: opts.pixels,
x: opts.x,
y: opts.y,
original_compressed: None,
},
opacity: opts.opacity,
z_index: opts.z_index,
user_data: None,
extra: None,
},
);
Ok(())
}
pub fn set_raw_cel(
&mut self,
layer: LayerRef,
frame: usize,
pixels: Pixels,
x: i16,
y: i16,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Raw { pixels, x, y },
opacity: 255,
z_index: 0,
user_data: None,
extra: None,
},
);
Ok(())
}
pub fn set_linked_cel(
&mut self,
layer: LayerRef,
frame: usize,
source_frame: usize,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
if source_frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(source_frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Linked {
source_frame,
x: 0,
y: 0,
},
opacity: 255,
z_index: 0,
user_data: None,
extra: None,
},
);
Ok(())
}
pub fn set_linked_cel_with(
&mut self,
layer: LayerRef,
frame: usize,
source_frame: usize,
opts: LinkedCelOptions,
) -> Result<(), AsepriteError> {
if frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(frame));
}
if source_frame >= self.frames.len() {
return Err(AsepriteError::FrameOutOfBounds(source_frame));
}
self.cels.insert(
(layer.0, frame),
Cel {
kind: CelKind::Linked {
source_frame,
x: opts.x,
y: opts.y,
},
opacity: opts.opacity,
z_index: opts.z_index,
user_data: opts.user_data,
extra: opts.extra,
},
);
Ok(())
}
pub fn add_tag(
&mut self,
name: &str,
frames: RangeInclusive<usize>,
direction: LoopDirection,
) -> Result<usize, AsepriteError> {
self.add_tag_with(name, frames, direction, 0)
}
pub fn add_tag_with(
&mut self,
name: &str,
frames: RangeInclusive<usize>,
direction: LoopDirection,
repeat: u16,
) -> Result<usize, AsepriteError> {
let from = *frames.start();
let to = *frames.end();
if !self.frames.is_empty() && to >= self.frames.len() {
return Err(AsepriteError::InvalidFrameRange);
}
let index = self.tags.len();
self.tags.push(Tag {
name: name.to_string(),
from_frame: from,
to_frame: to,
direction,
repeat,
user_data: None,
});
Ok(index)
}
pub fn set_palette(&mut self, colors: &[Color]) -> Result<(), AsepriteError> {
if colors.len() > 256 {
return Err(AsepriteError::FormatLimitExceeded {
field: "palette",
value: colors.len(),
max: 256,
});
}
self.palette = colors.to_vec();
Ok(())
}
pub fn set_transparent_index(&mut self, index: u8) {
self.transparent_index = index;
}
pub fn set_color_profile(&mut self, profile: ColorProfile) {
self.color_profile = Some(profile);
}
pub fn add_slice(&mut self, slice: Slice) {
self.slices.push(slice);
}
pub fn set_sprite_user_data(&mut self, ud: UserData) {
self.sprite_user_data = Some(ud);
}
pub fn add_tileset(&mut self, tileset: Tileset) {
self.tilesets.push(tileset);
}
pub fn add_external_file(&mut self, ef: ExternalFile) {
self.external_files.push(ef);
}
pub fn set_layer_user_data(&mut self, layer: LayerRef, ud: UserData) {
if let Some(l) = self.layers.get_mut(layer.0) {
l.user_data = Some(ud);
}
}
pub fn set_group_user_data(&mut self, group: GroupRef, ud: UserData) {
if let Some(l) = self.layers.get_mut(group.0) {
l.user_data = Some(ud);
}
}
pub fn set_cel_user_data(&mut self, layer: LayerRef, frame: usize, ud: UserData) {
if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) {
cel.user_data = Some(ud);
}
}
pub fn set_cel_extra(&mut self, layer: LayerRef, frame: usize, extra: CelExtra) {
if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) {
cel.extra = Some(extra);
}
}
pub fn set_tag_user_data(&mut self, tag_index: usize, ud: UserData) {
if let Some(tag) = self.tags.get_mut(tag_index) {
tag.user_data = Some(ud);
}
}
pub(crate) fn set_flags(&mut self, flags: u32) {
self.flags = flags;
}
pub(crate) fn set_deprecated_speed(&mut self, speed: u16) {
self.deprecated_speed = speed;
}
pub(crate) fn set_num_colors(&mut self, n: u16) {
self.num_colors = n;
}
pub(crate) fn set_pixel_ratio(&mut self, ratio: (u8, u8)) {
self.pixel_ratio = ratio;
}
pub(crate) fn set_grid(&mut self, grid: GridInfo) {
self.grid = grid;
}
pub(crate) fn push_legacy_mask(&mut self, mask: LegacyMask) {
self.legacy_masks.push(mask);
}
pub(crate) fn push_unknown_chunk(
&mut self,
frame_index: usize,
chunk_type: u16,
data: Vec<u8>,
) {
self.unknown_chunks.push(UnknownChunk {
frame_index,
chunk_type,
data,
});
}
pub(crate) fn push_tileset(&mut self, tileset: Tileset) {
self.tilesets.push(tileset);
}
pub(crate) fn push_external_file(&mut self, ef: ExternalFile) {
self.external_files.push(ef);
}
pub(crate) fn tilesets_mut(&mut self) -> &mut Vec<Tileset> {
&mut self.tilesets
}
pub(crate) fn push_layer_raw(&mut self, layer: Layer) {
self.layers.push(layer);
}
pub(crate) fn insert_cel(&mut self, layer_index: usize, frame_index: usize, cel: Cel) {
self.cels.insert((layer_index, frame_index), cel);
}
pub(crate) fn push_tag(&mut self, tag: Tag) {
self.tags.push(tag);
}
pub(crate) fn push_slice(&mut self, slice: Slice) {
self.slices.push(slice);
}
pub(crate) fn set_sprite_user_data_raw(&mut self, ud: UserData) {
self.sprite_user_data = Some(ud);
}
pub(crate) fn layers_mut(&mut self) -> &mut Vec<Layer> {
&mut self.layers
}
pub(crate) fn tags_mut(&mut self) -> &mut Vec<Tag> {
&mut self.tags
}
pub(crate) fn slices_mut(&mut self) -> &mut Vec<Slice> {
&mut self.slices
}
pub(crate) fn cel_mut(&mut self, layer_index: usize, frame_index: usize) -> Option<&mut Cel> {
self.cels.get_mut(&(layer_index, frame_index))
}
pub(crate) fn set_palette_entry(&mut self, index: usize, color: Color) {
if index >= self.palette.len() {
self.palette.resize(
index + 1,
Color {
r: 0,
g: 0,
b: 0,
a: 255,
name: None,
},
);
}
self.palette[index] = color;
}
pub(crate) fn cels_iter(&self) -> impl Iterator<Item = (&(usize, usize), &Cel)> {
self.cels.iter()
}
pub(crate) fn unknown_chunks_for_frame(
&self,
frame_index: usize,
) -> impl Iterator<Item = &UnknownChunk> {
self.unknown_chunks
.iter()
.filter(move |uc| uc.frame_index == frame_index)
}
pub(crate) fn push_chunk_order(
&mut self,
frame_index: usize,
chunk_type: u16,
layer_index: Option<usize>,
) {
self.chunk_order.push(ChunkOrderEntry {
frame_index,
chunk_type,
layer_index,
});
}
pub(crate) fn chunk_order_for_frame(
&self,
frame_index: usize,
) -> impl Iterator<Item = &ChunkOrderEntry> {
self.chunk_order
.iter()
.filter(move |e| e.frame_index == frame_index)
}
}