1use std::{
2 collections::{BTreeMap, HashMap},
3 ops::{Index, Range},
4 path::Path,
5};
6
7use image::{Pixel, Rgba, RgbaImage};
8use tracing::{error, warn};
9
10use crate::raw::RawAsepriteCel::Raw;
11use crate::{
12 error::{AseResult, AsepriteError, AsepriteInvalidError},
13 raw::{
14 AsepriteAnimationDirection, AsepriteBlendMode, AsepriteColor, AsepriteColorDepth,
15 AsepriteLayerType, AsepriteNinePatchInfo, AsepritePixel, RawAseprite, RawAsepriteCel,
16 RawAsepriteChunk, RawAsepritePaletteEntry,
17 },
18};
19
20#[derive(Debug, Clone)]
21pub struct Aseprite {
23 dimensions: (u16, u16),
24 tags: HashMap<String, AsepriteTag>,
25 slices: HashMap<String, AsepriteSlice>,
26 layers: BTreeMap<usize, AsepriteLayer>,
27 frame_count: usize,
28 palette: Option<AsepritePalette>,
29 transparent_palette: Option<u8>,
30 frame_infos: Vec<AsepriteFrameInfo>,
31}
32
33impl Aseprite {
34 pub fn tags(&self) -> AsepriteTags {
36 AsepriteTags { tags: &self.tags }
37 }
38
39 pub fn layers(&self) -> AsepriteLayers {
41 AsepriteLayers {
42 layers: &self.layers,
43 }
44 }
45
46 pub fn frames(&self) -> AsepriteFrames {
48 AsepriteFrames { aseprite: self }
49 }
50
51 pub fn frame_infos(&self) -> &[AsepriteFrameInfo] {
53 &self.frame_infos
54 }
55
56 pub fn slices(&self) -> AsepriteSlices {
58 AsepriteSlices { aseprite: self }
59 }
60}
61
62impl Aseprite {
63 pub fn from_raw(raw: RawAseprite) -> AseResult<Self> {
65 let mut tags = HashMap::new();
66 let mut layers = BTreeMap::new();
67 let mut palette = None;
68 let mut frame_infos = vec![];
69 let mut slices = HashMap::new();
70
71 let frame_count = raw.frames.len();
72
73 for frame in raw.frames {
74 frame_infos.push(AsepriteFrameInfo {
75 delay_ms: frame.duration_ms as usize,
76 });
77
78 for chunk in frame.chunks {
79 match chunk {
80 RawAsepriteChunk::Layer {
81 flags,
82 layer_type,
83 layer_child,
84 width: _,
85 height: _,
86 blend_mode,
87 opacity,
88 name,
89 } => {
90 let id = layers.len();
91 let layer = AsepriteLayer::new(
92 id,
93 name,
94 layer_type,
95 flags & 0x1 != 0,
96 blend_mode,
97 if raw.header.flags & 0x1 != 0 {
98 Some(opacity)
99 } else {
100 None
101 },
102 layer_child,
103 );
104 layers.insert(id, layer);
105 }
106 crate::raw::RawAsepriteChunk::Cel {
107 layer_index,
108 x,
109 y,
110 opacity,
111 cel,
112 } => {
113 let layer = layers
114 .get_mut(&(layer_index as usize))
115 .ok_or(AsepriteInvalidError::InvalidLayer(layer_index as usize))?;
116
117 layer.add_cel(AsepriteCel::new(x as f64, y as f64, opacity, cel))?;
118 }
119 crate::raw::RawAsepriteChunk::CelExtra {
120 flags: _,
121 x: _,
122 y: _,
123 width: _,
124 height: _,
125 } => warn!("Not yet implemented cel extra"),
126 crate::raw::RawAsepriteChunk::Tags { tags: raw_tags } => {
127 tags.extend(raw_tags.into_iter().map(|raw_tag| {
128 (
129 raw_tag.name.clone(),
130 AsepriteTag {
131 frames: raw_tag.from..raw_tag.to + 1,
132 animation_direction: raw_tag.anim_direction,
133 name: raw_tag.name,
134 },
135 )
136 }))
137 }
138 crate::raw::RawAsepriteChunk::Palette {
139 palette_size,
140 from_color,
141 to_color: _,
142 entries,
143 } => {
144 palette =
145 Some(AsepritePalette::from_raw(palette_size, from_color, entries));
146 }
147 crate::raw::RawAsepriteChunk::UserData { data: _ } => {
148 warn!("Not yet implemented user data")
149 }
150 crate::raw::RawAsepriteChunk::Slice {
151 flags: _,
152 name,
153 slices: raw_slices,
154 } => slices.extend(raw_slices.into_iter().map(
155 |crate::raw::RawAsepriteSlice {
156 frame,
157 x_origin,
158 y_origin,
159 width,
160 height,
161 nine_patch_info,
162 pivot: _,
163 }| {
164 (
165 name.clone(),
166 AsepriteSlice {
167 name: name.clone(),
168 valid_frame: frame as u16,
169 position_x: x_origin,
170 position_y: y_origin,
171 width,
172 height,
173 nine_patch_info,
174 },
175 )
176 },
177 )),
178 crate::raw::RawAsepriteChunk::ColorProfile {
179 profile_type: _,
180 flags: _,
181 gamma: _,
182 icc_profile: _,
183 } => warn!("Not yet implemented color profile"),
184 }
185 }
186 }
187
188 Ok(Aseprite {
189 dimensions: (raw.header.width, raw.header.height),
190 transparent_palette: if raw.header.color_depth == AsepriteColorDepth::Indexed {
191 Some(raw.header.transparent_palette)
192 } else {
193 None
194 },
195 tags,
196 layers,
197 frame_count,
198 palette,
199 frame_infos,
200 slices,
201 })
202 }
203
204 pub fn from_path<S: AsRef<Path>>(path: S) -> AseResult<Self> {
206 let buffer = std::fs::read(path)?;
207
208 let raw_aseprite = crate::raw::read_aseprite(&buffer)?;
209
210 Self::from_raw(raw_aseprite)
211 }
212
213 pub fn from_bytes<S: AsRef<[u8]>>(buffer: S) -> AseResult<Self> {
215 let raw_aseprite = crate::raw::read_aseprite(buffer.as_ref())?;
216
217 Self::from_raw(raw_aseprite)
218 }
219}
220
221#[derive(Debug, Clone)]
223pub struct AsepriteInfo {
224 pub dimensions: (u16, u16),
225 pub tags: HashMap<String, AsepriteTag>,
226 pub slices: HashMap<String, AsepriteSlice>,
227 pub frame_count: usize,
228 pub palette: Option<AsepritePalette>,
229 pub transparent_palette: Option<u8>,
230 pub frame_infos: Vec<AsepriteFrameInfo>,
231}
232
233impl Into<AsepriteInfo> for Aseprite {
234 fn into(self) -> AsepriteInfo {
235 AsepriteInfo {
236 dimensions: self.dimensions,
237 tags: self.tags,
238 slices: self.slices,
239 frame_count: self.frame_count,
240 palette: self.palette,
241 transparent_palette: self.transparent_palette,
242 frame_infos: self.frame_infos,
243 }
244 }
245}
246
247#[allow(missing_docs)]
249#[derive(Debug, Clone)]
250pub struct AsepritePalette {
251 pub entries: Vec<AsepriteColor>,
252}
253
254impl AsepritePalette {
255 fn from_raw(
256 palette_size: u32,
257 from_color: u32,
258 raw_entries: Vec<RawAsepritePaletteEntry>,
259 ) -> Self {
260 let mut entries = vec![
261 AsepriteColor {
262 red: 0,
263 green: 0,
264 blue: 0,
265 alpha: 0
266 };
267 palette_size as usize
268 ];
269
270 for (raw_idx, idx) in ((from_color as usize)..entries.len()).enumerate() {
271 entries[idx] = raw_entries[raw_idx].color;
272 }
273
274 AsepritePalette { entries }
275 }
276}
277
278pub struct AsepriteTags<'a> {
280 tags: &'a HashMap<String, AsepriteTag>,
281}
282
283impl<'a> AsepriteTags<'a> {
284 pub fn get_by_name<N: AsRef<str>>(&self, name: N) -> Option<&AsepriteTag> {
286 self.tags.get(name.as_ref())
287 }
288
289 pub fn all(&self) -> impl Iterator<Item = &AsepriteTag> {
291 self.tags.values()
292 }
293}
294
295impl<'a, 'r> Index<&'r str> for AsepriteTags<'a> {
296 type Output = AsepriteTag;
297
298 fn index(&self, index: &'r str) -> &Self::Output {
299 self.get_by_name(index).unwrap()
300 }
301}
302
303#[derive(Debug, Clone)]
304pub struct AsepriteTag {
306 pub frames: Range<u16>,
308 pub animation_direction: AsepriteAnimationDirection,
310 pub name: String,
312}
313
314#[derive(Debug, Clone)]
315pub struct AsepriteSlice {
317 pub name: String,
319 pub valid_frame: u16,
321 pub position_x: i32,
323 pub position_y: i32,
325 pub width: u32,
327 pub height: u32,
329 pub nine_patch_info: Option<AsepriteNinePatchInfo>,
331}
332
333pub struct AsepriteLayers<'a> {
335 layers: &'a BTreeMap<usize, AsepriteLayer>,
336}
337
338impl<'a> AsepriteLayers<'a> {
339 pub fn get_by_name<N: AsRef<str>>(&self, name: N) -> Option<&AsepriteLayer> {
343 let name = name.as_ref();
344 self.layers
345 .iter()
346 .find(|(_, layer)| layer.name() == name)
347 .map(|(_, layer)| layer)
348 }
349
350 pub fn get_by_id(&self, id: usize) -> Option<&AsepriteLayer> {
352 self.layers.get(&id)
353 }
354}
355
356#[derive(Debug, Clone)]
357pub enum AsepriteLayer {
359 Group {
361 name: String,
363 id: usize,
365 visible: bool,
367 child_level: u16,
369 },
370 Normal {
372 name: String,
374 id: usize,
376 blend_mode: AsepriteBlendMode,
378 opacity: Option<u8>,
380 visible: bool,
382 child_level: u16,
384 cels: Vec<AsepriteCel>,
386 },
387}
388
389impl AsepriteLayer {
390 fn new(
391 id: usize,
392 name: String,
393 layer_type: AsepriteLayerType,
394 visible: bool,
395 blend_mode: AsepriteBlendMode,
396 opacity: Option<u8>,
397 child_level: u16,
398 ) -> Self {
399 match layer_type {
400 AsepriteLayerType::Normal => AsepriteLayer::Normal {
401 name,
402 id,
403 blend_mode,
404 opacity,
405 visible,
406 child_level,
407 cels: vec![],
408 },
409 AsepriteLayerType::Group => AsepriteLayer::Group {
410 name,
411 id,
412 visible,
413 child_level,
414 },
415 }
416 }
417
418 pub fn name(&self) -> &str {
420 match self {
421 AsepriteLayer::Group { name, .. } | AsepriteLayer::Normal { name, .. } => name,
422 }
423 }
424
425 pub fn id(&self) -> usize {
427 match self {
428 AsepriteLayer::Group { id, .. } | AsepriteLayer::Normal { id, .. } => *id,
429 }
430 }
431
432 pub fn is_visible(&self) -> bool {
434 match self {
435 AsepriteLayer::Group { visible, .. } | AsepriteLayer::Normal { visible, .. } => {
436 *visible
437 }
438 }
439 }
440
441 #[must_use]
445 pub fn is_group(&self) -> bool {
446 matches!(self, Self::Group { .. })
447 }
448
449 fn cel_count(&self) -> usize {
450 match self {
451 AsepriteLayer::Group { .. } => 0,
452 AsepriteLayer::Normal { cels, .. } => cels.len(),
453 }
454 }
455
456 fn add_cel(&mut self, cel: AsepriteCel) -> AseResult<()> {
457 match self {
458 AsepriteLayer::Group { id, .. } => {
459 return Err(AsepriteError::InvalidConfiguration(
460 AsepriteInvalidError::InvalidLayer(*id),
461 ));
462 }
463 AsepriteLayer::Normal { cels, .. } => cels.push(cel),
464 }
465
466 Ok(())
467 }
468
469 fn get_cel(&self, frame: usize) -> AseResult<&AsepriteCel> {
470 match self {
471 AsepriteLayer::Group { id, .. } => Err(AsepriteError::InvalidConfiguration(
472 AsepriteInvalidError::InvalidLayer(*id),
473 )),
474 AsepriteLayer::Normal { cels, .. } => cels.get(frame).ok_or(
475 AsepriteError::InvalidConfiguration(AsepriteInvalidError::InvalidFrame(frame)),
476 ),
477 }
478 }
479}
480
481#[derive(Debug, Clone)]
482pub struct AsepriteCel {
484 x: f64,
485 y: f64,
486 opacity: u8,
487 raw_cel: RawAsepriteCel,
488}
489
490impl AsepriteCel {
491 fn new(x: f64, y: f64, opacity: u8, raw_cel: RawAsepriteCel) -> Self {
492 AsepriteCel {
493 x,
494 y,
495 opacity,
496 raw_cel,
497 }
498 }
499}
500
501pub struct AsepriteFrames<'a> {
503 aseprite: &'a Aseprite,
504}
505
506impl<'a> AsepriteFrames<'a> {
507 pub fn get_for(&self, range: &Range<u16>) -> AsepriteFrameRange {
509 AsepriteFrameRange {
510 aseprite: self.aseprite,
511 range: range.clone(),
512 }
513 }
514
515 pub fn count(&self) -> usize {
517 self.aseprite.frame_count
518 }
519}
520
521#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
522#[allow(missing_docs)]
524pub enum NineSlice {
525 TopLeft,
526 TopCenter,
527 TopRight,
528 RightCenter,
529 BottomRight,
530 BottomCenter,
531 BottomLeft,
532 LeftCenter,
533 Center,
534}
535
536#[allow(missing_docs)]
540pub struct AsepriteSliceImage {
541 pub image: RgbaImage,
542 pub nine_slices: Option<HashMap<NineSlice, RgbaImage>>,
543}
544
545pub struct AsepriteSlices<'a> {
547 aseprite: &'a Aseprite,
548}
549
550impl<'a> AsepriteSlices<'a> {
551 pub fn get_by_name(&self, name: &str) -> Option<&AsepriteSlice> {
553 self.aseprite.slices.get(name)
554 }
555
556 pub fn get_all(&self) -> impl Iterator<Item = &AsepriteSlice> + '_ {
558 self.aseprite.slices.values()
559 }
560
561 pub fn get_images<I: Iterator<Item = &'a AsepriteSlice>>(
563 &self,
564 wanted_slices: I,
565 ) -> AseResult<Vec<AsepriteSliceImage>> {
566 let mut slices = vec![];
567
568 for slice in wanted_slices {
569 let frame = image_for_frame(self.aseprite, slice.valid_frame)?;
570
571 let image = image::imageops::crop_imm(
572 &frame,
573 slice.position_x.max(0) as u32,
574 slice.position_y.max(0) as u32,
575 slice.width,
576 slice.height,
577 )
578 .to_image();
579
580 let slice_image = AsepriteSliceImage {
581 nine_slices: slice.nine_patch_info.as_ref().map(|info| {
582 let mut map: HashMap<_, RgbaImage> = HashMap::new();
583
584 let patch_x = info.x_center as u32;
585 let patch_y = info.y_center as u32;
586
587 let x = 0;
588 let y = 0;
589 let width = patch_x;
590 let height = patch_y;
591 map.insert(
592 NineSlice::TopLeft,
593 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
594 );
595
596 let x = patch_x;
597 let y = 0;
598 let width = info.width;
599 let height = patch_y;
600 map.insert(
601 NineSlice::TopCenter,
602 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
603 );
604
605 let x = patch_x + info.width;
606 let y = 0;
607 let width = slice.width - info.width - patch_x;
608 let height = patch_y;
609 map.insert(
610 NineSlice::TopRight,
611 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
612 );
613
614 let x = patch_x + info.width;
615 let y = patch_y;
616 let width = slice.width - info.width - patch_x;
617 let height = info.height;
618 map.insert(
619 NineSlice::RightCenter,
620 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
621 );
622
623 let x = patch_x + info.width;
624 let y = info.height + patch_y;
625 let width = slice.width - info.width - patch_x;
626 let height = slice.height - info.height - patch_y;
627 map.insert(
628 NineSlice::BottomRight,
629 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
630 );
631
632 let x = patch_x;
633 let y = patch_y + info.height;
634 let width = info.width;
635 let height = slice.height - info.height - patch_y;
636 map.insert(
637 NineSlice::BottomCenter,
638 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
639 );
640
641 let x = 0;
642 let y = patch_y + info.height;
643 let width = patch_x;
644 let height = slice.height - info.height - patch_y;
645 map.insert(
646 NineSlice::BottomLeft,
647 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
648 );
649
650 let x = 0;
651 let y = patch_y;
652 let width = patch_x;
653 let height = info.height;
654 map.insert(
655 NineSlice::LeftCenter,
656 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
657 );
658
659 let x = patch_x;
660 let y = patch_y;
661 let width = info.width;
662 let height = info.height;
663 map.insert(
664 NineSlice::Center,
665 image::imageops::crop_imm(&image, x, y, width, height).to_image(),
666 );
667
668 map
669 }),
670 image,
671 };
672
673 slices.push(slice_image);
674 }
675
676 Ok(slices)
677 }
678}
679
680#[derive(Debug, Clone)]
682pub struct AsepriteFrameInfo {
683 pub delay_ms: usize,
685}
686
687pub struct AsepriteFrameRange<'a> {
689 aseprite: &'a Aseprite,
690 range: Range<u16>,
691}
692
693impl<'a> AsepriteFrameRange<'a> {
694 pub fn get_infos(&self) -> AseResult<&[AsepriteFrameInfo]> {
696 Ok(&self.aseprite.frame_infos[self.range.start as usize..self.range.end as usize])
697 }
698
699 pub fn get_images(&self) -> AseResult<Vec<RgbaImage>> {
701 let mut frames = vec![];
702 for frame in self.range.clone() {
703 let image = image_for_frame(self.aseprite, frame)?;
704 frames.push(image);
705 }
706 Ok(frames)
707 }
708}
709
710fn image_for_frame(aseprite: &Aseprite, frame: u16) -> AseResult<RgbaImage> {
711 let dim = aseprite.dimensions;
712 let mut image = RgbaImage::new(dim.0 as u32, dim.1 as u32);
713 for (_layer_id, layer) in &aseprite.layers {
714 if !layer.is_visible() || layer.is_group() {
715 continue;
716 }
717
718 let mut blank_cel: AsepriteCel;
719
720 let cel = match layer.get_cel(frame as usize) {
721 Ok(aseprite_cel) => aseprite_cel,
722 Err(_) => {
723 blank_cel = AsepriteCel {
724 x: 0.0,
725 y: 0.0,
726 opacity: 0,
727 raw_cel: RawAsepriteCel::Raw {
728 width: dim.0,
729 height: dim.1,
730 pixels: vec![
731 AsepritePixel::RGBA(AsepriteColor {
732 red: 0,
733 green: 0,
734 blue: 0,
735 alpha: 0,
736 });
737 (dim.0 * dim.1) as usize
738 ],
739 },
740 };
741 &blank_cel
742 }
743 };
744
745 let mut write_to_image = |cel: &AsepriteCel,
746 width: u16,
747 height: u16,
748 pixels: &[AsepritePixel]|
749 -> AseResult<()> {
750 for x in 0..width {
751 for y in 0..height {
752 let pix_x = cel.x as i16 + x as i16;
753 let pix_y = cel.y as i16 + y as i16;
754
755 if pix_x < 0 || pix_y < 0 {
756 continue;
757 }
758 let raw_pixel = &pixels[(x + y * width) as usize];
759 let pixel = Rgba(
760 raw_pixel
761 .get_rgba(aseprite.palette.as_ref(), aseprite.transparent_palette)?,
762 );
763
764 image
765 .get_pixel_mut(pix_x as u32, pix_y as u32)
766 .blend(&pixel);
767 }
768 }
769 Ok(())
770 };
771
772 match &cel.raw_cel {
773 RawAsepriteCel::Raw {
774 width,
775 height,
776 pixels,
777 }
778 | RawAsepriteCel::Compressed {
779 width,
780 height,
781 pixels,
782 } => {
783 write_to_image(cel, *width, *height, pixels)?;
784 }
785 RawAsepriteCel::Linked { frame_position } => {
786 match &layer.get_cel(*frame_position as usize)?.raw_cel {
787 RawAsepriteCel::Raw {
788 width,
789 height,
790 pixels,
791 }
792 | RawAsepriteCel::Compressed {
793 width,
794 height,
795 pixels,
796 } => {
797 write_to_image(cel, *width, *height, pixels)?;
798 }
799 RawAsepriteCel::Linked { frame_position } => {
800 error!("Tried to draw a linked cel twice!");
801 return Err(AsepriteError::InvalidConfiguration(
802 AsepriteInvalidError::InvalidFrame(*frame_position as usize),
803 ));
804 }
805 }
806 }
807 }
808 }
809
810 Ok(image)
811}