Frames JSON

This commit is contained in:
Piotr Siuszko 2023-12-07 19:16:30 +01:00
parent 7e62334e54
commit 8b51af05cd
3 changed files with 133 additions and 16 deletions

43
Cargo.lock generated
View File

@ -885,6 +885,16 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "egui_json_tree"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c549dbaae5f70f0d4a9ec068617d24051a4e74f8611298e484bf76a09f06d0"
dependencies = [
"egui",
"serde_json",
]
[[package]] [[package]]
name = "ehttp" name = "ehttp"
version = "0.3.1" version = "0.3.1"
@ -1423,6 +1433,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]] [[package]]
name = "jni" name = "jni"
version = "0.21.1" version = "0.21.1"
@ -2173,11 +2189,13 @@ dependencies = [
"eframe", "eframe",
"egui", "egui",
"egui_extras", "egui_extras",
"egui_json_tree",
"env_logger", "env_logger",
"image", "image",
"js-sys", "js-sys",
"log", "log",
"serde", "serde",
"serde_json",
"texture_packer", "texture_packer",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@ -2220,6 +2238,12 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -2253,24 +2277,35 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.163" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.163" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.39", "syn 2.0.39",
] ]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.12" version = "0.1.12"

View File

@ -17,6 +17,8 @@ eframe = { version = "0.24.1", default-features = false, features = [
"persistence", # Enable restoring app state when restarting the app. "persistence", # Enable restoring app state when restarting the app.
] } ] }
log = "0.4" log = "0.4"
serde_json = "1"
egui_json_tree = "0.2"
# You only need serde if you want app persistence: # You only need serde if you want app persistence:
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }

View File

@ -3,9 +3,8 @@ use std::{collections::HashMap, io::Cursor};
use egui::{CollapsingHeader, Color32, DroppedFile, FontFamily, FontId, Image, RichText}; use egui::{CollapsingHeader, Color32, DroppedFile, FontFamily, FontId, Image, RichText};
use image::DynamicImage; use image::DynamicImage;
use texture_packer::{ use serde_json::Value;
importer::ImageImporter, texture::Texture, TexturePacker, TexturePackerConfig, use texture_packer::{importer::ImageImporter, TexturePacker, TexturePackerConfig};
};
pub const MY_ACCENT_COLOR32: Color32 = Color32::from_rgb(230, 102, 1); pub const MY_ACCENT_COLOR32: Color32 = Color32::from_rgb(230, 102, 1);
pub const TOP_SIDE_MARGIN: f32 = 10.0; pub const TOP_SIDE_MARGIN: f32 = 10.0;
pub const HEADER_HEIGHT: f32 = 45.0; pub const HEADER_HEIGHT: f32 = 45.0;
@ -16,9 +15,77 @@ pub const GIT_HASH: &str = env!("GIT_HASH");
pub struct Spritesheet { pub struct Spritesheet {
pub data: Vec<u8>, pub data: Vec<u8>,
pub frames: HashMap<String, texture_packer::Frame<String>>, pub frames: HashMap<String, texture_packer::Frame<String>>,
pub frames_json: Value,
pub size: (u32, u32), pub size: (u32, u32),
} }
/// Boundaries and properties of a packed texture.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SerializableFrame {
/// Key used to uniquely identify this frame.
pub key: String,
/// Rectangle describing the texture coordinates and size.
pub frame: SerializableRect,
/// True if the texture was rotated during packing.
/// If it was rotated, it was rotated 90 degrees clockwise.
pub rotated: bool,
/// True if the texture was trimmed during packing.
pub trimmed: bool,
// (x, y) is the trimmed frame position at original image
// (w, h) is original image size
//
// w
// +--------------+
// | (x, y) |
// | ^ |
// | | |
// | ********* |
// | * * | h
// | * * |
// | ********* |
// | |
// +--------------+
/// Source texture size before any trimming.
pub source: SerializableRect,
}
impl From<texture_packer::Frame<String>> for SerializableFrame {
fn from(value: texture_packer::Frame<String>) -> Self {
SerializableFrame {
key: value.key,
frame: value.frame.into(),
rotated: value.rotated,
trimmed: value.trimmed,
source: value.source.into(),
}
}
}
/// Defines a rectangle in pixels with the origin at the top-left of the texture atlas.
#[derive(Copy, Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SerializableRect {
/// Horizontal position the rectangle begins at.
pub x: u32,
/// Vertical position the rectangle begins at.
pub y: u32,
/// Width of the rectangle.
pub w: u32,
/// Height of the rectangle.
pub h: u32,
}
impl From<texture_packer::Rect> for SerializableRect {
fn from(value: texture_packer::Rect) -> Self {
SerializableRect {
h: value.h,
w: value.w,
x: value.x,
y: value.y,
}
}
}
/// We derive Deserialize/Serialize so we can persist app state on shutdown. /// We derive Deserialize/Serialize so we can persist app state on shutdown.
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state #[serde(default)] // if we add new fields, give them default values when deserializing old state
@ -105,15 +172,25 @@ impl TemplateApp {
println!(" {:7} : {:?}", name, frame.frame); println!(" {:7} : {:?}", name, frame.frame);
} }
let mut out_vec = vec![]; let mut out_vec = vec![];
let exporter = texture_packer::exporter::ImageExporter::export(&packer).unwrap(); let exported_image = texture_packer::exporter::ImageExporter::export(&packer).unwrap();
exporter let mut img = image::DynamicImage::new_rgba8(self.config.max_width, self.config.max_height);
.write_to(&mut Cursor::new(&mut out_vec), image::ImageFormat::Png) image::imageops::overlay(&mut img, &exported_image, 0, 0);
img.write_to(&mut Cursor::new(&mut out_vec), image::ImageFormat::Png)
.unwrap(); .unwrap();
let frames_info: Vec<SerializableFrame> = packer
.get_frames()
.values()
.map(|v| -> SerializableFrame { (*v).clone().into() })
.collect();
let frames_string = serde_json::to_string_pretty(&frames_info).unwrap();
let frames_json = serde_json::from_str(&frames_string).unwrap();
self.data = Some(Spritesheet { self.data = Some(Spritesheet {
data: out_vec.clone(), data: out_vec.clone(),
frames: packer.get_frames().clone(), frames: packer.get_frames().clone(),
size: (packer.width(), packer.height()), size: (img.width(), img.height()),
frames_json,
}); });
let id = format!("bytes://output_{}.png", self.counter); let id = format!("bytes://output_{}.png", self.counter);
self.image = None; self.image = None;
@ -300,6 +377,7 @@ impl eframe::App for TemplateApp {
}); });
ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui|{ ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui|{
egui::ScrollArea::vertical().auto_shrink(false).show(ui, |ui| {
if let Some(image) = &self.image { if let Some(image) = &self.image {
ui.horizontal_top(|ui|{ ui.horizontal_top(|ui|{
let data = &self.data.clone().unwrap(); let data = &self.data.clone().unwrap();
@ -308,11 +386,13 @@ impl eframe::App for TemplateApp {
CollapsingHeader::new("Preview") CollapsingHeader::new("Preview")
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
ui.label(RichText::new("Frames JSON").strong());
egui_json_tree::JsonTree::new("simple-tree", &self.data.clone().unwrap().frames_json).show(ui);
ui.label(RichText::new("Spritesheet").strong());
ui.add(image.clone()); ui.add(image.clone());
}); });
} }
ui.separator(); ui.separator();
egui::ScrollArea::vertical().auto_shrink(false).show(ui, |ui| {
let mut index_to_remove : Option<usize> = None; let mut index_to_remove : Option<usize> = None;
for (i, file) in self.dropped_files.iter().enumerate() { for (i, file) in self.dropped_files.iter().enumerate() {
let mut info = if let Some(path) = &file.path { let mut info = if let Some(path) = &file.path {