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"
version = "0.6.0"
dependencies = [
"asefile",
"image",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"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]]
name = "autocfg"
version = "1.1.0"
@ -81,6 +95,18 @@ dependencies = [
"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]]
name = "image"
version = "0.24.1"
@ -97,10 +123,19 @@ dependencies = [
]
[[package]]
name = "itoa"
version = "1.0.1"
name = "libc"
version = "0.2.119"
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]]
name = "miniz_oxide"
@ -111,6 +146,22 @@ dependencies = [
"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]]
name = "num-integer"
version = "0.1.44"
@ -161,7 +212,7 @@ dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide",
"miniz_oxide 0.3.7",
]
[[package]]
@ -182,12 +233,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "serde"
version = "1.0.136"
@ -208,17 +253,6 @@ dependencies = [
"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]]
name = "syn"
version = "1.0.86"

View file

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

View file

@ -1,102 +1,23 @@
use std::{
fs::File,
path::{Path, PathBuf},
process::Command,
str,
};
use std::path::Path;
use asefile::{AsepriteFile, Tag};
use image::DynamicImage;
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Aseprite {
pub frames: Vec<Frame>,
pub meta: Meta,
pub fn generate_from_file(filename: &Path) -> (Vec<DynamicImage>, Vec<Tag>) {
let ase = AsepriteFile::read_file(filename).expect("Aseprite file should exist");
let mut images = Vec::new();
let mut tags = Vec::new();
for frame in 0..ase.num_frames() {
let image = ase.frame(frame).image();
images.push(DynamicImage::ImageRgba8(image))
}
#[derive(Deserialize)]
pub struct Meta {
pub app: String,
pub version: String,
pub image: String,
pub format: String,
pub size: Size,
pub scale: String,
#[serde(rename = "frameTags")]
pub frame_tags: Vec<FrameTag>,
for tag in 0..ase.num_tags() {
tags.push(ase.tag(tag).clone())
}
#[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"),
)
(images, tags)
}

View file

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

78
agb/Cargo.lock generated
View file

@ -41,11 +41,11 @@ dependencies = [
name = "agb_image_converter"
version = "0.6.0"
dependencies = [
"asefile",
"image",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"toml",
]
@ -80,6 +80,20 @@ dependencies = [
"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]]
name = "autocfg"
version = "1.1.0"
@ -140,6 +154,18 @@ dependencies = [
"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]]
name = "getrandom"
version = "0.2.5"
@ -181,18 +207,21 @@ dependencies = [
"png",
]
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "miniz_oxide"
version = "0.5.1"
@ -202,6 +231,16 @@ dependencies = [
"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 = "modular-bitfield"
version = "0.11.2"
@ -223,6 +262,12 @@ dependencies = [
"syn",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]]
name = "num-integer"
version = "0.1.44"
@ -323,7 +368,7 @@ dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide",
"miniz_oxide 0.3.7",
]
[[package]]
@ -402,12 +447,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "serde"
version = "1.0.136"
@ -428,17 +467,6 @@ dependencies = [
"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]]
name = "siphasher"
version = "0.3.9"