1use std::collections::BTreeMap;
2use std::ops::RangeInclusive;
3
4use crate::error::AsepriteError;
5
6#[non_exhaustive]
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum ColorMode {
12 Rgba,
13 Grayscale,
14 Indexed,
15}
16
17impl ColorMode {
18 pub fn bytes_per_pixel(self) -> usize {
20 match self {
21 Self::Rgba => 4,
22 Self::Grayscale => 2,
23 Self::Indexed => 1,
24 }
25 }
26
27 pub(crate) fn from_depth(depth: u16) -> Result<Self, AsepriteError> {
28 match depth {
29 32 => Ok(Self::Rgba),
30 16 => Ok(Self::Grayscale),
31 8 => Ok(Self::Indexed),
32 d => Err(AsepriteError::UnsupportedColorDepth(d)),
33 }
34 }
35
36 pub(crate) fn to_depth(self) -> u16 {
37 match self {
38 Self::Rgba => 32,
39 Self::Grayscale => 16,
40 Self::Indexed => 8,
41 }
42 }
43}
44
45#[non_exhaustive]
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48pub enum LayerKind {
49 Normal,
50 Group,
51 Tilemap { tileset_index: u32 },
53}
54
55#[non_exhaustive]
57#[derive(Copy, Clone, Debug, PartialEq, Eq)]
58pub enum BlendMode {
59 Normal,
60 Multiply,
61 Screen,
62 Overlay,
63 Darken,
64 Lighten,
65 ColorDodge,
66 ColorBurn,
67 HardLight,
68 SoftLight,
69 Difference,
70 Exclusion,
71 Hue,
72 Saturation,
73 Color,
74 Luminosity,
75 Addition,
76 Subtract,
77 Divide,
78}
79
80impl BlendMode {
81 pub(crate) fn from_u16(v: u16) -> Self {
82 match v {
83 0 => Self::Normal, 1 => Self::Multiply, 2 => Self::Screen,
84 3 => Self::Overlay, 4 => Self::Darken, 5 => Self::Lighten,
85 6 => Self::ColorDodge, 7 => Self::ColorBurn, 8 => Self::HardLight,
86 9 => Self::SoftLight, 10 => Self::Difference, 11 => Self::Exclusion,
87 12 => Self::Hue, 13 => Self::Saturation, 14 => Self::Color,
88 15 => Self::Luminosity, 16 => Self::Addition, 17 => Self::Subtract,
89 18 => Self::Divide, _ => Self::Normal,
90 }
91 }
92
93 pub(crate) fn to_u16(self) -> u16 {
94 match self {
95 Self::Normal => 0, Self::Multiply => 1, Self::Screen => 2,
96 Self::Overlay => 3, Self::Darken => 4, Self::Lighten => 5,
97 Self::ColorDodge => 6, Self::ColorBurn => 7, Self::HardLight => 8,
98 Self::SoftLight => 9, Self::Difference => 10, Self::Exclusion => 11,
99 Self::Hue => 12, Self::Saturation => 13, Self::Color => 14,
100 Self::Luminosity => 15, Self::Addition => 16, Self::Subtract => 17,
101 Self::Divide => 18,
102 }
103 }
104}
105
106#[non_exhaustive]
108#[derive(Copy, Clone, Debug, PartialEq, Eq)]
109pub enum LoopDirection {
110 Forward,
111 Reverse,
112 PingPong,
113 PingPongReverse,
114}
115
116impl LoopDirection {
117 pub(crate) fn from_u8(v: u8) -> Self {
118 match v {
119 0 => Self::Forward, 1 => Self::Reverse,
120 2 => Self::PingPong, 3 => Self::PingPongReverse,
121 _ => Self::Forward,
122 }
123 }
124
125 pub(crate) fn to_u8(self) -> u8 {
126 match self {
127 Self::Forward => 0, Self::Reverse => 1,
128 Self::PingPong => 2, Self::PingPongReverse => 3,
129 }
130 }
131}
132
133#[non_exhaustive]
135#[derive(Clone, Debug, PartialEq)]
136pub enum ColorProfile {
137 None,
138 SRgb { flags: u16, gamma: u32 },
139 Icc { flags: u16, gamma: u32, data: Vec<u8> },
140}
141
142#[derive(Copy, Clone, Debug, PartialEq, Eq)]
146pub struct LayerRef(pub(crate) usize);
147
148#[derive(Copy, Clone, Debug, PartialEq, Eq)]
150pub struct GroupRef(pub(crate) usize);
151
152impl LayerRef {
153 pub fn index(&self) -> usize { self.0 }
155}
156
157impl GroupRef {
158 pub fn index(&self) -> usize { self.0 }
160}
161
162#[derive(Clone, Debug, PartialEq)]
166pub struct Pixels {
167 pub data: Vec<u8>,
168 pub width: u16,
169 pub height: u16,
170}
171
172impl Pixels {
173 pub fn new(data: Vec<u8>, width: u16, height: u16, color_mode: ColorMode) -> Result<Self, AsepriteError> {
175 let expected = width as usize * height as usize * color_mode.bytes_per_pixel();
176 if data.len() != expected {
177 return Err(AsepriteError::PixelSizeMismatch { expected, actual: data.len() });
178 }
179 Ok(Self { data, width, height })
180 }
181}
182
183#[derive(Clone, Debug, PartialEq, Eq)]
185pub struct Color {
186 pub r: u8,
187 pub g: u8,
188 pub b: u8,
189 pub a: u8,
190 pub name: Option<String>,
191}
192
193#[derive(Clone, Debug, PartialEq)]
195pub struct GridInfo {
196 pub x: i16,
197 pub y: i16,
198 pub width: u16,
199 pub height: u16,
200}
201
202impl Default for GridInfo {
203 fn default() -> Self {
204 Self { x: 0, y: 0, width: 16, height: 16 }
205 }
206}
207
208#[derive(Clone)]
209pub(crate) struct UnknownChunk {
210 pub frame_index: usize,
211 pub chunk_type: u16,
212 pub data: Vec<u8>,
213}
214
215#[derive(Clone, Debug)]
216pub(crate) struct ChunkOrderEntry {
217 pub frame_index: usize,
218 pub chunk_type: u16,
219 pub layer_index: Option<usize>,
221}
222
223#[derive(Clone, Debug, PartialEq)]
227pub struct Layer {
228 pub name: String,
229 pub kind: LayerKind,
230 pub parent: Option<usize>,
231 pub opacity: u8,
232 pub blend_mode: BlendMode,
233 pub visible: bool,
234 pub editable: bool,
235 pub lock_movement: bool,
236 pub background: bool,
237 pub prefer_linked_cels: bool,
238 pub collapsed: bool,
239 pub reference_layer: bool,
240 pub user_data: Option<UserData>,
241}
242
243pub struct LayerOptions {
245 pub opacity: u8,
246 pub blend_mode: BlendMode,
247 pub visible: bool,
248 pub editable: bool,
249 pub lock_movement: bool,
250 pub background: bool,
251 pub collapsed: bool,
252 pub prefer_linked_cels: bool,
253 pub reference_layer: bool,
254}
255
256impl Default for LayerOptions {
257 fn default() -> Self {
258 Self {
259 opacity: 255, blend_mode: BlendMode::Normal,
260 visible: true, editable: true, lock_movement: false,
261 background: false, collapsed: false,
262 prefer_linked_cels: false, reference_layer: false,
263 }
264 }
265}
266
267#[derive(Clone, Debug, PartialEq, Eq)]
271pub struct Frame {
272 pub duration_ms: u16,
273}
274
275#[derive(Clone, Debug, PartialEq)]
279pub struct Tag {
280 pub name: String,
281 pub from_frame: usize,
282 pub to_frame: usize,
283 pub direction: LoopDirection,
284 pub repeat: u16,
285 pub user_data: Option<UserData>,
286}
287
288#[derive(Clone, Debug, PartialEq)]
292pub struct Cel {
293 pub kind: CelKind,
294 pub opacity: u8,
295 pub z_index: i16,
296 pub user_data: Option<UserData>,
297 pub extra: Option<CelExtra>,
298}
299
300#[non_exhaustive]
302#[derive(Clone, Debug, PartialEq)]
303pub enum CelKind {
304 Raw { pixels: Pixels, x: i16, y: i16 },
306 Compressed { pixels: Pixels, x: i16, y: i16, original_compressed: Option<Vec<u8>> },
308 Linked { source_frame: usize, x: i16, y: i16 },
310 Tilemap {
312 width: u16,
313 height: u16,
314 bits_per_tile: u16,
315 tile_id_bitmask: u32,
316 x_flip_bitmask: u32,
317 y_flip_bitmask: u32,
318 d_flip_bitmask: u32,
319 tiles: Vec<u32>,
320 x: i16,
321 y: i16,
322 original_compressed: Option<Vec<u8>>,
323 },
324}
325
326pub struct CelOptions {
328 pub pixels: Pixels,
329 pub x: i16,
330 pub y: i16,
331 pub opacity: u8,
332 pub z_index: i16,
333}
334
335impl Default for CelOptions {
336 fn default() -> Self {
337 Self {
338 pixels: Pixels { data: vec![], width: 0, height: 0 },
339 x: 0, y: 0, opacity: 255, z_index: 0,
340 }
341 }
342}
343
344#[derive(Clone, Debug, Default, PartialEq)]
348pub struct UserData {
349 pub text: Option<String>,
350 pub color: Option<Color>,
351 pub properties: Vec<PropertiesMap>,
352}
353
354#[derive(Clone, Debug, PartialEq)]
356pub struct PropertiesMap {
357 pub key: u32,
358 pub entries: Vec<(String, PropertyValue)>,
359}
360
361#[non_exhaustive]
363#[derive(Clone, Debug, PartialEq)]
364pub enum PropertyValue {
365 Bool(bool), Int8(i8), UInt8(u8), Int16(i16), UInt16(u16),
366 Int32(i32), UInt32(u32), Int64(i64), UInt64(u64),
367 Fixed(u32), Float(f32), Double(f64), String(String),
368 Point(i32, i32), Size(i32, i32), Rect(i32, i32, i32, i32),
369 Vector(Vec<PropertyValue>), Properties(Vec<(String, PropertyValue)>),
370 Uuid([u8; 16]),
371}
372
373#[derive(Clone, Debug, PartialEq)]
377pub struct Slice {
378 pub name: String,
379 pub keys: Vec<SliceKey>,
380 pub has_nine_patch: bool,
381 pub has_pivot: bool,
382 pub user_data: Option<UserData>,
383}
384
385#[derive(Clone, Debug, PartialEq)]
387pub struct SliceKey {
388 pub frame: u32, pub x: i32, pub y: i32,
389 pub width: u32, pub height: u32,
390 pub nine_patch: Option<NinePatch>,
391 pub pivot: Option<(i32, i32)>,
392}
393
394#[derive(Clone, Debug, PartialEq)]
396pub struct NinePatch {
397 pub center_x: i32, pub center_y: i32,
398 pub center_width: u32, pub center_height: u32,
399}
400
401#[derive(Clone, Debug, PartialEq)]
405pub struct CelExtra {
406 pub precise_x: u32, pub precise_y: u32,
407 pub width: u32, pub height: u32,
408}
409
410#[derive(Copy, Clone, Debug, PartialEq, Eq)]
414pub struct TilesetFlags(pub u32);
415
416impl TilesetFlags {
417 pub fn has_external_link(self) -> bool { self.0 & 1 != 0 }
419 pub fn has_embedded_tiles(self) -> bool { self.0 & 2 != 0 }
421 pub fn empty_tile_is_zero(self) -> bool { self.0 & 4 != 0 }
423}
424
425#[derive(Clone, Debug, PartialEq)]
427pub struct Tileset {
428 pub id: u32,
429 pub flags: TilesetFlags,
430 pub name: String,
431 pub tile_count: u32,
432 pub tile_width: u16,
433 pub tile_height: u16,
434 pub base_index: i16,
435 pub data: TilesetData,
436 pub user_data: Option<UserData>,
437 pub tile_user_data: Vec<Option<UserData>>,
438}
439
440#[non_exhaustive]
442#[derive(Clone, Debug, PartialEq)]
443pub enum TilesetData {
444 Embedded { pixels: Vec<u8>, original_compressed: Option<Vec<u8>> },
445 External { external_file_id: u32, tileset_id_in_external: u32 },
446 Empty,
447}
448
449#[derive(Clone, Debug, PartialEq)]
453pub struct ExternalFile {
454 pub id: u32,
455 pub file_type: ExternalFileType,
456 pub name: String,
457}
458
459#[non_exhaustive]
461#[derive(Copy, Clone, Debug, PartialEq, Eq)]
462pub enum ExternalFileType {
463 Palette,
464 Tileset,
465 ExtensionProps,
466 ExtensionTileMgmt,
467}
468
469impl ExternalFileType {
470 pub(crate) fn from_u8(v: u8) -> Self {
471 match v {
472 0 => Self::Palette,
473 1 => Self::Tileset,
474 2 => Self::ExtensionProps,
475 3 => Self::ExtensionTileMgmt,
476 _ => Self::Palette,
477 }
478 }
479
480 pub(crate) fn to_u8(self) -> u8 {
481 match self {
482 Self::Palette => 0,
483 Self::Tileset => 1,
484 Self::ExtensionProps => 2,
485 Self::ExtensionTileMgmt => 3,
486 }
487 }
488}
489
490#[derive(Clone, Debug, PartialEq)]
494pub struct LegacyMask {
495 pub x: i16,
496 pub y: i16,
497 pub width: u16,
498 pub height: u16,
499 pub name: String,
500 pub bitmap: Vec<u8>,
501}
502
503#[derive(Clone)]
530pub struct AsepriteFile {
531 width: u16,
532 height: u16,
533 color_mode: ColorMode,
534 flags: u32,
535 deprecated_speed: u16,
536 num_colors: u16,
537 transparent_index: u8,
538 pixel_ratio: (u8, u8),
539 grid: GridInfo,
540 color_profile: Option<ColorProfile>,
541 palette: Vec<Color>,
542 layers: Vec<Layer>,
543 frames: Vec<Frame>,
544 tags: Vec<Tag>,
545 slices: Vec<Slice>,
546 sprite_user_data: Option<UserData>,
547 cels: BTreeMap<(usize, usize), Cel>,
548 tilesets: Vec<Tileset>,
549 external_files: Vec<ExternalFile>,
550 legacy_masks: Vec<LegacyMask>,
551 pub(crate) unknown_chunks: Vec<UnknownChunk>,
552 pub(crate) chunk_order: Vec<ChunkOrderEntry>,
553}
554
555impl std::fmt::Debug for AsepriteFile {
556 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
557 f.debug_struct("AsepriteFile")
558 .field("width", &self.width)
559 .field("height", &self.height)
560 .field("color_mode", &self.color_mode)
561 .field("layers", &self.layers.len())
562 .field("frames", &self.frames.len())
563 .field("tags", &self.tags.len())
564 .field("cels", &self.cels.len())
565 .finish()
566 }
567}
568
569impl AsepriteFile {
570 pub fn new(width: u16, height: u16, color_mode: ColorMode) -> Self {
572 Self {
573 width, height, color_mode,
574 flags: 1, deprecated_speed: 0, num_colors: 0,
575 transparent_index: 0,
576 pixel_ratio: (1, 1), grid: GridInfo::default(),
577 color_profile: None, palette: Vec::new(),
578 layers: Vec::new(), frames: Vec::new(),
579 tags: Vec::new(), slices: Vec::new(),
580 sprite_user_data: None,
581 cels: BTreeMap::new(),
582 tilesets: Vec::new(),
583 external_files: Vec::new(),
584 legacy_masks: Vec::new(),
585 unknown_chunks: Vec::new(),
586 chunk_order: Vec::new(),
587 }
588 }
589
590 pub fn width(&self) -> u16 { self.width }
593 pub fn height(&self) -> u16 { self.height }
595 pub fn color_mode(&self) -> ColorMode { self.color_mode }
597 pub fn flags(&self) -> u32 { self.flags }
599 pub(crate) fn deprecated_speed(&self) -> u16 { self.deprecated_speed }
600 pub(crate) fn num_colors(&self) -> u16 { self.num_colors }
601 pub fn pixel_ratio(&self) -> (u8, u8) { self.pixel_ratio }
603 pub fn grid(&self) -> &GridInfo { &self.grid }
605 pub fn color_profile(&self) -> &Option<ColorProfile> { &self.color_profile }
607 pub fn palette(&self) -> &[Color] { &self.palette }
609 pub fn layers(&self) -> &[Layer] { &self.layers }
611 pub fn frames(&self) -> &[Frame] { &self.frames }
613 pub fn tags(&self) -> &[Tag] { &self.tags }
615 pub fn slices(&self) -> &[Slice] { &self.slices }
617 pub fn sprite_user_data(&self) -> &Option<UserData> { &self.sprite_user_data }
619 pub fn transparent_index(&self) -> u8 { self.transparent_index }
621 pub fn legacy_masks(&self) -> &[LegacyMask] { &self.legacy_masks }
623 pub fn tilesets(&self) -> &[Tileset] { &self.tilesets }
625 pub fn external_files(&self) -> &[ExternalFile] { &self.external_files }
627
628 pub fn cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
630 self.cels.get(&(layer.0, frame))
631 }
632
633 pub fn resolve_cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
638 let cel = self.cels.get(&(layer.0, frame))?;
639 match &cel.kind {
640 CelKind::Linked { source_frame, .. } => self.cels.get(&(layer.0, *source_frame)),
641 _ => Some(cel),
642 }
643 }
644
645 pub fn layer_ref(&self, index: usize) -> Option<LayerRef> {
648 self.layers.get(index).and_then(|l| {
649 match l.kind {
650 LayerKind::Normal | LayerKind::Tilemap { .. } => Some(LayerRef(index)),
651 LayerKind::Group => None,
652 }
653 })
654 }
655
656 pub fn group_ref(&self, index: usize) -> Option<GroupRef> {
658 self.layers.get(index).and_then(|l| {
659 if l.kind == LayerKind::Group { Some(GroupRef(index)) } else { None }
660 })
661 }
662
663 fn push_layer(&mut self, name: &str, kind: LayerKind, parent: Option<usize>, opts: &LayerOptions) -> usize {
665 let index = self.layers.len();
666 self.layers.push(Layer {
667 name: name.to_string(), kind, parent,
668 opacity: opts.opacity, blend_mode: opts.blend_mode,
669 visible: opts.visible, editable: opts.editable,
670 lock_movement: opts.lock_movement, background: opts.background,
671 prefer_linked_cels: opts.prefer_linked_cels,
672 collapsed: opts.collapsed, reference_layer: opts.reference_layer,
673 user_data: None,
674 });
675 index
676 }
677
678 pub fn add_layer(&mut self, name: &str) -> LayerRef {
680 LayerRef(self.push_layer(name, LayerKind::Normal, None, &LayerOptions::default()))
681 }
682 pub fn add_layer_with(&mut self, name: &str, opts: LayerOptions) -> LayerRef {
684 LayerRef(self.push_layer(name, LayerKind::Normal, None, &opts))
685 }
686 pub fn add_group(&mut self, name: &str) -> GroupRef {
688 GroupRef(self.push_layer(name, LayerKind::Group, None, &LayerOptions::default()))
689 }
690 pub fn add_group_with(&mut self, name: &str, opts: LayerOptions) -> GroupRef {
692 GroupRef(self.push_layer(name, LayerKind::Group, None, &opts))
693 }
694 pub fn add_layer_in(&mut self, name: &str, parent: GroupRef) -> LayerRef {
696 LayerRef(self.push_layer(name, LayerKind::Normal, Some(parent.0), &LayerOptions::default()))
697 }
698 pub fn add_layer_in_with(&mut self, name: &str, parent: GroupRef, opts: LayerOptions) -> LayerRef {
700 LayerRef(self.push_layer(name, LayerKind::Normal, Some(parent.0), &opts))
701 }
702 pub fn add_group_in(&mut self, name: &str, parent: GroupRef) -> GroupRef {
704 GroupRef(self.push_layer(name, LayerKind::Group, Some(parent.0), &LayerOptions::default()))
705 }
706 pub fn add_group_in_with(&mut self, name: &str, parent: GroupRef, opts: LayerOptions) -> GroupRef {
708 GroupRef(self.push_layer(name, LayerKind::Group, Some(parent.0), &opts))
709 }
710
711 pub fn add_tilemap_layer(&mut self, name: &str, tileset_index: u32) -> LayerRef {
713 let index = self.layers.len();
714 self.layers.push(Layer {
715 name: name.to_string(),
716 kind: LayerKind::Tilemap { tileset_index },
717 parent: None,
718 opacity: 255,
719 blend_mode: BlendMode::Normal,
720 visible: true,
721 editable: true,
722 lock_movement: false,
723 background: false,
724 prefer_linked_cels: false,
725 collapsed: false,
726 reference_layer: false,
727 user_data: None,
728 });
729 LayerRef(index)
730 }
731
732 #[allow(clippy::too_many_arguments)]
734 pub fn set_tilemap_cel(
735 &mut self, layer: LayerRef, frame: usize,
736 tiles: Vec<u32>, width: u16, height: u16, x: i16, y: i16,
737 ) -> Result<(), AsepriteError> {
738 if frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(frame)); }
739 self.cels.insert((layer.0, frame), Cel {
740 kind: CelKind::Tilemap {
741 width, height, bits_per_tile: 32,
742 tile_id_bitmask: 0x1fff_ffff, x_flip_bitmask: 0x2000_0000,
743 y_flip_bitmask: 0x4000_0000, d_flip_bitmask: 0x8000_0000,
744 tiles, x, y, original_compressed: None,
745 },
746 opacity: 255, z_index: 0, user_data: None, extra: None,
747 });
748 Ok(())
749 }
750
751 pub fn add_frame(&mut self, duration_ms: u16) -> usize {
754 let index = self.frames.len();
755 self.frames.push(Frame { duration_ms });
756 index
757 }
758
759 pub fn set_cel(&mut self, layer: LayerRef, frame: usize, pixels: Pixels, x: i16, y: i16) -> Result<(), AsepriteError> {
762 if frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(frame)); }
763 self.cels.insert((layer.0, frame), Cel {
764 kind: CelKind::Compressed { pixels, x, y, original_compressed: None },
765 opacity: 255, z_index: 0, user_data: None, extra: None,
766 });
767 Ok(())
768 }
769
770 pub fn set_cel_with(&mut self, layer: LayerRef, frame: usize, opts: CelOptions) -> Result<(), AsepriteError> {
772 if frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(frame)); }
773 self.cels.insert((layer.0, frame), Cel {
774 kind: CelKind::Compressed { pixels: opts.pixels, x: opts.x, y: opts.y, original_compressed: None },
775 opacity: opts.opacity, z_index: opts.z_index, user_data: None, extra: None,
776 });
777 Ok(())
778 }
779
780 pub fn set_raw_cel(&mut self, layer: LayerRef, frame: usize, pixels: Pixels, x: i16, y: i16) -> Result<(), AsepriteError> {
782 if frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(frame)); }
783 self.cels.insert((layer.0, frame), Cel {
784 kind: CelKind::Raw { pixels, x, y }, opacity: 255, z_index: 0,
785 user_data: None, extra: None,
786 });
787 Ok(())
788 }
789
790 pub fn set_linked_cel(&mut self, layer: LayerRef, frame: usize, source_frame: usize) -> Result<(), AsepriteError> {
792 if frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(frame)); }
793 if source_frame >= self.frames.len() { return Err(AsepriteError::FrameOutOfBounds(source_frame)); }
794 self.cels.insert((layer.0, frame), Cel {
795 kind: CelKind::Linked { source_frame, x: 0, y: 0 }, opacity: 255, z_index: 0,
796 user_data: None, extra: None,
797 });
798 Ok(())
799 }
800
801 pub fn add_tag(&mut self, name: &str, frames: RangeInclusive<usize>, direction: LoopDirection) -> Result<usize, AsepriteError> {
804 self.add_tag_with(name, frames, direction, 0)
805 }
806
807 pub fn add_tag_with(&mut self, name: &str, frames: RangeInclusive<usize>, direction: LoopDirection, repeat: u16) -> Result<usize, AsepriteError> {
809 let from = *frames.start();
810 let to = *frames.end();
811 if !self.frames.is_empty() && to >= self.frames.len() {
812 return Err(AsepriteError::InvalidFrameRange);
813 }
814 let index = self.tags.len();
815 self.tags.push(Tag { name: name.to_string(), from_frame: from, to_frame: to, direction, repeat, user_data: None });
816 Ok(index)
817 }
818
819 pub fn set_palette(&mut self, colors: &[Color]) -> Result<(), AsepriteError> {
822 if colors.len() > 256 {
823 return Err(AsepriteError::FormatLimitExceeded { field: "palette", value: colors.len(), max: 256 });
824 }
825 self.palette = colors.to_vec();
826 Ok(())
827 }
828
829 pub fn set_transparent_index(&mut self, index: u8) { self.transparent_index = index; }
831 pub fn set_color_profile(&mut self, profile: ColorProfile) { self.color_profile = Some(profile); }
833
834 pub fn add_slice(&mut self, slice: Slice) { self.slices.push(slice); }
836 pub fn set_sprite_user_data(&mut self, ud: UserData) { self.sprite_user_data = Some(ud); }
838 pub fn add_tileset(&mut self, tileset: Tileset) { self.tilesets.push(tileset); }
840 pub fn add_external_file(&mut self, ef: ExternalFile) { self.external_files.push(ef); }
842
843 pub fn set_layer_user_data(&mut self, layer: LayerRef, ud: UserData) {
845 if let Some(l) = self.layers.get_mut(layer.0) { l.user_data = Some(ud); }
846 }
847 pub fn set_group_user_data(&mut self, group: GroupRef, ud: UserData) {
849 if let Some(l) = self.layers.get_mut(group.0) { l.user_data = Some(ud); }
850 }
851 pub fn set_cel_user_data(&mut self, layer: LayerRef, frame: usize, ud: UserData) {
853 if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) { cel.user_data = Some(ud); }
854 }
855 pub fn set_cel_extra(&mut self, layer: LayerRef, frame: usize, extra: CelExtra) {
857 if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) { cel.extra = Some(extra); }
858 }
859 pub fn set_tag_user_data(&mut self, tag_index: usize, ud: UserData) {
861 if let Some(tag) = self.tags.get_mut(tag_index) { tag.user_data = Some(ud); }
862 }
863
864 pub(crate) fn set_flags(&mut self, flags: u32) { self.flags = flags; }
866 pub(crate) fn set_deprecated_speed(&mut self, speed: u16) { self.deprecated_speed = speed; }
867 pub(crate) fn set_num_colors(&mut self, n: u16) { self.num_colors = n; }
868 pub(crate) fn set_pixel_ratio(&mut self, ratio: (u8, u8)) { self.pixel_ratio = ratio; }
869 pub(crate) fn set_grid(&mut self, grid: GridInfo) { self.grid = grid; }
870 pub(crate) fn push_legacy_mask(&mut self, mask: LegacyMask) { self.legacy_masks.push(mask); }
871 pub(crate) fn push_unknown_chunk(&mut self, frame_index: usize, chunk_type: u16, data: Vec<u8>) {
872 self.unknown_chunks.push(UnknownChunk { frame_index, chunk_type, data });
873 }
874 pub(crate) fn push_tileset(&mut self, tileset: Tileset) { self.tilesets.push(tileset); }
875 pub(crate) fn push_external_file(&mut self, ef: ExternalFile) { self.external_files.push(ef); }
876 pub(crate) fn tilesets_mut(&mut self) -> &mut Vec<Tileset> { &mut self.tilesets }
877 pub(crate) fn push_layer_raw(&mut self, layer: Layer) { self.layers.push(layer); }
878 pub(crate) fn insert_cel(&mut self, layer_index: usize, frame_index: usize, cel: Cel) {
879 self.cels.insert((layer_index, frame_index), cel);
880 }
881 pub(crate) fn push_tag(&mut self, tag: Tag) { self.tags.push(tag); }
882 pub(crate) fn push_slice(&mut self, slice: Slice) { self.slices.push(slice); }
883 pub(crate) fn set_sprite_user_data_raw(&mut self, ud: UserData) { self.sprite_user_data = Some(ud); }
884 pub(crate) fn layers_mut(&mut self) -> &mut Vec<Layer> { &mut self.layers }
885 pub(crate) fn tags_mut(&mut self) -> &mut Vec<Tag> { &mut self.tags }
886 pub(crate) fn slices_mut(&mut self) -> &mut Vec<Slice> { &mut self.slices }
887 pub(crate) fn cel_mut(&mut self, layer_index: usize, frame_index: usize) -> Option<&mut Cel> {
888 self.cels.get_mut(&(layer_index, frame_index))
889 }
890 pub(crate) fn set_palette_entry(&mut self, index: usize, color: Color) {
891 if index >= self.palette.len() {
892 self.palette.resize(index + 1, Color { r: 0, g: 0, b: 0, a: 255, name: None });
893 }
894 self.palette[index] = color;
895 }
896 pub(crate) fn cels_iter(&self) -> impl Iterator<Item = (&(usize, usize), &Cel)> { self.cels.iter() }
897 pub(crate) fn unknown_chunks_for_frame(&self, frame_index: usize) -> impl Iterator<Item = &UnknownChunk> {
898 self.unknown_chunks.iter().filter(move |uc| uc.frame_index == frame_index)
899 }
900
901 pub(crate) fn push_chunk_order(&mut self, frame_index: usize, chunk_type: u16, layer_index: Option<usize>) {
902 self.chunk_order.push(ChunkOrderEntry { frame_index, chunk_type, layer_index });
903 }
904
905 pub(crate) fn chunk_order_for_frame(&self, frame_index: usize) -> impl Iterator<Item = &ChunkOrderEntry> {
906 self.chunk_order.iter().filter(move |e| e.frame_index == frame_index)
907 }
908}