mabel_aseprite/lib.rs
1#![warn(clippy::all)]
2#![allow(missing_docs)]
3/*!
4
5Utilities for loading [Aseprite](https://www.aseprite.org/) files. This
6library directly reads the binary Aseprite files ([file format
7specification][spec]) and does not require you to export files to JSON. This
8should make it fast enough to load your assets when the game boots up. You can
9also use it to build your own asset pipelines.
10
11Note that this library can be rather slow when compiled without optimizations.
12We recommend that you override the optimization settings for this dependency
13in dev mode by adding the following to your `Cargo.toml`:
14
15```text
16[profile.dev.package.asefile]
17opt-level = 2 # or 3
18```
19
20This is not necessary if you already have a wildcard override. See
21[Cargo profile overrides][overrides] for more info.
22
23[spec]: https://github.com/aseprite/aseprite/blob/master/docs/ase-file-specs.md
24[overrides]: https://doc.rust-lang.org/cargo/reference/profiles.html#overrides
25
26# Basic Usage
27
28## Load file
29
30The easiest way is to use [AsepriteFile::read_file] to load a file.
31
32```
33use asefile::AsepriteFile;
34# use std::path::Path;
35# let path = Path::new("./tests/data/basic-16x16.aseprite");
36let ase = AsepriteFile::read_file(&path).unwrap();
37
38println!("Size: {}x{}", ase.width(), ase.height());
39println!("Frames: {}", ase.num_frames());
40println!("Layers: {}", ase.num_layers());
41```
42
43## Save frame as image
44
45Aseprite files consist of multiple layers. Usually you just want the final
46image. You can do this by using [Frame::image]. This will return an
47`image::RgbaImage` from the [image](https://docs.rs/image) library.
48
49```
50# use asefile::AsepriteFile;
51# use std::path::Path;
52# let asefile_path = Path::new("./tests/data/basic-16x16.aseprite");
53# let output_dir = Path::new("./tests/data");
54# let ase = AsepriteFile::read_file(&asefile_path).unwrap();
55let image = ase.frame(0).image();
56let output_path = output_dir.join("example.png");
57image.save(&output_path).unwrap();
58```
59
60This blends together all visible layers the same way Aseprite would.
61
62## Layers
63
64You can access a [Layer] by name or by ID.
65
66```
67# use asefile::AsepriteFile;
68# use std::path::Path;
69# let path = Path::new("./tests/data/basic-16x16.aseprite");
70# let ase = AsepriteFile::read_file(&path).unwrap();
71let layer = ase.layer(0);
72println!("Name of layer 0: {}", layer.name());
73let layer = ase.layer_by_name("Layer 1").unwrap();
74println!("Layer 1 is visible? {}", layer.is_visible());
75```
76
77## Cels
78
79A cel is the intersection of a frame and a layer. Because of this there are
80multiple ways to access a cel:
81
82```
83# use asefile::AsepriteFile;
84# use std::path::Path;
85# let path = Path::new("./tests/data/layers_and_tags.aseprite");
86# let ase = AsepriteFile::read_file(&path).unwrap();
87
88let layer0 = ase.layer(0);
89// These are all the same cel
90let cel1 = layer0.frame(1);
91let cel2 = ase.frame(1).layer(0);
92let cel3 = ase.cel(1, 0); // or directly, which can avoid some borrowing issues
93
94let image = cel1.image();
95```
96
97## Tilesets
98
99Since Aseprite 1.3 you can also create tilesets and tilemaps layers.
100
101You access each tile separately, or export them all as one image which is one
102tile wide.
103
104```
105# use asefile::AsepriteFile;
106# use std::path::Path;
107# use image::RgbaImage;
108# let path = Path::new("./tests/data/tileset.aseprite");
109# let ase = AsepriteFile::read_file(&path).unwrap();
110
111let num_tilesets = ase.tilesets().len();
112let tileset = ase.tilesets().get(0).unwrap();
113
114let all_tiles: RgbaImage = tileset.image();
115let single_tile: RgbaImage = tileset.tile_image(1);
116// Note: tile 0 is the empty tile
117assert_eq!(
118 all_tiles.dimensions().0,
119 tileset.tile_size().width() as u32
120);
121assert_eq!(
122 all_tiles.dimensions().1,
123 tileset.tile_size().height() as u32 * tileset.tile_count()
124)
125```
126
127## Tilemaps
128
129Aseprite also supports tilemaps which are layers that are composed entirely out
130of tiles from a tileset.
131
132You can export those layers as a single large image or you can do some custom
133processing by looking at the tile indexes in the layer.
134
135```
136# use asefile::AsepriteFile;
137# use std::path::Path;
138# use image::RgbaImage;
139# let path = Path::new("./tests/data/tilemap_multi.aseprite");
140# let ase = AsepriteFile::read_file(&path).unwrap();
141
142let layer = ase.layer_by_name("Tilemap 1").unwrap().id();
143let tilemap = ase.tilemap(layer, 0).unwrap();
144
145// This is the same as getting the image for the cel.
146let tilemap_image = tilemap.image();
147
148let num_tiles_x = tilemap.width();
149let num_tiles_y = tilemap.height();
150let (tile_width, tile_height) = tilemap.tile_size();
151// Get a specific tile. Always succeeds. If the tile is out of bounds returns
152// the empty tile (id 0).
153let tile = tilemap.tile(0, 1);
154println!("tile at (0, 1) references tile from tileset: {}", tile.id());
155// You can access the tileset right through the tilemap.
156let image = tilemap.tileset().tile_image(tile.id());
157```
158
159## User data
160
161Aseprite gives you the option to annotate certain entities with custom data.
162Usually, that's a color and a text field. Each of those entities has a
163`user_data()` method.
164
165*/
166
167pub(crate) mod blend;
168pub mod cel;
169pub(crate) mod color_profile;
170pub(crate) mod error;
171pub(crate) mod external_file;
172pub(crate) mod file;
173pub(crate) mod layer;
174pub(crate) mod palette;
175pub(crate) mod parse;
176mod pixel;
177mod reader;
178pub(crate) mod slice;
179pub(crate) mod tags;
180#[cfg(test)]
181mod tests;
182mod tile;
183mod tilemap;
184mod tileset;
185pub(crate) mod user_data;
186#[cfg(feature = "utils")]
187pub mod util;
188
189/// A specialized `Result` type for Aseprite parsing functions.
190pub type Result<T> = std::result::Result<T, AsepriteParseError>;
191
192pub use cel::Cel;
193// pub use color_profile::ColorProfile;
194pub use error::AsepriteParseError;
195pub use external_file::{ExternalFile, ExternalFileId, ExternalFilesById};
196pub use file::{AsepriteFile, Frame, LayersIter, PixelFormat};
197pub use layer::{BlendMode, Layer, LayerFlags, LayerType};
198pub use palette::{ColorPalette, ColorPaletteEntry};
199pub use slice::{Slice, Slice9, SliceKey};
200pub use tags::{AnimationDirection, Tag};
201pub use tile::Tile;
202pub use tilemap::Tilemap;
203pub use tileset::{ExternalTilesetReference, TileSize, Tileset, TilesetImageError, TilesetsById};
204pub use user_data::UserData;