mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/data/renderer.rs')
| -rw-r--r-- | src/data/renderer.rs | 248 |
1 files changed, 63 insertions, 185 deletions
diff --git a/src/data/renderer.rs b/src/data/renderer.rs index 6230e71..93f1a7e 100644 --- a/src/data/renderer.rs +++ b/src/data/renderer.rs @@ -2,91 +2,13 @@ pub(crate) use super::autotile::*; use super::schematic::Schematic; use super::GridPos; -use crate::block::environment::METAL_FLOOR; use crate::block::Rotation; -pub(crate) use crate::utils::{ImageUtils, Overlay, Repeat}; +pub(crate) use crate::utils::{Image, ImageHolder, ImageUtils, Overlay, Repeat}; use crate::Map; -pub(crate) use image::{ - DynamicImage, GenericImage, GenericImageView, Pixel, Rgb, RgbImage, Rgba, RgbaImage, -}; -pub(crate) use std::borrow::{Borrow, BorrowMut}; -use std::ops::{Deref, DerefMut}; include!(concat!(env!("OUT_DIR"), "/full.rs")); include!(concat!(env!("OUT_DIR"), "/quar.rs")); include!(concat!(env!("OUT_DIR"), "/eigh.rs")); -pub enum ImageHolder { - Borrow(&'static RgbaImage), - Own(RgbaImage), -} - -impl ImageHolder { - #[must_use] - pub fn own(self) -> RgbaImage { - match self { - Self::Own(x) => x, - Self::Borrow(x) => x.clone(), - } - } - - pub fn rotate(&mut self, times: u8) -> &mut Self { - if times == 0 { - return self; - } - let p: &mut RgbaImage = self.borrow_mut(); - p.rotate(times); - self - } -} - -impl Borrow<RgbaImage> for ImageHolder { - fn borrow(&self) -> &RgbaImage { - match self { - Self::Own(x) => x, - Self::Borrow(x) => x, - } - } -} - -impl BorrowMut<RgbaImage> for ImageHolder { - fn borrow_mut(&mut self) -> &mut RgbaImage { - match self { - Self::Own(x) => x, - Self::Borrow(_) => { - *self = Self::from(std::mem::replace(self, Self::from(RgbaImage::new(0, 0))).own()); - self.borrow_mut() - } - } - } -} - -impl Deref for ImageHolder { - type Target = RgbaImage; - fn deref(&self) -> &Self::Target { - self.borrow() - } -} - -impl DerefMut for ImageHolder { - fn deref_mut(&mut self) -> &mut Self::Target { - self.borrow_mut() - } -} - -impl From<&'static RgbaImage> for ImageHolder { - fn from(value: &'static RgbaImage) -> Self { - debug_assert_ne!(value.width(), 0); - debug_assert_ne!(value.height(), 0); - Self::Borrow(value) - } -} - -impl From<RgbaImage> for ImageHolder { - fn from(value: RgbaImage) -> Self { - Self::Own(value) - } -} - #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum Scale { @@ -116,22 +38,22 @@ impl std::ops::Mul<u32> for Scale { #[macro_export] macro_rules! load { - ("empty", $scale:ident) => { - ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale { + ("empty", $scale:expr) => { + ImageHolder::from(match $scale { $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::EMPTY, $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::EMPTY, $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::EMPTY, - })}) + }.copy()) }; - ($name:literal, $scale:ident) => { paste::paste! { - ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale { - $crate::data::renderer::Scale::Quarter => $crate::data::renderer::quar::[<$name:snake:upper>], - $crate::data::renderer::Scale::Eigth => $crate::data::renderer::eigh::[<$name:snake:upper>], - $crate::data::renderer::Scale::Full => $crate::data::renderer::full::[<$name:snake:upper>], - })}) + ($name:literal, $scale:expr) => { paste::paste! { + ImageHolder::from(match $scale { + $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::[<$name:snake:upper>], + $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::[<$name:snake:upper>], + $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::[<$name:snake:upper>], + }.copy()) } }; ($name: literal) => { paste::paste! { - [$crate::data::renderer::full::[<$name:snake:upper>], $crate::data::renderer::quar::[<$name:snake:upper>], $crate::data::renderer::eigh::[<$name:snake:upper>]] + [$crate::data::renderer::full::[<$name:snake:upper>].copy(), $crate::data::renderer::quar::[<$name:snake:upper>].copy(), $crate::data::renderer::eigh::[<$name:snake:upper>].copy()] } }; (from $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => { $crate::data::renderer::load!($scale -> match $v { @@ -149,11 +71,11 @@ macro_rules! load { (concat $x:expr => $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => { paste::paste! { match $v { $($k => - ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale { - $crate::data::renderer::Scale::Quarter => $crate::data::renderer::quar::[<$k:snake:upper _ $x:snake:upper>], - $crate::data::renderer::Scale::Eigth => $crate::data::renderer::eigh::[<$k:snake:upper _ $x:snake:upper>], - $crate::data::renderer::Scale::Full => $crate::data::renderer::full::[<$k:snake:upper _ $x:snake:upper>], - }) }), + ImageHolder::from(match $scale { + $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::[<$k:snake:upper _ $x:snake:upper>], + $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::[<$k:snake:upper _ $x:snake:upper>], + $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::[<$k:snake:upper _ $x:snake:upper>], + }.copy()), )+ #[allow(unreachable_patterns)] n => unreachable!("{n:?}"), @@ -165,11 +87,8 @@ pub(crate) use load; /// trait for renderable objects pub trait Renderable { /// create a picture - /// - /// # Safety - /// - /// UB if called before [`warmup`](crate::warmup) - unsafe fn render(&self) -> RgbImage; + #[must_use = "i did so much work for you"] + fn render(&self) -> Image<Vec<u8>, 3>; } impl Renderable for Schematic<'_> { @@ -180,28 +99,15 @@ impl Renderable for Schematic<'_> { /// s.put(0, 0, &block::distribution::DISTRIBUTOR); /// s.put(0, 2, &block::distribution::ROUTER); /// s.put(1, 2, &block::walls::COPPER_WALL); - /// // warm up the images for the first time - /// unsafe { warmup(); } - /// // this is now safe, because we have warmed up - /// let output /*: RgbImage */ = unsafe { s.render() }; + /// let output /*: Image */ = s.render(); /// ``` - /// - /// # Safety - /// - /// UB if called before [`warmup`](crate::warmup) - unsafe fn render(&self) -> RgbImage { + fn render(&self) -> Image<Vec<u8>, 3> { // fill background - let mut bg = RgbImage::repeated( - &DynamicImage::from( - METAL_FLOOR - .image(None, None, Rotation::Up, Scale::Full) - .own(), - ) - .into_rgb8(), + let mut bg = load!("metal-floor", Scale::Full).borrow().repeated( ((self.width + 2) * 32) as u32, ((self.height + 2) * 32) as u32, ); - let mut canvas = RgbaImage::new( + let mut canvas = Image::alloc( ((self.width + 2) * 32) as u32, ((self.height + 2) * 32) as u32, ); @@ -221,7 +127,7 @@ impl Renderable for Schematic<'_> { }; let x = x as u32 - ((tile.block.get_size() - 1) / 2) as u32; let y = self.height as u32 - y as u32 - ((tile.block.get_size() / 2) + 1) as u32; - canvas.overlay_at( + canvas.as_mut().overlay_at( tile.image( ctx.as_ref(), tile.get_rotation().unwrap_or(Rotation::Up), @@ -232,41 +138,32 @@ impl Renderable for Schematic<'_> { (y + 1) * 32, ); } - canvas.shadow(); + canvas.as_mut().shadow(); for x in 0..canvas.width() { for y in 0..canvas.height() { - let p2 = unsafe { canvas.unsafe_get_pixel(x, y) }; - let Rgb([r2, g2, b2]) = unsafe { bg.unsafe_get_pixel(x, y) }; - let mut p = Rgba([r2, g2, b2, u8::MAX]); - p.blend(&p2); - let Rgba([r, g, b, a]) = p; - let a = a as f32 / 255.; - let p = Rgb([ - (((r as f32 / 255.) * a) * 255.) as u8, - (((g as f32 / 255.) * a) * 255.) as u8, - (((b as f32 / 255.) * a) * 255.) as u8, - ]); - unsafe { bg.unsafe_put_pixel(x, y, p) }; + // canvas has a shadow + let p2 = unsafe { canvas.pixel(x, y) }; + let p = unsafe { bg.pixel_mut(x, y) }; + crate::utils::image::blend(p.try_into().unwrap(), p2); } } - bg + bg.remove_channel() } } impl Renderable for Map<'_> { /// Draws a map - /// - /// # Safety - /// UB if called before [`warmup`](crate::warmup) - unsafe fn render(&self) -> RgbImage { + fn render(&self) -> Image<Vec<u8>, 3> { let scale = if self.width + self.height < 2000 { Scale::Quarter } else { Scale::Eigth }; // todo combine these (beware of floor drawing atop buildings) (planned solution:? ptr blocks) - let mut floor = RgbImage::new(scale * self.width as u32, scale * self.height as u32); - let mut top = RgbaImage::new(scale * self.width as u32, scale * self.height as u32); + let mut floor: Image<_, 3> = + Image::alloc(scale * self.width as u32, scale * self.height as u32); + let mut top: Image<_, 4> = + Image::alloc(scale * self.width as u32, scale * self.height as u32); for (x, y, j, tile) in self.tiles.iter().enumerate().map(|(j, t)| { ( (j % self.width), @@ -277,16 +174,18 @@ impl Renderable for Map<'_> { ) }) { // draw the floor first. - let flo: &RgbaImage = &tile.floor(scale); // println!("draw {tile:?} ({x}, {y}) + {scale:?}"); - // debug_assert_eq!(floor.width(), scale.px() as u32); - // debug_assert_eq!(floor.height(), scale.px() as u32); - floor.overlay_at(flo, scale * x as u32, scale * y as u32); + floor.as_mut().overlay_at( + tile.floor(scale).borrow(), + scale * x as u32, + scale * y as u32, + ); if tile.has_ore() { - let ore: &RgbaImage = &tile.ore(scale); - // debug_assert_eq!(ore.width(), scale.px() as u32); - // debug_assert_eq!(ore.height(), scale.px() as u32); - floor.overlay_at(ore, scale * x as u32, scale * y as u32); + floor.as_mut().overlay_at( + tile.ore(scale).borrow(), + scale * x as u32, + scale * y as u32, + ); } if let Some(build) = tile.build() { @@ -307,41 +206,22 @@ impl Renderable for Map<'_> { } else { None }; - let img: &RgbaImage = &tile.build_image(ctx.as_ref(), scale); - // debug_assert_eq!(img.width(), scale * build.block.get_size() as u32); - // debug_assert_eq!(img.height(), scale * build.block.get_size() as u32); - top.overlay_at(img, scale * x as u32, scale * y as u32); + top.as_mut().overlay_at( + tile.build_image(ctx.as_ref(), scale).borrow(), + scale * x as u32, + scale * y as u32, + ); } } - floor.overlay_at(&top, 0, 0); + floor.as_mut().overlay_at(top.as_ref(), 0, 0); floor } } -#[allow(clippy::needless_doctest_main)] -/// Loads all the images into memory (about 300mb) -/// This is a necessary function. Call it once in main. -/// -/// ``` -/// fn main() { -/// unsafe { mindus::warmup(); } -/// } -/// ``` -/// -/// # Safety -/// -/// only call once, else UB -pub unsafe fn warmup() { - full::warmup(); - quar::warmup(); - eigh::warmup(); -} - #[test] fn all_blocks() { use crate::block::content::Type; use crate::content::Content; - unsafe { warmup() }; let reg = crate::block::build_registry(); for t in 19..Type::WorldMessage as u16 { let t = Type::try_from(t).unwrap(); @@ -357,20 +237,18 @@ fn all_blocks() { } let name = dbg!(t.get_name()); let t = reg.get(name).unwrap(); - let _ = unsafe { - t.image( - None, - Some(&RenderingContext { - cross: [None; 4], - position: PositionContext { - position: GridPos(0, 0), - width: 5, - height: 5, - }, - }), - Rotation::Up, - Scale::Quarter, - ) - }; + let _ = t.image( + None, + Some(&RenderingContext { + cross: [None; 4], + position: PositionContext { + position: GridPos(0, 0), + width: 5, + height: 5, + }, + }), + Rotation::Up, + Scale::Quarter, + ); } } |