From f5291a4d6086f351719737ca5451ffc3a1ce89bc Mon Sep 17 00:00:00 2001 From: gered Date: Mon, 29 May 2023 14:29:12 -0400 Subject: [PATCH] add support for loading and saving BitmapAtlas "descriptor" json files as well as support for directly instantiating a BitmapAtlas from such a descriptor. saving a BitmapAtlas to a descriptor file directly is not added (yet?) as my gut feeling is that these files would probably be hand-written? saving from a BitmapAtlas directly would (in a simple/naive impl) always result in a long-ish list of "tiles" anyway, since grids are turned into tiles. this further reinforces me feeling that you'd either write these files by hand, or at the very least, just construct a descriptor instance in code somehow and save that --- ggdt/Cargo.toml | 2 + ggdt/src/graphics/bitmapatlas.rs | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/ggdt/Cargo.toml b/ggdt/Cargo.toml index 246ed01..fabe646 100644 --- a/ggdt/Cargo.toml +++ b/ggdt/Cargo.toml @@ -16,6 +16,8 @@ num-traits = "0.2.15" bitflags = "2.1.0" flate2 = "1.0.25" crc32fast = "1.3.2" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" [target.'cfg(not(windows))'.dependencies] sdl2 = { git = "https://github.com/Rust-SDL2/rust-sdl2/", rev = "819ab438ac971a922d6ee1da558822002d343b4e", features = ["static-link", "bundled", "use-pkgconfig", "unsafe_textures"] } diff --git a/ggdt/src/graphics/bitmapatlas.rs b/ggdt/src/graphics/bitmapatlas.rs index dd26587..3fe5b7b 100644 --- a/ggdt/src/graphics/bitmapatlas.rs +++ b/ggdt/src/graphics/bitmapatlas.rs @@ -1,4 +1,7 @@ +use std::fs::File; +use std::io::{BufReader, BufWriter, Read, Write}; use std::ops::Index; +use std::path::Path; use thiserror::Error; @@ -15,6 +18,9 @@ pub enum BitmapAtlasError { #[error("Invalid dimensions for region")] InvalidDimensions, + + #[error("No bitmap atlas entries in the descriptor")] + NoEntries, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -40,6 +46,27 @@ where } } + pub fn from_descriptor(descriptor: &BitmapAtlasDescriptor, bitmap: BitmapType) -> Result { + if descriptor.tiles.is_empty() { + return Err(BitmapAtlasError::NoEntries); + } + + let mut atlas = BitmapAtlas::new(bitmap); + for entry in descriptor.tiles.iter() { + use BitmapAtlasDescriptorEntry::*; + match entry { + Tile { x, y, width, height } => { + atlas.add(Rect::new(*x as i32, *y as i32, *width, *height))?; + } + Autogrid { x, y, tile_width, tile_height, num_tiles_x, num_tiles_y, border } => { + atlas.add_custom_grid(*x, *y, *tile_width, *tile_height, *num_tiles_x, *num_tiles_y, *border)?; + } + } + } + + Ok(atlas) + } + pub fn add(&mut self, rect: Rect) -> Result { if rect.width == 0 || rect.height == 0 { return Err(BitmapAtlasError::InvalidDimensions); @@ -158,6 +185,70 @@ where } } +#[derive(Error, Debug)] +pub enum BitmapAtlasDescriptorError { + #[error("Serde Json serialization/deserialization error: {0}")] + SerdeJsonError(String), + + #[error("I/O error")] + IOError(#[from] std::io::Error), +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "lowercase")] +pub enum BitmapAtlasDescriptorEntry { + Tile { + x: u32, // + y: u32, + width: u32, + height: u32, + }, + Autogrid { + x: u32, // + y: u32, + tile_width: u32, + tile_height: u32, + num_tiles_x: u32, + num_tiles_y: u32, + border: u32, + }, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct BitmapAtlasDescriptor { + pub bitmap: String, + pub tiles: Vec, +} + +impl BitmapAtlasDescriptor { + pub fn load_from_file(path: &Path) -> Result { + let f = File::open(path)?; + let mut reader = BufReader::new(f); + Self::load_from_bytes(&mut reader) + } + + pub fn load_from_bytes(reader: &mut T) -> Result { + match serde_json::from_reader(reader) { + Ok(desc) => Ok(desc), + Err(error) => Err(BitmapAtlasDescriptorError::SerdeJsonError(error.to_string())), + } + } + + pub fn to_file(&self, path: &Path) -> Result<(), BitmapAtlasDescriptorError> { + let f = File::create(path)?; + let mut writer = BufWriter::new(f); + self.to_bytes(&mut writer) + } + + pub fn to_bytes(&self, writer: &mut T) -> Result<(), BitmapAtlasDescriptorError> { + if let Err(error) = serde_json::to_writer_pretty(writer, &self) { + Err(BitmapAtlasDescriptorError::SerdeJsonError(error.to_string())) + } else { + Ok(()) + } + } +} + #[cfg(test)] mod tests { use claim::*;