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 {
53 tileset_index: u32,
54 },
55}
56
57#[non_exhaustive]
59#[derive(Copy, Clone, Debug, PartialEq, Eq)]
60pub enum BlendMode {
61 Normal,
62 Multiply,
63 Screen,
64 Overlay,
65 Darken,
66 Lighten,
67 ColorDodge,
68 ColorBurn,
69 HardLight,
70 SoftLight,
71 Difference,
72 Exclusion,
73 Hue,
74 Saturation,
75 Color,
76 Luminosity,
77 Addition,
78 Subtract,
79 Divide,
80}
81
82impl BlendMode {
83 pub(crate) fn from_u16(v: u16) -> Self {
84 match v {
85 0 => Self::Normal,
86 1 => Self::Multiply,
87 2 => Self::Screen,
88 3 => Self::Overlay,
89 4 => Self::Darken,
90 5 => Self::Lighten,
91 6 => Self::ColorDodge,
92 7 => Self::ColorBurn,
93 8 => Self::HardLight,
94 9 => Self::SoftLight,
95 10 => Self::Difference,
96 11 => Self::Exclusion,
97 12 => Self::Hue,
98 13 => Self::Saturation,
99 14 => Self::Color,
100 15 => Self::Luminosity,
101 16 => Self::Addition,
102 17 => Self::Subtract,
103 18 => Self::Divide,
104 _ => Self::Normal,
105 }
106 }
107
108 pub(crate) fn to_u16(self) -> u16 {
109 match self {
110 Self::Normal => 0,
111 Self::Multiply => 1,
112 Self::Screen => 2,
113 Self::Overlay => 3,
114 Self::Darken => 4,
115 Self::Lighten => 5,
116 Self::ColorDodge => 6,
117 Self::ColorBurn => 7,
118 Self::HardLight => 8,
119 Self::SoftLight => 9,
120 Self::Difference => 10,
121 Self::Exclusion => 11,
122 Self::Hue => 12,
123 Self::Saturation => 13,
124 Self::Color => 14,
125 Self::Luminosity => 15,
126 Self::Addition => 16,
127 Self::Subtract => 17,
128 Self::Divide => 18,
129 }
130 }
131}
132
133#[non_exhaustive]
135#[derive(Copy, Clone, Debug, PartialEq, Eq)]
136pub enum LoopDirection {
137 Forward,
138 Reverse,
139 PingPong,
140 PingPongReverse,
141}
142
143impl LoopDirection {
144 pub(crate) fn from_u8(v: u8) -> Self {
145 match v {
146 0 => Self::Forward,
147 1 => Self::Reverse,
148 2 => Self::PingPong,
149 3 => Self::PingPongReverse,
150 _ => Self::Forward,
151 }
152 }
153
154 pub(crate) fn to_u8(self) -> u8 {
155 match self {
156 Self::Forward => 0,
157 Self::Reverse => 1,
158 Self::PingPong => 2,
159 Self::PingPongReverse => 3,
160 }
161 }
162}
163
164#[non_exhaustive]
166#[derive(Clone, Debug, PartialEq)]
167pub enum ColorProfile {
168 None,
169 SRgb {
170 flags: u16,
171 gamma: u32,
172 },
173 Icc {
174 flags: u16,
175 gamma: u32,
176 data: Vec<u8>,
177 },
178}
179
180#[derive(Copy, Clone, Debug, PartialEq, Eq)]
184pub struct LayerRef(pub(crate) usize);
185
186#[derive(Copy, Clone, Debug, PartialEq, Eq)]
188pub struct GroupRef(pub(crate) usize);
189
190impl LayerRef {
191 pub fn index(&self) -> usize {
193 self.0
194 }
195}
196
197impl GroupRef {
198 pub fn index(&self) -> usize {
200 self.0
201 }
202}
203
204#[derive(Clone, Debug, PartialEq)]
208pub struct Pixels {
209 pub data: Vec<u8>,
210 pub width: u16,
211 pub height: u16,
212}
213
214impl Pixels {
215 pub fn new(
217 data: Vec<u8>,
218 width: u16,
219 height: u16,
220 color_mode: ColorMode,
221 ) -> Result<Self, AsepriteError> {
222 let expected = width as usize * height as usize * color_mode.bytes_per_pixel();
223 if data.len() != expected {
224 return Err(AsepriteError::PixelSizeMismatch {
225 expected,
226 actual: data.len(),
227 });
228 }
229 Ok(Self {
230 data,
231 width,
232 height,
233 })
234 }
235}
236
237#[derive(Clone, Debug, PartialEq, Eq)]
239pub struct Color {
240 pub r: u8,
241 pub g: u8,
242 pub b: u8,
243 pub a: u8,
244 pub name: Option<String>,
245}
246
247#[derive(Clone, Debug, PartialEq)]
249pub struct GridInfo {
250 pub x: i16,
251 pub y: i16,
252 pub width: u16,
253 pub height: u16,
254}
255
256impl Default for GridInfo {
257 fn default() -> Self {
258 Self {
259 x: 0,
260 y: 0,
261 width: 16,
262 height: 16,
263 }
264 }
265}
266
267#[derive(Clone)]
268pub(crate) struct UnknownChunk {
269 pub frame_index: usize,
270 pub chunk_type: u16,
271 pub data: Vec<u8>,
272}
273
274#[derive(Clone, Debug)]
275pub(crate) struct ChunkOrderEntry {
276 pub frame_index: usize,
277 pub chunk_type: u16,
278 pub layer_index: Option<usize>,
280}
281
282#[derive(Clone, Debug, PartialEq)]
286pub struct Layer {
287 pub name: String,
288 pub kind: LayerKind,
289 pub parent: Option<usize>,
290 pub opacity: u8,
291 pub blend_mode: BlendMode,
292 pub visible: bool,
293 pub editable: bool,
294 pub lock_movement: bool,
295 pub background: bool,
296 pub prefer_linked_cels: bool,
297 pub collapsed: bool,
298 pub reference_layer: bool,
299 pub user_data: Option<UserData>,
300}
301
302pub struct LayerOptions {
304 pub opacity: u8,
305 pub blend_mode: BlendMode,
306 pub visible: bool,
307 pub editable: bool,
308 pub lock_movement: bool,
309 pub background: bool,
310 pub collapsed: bool,
311 pub prefer_linked_cels: bool,
312 pub reference_layer: bool,
313}
314
315impl Default for LayerOptions {
316 fn default() -> Self {
317 Self {
318 opacity: 255,
319 blend_mode: BlendMode::Normal,
320 visible: true,
321 editable: true,
322 lock_movement: false,
323 background: false,
324 collapsed: false,
325 prefer_linked_cels: false,
326 reference_layer: false,
327 }
328 }
329}
330
331#[derive(Clone, Debug, PartialEq, Eq)]
335pub struct Frame {
336 pub duration_ms: u16,
337}
338
339#[derive(Clone, Debug, PartialEq)]
343pub struct Tag {
344 pub name: String,
345 pub from_frame: usize,
346 pub to_frame: usize,
347 pub direction: LoopDirection,
348 pub repeat: u16,
349 pub user_data: Option<UserData>,
350}
351
352#[derive(Clone, Debug, PartialEq)]
356pub struct Cel {
357 pub kind: CelKind,
358 pub opacity: u8,
359 pub z_index: i16,
360 pub user_data: Option<UserData>,
361 pub extra: Option<CelExtra>,
362}
363
364#[non_exhaustive]
366#[derive(Clone, Debug, PartialEq)]
367pub enum CelKind {
368 Raw { pixels: Pixels, x: i16, y: i16 },
370 Compressed {
372 pixels: Pixels,
373 x: i16,
374 y: i16,
375 original_compressed: Option<Vec<u8>>,
376 },
377 Linked { source_frame: usize, x: i16, y: i16 },
379 Tilemap {
381 width: u16,
382 height: u16,
383 bits_per_tile: u16,
384 tile_id_bitmask: u32,
385 x_flip_bitmask: u32,
386 y_flip_bitmask: u32,
387 d_flip_bitmask: u32,
388 tiles: Vec<u32>,
389 x: i16,
390 y: i16,
391 original_compressed: Option<Vec<u8>>,
392 },
393}
394
395pub struct CelOptions {
397 pub pixels: Pixels,
398 pub x: i16,
399 pub y: i16,
400 pub opacity: u8,
401 pub z_index: i16,
402}
403
404impl Default for CelOptions {
405 fn default() -> Self {
406 Self {
407 pixels: Pixels {
408 data: vec![],
409 width: 0,
410 height: 0,
411 },
412 x: 0,
413 y: 0,
414 opacity: 255,
415 z_index: 0,
416 }
417 }
418}
419
420pub struct LinkedCelOptions {
422 pub x: i16,
423 pub y: i16,
424 pub opacity: u8,
425 pub z_index: i16,
426 pub user_data: Option<UserData>,
427 pub extra: Option<CelExtra>,
428}
429
430impl Default for LinkedCelOptions {
431 fn default() -> Self {
432 Self {
433 x: 0,
434 y: 0,
435 opacity: 255,
436 z_index: 0,
437 user_data: None,
438 extra: None,
439 }
440 }
441}
442
443#[derive(Clone, Debug, Default, PartialEq)]
447pub struct UserData {
448 pub text: Option<String>,
449 pub color: Option<Color>,
450 pub properties: Vec<PropertiesMap>,
451}
452
453#[derive(Clone, Debug, PartialEq)]
455pub struct PropertiesMap {
456 pub key: u32,
457 pub entries: Vec<(String, PropertyValue)>,
458}
459
460#[non_exhaustive]
462#[derive(Clone, Debug, PartialEq)]
463pub enum PropertyValue {
464 Bool(bool),
465 Int8(i8),
466 UInt8(u8),
467 Int16(i16),
468 UInt16(u16),
469 Int32(i32),
470 UInt32(u32),
471 Int64(i64),
472 UInt64(u64),
473 Fixed(u32),
474 Float(f32),
475 Double(f64),
476 String(String),
477 Point(i32, i32),
478 Size(i32, i32),
479 Rect(i32, i32, i32, i32),
480 Vector(Vec<PropertyValue>),
481 Properties(Vec<(String, PropertyValue)>),
482 Uuid([u8; 16]),
483}
484
485#[derive(Clone, Debug, PartialEq)]
489pub struct Slice {
490 pub name: String,
491 pub keys: Vec<SliceKey>,
492 pub has_nine_patch: bool,
493 pub has_pivot: bool,
494 pub user_data: Option<UserData>,
495}
496
497#[derive(Clone, Debug, PartialEq)]
499pub struct SliceKey {
500 pub frame: u32,
501 pub x: i32,
502 pub y: i32,
503 pub width: u32,
504 pub height: u32,
505 pub nine_patch: Option<NinePatch>,
506 pub pivot: Option<(i32, i32)>,
507}
508
509#[derive(Clone, Debug, PartialEq)]
511pub struct NinePatch {
512 pub center_x: i32,
513 pub center_y: i32,
514 pub center_width: u32,
515 pub center_height: u32,
516}
517
518#[derive(Clone, Debug, PartialEq)]
522pub struct CelExtra {
523 pub precise_x: u32,
524 pub precise_y: u32,
525 pub width: u32,
526 pub height: u32,
527}
528
529#[derive(Copy, Clone, Debug, PartialEq, Eq)]
533pub struct TilesetFlags(pub u32);
534
535impl TilesetFlags {
536 pub fn has_external_link(self) -> bool {
538 self.0 & 1 != 0
539 }
540 pub fn has_embedded_tiles(self) -> bool {
542 self.0 & 2 != 0
543 }
544 pub fn empty_tile_is_zero(self) -> bool {
546 self.0 & 4 != 0
547 }
548}
549
550#[derive(Clone, Debug, PartialEq)]
552pub struct Tileset {
553 pub id: u32,
554 pub flags: TilesetFlags,
555 pub name: String,
556 pub tile_count: u32,
557 pub tile_width: u16,
558 pub tile_height: u16,
559 pub base_index: i16,
560 pub data: TilesetData,
561 pub user_data: Option<UserData>,
562 pub tile_user_data: Vec<Option<UserData>>,
563}
564
565#[non_exhaustive]
567#[derive(Clone, Debug, PartialEq)]
568pub enum TilesetData {
569 Embedded {
570 pixels: Vec<u8>,
571 original_compressed: Option<Vec<u8>>,
572 },
573 External {
574 external_file_id: u32,
575 tileset_id_in_external: u32,
576 },
577 Empty,
578}
579
580#[derive(Clone, Debug, PartialEq)]
584pub struct ExternalFile {
585 pub id: u32,
586 pub file_type: ExternalFileType,
587 pub name: String,
588}
589
590#[non_exhaustive]
592#[derive(Copy, Clone, Debug, PartialEq, Eq)]
593pub enum ExternalFileType {
594 Palette,
595 Tileset,
596 ExtensionProps,
597 ExtensionTileMgmt,
598}
599
600impl ExternalFileType {
601 pub(crate) fn from_u8(v: u8) -> Self {
602 match v {
603 0 => Self::Palette,
604 1 => Self::Tileset,
605 2 => Self::ExtensionProps,
606 3 => Self::ExtensionTileMgmt,
607 _ => Self::Palette,
608 }
609 }
610
611 pub(crate) fn to_u8(self) -> u8 {
612 match self {
613 Self::Palette => 0,
614 Self::Tileset => 1,
615 Self::ExtensionProps => 2,
616 Self::ExtensionTileMgmt => 3,
617 }
618 }
619}
620
621#[derive(Clone, Debug, PartialEq)]
625pub struct LegacyMask {
626 pub x: i16,
627 pub y: i16,
628 pub width: u16,
629 pub height: u16,
630 pub name: String,
631 pub bitmap: Vec<u8>,
632}
633
634#[derive(Clone)]
661pub struct AsepriteFile {
662 width: u16,
663 height: u16,
664 color_mode: ColorMode,
665 flags: u32,
666 deprecated_speed: u16,
667 num_colors: u16,
668 transparent_index: u8,
669 pixel_ratio: (u8, u8),
670 grid: GridInfo,
671 color_profile: Option<ColorProfile>,
672 palette: Vec<Color>,
673 layers: Vec<Layer>,
674 frames: Vec<Frame>,
675 tags: Vec<Tag>,
676 slices: Vec<Slice>,
677 sprite_user_data: Option<UserData>,
678 cels: BTreeMap<(usize, usize), Cel>,
679 tilesets: Vec<Tileset>,
680 external_files: Vec<ExternalFile>,
681 legacy_masks: Vec<LegacyMask>,
682 pub(crate) unknown_chunks: Vec<UnknownChunk>,
683 pub(crate) chunk_order: Vec<ChunkOrderEntry>,
684}
685
686impl std::fmt::Debug for AsepriteFile {
687 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
688 f.debug_struct("AsepriteFile")
689 .field("width", &self.width)
690 .field("height", &self.height)
691 .field("color_mode", &self.color_mode)
692 .field("layers", &self.layers.len())
693 .field("frames", &self.frames.len())
694 .field("tags", &self.tags.len())
695 .field("cels", &self.cels.len())
696 .finish()
697 }
698}
699
700impl AsepriteFile {
701 pub fn new(width: u16, height: u16, color_mode: ColorMode) -> Self {
703 Self {
704 width,
705 height,
706 color_mode,
707 flags: 1,
708 deprecated_speed: 0,
709 num_colors: 0,
710 transparent_index: 0,
711 pixel_ratio: (1, 1),
712 grid: GridInfo::default(),
713 color_profile: None,
714 palette: Vec::new(),
715 layers: Vec::new(),
716 frames: Vec::new(),
717 tags: Vec::new(),
718 slices: Vec::new(),
719 sprite_user_data: None,
720 cels: BTreeMap::new(),
721 tilesets: Vec::new(),
722 external_files: Vec::new(),
723 legacy_masks: Vec::new(),
724 unknown_chunks: Vec::new(),
725 chunk_order: Vec::new(),
726 }
727 }
728
729 pub fn width(&self) -> u16 {
732 self.width
733 }
734 pub fn height(&self) -> u16 {
736 self.height
737 }
738 pub fn color_mode(&self) -> ColorMode {
740 self.color_mode
741 }
742 pub fn flags(&self) -> u32 {
744 self.flags
745 }
746 pub(crate) fn deprecated_speed(&self) -> u16 {
747 self.deprecated_speed
748 }
749 pub(crate) fn num_colors(&self) -> u16 {
750 self.num_colors
751 }
752 pub fn pixel_ratio(&self) -> (u8, u8) {
754 self.pixel_ratio
755 }
756 pub fn grid(&self) -> &GridInfo {
758 &self.grid
759 }
760 pub fn color_profile(&self) -> &Option<ColorProfile> {
762 &self.color_profile
763 }
764 pub fn palette(&self) -> &[Color] {
766 &self.palette
767 }
768 pub fn layers(&self) -> &[Layer] {
770 &self.layers
771 }
772 pub fn frames(&self) -> &[Frame] {
774 &self.frames
775 }
776 pub fn tags(&self) -> &[Tag] {
778 &self.tags
779 }
780 pub fn slices(&self) -> &[Slice] {
782 &self.slices
783 }
784 pub fn sprite_user_data(&self) -> &Option<UserData> {
786 &self.sprite_user_data
787 }
788 pub fn transparent_index(&self) -> u8 {
790 self.transparent_index
791 }
792 pub fn legacy_masks(&self) -> &[LegacyMask] {
794 &self.legacy_masks
795 }
796 pub fn tilesets(&self) -> &[Tileset] {
798 &self.tilesets
799 }
800 pub fn external_files(&self) -> &[ExternalFile] {
802 &self.external_files
803 }
804
805 pub fn cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
807 self.cels.get(&(layer.0, frame))
808 }
809
810 pub fn resolve_cel(&self, layer: LayerRef, frame: usize) -> Option<&Cel> {
815 let cel = self.cels.get(&(layer.0, frame))?;
816 match &cel.kind {
817 CelKind::Linked { source_frame, .. } => self.cels.get(&(layer.0, *source_frame)),
818 _ => Some(cel),
819 }
820 }
821
822 pub fn layer_ref(&self, index: usize) -> Option<LayerRef> {
825 self.layers.get(index).and_then(|l| match l.kind {
826 LayerKind::Normal | LayerKind::Tilemap { .. } => Some(LayerRef(index)),
827 LayerKind::Group => None,
828 })
829 }
830
831 pub fn group_ref(&self, index: usize) -> Option<GroupRef> {
833 self.layers.get(index).and_then(|l| {
834 if l.kind == LayerKind::Group {
835 Some(GroupRef(index))
836 } else {
837 None
838 }
839 })
840 }
841
842 fn push_layer(
844 &mut self,
845 name: &str,
846 kind: LayerKind,
847 parent: Option<usize>,
848 opts: &LayerOptions,
849 ) -> usize {
850 let index = self.layers.len();
851 self.layers.push(Layer {
852 name: name.to_string(),
853 kind,
854 parent,
855 opacity: opts.opacity,
856 blend_mode: opts.blend_mode,
857 visible: opts.visible,
858 editable: opts.editable,
859 lock_movement: opts.lock_movement,
860 background: opts.background,
861 prefer_linked_cels: opts.prefer_linked_cels,
862 collapsed: opts.collapsed,
863 reference_layer: opts.reference_layer,
864 user_data: None,
865 });
866 index
867 }
868
869 pub fn add_layer(&mut self, name: &str) -> LayerRef {
871 LayerRef(self.push_layer(name, LayerKind::Normal, None, &LayerOptions::default()))
872 }
873 pub fn add_layer_with(&mut self, name: &str, opts: LayerOptions) -> LayerRef {
875 LayerRef(self.push_layer(name, LayerKind::Normal, None, &opts))
876 }
877 pub fn add_group(&mut self, name: &str) -> GroupRef {
879 GroupRef(self.push_layer(name, LayerKind::Group, None, &LayerOptions::default()))
880 }
881 pub fn add_group_with(&mut self, name: &str, opts: LayerOptions) -> GroupRef {
883 GroupRef(self.push_layer(name, LayerKind::Group, None, &opts))
884 }
885 pub fn add_layer_in(&mut self, name: &str, parent: GroupRef) -> LayerRef {
887 LayerRef(self.push_layer(
888 name,
889 LayerKind::Normal,
890 Some(parent.0),
891 &LayerOptions::default(),
892 ))
893 }
894 pub fn add_layer_in_with(
896 &mut self,
897 name: &str,
898 parent: GroupRef,
899 opts: LayerOptions,
900 ) -> LayerRef {
901 LayerRef(self.push_layer(name, LayerKind::Normal, Some(parent.0), &opts))
902 }
903 pub fn add_group_in(&mut self, name: &str, parent: GroupRef) -> GroupRef {
905 GroupRef(self.push_layer(
906 name,
907 LayerKind::Group,
908 Some(parent.0),
909 &LayerOptions::default(),
910 ))
911 }
912 pub fn add_group_in_with(
914 &mut self,
915 name: &str,
916 parent: GroupRef,
917 opts: LayerOptions,
918 ) -> GroupRef {
919 GroupRef(self.push_layer(name, LayerKind::Group, Some(parent.0), &opts))
920 }
921
922 pub fn add_tilemap_layer(&mut self, name: &str, tileset_index: u32) -> LayerRef {
924 let index = self.layers.len();
925 self.layers.push(Layer {
926 name: name.to_string(),
927 kind: LayerKind::Tilemap { tileset_index },
928 parent: None,
929 opacity: 255,
930 blend_mode: BlendMode::Normal,
931 visible: true,
932 editable: true,
933 lock_movement: false,
934 background: false,
935 prefer_linked_cels: false,
936 collapsed: false,
937 reference_layer: false,
938 user_data: None,
939 });
940 LayerRef(index)
941 }
942
943 #[allow(clippy::too_many_arguments)]
945 pub fn set_tilemap_cel(
946 &mut self,
947 layer: LayerRef,
948 frame: usize,
949 tiles: Vec<u32>,
950 width: u16,
951 height: u16,
952 x: i16,
953 y: i16,
954 ) -> Result<(), AsepriteError> {
955 if frame >= self.frames.len() {
956 return Err(AsepriteError::FrameOutOfBounds(frame));
957 }
958 self.cels.insert(
959 (layer.0, frame),
960 Cel {
961 kind: CelKind::Tilemap {
962 width,
963 height,
964 bits_per_tile: 32,
965 tile_id_bitmask: 0x1fff_ffff,
966 x_flip_bitmask: 0x2000_0000,
967 y_flip_bitmask: 0x4000_0000,
968 d_flip_bitmask: 0x8000_0000,
969 tiles,
970 x,
971 y,
972 original_compressed: None,
973 },
974 opacity: 255,
975 z_index: 0,
976 user_data: None,
977 extra: None,
978 },
979 );
980 Ok(())
981 }
982
983 pub fn add_frame(&mut self, duration_ms: u16) -> usize {
986 let index = self.frames.len();
987 self.frames.push(Frame { duration_ms });
988 index
989 }
990
991 pub fn set_cel(
994 &mut self,
995 layer: LayerRef,
996 frame: usize,
997 pixels: Pixels,
998 x: i16,
999 y: i16,
1000 ) -> Result<(), AsepriteError> {
1001 if frame >= self.frames.len() {
1002 return Err(AsepriteError::FrameOutOfBounds(frame));
1003 }
1004 self.cels.insert(
1005 (layer.0, frame),
1006 Cel {
1007 kind: CelKind::Compressed {
1008 pixels,
1009 x,
1010 y,
1011 original_compressed: None,
1012 },
1013 opacity: 255,
1014 z_index: 0,
1015 user_data: None,
1016 extra: None,
1017 },
1018 );
1019 Ok(())
1020 }
1021
1022 pub fn set_cel_with(
1024 &mut self,
1025 layer: LayerRef,
1026 frame: usize,
1027 opts: CelOptions,
1028 ) -> Result<(), AsepriteError> {
1029 if frame >= self.frames.len() {
1030 return Err(AsepriteError::FrameOutOfBounds(frame));
1031 }
1032 self.cels.insert(
1033 (layer.0, frame),
1034 Cel {
1035 kind: CelKind::Compressed {
1036 pixels: opts.pixels,
1037 x: opts.x,
1038 y: opts.y,
1039 original_compressed: None,
1040 },
1041 opacity: opts.opacity,
1042 z_index: opts.z_index,
1043 user_data: None,
1044 extra: None,
1045 },
1046 );
1047 Ok(())
1048 }
1049
1050 pub fn set_raw_cel(
1052 &mut self,
1053 layer: LayerRef,
1054 frame: usize,
1055 pixels: Pixels,
1056 x: i16,
1057 y: i16,
1058 ) -> Result<(), AsepriteError> {
1059 if frame >= self.frames.len() {
1060 return Err(AsepriteError::FrameOutOfBounds(frame));
1061 }
1062 self.cels.insert(
1063 (layer.0, frame),
1064 Cel {
1065 kind: CelKind::Raw { pixels, x, y },
1066 opacity: 255,
1067 z_index: 0,
1068 user_data: None,
1069 extra: None,
1070 },
1071 );
1072 Ok(())
1073 }
1074
1075 pub fn set_linked_cel(
1077 &mut self,
1078 layer: LayerRef,
1079 frame: usize,
1080 source_frame: usize,
1081 ) -> Result<(), AsepriteError> {
1082 if frame >= self.frames.len() {
1083 return Err(AsepriteError::FrameOutOfBounds(frame));
1084 }
1085 if source_frame >= self.frames.len() {
1086 return Err(AsepriteError::FrameOutOfBounds(source_frame));
1087 }
1088 self.cels.insert(
1089 (layer.0, frame),
1090 Cel {
1091 kind: CelKind::Linked {
1092 source_frame,
1093 x: 0,
1094 y: 0,
1095 },
1096 opacity: 255,
1097 z_index: 0,
1098 user_data: None,
1099 extra: None,
1100 },
1101 );
1102 Ok(())
1103 }
1104
1105 pub fn set_linked_cel_with(
1107 &mut self,
1108 layer: LayerRef,
1109 frame: usize,
1110 source_frame: usize,
1111 opts: LinkedCelOptions,
1112 ) -> Result<(), AsepriteError> {
1113 if frame >= self.frames.len() {
1114 return Err(AsepriteError::FrameOutOfBounds(frame));
1115 }
1116 if source_frame >= self.frames.len() {
1117 return Err(AsepriteError::FrameOutOfBounds(source_frame));
1118 }
1119
1120 self.cels.insert(
1121 (layer.0, frame),
1122 Cel {
1123 kind: CelKind::Linked {
1124 source_frame,
1125 x: opts.x,
1126 y: opts.y,
1127 },
1128 opacity: opts.opacity,
1129 z_index: opts.z_index,
1130 user_data: opts.user_data,
1131 extra: opts.extra,
1132 },
1133 );
1134
1135 Ok(())
1136 }
1137
1138 pub fn add_tag(
1141 &mut self,
1142 name: &str,
1143 frames: RangeInclusive<usize>,
1144 direction: LoopDirection,
1145 ) -> Result<usize, AsepriteError> {
1146 self.add_tag_with(name, frames, direction, 0)
1147 }
1148
1149 pub fn add_tag_with(
1151 &mut self,
1152 name: &str,
1153 frames: RangeInclusive<usize>,
1154 direction: LoopDirection,
1155 repeat: u16,
1156 ) -> Result<usize, AsepriteError> {
1157 let from = *frames.start();
1158 let to = *frames.end();
1159 if !self.frames.is_empty() && to >= self.frames.len() {
1160 return Err(AsepriteError::InvalidFrameRange);
1161 }
1162 let index = self.tags.len();
1163 self.tags.push(Tag {
1164 name: name.to_string(),
1165 from_frame: from,
1166 to_frame: to,
1167 direction,
1168 repeat,
1169 user_data: None,
1170 });
1171 Ok(index)
1172 }
1173
1174 pub fn set_palette(&mut self, colors: &[Color]) -> Result<(), AsepriteError> {
1177 if colors.len() > 256 {
1178 return Err(AsepriteError::FormatLimitExceeded {
1179 field: "palette",
1180 value: colors.len(),
1181 max: 256,
1182 });
1183 }
1184 self.palette = colors.to_vec();
1185 Ok(())
1186 }
1187
1188 pub fn set_transparent_index(&mut self, index: u8) {
1190 self.transparent_index = index;
1191 }
1192 pub fn set_color_profile(&mut self, profile: ColorProfile) {
1194 self.color_profile = Some(profile);
1195 }
1196
1197 pub fn add_slice(&mut self, slice: Slice) {
1199 self.slices.push(slice);
1200 }
1201 pub fn set_sprite_user_data(&mut self, ud: UserData) {
1203 self.sprite_user_data = Some(ud);
1204 }
1205 pub fn add_tileset(&mut self, tileset: Tileset) {
1207 self.tilesets.push(tileset);
1208 }
1209 pub fn add_external_file(&mut self, ef: ExternalFile) {
1211 self.external_files.push(ef);
1212 }
1213
1214 pub fn set_layer_user_data(&mut self, layer: LayerRef, ud: UserData) {
1216 if let Some(l) = self.layers.get_mut(layer.0) {
1217 l.user_data = Some(ud);
1218 }
1219 }
1220 pub fn set_group_user_data(&mut self, group: GroupRef, ud: UserData) {
1222 if let Some(l) = self.layers.get_mut(group.0) {
1223 l.user_data = Some(ud);
1224 }
1225 }
1226 pub fn set_cel_user_data(&mut self, layer: LayerRef, frame: usize, ud: UserData) {
1228 if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) {
1229 cel.user_data = Some(ud);
1230 }
1231 }
1232 pub fn set_cel_extra(&mut self, layer: LayerRef, frame: usize, extra: CelExtra) {
1234 if let Some(cel) = self.cels.get_mut(&(layer.0, frame)) {
1235 cel.extra = Some(extra);
1236 }
1237 }
1238 pub fn set_tag_user_data(&mut self, tag_index: usize, ud: UserData) {
1240 if let Some(tag) = self.tags.get_mut(tag_index) {
1241 tag.user_data = Some(ud);
1242 }
1243 }
1244
1245 pub(crate) fn set_flags(&mut self, flags: u32) {
1247 self.flags = flags;
1248 }
1249 pub(crate) fn set_deprecated_speed(&mut self, speed: u16) {
1250 self.deprecated_speed = speed;
1251 }
1252 pub(crate) fn set_num_colors(&mut self, n: u16) {
1253 self.num_colors = n;
1254 }
1255 pub(crate) fn set_pixel_ratio(&mut self, ratio: (u8, u8)) {
1256 self.pixel_ratio = ratio;
1257 }
1258 pub(crate) fn set_grid(&mut self, grid: GridInfo) {
1259 self.grid = grid;
1260 }
1261 pub(crate) fn push_legacy_mask(&mut self, mask: LegacyMask) {
1262 self.legacy_masks.push(mask);
1263 }
1264 pub(crate) fn push_unknown_chunk(
1265 &mut self,
1266 frame_index: usize,
1267 chunk_type: u16,
1268 data: Vec<u8>,
1269 ) {
1270 self.unknown_chunks.push(UnknownChunk {
1271 frame_index,
1272 chunk_type,
1273 data,
1274 });
1275 }
1276 pub(crate) fn push_tileset(&mut self, tileset: Tileset) {
1277 self.tilesets.push(tileset);
1278 }
1279 pub(crate) fn push_external_file(&mut self, ef: ExternalFile) {
1280 self.external_files.push(ef);
1281 }
1282 pub(crate) fn tilesets_mut(&mut self) -> &mut Vec<Tileset> {
1283 &mut self.tilesets
1284 }
1285 pub(crate) fn push_layer_raw(&mut self, layer: Layer) {
1286 self.layers.push(layer);
1287 }
1288 pub(crate) fn insert_cel(&mut self, layer_index: usize, frame_index: usize, cel: Cel) {
1289 self.cels.insert((layer_index, frame_index), cel);
1290 }
1291 pub(crate) fn push_tag(&mut self, tag: Tag) {
1292 self.tags.push(tag);
1293 }
1294 pub(crate) fn push_slice(&mut self, slice: Slice) {
1295 self.slices.push(slice);
1296 }
1297 pub(crate) fn set_sprite_user_data_raw(&mut self, ud: UserData) {
1298 self.sprite_user_data = Some(ud);
1299 }
1300 pub(crate) fn layers_mut(&mut self) -> &mut Vec<Layer> {
1301 &mut self.layers
1302 }
1303 pub(crate) fn tags_mut(&mut self) -> &mut Vec<Tag> {
1304 &mut self.tags
1305 }
1306 pub(crate) fn slices_mut(&mut self) -> &mut Vec<Slice> {
1307 &mut self.slices
1308 }
1309 pub(crate) fn cel_mut(&mut self, layer_index: usize, frame_index: usize) -> Option<&mut Cel> {
1310 self.cels.get_mut(&(layer_index, frame_index))
1311 }
1312 pub(crate) fn set_palette_entry(&mut self, index: usize, color: Color) {
1313 if index >= self.palette.len() {
1314 self.palette.resize(
1315 index + 1,
1316 Color {
1317 r: 0,
1318 g: 0,
1319 b: 0,
1320 a: 255,
1321 name: None,
1322 },
1323 );
1324 }
1325 self.palette[index] = color;
1326 }
1327 pub(crate) fn cels_iter(&self) -> impl Iterator<Item = (&(usize, usize), &Cel)> {
1328 self.cels.iter()
1329 }
1330 pub(crate) fn unknown_chunks_for_frame(
1331 &self,
1332 frame_index: usize,
1333 ) -> impl Iterator<Item = &UnknownChunk> {
1334 self.unknown_chunks
1335 .iter()
1336 .filter(move |uc| uc.frame_index == frame_index)
1337 }
1338
1339 pub(crate) fn push_chunk_order(
1340 &mut self,
1341 frame_index: usize,
1342 chunk_type: u16,
1343 layer_index: Option<usize>,
1344 ) {
1345 self.chunk_order.push(ChunkOrderEntry {
1346 frame_index,
1347 chunk_type,
1348 layer_index,
1349 });
1350 }
1351
1352 pub(crate) fn chunk_order_for_frame(
1353 &self,
1354 frame_index: usize,
1355 ) -> impl Iterator<Item = &ChunkOrderEntry> {
1356 self.chunk_order
1357 .iter()
1358 .filter(move |e| e.frame_index == frame_index)
1359 }
1360}