load aseprite files directly with asefile

This commit is contained in:
Corwin 2022-02-23 18:05:48 +00:00
parent e67f1ff45f
commit 84e6af5d2f
5 changed files with 150 additions and 176 deletions

View file

@ -18,15 +18,29 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"serde_json",
"syn", "syn",
"toml", "toml",
] ]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -81,6 +95,18 @@ dependencies = [
"adler32", "adler32",
] ]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
]
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.24.1"
@ -97,10 +123,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "itoa" name = "libc"
version = "1.0.1" version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
@ -111,6 +146,22 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -161,7 +212,7 @@ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]] [[package]]
@ -182,12 +233,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.136" version = "1.0.136"
@ -208,17 +253,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"

View file

@ -16,4 +16,4 @@ serde = { version = "1.0", features = ["derive"] }
syn = "1.0.86" syn = "1.0.86"
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
serde_json = "1.0" asefile = "0.3.2"

View file

@ -1,102 +1,23 @@
use std::{ use std::path::Path;
fs::File,
path::{Path, PathBuf},
process::Command,
str,
};
use asefile::{AsepriteFile, Tag};
use image::DynamicImage; use image::DynamicImage;
use serde::Deserialize;
#[derive(Deserialize)] pub fn generate_from_file(filename: &Path) -> (Vec<DynamicImage>, Vec<Tag>) {
pub struct Aseprite { let ase = AsepriteFile::read_file(filename).expect("Aseprite file should exist");
pub frames: Vec<Frame>,
pub meta: Meta, let mut images = Vec::new();
} let mut tags = Vec::new();
#[derive(Deserialize)] for frame in 0..ase.num_frames() {
pub struct Meta { let image = ase.frame(frame).image();
pub app: String,
pub version: String, images.push(DynamicImage::ImageRgba8(image))
pub image: String, }
pub format: String,
pub size: Size, for tag in 0..ase.num_tags() {
pub scale: String, tags.push(ase.tag(tag).clone())
#[serde(rename = "frameTags")] }
pub frame_tags: Vec<FrameTag>,
} (images, tags)
#[derive(Deserialize)]
pub struct Size {
pub w: u32,
pub h: u32,
}
#[derive(Deserialize, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum Direction {
Forward,
Backward,
Pingpong,
}
#[derive(Deserialize, Clone)]
pub struct FrameTag {
pub name: String,
pub from: u32,
pub to: u32,
pub direction: Direction,
}
#[derive(Deserialize, Clone)]
pub struct Frame {
pub frame: Frame2,
pub trimmed: bool,
}
#[derive(Deserialize, Clone)]
pub struct Frame2 {
pub x: u32,
pub y: u32,
pub w: u32,
pub h: u32,
}
pub fn generate_from_file(filename: &Path) -> (Aseprite, DynamicImage) {
let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR");
let output_filename = Path::new(&out_dir).join(filename.file_name().unwrap());
let image_output = output_filename.with_extension("png");
let json_output = output_filename.with_extension("json");
let command = Command::new("aseprite")
.args([
&PathBuf::from("-b"),
&PathBuf::from(filename),
&"--sheet".into(),
&image_output,
&"--format".into(),
&"json-array".into(),
&"--data".into(),
&json_output,
&"--list-tags".into(),
])
.output()
.expect("Could not run aseprite");
assert!(
command.status.success(),
"Aseprite did not complete successfully : {}",
str::from_utf8(&*command.stdout).unwrap_or("Output contains invalid string")
);
let json: Aseprite = serde_json::from_reader(
File::open(&json_output).expect("The json output from aseprite could not be openned"),
)
.expect("The output from aseprite could not be decoded");
(
json,
image::open(image_output).expect("Image should be readable"),
)
} }

View file

@ -16,6 +16,7 @@ mod image_loader;
mod palette16; mod palette16;
mod rust_generator; mod rust_generator;
use image::GenericImageView;
use image_loader::Image; use image_loader::Image;
use colour::Colour; use colour::Colour;
@ -93,7 +94,6 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
let mut optimiser = palette16::Palette16Optimiser::new(); let mut optimiser = palette16::Palette16Optimiser::new();
let mut images = Vec::new(); let mut images = Vec::new();
let mut frames = Vec::new();
let mut tags = Vec::new(); let mut tags = Vec::new();
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
@ -105,24 +105,18 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
.collect(); .collect();
for filename in filenames.iter() { for filename in filenames.iter() {
let (json, image) = aseprite::generate_from_file(filename); let (frames, tag) = aseprite::generate_from_file(filename);
let tile_size = json.frames[0].frame.w;
for frame in json.frames.iter() { tags.push((tag, images.len()));
assert!(frame.frame.w == tile_size);
assert!(
frame.frame.w == frame.frame.h
&& frame.frame.w.is_power_of_two()
&& frame.frame.w <= 32
);
}
let image = Image::load_from_dyn_image(image); for frame in frames {
let width = frame.width();
assert!(width == frame.height() && width.is_power_of_two() && width <= 32);
add_to_optimiser(&mut optimiser, &image, tile_size as usize); let image = Image::load_from_dyn_image(frame);
add_to_optimiser(&mut optimiser, &image, width as usize);
images.push(image); images.push(image);
frames.push(json.frames.clone()); }
tags.push(json.meta.frame_tags.clone());
} }
let optimised_results = optimiser.optimise_palettes(None); let optimised_results = optimiser.optimise_palettes(None);
@ -138,17 +132,16 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
}); });
let mut pre = 0; let mut pre = 0;
let sprites = frames let sprites = images
.iter() .iter()
.flatten()
.zip(assignments.iter()) .zip(assignments.iter())
.map(|(f, assignment)| { .map(|(f, assignment)| {
let start: usize = pre; let start: usize = pre;
let end: usize = pre + (f.frame.w as usize / 8) * (f.frame.h as usize / 8) * 32; let end: usize = pre + (f.width / 8) * (f.height / 8) * 32;
let data = ByteString(&tile_data[start..end]); let data = ByteString(&tile_data[start..end]);
pre = end; pre = end;
let width = f.frame.w as usize; let width = f.width;
let height = f.frame.h as usize; let height = f.height;
quote! { quote! {
Sprite::new( Sprite::new(
&PALETTES[#assignment], &PALETTES[#assignment],
@ -160,15 +153,13 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
let tags = tags let tags = tags
.iter() .iter()
.enumerate() .map(|(tag, num_images)| {
.map(|(i, tag)| { tag.iter().map(move |tag| {
tag.iter().map(move |tag| (i, tag)).map(|(i, tag)| { let start = tag.from_frame() as usize + num_images;
let offset: usize = frames[0..i].iter().map(|s| s.len()).sum(); let end = tag.to_frame() as usize + num_images;
let start = tag.from as usize + offset; let direction = tag.animation_direction() as usize;
let end = tag.to as usize + offset;
let direction = tag.direction as usize;
let name = &tag.name; let name = tag.name();
assert!(start <= end, "Tag {} has start > end", name); assert!(start <= end, "Tag {} has start > end", name);
quote! { quote! {

78
agb/Cargo.lock generated
View file

@ -41,11 +41,11 @@ dependencies = [
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"serde_json",
"syn", "syn",
"toml", "toml",
] ]
@ -80,6 +80,20 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -140,6 +154,18 @@ dependencies = [
"adler32", "adler32",
] ]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.5" version = "0.2.5"
@ -181,18 +207,21 @@ dependencies = [
"png", "png",
] ]
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.119" version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.5.1" version = "0.5.1"
@ -202,6 +231,16 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "modular-bitfield" name = "modular-bitfield"
version = "0.11.2" version = "0.11.2"
@ -223,6 +262,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -323,7 +368,7 @@ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]] [[package]]
@ -402,12 +447,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.136" version = "1.0.136"
@ -428,17 +467,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.9" version = "0.3.9"