mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/data/renderer.rs')
| -rw-r--r-- | src/data/renderer.rs | 133 |
1 files changed, 114 insertions, 19 deletions
diff --git a/src/data/renderer.rs b/src/data/renderer.rs index 05486d8..a1af661 100644 --- a/src/data/renderer.rs +++ b/src/data/renderer.rs @@ -1,24 +1,86 @@ //! schematic drawing -use std::io::{BufReader, Cursor}; -use std::path::Path; - +use dashmap::mapref::one::Ref; +use dashmap::DashMap; use image::codecs::png::PngDecoder; -use image::imageops::overlay; use image::{DynamicImage, RgbaImage}; +use std::io::{BufReader, Cursor}; +use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use zip::ZipArchive; +use crate::block::environment::METAL_FLOOR; +use crate::data::map::Tile; use crate::team::SHARDED; -use crate::utils::image::*; +use crate::utils::ImageUtils; +use crate::Map; +pub use std::borrow::Borrow; use super::schematic::Schematic; -pub(crate) fn load(category: &str, name: &str) -> Option<RgbaImage> { - let mut p = Path::new("blocks").join(category).join(name); - p.set_extension("png"); - load_raw(p) +type Cache = DashMap<PathBuf, RgbaImage>; +fn cache() -> &'static Cache { + CACHE.get_or_init(Cache::new) +} + +pub enum ImageHolder { + Borrow(Ref<'static, PathBuf, RgbaImage>), + Own(RgbaImage), +} + +impl ImageHolder { + pub fn own(self) -> RgbaImage { + match self { + Self::Own(x) => x, + Self::Borrow(x) => x.clone(), + } + } } -pub(crate) fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> { +impl Borrow<RgbaImage> for ImageHolder { + fn borrow(&self) -> &RgbaImage { + match self { + Self::Own(x) => x, + Self::Borrow(x) => x.value(), + } + } +} + +impl From<Option<Ref<'static, PathBuf, RgbaImage>>> for ImageHolder { + fn from(value: Option<Ref<'static, PathBuf, RgbaImage>>) -> Self { + Self::Borrow(value.unwrap()) + } +} + +impl From<Ref<'static, PathBuf, RgbaImage>> for ImageHolder { + fn from(value: Ref<'static, PathBuf, RgbaImage>) -> Self { + Self::Borrow(value) + } +} + +impl From<RgbaImage> for ImageHolder { + fn from(value: RgbaImage) -> Self { + Self::Own(value) + } +} + +static CACHE: OnceLock<Cache> = OnceLock::new(); +pub(crate) fn load(category: &str, name: &str) -> Option<Ref<'static, PathBuf, RgbaImage>> { + let key = Path::new("blocks").join(category).join(name); + let mut p = key.clone(); + use dashmap::mapref::entry::Entry::*; + Some(match cache().entry(key) { + Occupied(v) => v.into_ref().downgrade(), + Vacant(entry) => { + p.set_extension("png"); + let Some(i) = load_raw(p) else { + return None; + }; + entry.insert(i).downgrade() + } + }) +} + +fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> { let f = std::fs::File::open(Path::new("target/out").join(f)).ok()?; let r = PngDecoder::new(BufReader::new(f)).unwrap(); Some(DynamicImage::from_decoder(r).unwrap().into_rgba8()) @@ -55,11 +117,12 @@ where { let mut c = RgbaImage::new(size.into() * 32, size.into() * 32); for suffix in suffixes { - if let Some(mut p) = load(category, &format!("{name}{suffix}")) { + if let Some(p) = load(category, &format!("{name}{suffix}")) { if suffix == &"-team" { - tint(&mut p, SHARDED.color()); + c.overlay(p.clone().tint(SHARDED.color()), 0, 0); + continue; } - image::imageops::overlay(&mut c, &p, 0, 0); + c.overlay(&p, 0, 0); } } c @@ -76,17 +139,49 @@ impl<'l> Renderer { /// s.put(0, 0, &block::distribution::DISTRIBUTOR); /// s.put(0, 3, &block::distribution::ROUTER); /// s.put(1, 3, &block::walls::COPPER_WALL); - /// let output /*: RgbaImage */ = Renderer::render(&s); + /// let output /*: RgbaImage */ = Renderer::render_schematic(&s); /// ``` - pub fn render(s: &'l Schematic<'_>) -> RgbaImage { + pub fn render_schematic(s: &'l Schematic<'_>) -> RgbaImage { load_zip(); let mut canvas = RgbaImage::new((s.width * 32).into(), (s.height * 32).into()); // fill background - repeat(&mut canvas, &load("environment", "metal-floor").unwrap()); + canvas.repeat(METAL_FLOOR.image(None).borrow()); for tile in s.block_iter() { - let x = (tile.pos.0 - ((tile.block.get_size() - 1) / 2) as u16) as i64; - let y = (s.height - tile.pos.1 - ((tile.block.get_size() / 2) + 1) as u16) as i64; - overlay(&mut canvas, &tile.image(), x * 32, y * 32); + let x = (tile.pos.0 - ((tile.block.get_size() - 1) / 2) as u16) as u32; + let y = (s.height - tile.pos.1 - ((tile.block.get_size() / 2) + 1) as u16) as u32; + canvas.overlay(tile.image().borrow(), x * 32, y * 32); + } + canvas + } + + pub fn render_map(m: &'l Map<'_>) -> RgbaImage { + load_zip(); + let mut canvas = RgbaImage::new(m.width * 8, m.height * 8); + const VEC: Vec<&Tile<'_>> = vec![]; + let mut layers = [VEC; 2]; + for tile in m.tiles.iter() { + if tile.has_building() { + layers[1].push(tile) + } else { + layers[0].push(tile) + } + } + for tiles in layers { + for tile in tiles { + let s = if let Some(build) = &tile.build { + build.block.get_size() + } else { + 1 + }; + let x = (tile.pos.0 - ((s - 1) / 2) as u16) as u32; + let y = (m.height as u16 - tile.pos.1 - ((s / 2) + 1) as u16) as u32; + canvas.overlay( + // SAFETY: surely not 0. (tile.size can never be 0). im not sure if you can load a 0 sized image.. but you might be able to. + unsafe { &tile.image().own().scale(tile.size() as u32 * 8) }, + x * 8, + y * 8, + ); + } } canvas } |