aseprite_packer/
lib.rs

1use asefile::{AsepriteFile, AsepriteParseError};
2use image::{DynamicImage, ImageBuffer, Rgba};
3use ron::ser::{to_string_pretty, PrettyConfig};
4use serde::{Deserialize, Serialize};
5use std::{collections::HashMap, fs::File, path::Path};
6use std::{fs, io::Write, path::PathBuf};
7use texture_packer::{exporter::ImageExporter, TexturePacker, TexturePackerConfig};
8
9#[derive(Serialize, Deserialize, Debug)]
10pub struct AseTextureData {
11    pub width: u32,
12    pub height: u32,
13    pub basename: String,
14    pub frame: u32,
15    pub x: u32,
16    pub y: u32,
17}
18
19#[derive(Debug)]
20pub struct AsepritePackerConfig<'a> {
21    pub aseprite_file_names: &'a [&'a str],
22    pub path: &'a Path,
23    pub output_image_location: Option<&'a Path>,
24    pub output_ron_location: Option<&'a Path>,
25    pub trim: bool,
26}
27
28impl<'a> Default for AsepritePackerConfig<'a> {
29    fn default() -> Self {
30        AsepritePackerConfig {
31            aseprite_file_names: &[],
32            path: Path::new("."),
33            output_image_location: None,
34            output_ron_location: None,
35            trim: false,
36        }
37    }
38}
39
40struct AseFile {
41    path: PathBuf,
42    name: String,
43}
44
45pub struct AsepritePacker {
46    pub image: DynamicImage,
47    pub packed_texture_data: HashMap<String, AseTextureData>,
48}
49
50impl AsepritePacker {
51    pub fn new(config: AsepritePackerConfig) -> Self {
52        let AsepritePackerConfig {
53            aseprite_file_names,
54            path,
55            output_image_location,
56            output_ron_location,
57            trim,
58        } = config;
59
60        let texture_packer_config = TexturePackerConfig {
61            max_width: std::u32::MAX,
62            max_height: std::u32::MAX,
63            allow_rotation: false,
64            texture_outlines: false,
65            border_padding: 0,
66            texture_padding: 0,
67            trim,
68            ..Default::default()
69        };
70
71        let mut packer: TexturePacker<ImageBuffer<Rgba<u8>, Vec<u8>>, String> =
72            TexturePacker::new_skyline(texture_packer_config);
73
74        let mut packed_texture_data: HashMap<String, AseTextureData> = HashMap::default();
75
76        let ase_files: Vec<AseFile> = if !aseprite_file_names.is_empty() {
77            aseprite_file_names
78                .iter()
79                .map(|name| {
80                    let resolved_name = format!("{}.aseprite", name);
81                    AseFile {
82                        path: path.clone().join(resolved_name.to_string()),
83                        name: name.to_string(),
84                    }
85                })
86                .collect()
87        } else {
88            println!("{}", path.display());
89            let paths = fs::read_dir(path).unwrap();
90            paths
91                .map(|p| {
92                    let path_buff = p.unwrap();
93                    let name = path_buff
94                        .path()
95                        .file_stem()
96                        .unwrap()
97                        .to_str()
98                        .unwrap()
99                        .to_string();
100                    AseFile {
101                        path: path_buff.path().as_path().to_owned(),
102                        name,
103                    }
104                })
105                .collect()
106        };
107
108        for file in ase_files {
109            // let path = env::current_dir().unwrap();
110            let ase_file = load_ase(file.path.as_path());
111            match ase_file {
112                Err(e) => panic!("{}", e),
113                Ok(ase) => {
114                    for frame_num in 0..ase.num_frames() {
115                        ase.width();
116                        let key: String = if ase.num_frames() > 1 {
117                            format!("{}_{}", file.name.to_string(), frame_num)
118                        } else {
119                            file.name.to_string()
120                        };
121                        let _result = packer.pack_own(key.clone(), ase.frame(frame_num).image());
122                        match _result {
123                            Ok(_) => {}
124                            Err(e) => panic!("Error packing file: {:?}", e),
125                        }
126                        let frame_data = packer.get_frame(&key).expect("Frame not found");
127                        let source = frame_data.frame;
128                        packed_texture_data.insert(
129                            key.clone(),
130                            AseTextureData {
131                                width: source.w,
132                                height: source.h,
133                                x: source.x,
134                                y: source.y,
135                                basename: file.name.to_string(),
136                                frame: frame_num,
137                            },
138                        );
139                    }
140                }
141            }
142        }
143
144        let image = ImageExporter::export(&packer).unwrap();
145
146        if let Some(output) = output_image_location {
147            let mut file = std::fs::File::create(output).unwrap();
148            image.write_to(&mut file, image::ImageFormat::Png).unwrap();
149        }
150
151        if let Some(output) = output_ron_location {
152            let mut file = std::fs::File::create(output).unwrap();
153            let str = to_string_pretty(&packed_texture_data, PrettyConfig::default()).unwrap();
154            let _result = file.write_all(str.as_bytes());
155            match _result {
156                Ok(_) => {}
157                Err(e) => panic!("{}", e),
158            }
159        }
160
161        let ase_packer = AsepritePacker {
162            packed_texture_data,
163            image,
164        };
165
166        ase_packer
167    }
168}
169
170fn load_ase(file_path: &Path) -> Result<AsepriteFile, AsepriteParseError> {
171    let f = File::open(file_path).unwrap();
172    AsepriteFile::read(&f)
173}