aseprite_reader2/
computed.rs

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)]
20/// Data structure representing an Aseprite file
21pub 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    /// Get the [`AsepriteTag`]s defined in this Aseprite
34    pub fn tags(&self) -> AsepriteTags {
35        AsepriteTags { tags: &self.tags }
36    }
37
38    /// Get the associated [`AsepriteLayer`]s defined in this Aseprite
39    pub fn layers(&self) -> AsepriteLayers {
40        AsepriteLayers {
41            layers: &self.layers,
42        }
43    }
44
45    /// Get the frames inside this aseprite
46    pub fn frames(&self) -> AsepriteFrames {
47        AsepriteFrames { aseprite: self }
48    }
49
50    /// Get infos about the contained frames
51    pub fn frame_infos(&self) -> &[AsepriteFrameInfo] {
52        &self.frame_infos
53    }
54
55    /// Get the slices inside this aseprite
56    pub fn slices(&self) -> AsepriteSlices {
57        AsepriteSlices { aseprite: self }
58    }
59}
60
61impl Aseprite {
62    /// Construct a [`Aseprite`] from a [`RawAseprite`]
63    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    /// Construct a [`Aseprite`] from a [`Path`]
204    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    /// Construct a [`Aseprite`] from a `&[u8]`
213    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/// The palette entries in the aseprite file
221#[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
251/// All the tags defined in the corresponding aseprite
252pub struct AsepriteTags<'a> {
253    tags: &'a HashMap<String, AsepriteTag>,
254}
255
256impl<'a> AsepriteTags<'a> {
257    /// Get a tag defined by its name
258    pub fn get_by_name<N: AsRef<str>>(&self, name: N) -> Option<&AsepriteTag> {
259        self.tags.get(name.as_ref())
260    }
261
262    /// Get all available tags
263    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)]
277/// A single Aseprite tag
278pub struct AsepriteTag {
279    /// The frames which this tag represents
280    pub frames: Range<u16>,
281    /// The direction of its animation
282    pub animation_direction: AsepriteAnimationDirection,
283    /// The tag name
284    pub name: String,
285}
286
287#[derive(Debug, Clone)]
288/// A single Aseprite slice
289pub struct AsepriteSlice {
290    /// The slice name
291    pub name: String,
292    /// The frame from which it is valid
293    pub valid_frame: u16,
294    /// The slice's x position
295    pub position_x: i32,
296    /// The slice's y position
297    pub position_y: i32,
298    /// The slice's width
299    pub width: u32,
300    /// The slice's height
301    pub height: u32,
302    /// Nine-Patch Info if it exists
303    pub nine_patch_info: Option<AsepriteNinePatchInfo>,
304}
305
306/// The layers inside an aseprite file
307pub struct AsepriteLayers<'a> {
308    layers: &'a BTreeMap<usize, AsepriteLayer>,
309}
310
311impl<'a> AsepriteLayers<'a> {
312    /// Get a layer by its name
313    ///
314    /// If you have its id, prefer fetching it using [`get_by_id`]
315    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    /// Get a layer by its id
324    pub fn get_by_id(&self, id: usize) -> Option<&AsepriteLayer> {
325        self.layers.get(&id)
326    }
327}
328
329#[derive(Debug, Clone)]
330/// An aseprite layer
331pub enum AsepriteLayer {
332    /// A layer group
333    Group {
334        /// Name of the layer
335        name: String,
336        /// Id of the layer
337        id: usize,
338        /// Visibility of the layer
339        visible: bool,
340        /// How deep it is nested in the layer hierarchy
341        child_level: u16,
342    },
343    /// A normal layer
344    Normal {
345        /// Name of the layer
346        name: String,
347        /// Id of the layer
348        id: usize,
349        /// Blend mode of this layer
350        blend_mode: AsepriteBlendMode,
351        /// Opacity of this layer (if enabled)
352        opacity: Option<u8>,
353        /// Visibility of this layer
354        visible: bool,
355        /// How deep it is nested in the layer hierarchy
356        child_level: u16,
357        /// Cels
358        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    /// Get the name of the layer
392    pub fn name(&self) -> &str {
393        match self {
394            AsepriteLayer::Group { name, .. } | AsepriteLayer::Normal { name, .. } => &name,
395        }
396    }
397
398    /// Get the id of the layer
399    pub fn id(&self) -> usize {
400        match self {
401            AsepriteLayer::Group { id, .. } | AsepriteLayer::Normal { id, .. } => *id,
402        }
403    }
404
405    /// Get the visibility of the layer
406    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)]
449/// A single cel in a frame in a layer
450pub 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
468/// The frames contained in an aseprite
469pub struct AsepriteFrames<'a> {
470    aseprite: &'a Aseprite,
471}
472
473impl<'a> AsepriteFrames<'a> {
474    /// Get a range of frames
475    pub fn get_for(&self, range: &Range<u16>) -> AsepriteFrameRange {
476        AsepriteFrameRange {
477            aseprite: self.aseprite,
478            range: range.clone(),
479        }
480    }
481
482    /// Get the amount of frames in this aseprite
483    pub fn count(&self) -> usize {
484        self.aseprite.frame_count
485    }
486}
487
488#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
489/// The nine slices in a nine-patch image
490#[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/// A single slice image
504///
505/// Only contains nine-patch info if the aseprite also contained one
506#[allow(missing_docs)]
507pub struct AsepriteSliceImage {
508    pub image: RgbaImage,
509    pub nine_slices: Option<HashMap<NineSlice, RgbaImage>>,
510}
511
512/// The slices contained in an aseprite
513pub struct AsepriteSlices<'a> {
514    aseprite: &'a Aseprite,
515}
516
517impl<'a> AsepriteSlices<'a> {
518    /// Get a slice by name
519    pub fn get_by_name(&self, name: &str) -> Option<&AsepriteSlice> {
520        self.aseprite.slices.get(name)
521    }
522
523    /// Get all slices in this aseprite
524    pub fn get_all(&self) -> impl Iterator<Item = &AsepriteSlice> + '_ {
525        self.aseprite.slices.values()
526    }
527
528    /// Get the images represented by the slices
529    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/// Information about a single animation frame
648#[derive(Debug, Clone)]
649pub struct AsepriteFrameInfo {
650    /// The delay of this frame in milliseconds
651    pub delay_ms: usize,
652}
653
654/// A range of frames in an aseprite
655pub struct AsepriteFrameRange<'a> {
656    aseprite: &'a Aseprite,
657    range: Range<u16>,
658}
659
660impl<'a> AsepriteFrameRange<'a> {
661    /// Get the timings attached to each frame
662    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    /// Get the images represented by this range
667    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}