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",
]
[[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]]
name = "ehttp"
version = "0.3.1"
@ -1423,6 +1433,12 @@ dependencies = [
"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]]
name = "jni"
version = "0.21.1"
@ -2173,11 +2189,13 @@ dependencies = [
"eframe",
"egui",
"egui_extras",
"egui_json_tree",
"env_logger",
"image",
"js-sys",
"log",
"serde",
"serde_json",
"texture_packer",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -2220,6 +2238,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
@ -2253,24 +2277,35 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.163"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.163"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"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]]
name = "serde_repr"
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.
] }
log = "0.4"
serde_json = "1"
egui_json_tree = "0.2"
# You only need serde if you want app persistence:
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 image::DynamicImage;
use texture_packer::{
importer::ImageImporter, texture::Texture, TexturePacker, TexturePackerConfig,
};
use serde_json::Value;
use texture_packer::{importer::ImageImporter, TexturePacker, TexturePackerConfig};
pub const MY_ACCENT_COLOR32: Color32 = Color32::from_rgb(230, 102, 1);
pub const TOP_SIDE_MARGIN: f32 = 10.0;
pub const HEADER_HEIGHT: f32 = 45.0;
@ -16,9 +15,77 @@ pub const GIT_HASH: &str = env!("GIT_HASH");
pub struct Spritesheet {
pub data: Vec<u8>,
pub frames: HashMap<String, texture_packer::Frame<String>>,
pub frames_json: Value,
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.
#[derive(serde::Deserialize, serde::Serialize)]
#[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);
}
let mut out_vec = vec![];
let exporter = texture_packer::exporter::ImageExporter::export(&packer).unwrap();
exporter
.write_to(&mut Cursor::new(&mut out_vec), image::ImageFormat::Png)
let exported_image = texture_packer::exporter::ImageExporter::export(&packer).unwrap();
let mut img = image::DynamicImage::new_rgba8(self.config.max_width, self.config.max_height);
image::imageops::overlay(&mut img, &exported_image, 0, 0);
img.write_to(&mut Cursor::new(&mut out_vec), image::ImageFormat::Png)
.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 {
data: out_vec.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);
self.image = None;
@ -300,19 +377,22 @@ impl eframe::App for TemplateApp {
});
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 {
ui.horizontal_top(|ui|{
let data = &self.data.clone().unwrap();
ui.label(format!("{} frames, size: {}x{}",data.frames.len(),data.size.0,data.size.1));
});
CollapsingHeader::new("Preview")
.default_open(true)
.show(ui, |ui| {
ui.add(image.clone());
CollapsingHeader::new("Preview")
.default_open(true)
.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.separator();
egui::ScrollArea::vertical().auto_shrink(false).show(ui, |ui| {
let mut index_to_remove : Option<usize> = None;
for (i, file) in self.dropped_files.iter().enumerate() {
let mut info = if let Some(path) = &file.path {