1use std::{
2 fs::File,
3 io::{BufReader, Read},
4 path::Path,
5 sync::Arc,
6};
7
8use crate::{
9 blend::{self, mul_un8, Color8},
10 cel::{CelCommon, CelId, CelsData, ImageContent, ImageSize},
11 external_file::{ExternalFile, ExternalFileId, ExternalFilesById},
12 layer::{Layer, LayerType, LayersData},
13 pixel::Pixels,
14 slice::Slice,
15 tile::TileId,
16 tilemap::{Tilemap, TilemapData},
17 tileset::{TileSize, Tileset, TilesetsById},
18 user_data::UserData,
19};
20use crate::{cel::Cel, *};
21use cel::{CelContent, RawCel};
22use image::{Rgba, RgbaImage};
23
24#[derive(Debug)]
26pub struct AsepriteFile {
27 pub(crate) width: u16,
28 pub(crate) height: u16,
29 pub(crate) num_frames: u16,
30 pub(crate) pixel_format: PixelFormat,
31 pub(crate) palette: Option<Arc<ColorPalette>>,
33 pub(crate) layers: LayersData,
34 pub(crate) frame_times: Vec<u16>,
36 pub(crate) tags: Vec<Tag>,
37 pub(crate) framedata: CelsData<Pixels>, pub(crate) external_files: ExternalFilesById,
39 pub(crate) tilesets: TilesetsById,
40 pub(crate) sprite_user_data: Option<UserData>,
41 pub(crate) slices: Vec<Slice>,
42}
43
44#[derive(Debug)]
46pub struct Frame<'a> {
47 file: &'a AsepriteFile,
48 index: u32,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum PixelFormat {
54 Rgba,
56 Grayscale,
58 #[allow(missing_docs)]
62 Indexed { transparent_color_index: u8 },
63}
64
65impl PixelFormat {
66 pub fn bytes_per_pixel(&self) -> usize {
68 match self {
69 PixelFormat::Rgba => 4,
70 PixelFormat::Grayscale => 2,
71 PixelFormat::Indexed { .. } => 1,
72 }
73 }
74 pub fn transparent_color_index(&self) -> Option<u8> {
76 match self {
77 PixelFormat::Indexed {
78 transparent_color_index,
79 } => Some(*transparent_color_index),
80 _ => None,
81 }
82 }
83}
84
85impl AsepriteFile {
86 pub fn read_file(path: &Path) -> Result<Self> {
88 let file = File::open(path)?;
89 let reader = BufReader::new(file);
90 parse::read_aseprite(reader)
91 }
92
93 pub fn read<R: Read>(input: R) -> Result<AsepriteFile> {
97 parse::read_aseprite(input)
98 }
99
100 pub fn width(&self) -> usize {
102 self.width as usize
103 }
104
105 pub fn height(&self) -> usize {
107 self.height as usize
108 }
109
110 pub fn size(&self) -> (usize, usize) {
112 (self.width(), self.height())
113 }
114
115 pub fn num_frames(&self) -> u32 {
117 self.num_frames as u32
118 }
119
120 pub fn num_layers(&self) -> u32 {
122 self.layers.layers.len() as u32
123 }
124
125 pub fn pixel_format(&self) -> PixelFormat {
128 self.pixel_format
129 }
130
131 pub fn palette(&self) -> Option<&ColorPalette> {
137 self.palette.as_deref()
138 }
139
140 pub fn is_indexed_color(&self) -> bool {
142 match self.pixel_format() {
143 PixelFormat::Indexed { .. } => true,
144 PixelFormat::Rgba | PixelFormat::Grayscale => false,
145 }
146 }
147
148 pub fn transparent_color_index(&self) -> Option<u8> {
150 match self.pixel_format() {
151 PixelFormat::Indexed {
152 transparent_color_index,
153 } => Some(transparent_color_index),
154 PixelFormat::Rgba | PixelFormat::Grayscale => None,
155 }
156 }
157
158 pub fn layer(&self, id: u32) -> Layer {
164 assert!(id < self.num_layers());
165 Layer {
166 file: self,
167 layer_id: id,
168 }
169 }
170
171 pub fn layer_by_name(&self, name: &str) -> Option<Layer> {
176 for layer_id in 0..self.num_layers() {
177 let l = self.layer(layer_id);
178 if l.name() == name {
179 return Some(l);
180 }
181 }
182 None
183 }
184
185 pub fn layers(&self) -> LayersIter {
187 LayersIter {
188 file: self,
189 next: 0,
190 }
191 }
192
193 pub fn frame(&self, index: u32) -> Frame {
199 assert!(index < self.num_frames as u32);
200 Frame { file: self, index }
201 }
202
203 pub fn cel(&self, frame: u32, layer: u32) -> Cel {
212 assert!(frame < self.num_frames as u32 && layer < self.num_layers());
213 Cel {
214 file: self,
215 cel_id: CelId {
216 frame: frame as u16,
217 layer: layer as u16,
218 },
219 }
220 }
221
222 pub fn external_files(&self) -> &ExternalFilesById {
224 &self.external_files
225 }
226
227 pub fn external_file_by_id(&self, id: &ExternalFileId) -> Option<&ExternalFile> {
229 self.external_files.get(id)
230 }
231
232 pub fn num_tags(&self) -> u32 {
234 self.tags.len() as u32
235 }
236
237 pub fn get_tag(&self, tag_id: u32) -> Option<&Tag> {
239 self.tags.get(tag_id as usize)
240 }
241
242 pub fn tag(&self, tag_id: u32) -> &Tag {
248 &self.tags[tag_id as usize]
249 }
250
251 pub fn tag_by_name(&self, name: &str) -> Option<&Tag> {
256 self.tags.iter().find(|&tag| tag.name() == name)
257 }
258
259 pub fn tilesets(&self) -> &TilesetsById {
261 &self.tilesets
262 }
263
264 pub fn tilemap(&self, layer_id: u32, frame: u32) -> Option<Tilemap> {
268 if layer_id >= self.num_layers() || frame >= self.num_frames() {
269 return None;
270 }
271 match self.layer(layer_id).layer_type() {
272 LayerType::Tilemap(tileset_id) => {
273 let tileset = self.tilesets().get(tileset_id)?;
274 let cel = self.cel(frame, layer_id);
275 if !cel.is_tilemap() {
276 return None;
277 }
278 let pixel_width = self.width() as u32;
279 let pixel_height = self.height() as u32;
280 let (tile_width, tile_height) = tileset.tile_size().into();
281 let w = (pixel_width + tile_width - 1) / tile_width;
282 let h = (pixel_height + tile_height - 1) / tile_height;
283 assert!(w < (1u32 << 16) && h < (1u32 << 16));
284 Some(Tilemap {
285 cel,
286 tileset,
287 logical_size: (w as u16, h as u16),
288 })
289 }
290 LayerType::Image | LayerType::Group => None,
291 }
292 }
293
294 pub fn sprite_user_data(&self) -> Option<&UserData> {
296 self.sprite_user_data.as_ref()
297 }
298
299 pub fn slices(&self) -> &[Slice] {
301 &self.slices
302 }
303
304 fn frame_image(&self, frame: u16) -> RgbaImage {
315 let mut image = RgbaImage::new(self.width as u32, self.height as u32);
316
317 for (layer_id, cel) in self.framedata.frame_cels(frame) {
318 if !self.layer(layer_id).is_visible() {
320 continue;
321 }
322 self.write_cel(&mut image, cel);
323 }
324
325 image
326 }
327
328 fn write_cel(&self, image: &mut RgbaImage, cel: &RawCel<Pixels>) {
329 let RawCel { data, content, .. } = cel;
330 let layer = self.layer(data.layer_index as u32);
331 let blend_mode = layer.blend_mode();
332 match &content {
338 CelContent::Raw(image_content) => {
339 let ImageContent { size, pixels } = image_content;
340 let image_pixels = pixels.clone_as_image_rgba();
341
342 write_raw_cel_to_image(
343 image,
344 data,
345 size,
346 image_pixels.as_ref(),
347 &blend_mode,
348 layer.opacity(),
349 );
350 }
351 CelContent::Tilemap(tilemap_data) => {
352 let layer_type = layer.layer_type();
353 let tileset_id = if let LayerType::Tilemap(tileset_id) = layer_type {
354 tileset_id
355 } else {
356 panic!(
357 "Tilemap cel not in tilemap layer. Should have been caught by CelsData::validate"
358 );
359 };
360 let tileset = self
361 .tilesets()
362 .get(tileset_id)
363 .expect("Tilemap layer references a missing tileset. Should have been caught by LayersData::validate()");
364 let tileset_pixels = tileset
365 .pixels
366 .as_ref()
367 .expect("Expected Tileset data to contain pixels. Should have been caught by TilesetsById::validate()");
368 let rgba_pixels = tileset_pixels.clone_as_image_rgba();
369
370 write_tilemap_cel_to_image(
371 image,
372 data,
373 tilemap_data,
374 tileset,
375 rgba_pixels.as_ref(),
376 &blend_mode,
377 layer.opacity(),
378 );
379 }
380 CelContent::Linked(frame) => {
381 if let Some(cel) = self.framedata.cel(CelId {
382 frame: *frame,
383 layer: data.layer_index,
384 }) {
385 if let CelContent::Linked(_) = cel.content {
386 panic!(
387 "Cel links to empty cel. Should have been caught by CelsData::validate"
388 );
389 } else {
390 self.write_cel(image, cel);
392 }
393 }
394 }
395 }
396 }
397
398 pub(crate) fn layer_image(&self, cel_id: CelId) -> RgbaImage {
399 let mut image = RgbaImage::new(self.width as u32, self.height as u32);
400 if let Some(cel) = self.framedata.cel(cel_id) {
401 self.write_cel(&mut image, cel);
402 }
403 image
404 }
405
406 }
413
414#[derive(Debug)]
416pub struct LayersIter<'a> {
417 file: &'a AsepriteFile,
418 next: u32,
419}
420
421impl<'a> Iterator for LayersIter<'a> {
422 type Item = Layer<'a>;
423
424 fn next(&mut self) -> Option<Self::Item> {
425 if self.next < self.file.num_layers() {
426 let item = self.file.layer(self.next);
427 self.next += 1;
428 Some(item)
429 } else {
430 None
431 }
432 }
433}
434
435impl<'a> Frame<'a> {
436 pub fn image(&self) -> RgbaImage {
441 self.file.frame_image(self.index as u16)
442 }
443
444 pub fn id(&self) -> u32 {
446 self.index
447 }
448
449 pub fn layer(&self, layer_id: u32) -> Cel {
451 assert!(layer_id < self.file.num_layers());
452 let cel_id = CelId {
453 frame: self.index as u16,
454 layer: layer_id as u16,
455 };
456 Cel {
457 file: self.file,
458 cel_id,
459 }
460 }
461
462 pub fn duration(&self) -> u32 {
464 self.file.frame_times[self.index as usize] as u32
465 }
466}
467
468type BlendFn = Box<dyn Fn(Color8, Color8, u8) -> Color8>;
469
470fn blend_mode_to_blend_fn(mode: BlendMode) -> BlendFn {
471 match mode {
473 BlendMode::Normal => Box::new(blend::normal),
474 BlendMode::Multiply => Box::new(blend::multiply),
475 BlendMode::Screen => Box::new(blend::screen),
476 BlendMode::Overlay => Box::new(blend::overlay),
477 BlendMode::Darken => Box::new(blend::darken),
478 BlendMode::Lighten => Box::new(blend::lighten),
479 BlendMode::ColorDodge => Box::new(blend::color_dodge),
480 BlendMode::ColorBurn => Box::new(blend::color_burn),
481 BlendMode::HardLight => Box::new(blend::hard_light),
482 BlendMode::SoftLight => Box::new(blend::soft_light),
483 BlendMode::Difference => Box::new(blend::difference),
484 BlendMode::Exclusion => Box::new(blend::exclusion),
485 BlendMode::Hue => Box::new(blend::hsl_hue),
486 BlendMode::Saturation => Box::new(blend::hsl_saturation),
487 BlendMode::Color => Box::new(blend::hsl_color),
488 BlendMode::Luminosity => Box::new(blend::hsl_luminosity),
489 BlendMode::Addition => Box::new(blend::addition),
490 BlendMode::Subtract => Box::new(blend::subtract),
491 BlendMode::Divide => Box::new(blend::divide),
492 }
493}
494
495fn tile_slice<'a, T>(pixels: &'a [T], tile_size: &TileSize, tile_id: &TileId) -> &'a [T] {
496 let pixels_per_tile = tile_size.pixels_per_tile() as usize;
497 let start = pixels_per_tile * (tile_id.0 as usize);
498 let end = start + pixels_per_tile;
499 &pixels[start..end]
500}
501
502fn write_tilemap_cel_to_image(
503 image: &mut RgbaImage,
504 cel_data: &CelCommon,
505 tilemap_data: &TilemapData,
506 tileset: &Tileset,
507 pixels: &[Rgba<u8>],
508 blend_mode: &BlendMode,
509 outer_opacity: u8,
510) {
511 let CelCommon {
512 x,
513 y,
514 opacity: cel_opacity,
515 ..
516 } = cel_data;
517 let cel_x = *x as i32;
518 let cel_y = *y as i32;
519 let opacity = mul_un8(outer_opacity as i32, *cel_opacity as i32);
520 let tilemap_width = tilemap_data.width() as i32;
522 let tilemap_height = tilemap_data.height() as i32;
523 let tile_size = tileset.tile_size();
526 let tile_width = tile_size.width() as i32;
527 let tile_height = tile_size.height() as i32;
528 let blend_fn = blend_mode_to_blend_fn(*blend_mode);
530
531 for tile_y in 0..tilemap_height {
532 for tile_x in 0..tilemap_width {
533 let tile = tilemap_data
535 .tile(tile_x as u16, tile_y as u16)
536 .expect("Invalid tile index");
537 let tile_id = &tile.id;
538 let tile_pixels = tile_slice(pixels, &tile_size, tile_id);
539 for pixel_y in 0..tile_height {
540 for pixel_x in 0..tile_width {
541 let pixel_idx = ((pixel_y * tile_width) + pixel_x) as usize;
542 let image_pixel = tile_pixels[pixel_idx];
543 let image_x = (tile_x * tile_width) + pixel_x + cel_x;
544 let image_y = (tile_y * tile_height) + pixel_y + cel_y;
545 let x_in_bounds = (0..(image.width() as i32)).contains(&image_x);
547 let y_in_bounds = (0..(image.height() as i32)).contains(&image_y);
548 if x_in_bounds && y_in_bounds {
549 let image_x = image_x as u32;
550 let image_y = image_y as u32;
551 let src = *image.get_pixel(image_x, image_y);
552 let new = blend_fn(src, image_pixel, opacity);
553 image.put_pixel(image_x, image_y, new);
554 }
555 }
556 }
557 }
558 }
559}
560
561fn write_raw_cel_to_image(
562 image: &mut RgbaImage,
563 cel_data: &CelCommon,
564 image_size: &ImageSize,
565 pixels: &[Rgba<u8>],
566 blend_mode: &BlendMode,
567 outer_opacity: u8,
568) {
569 let ImageSize { width, height } = image_size;
570 let CelCommon {
571 x,
572 y,
573 opacity: cel_opacity,
574 ..
575 } = cel_data;
576 let opacity = mul_un8(outer_opacity as i32, *cel_opacity as i32);
577 let blend_fn = blend_mode_to_blend_fn(*blend_mode);
578 let x0 = *x as i32;
579 let y0 = *y as i32;
580 let x_end = x0 + (*width as i32);
581 let y_end = y0 + (*height as i32);
582 let (img_width, img_height) = image.dimensions();
583
584 for y in y0..y_end {
585 if y < 0 || y >= img_height as i32 {
586 continue;
587 }
588 for x in x0..x_end {
589 if x < 0 || x >= img_width as i32 {
590 continue;
591 }
592 let idx = (y - y0) as usize * *width as usize + (x - x0) as usize;
593 let image_pixel = pixels[idx];
594 let src = *image.get_pixel(x as u32, y as u32);
595 let new = blend_fn(src, image_pixel, opacity);
596 image.put_pixel(x as u32, y as u32, new);
597 }
598 }
599}