bevy_aseprite_reader/
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, 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)]
21/// Data structure representing an Aseprite file
22pub 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    /// Get the [`AsepriteTag`]s defined in this Aseprite
35    pub fn tags(&self) -> AsepriteTags {
36        AsepriteTags { tags: &self.tags }
37    }
38
39    /// Get the associated [`AsepriteLayer`]s defined in this Aseprite
40    pub fn layers(&self) -> AsepriteLayers {
41        AsepriteLayers {
42            layers: &self.layers,
43        }
44    }
45
46    /// Get the frames inside this aseprite
47    pub fn frames(&self) -> AsepriteFrames {
48        AsepriteFrames { aseprite: self }
49    }
50
51    /// Get infos about the contained frames
52    pub fn frame_infos(&self) -> &[AsepriteFrameInfo] {
53        &self.frame_infos
54    }
55
56    /// Get the slices inside this aseprite
57    pub fn slices(&self) -> AsepriteSlices {
58        AsepriteSlices { aseprite: self }
59    }
60}
61
62impl Aseprite {
63    /// Construct a [`Aseprite`] from a [`RawAseprite`]
64    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    /// Construct a [`Aseprite`] from a [`Path`]
205    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    /// Construct a [`Aseprite`] from a `&[u8]`
214    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/// The loaded aseprite file without image data
222#[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/// The palette entries in the aseprite file
248#[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
278/// All the tags defined in the corresponding aseprite
279pub struct AsepriteTags<'a> {
280    tags: &'a HashMap<String, AsepriteTag>,
281}
282
283impl<'a> AsepriteTags<'a> {
284    /// Get a tag defined by its name
285    pub fn get_by_name<N: AsRef<str>>(&self, name: N) -> Option<&AsepriteTag> {
286        self.tags.get(name.as_ref())
287    }
288
289    /// Get all available tags
290    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)]
304/// A single Aseprite tag
305pub struct AsepriteTag {
306    /// The frames which this tag represents
307    pub frames: Range<u16>,
308    /// The direction of its animation
309    pub animation_direction: AsepriteAnimationDirection,
310    /// The tag name
311    pub name: String,
312}
313
314#[derive(Debug, Clone)]
315/// A single Aseprite slice
316pub struct AsepriteSlice {
317    /// The slice name
318    pub name: String,
319    /// The frame from which it is valid
320    pub valid_frame: u16,
321    /// The slice's x position
322    pub position_x: i32,
323    /// The slice's y position
324    pub position_y: i32,
325    /// The slice's width
326    pub width: u32,
327    /// The slice's height
328    pub height: u32,
329    /// Nine-Patch Info if it exists
330    pub nine_patch_info: Option<AsepriteNinePatchInfo>,
331}
332
333/// The layers inside an aseprite file
334pub struct AsepriteLayers<'a> {
335    layers: &'a BTreeMap<usize, AsepriteLayer>,
336}
337
338impl<'a> AsepriteLayers<'a> {
339    /// Get a layer by its name
340    ///
341    /// If you have its id, prefer fetching it using [`get_by_id`]
342    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    /// Get a layer by its id
351    pub fn get_by_id(&self, id: usize) -> Option<&AsepriteLayer> {
352        self.layers.get(&id)
353    }
354}
355
356#[derive(Debug, Clone)]
357/// An aseprite layer
358pub enum AsepriteLayer {
359    /// A layer group
360    Group {
361        /// Name of the layer
362        name: String,
363        /// Id of the layer
364        id: usize,
365        /// Visibility of the layer
366        visible: bool,
367        /// How deep it is nested in the layer hierarchy
368        child_level: u16,
369    },
370    /// A normal layer
371    Normal {
372        /// Name of the layer
373        name: String,
374        /// Id of the layer
375        id: usize,
376        /// Blend mode of this layer
377        blend_mode: AsepriteBlendMode,
378        /// Opacity of this layer (if enabled)
379        opacity: Option<u8>,
380        /// Visibility of this layer
381        visible: bool,
382        /// How deep it is nested in the layer hierarchy
383        child_level: u16,
384        /// Cels
385        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    /// Get the name of the layer
419    pub fn name(&self) -> &str {
420        match self {
421            AsepriteLayer::Group { name, .. } | AsepriteLayer::Normal { name, .. } => name,
422        }
423    }
424
425    /// Get the id of the layer
426    pub fn id(&self) -> usize {
427        match self {
428            AsepriteLayer::Group { id, .. } | AsepriteLayer::Normal { id, .. } => *id,
429        }
430    }
431
432    /// Get the visibility of the layer
433    pub fn is_visible(&self) -> bool {
434        match self {
435            AsepriteLayer::Group { visible, .. } | AsepriteLayer::Normal { visible, .. } => {
436                *visible
437            }
438        }
439    }
440
441    /// Returns `true` if the aseprite layer is [`Group`].
442    ///
443    /// [`Group`]: AsepriteLayer::Group
444    #[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)]
482/// A single cel in a frame in a layer
483pub 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
501/// The frames contained in an aseprite
502pub struct AsepriteFrames<'a> {
503    aseprite: &'a Aseprite,
504}
505
506impl<'a> AsepriteFrames<'a> {
507    /// Get a range of frames
508    pub fn get_for(&self, range: &Range<u16>) -> AsepriteFrameRange {
509        AsepriteFrameRange {
510            aseprite: self.aseprite,
511            range: range.clone(),
512        }
513    }
514
515    /// Get the amount of frames in this aseprite
516    pub fn count(&self) -> usize {
517        self.aseprite.frame_count
518    }
519}
520
521#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
522/// The nine slices in a nine-patch image
523#[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/// A single slice image
537///
538/// Only contains nine-patch info if the aseprite also contained one
539#[allow(missing_docs)]
540pub struct AsepriteSliceImage {
541    pub image: RgbaImage,
542    pub nine_slices: Option<HashMap<NineSlice, RgbaImage>>,
543}
544
545/// The slices contained in an aseprite
546pub struct AsepriteSlices<'a> {
547    aseprite: &'a Aseprite,
548}
549
550impl<'a> AsepriteSlices<'a> {
551    /// Get a slice by name
552    pub fn get_by_name(&self, name: &str) -> Option<&AsepriteSlice> {
553        self.aseprite.slices.get(name)
554    }
555
556    /// Get all slices in this aseprite
557    pub fn get_all(&self) -> impl Iterator<Item = &AsepriteSlice> + '_ {
558        self.aseprite.slices.values()
559    }
560
561    /// Get the images represented by the slices
562    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/// Information about a single animation frame
681#[derive(Debug, Clone)]
682pub struct AsepriteFrameInfo {
683    /// The delay of this frame in milliseconds
684    pub delay_ms: usize,
685}
686
687/// A range of frames in an aseprite
688pub struct AsepriteFrameRange<'a> {
689    aseprite: &'a Aseprite,
690    range: Range<u16>,
691}
692
693impl<'a> AsepriteFrameRange<'a> {
694    /// Get the timings attached to each frame
695    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    /// Get the images represented by this range
700    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}