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