mindustry logic execution, map- and schematic- parsing and rendering
some optimizations:
- use slice_as_chunks - dont load zips, just put it all in - some clone saves
bendn 2023-07-28
parent 78c704a · commit 6534297
-rw-r--r--Cargo.toml15
-rw-r--r--README.md5
-rw-r--r--assets/blocks/storage/unloader-center.png (renamed from assets/blocks/storage/center.png)bin267 -> 267 bytes
-rw-r--r--build.rs87
-rw-r--r--rust-toolchain.toml2
-rw-r--r--src/block/distribution.rs175
-rw-r--r--src/block/drills.rs38
-rw-r--r--src/block/environment.rs13
-rw-r--r--src/block/liquid.rs28
-rw-r--r--src/block/logic.rs53
-rw-r--r--src/block/mod.rs29
-rw-r--r--src/block/payload.rs22
-rw-r--r--src/block/power.rs39
-rw-r--r--src/block/production.rs6
-rw-r--r--src/block/simple.rs17
-rw-r--r--src/block/turrets.rs13
-rw-r--r--src/block/units.rs180
-rw-r--r--src/block/walls.rs27
-rw-r--r--src/data/autotile.rs34
-rw-r--r--src/data/map.rs14
-rw-r--r--src/data/renderer.rs105
-rw-r--r--src/lib.rs1
-rw-r--r--src/utils/image.rs28
23 files changed, 405 insertions, 526 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 165bafb..ebf6044 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "1.4.3"
+version = "1.4.5"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)"
authors = [
@@ -16,17 +16,16 @@ flate2 = { version = "1.0", features = ["zlib"], default-features = false }
base64 = "0.21"
paste = "1.0"
strconv = "0.1"
+# png feature not needed; only for binary. todo seperate bin
image = { version = "0.24", features = ["png"], default-features = false }
-const-str = "0.5"
color-hex = "0.2"
-zip = { version = "0.6", features = ["zstd"], default-features = false }
tinyrand = "0.5"
tinyrand-std = "0.5"
-dashmap = "5.4"
fast_image_resize = "2.7"
thiserror = "1.0"
bobbin-bits = "0.1"
blurslice = { version = "0.1", optional = true }
+dashmap = { version = "5.5", features = ["inline"] }
[features]
schem_shadow = ["dep:blurslice"]
@@ -34,7 +33,7 @@ map_shadow = ["dep:blurslice"]
default = ["schem_shadow", "map_shadow"]
[build-dependencies]
-zip = { version = "0.6.6", features = ["zstd"], default-features = false }
+image = { version = "0.24", features = ["png"], default-features = false }
walkdir = "2"
[[bin]]
@@ -47,5 +46,11 @@ opt-level = 3
lto = true
incremental = true
+[profile.dev.build-override]
+opt-level = 3
+
+[profile.release.build-override]
+opt-level = 3
+
[dev-dependencies]
diff = "0.1"
diff --git a/README.md b/README.md
index bb2d76f..9aa8287 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,7 @@
# mindus
+
+![msrv](https://img.shields.io/badge/msrv-nightly-blue?style=for-the-badge&logo=rust)
+
Mindus is a library for working with [Mindustry](https://github.com/Anuken/Mindustry) formats.
## Usage
@@ -14,4 +17,4 @@ output.save("output.png");
This produces:
-![image](https://raw.githubusercontent.com/bend-n/mindus/master/.github/example.png) \ No newline at end of file
+![image](https://raw.githubusercontent.com/bend-n/mindus/master/.github/example.png)
diff --git a/assets/blocks/storage/center.png b/assets/blocks/storage/unloader-center.png
index 847b884..847b884 100644
--- a/assets/blocks/storage/center.png
+++ b/assets/blocks/storage/unloader-center.png
Binary files differ
diff --git a/build.rs b/build.rs
index daa0ccd..ab1cc61 100644
--- a/build.rs
+++ b/build.rs
@@ -1,67 +1,38 @@
+#![feature(let_chains)]
+use image::codecs::png::PngDecoder;
+use image::DynamicImage;
use std::fs::File;
-use std::io::prelude::*;
-use std::io::{Seek, Write};
+use std::io::{BufReader, Write as _};
use std::iter::Iterator;
use std::path::Path;
-use walkdir::{DirEntry, WalkDir};
-use zip::result::ZipError;
-use zip::write::FileOptions;
+use walkdir::WalkDir;
-fn zip_dir<T>(
- it: &mut dyn Iterator<Item = DirEntry>,
- prefix: &str,
- writer: T,
-) -> zip::result::ZipResult<()>
-where
- T: Write + Seek,
-{
- let mut zip = zip::ZipWriter::new(writer);
- let mut options = FileOptions::default()
- .compression_method(zip::CompressionMethod::Zstd)
- .unix_permissions(0o755);
- if let Ok(v) = std::env::var("COMPRESS") {
- if v == "1" {
- options = options.compression_level(Some(22));
- }
- }
-
- let mut buffer = Vec::new();
- for entry in it {
- let path = entry.path();
- let name = path.strip_prefix(Path::new(prefix)).unwrap();
-
- // Write file or directory explicitly
- // Some unzip tools unzip files with directory paths correctly, some do not!
- if path.is_file() {
- println!("adding file {path:?} as {name:?} ...");
- #[allow(deprecated)]
- zip.start_file_from_path(name, options)?;
- let mut f = File::open(path)?;
-
- f.read_to_end(&mut buffer)?;
- zip.write_all(&buffer)?;
- buffer.clear();
- } else if !name.as_os_str().is_empty() {
- // Only if not root! Avoids path spec / warning
- // and mapname conversion failed error on unzip
- println!("adding dir {path:?} as {name:?} ...");
- #[allow(deprecated)]
- zip.add_directory_from_path(name, options)?;
- }
- }
- zip.finish()?;
- Result::Ok(())
-}
-
-fn main() -> Result<(), ZipError> {
+fn main() {
let _ = std::fs::remove_dir_all("target/out");
let walkdir = WalkDir::new("assets");
- let it = walkdir.into_iter();
println!("cargo:rerun-if-changed=assets/");
println!("cargo:rerun-if-changed=build.rs");
- zip_dir(
- &mut it.filter_map(|e| e.ok()),
- "assets",
- File::create(std::env::var("OUT_DIR").unwrap() + "/asset").unwrap(),
- )
+ let o = std::env::var("OUT_DIR").unwrap();
+ let mut f = File::create(Path::new(&o).join("asset")).unwrap();
+ let mut n = 1usize;
+ f.write_all(b"fn put(map: &DashMap<String, RgbaImage>) {")
+ .unwrap();
+ let mut s = String::new(); // idk write_all / write wasnt working
+ for e in walkdir.into_iter().filter_map(|e| e.ok()) {
+ let path = e.path();
+ if path.is_file() && let Some(e) = path.extension() && e == "png" {
+ let p = DynamicImage::from_decoder(PngDecoder::new(BufReader::new(File::open(path).unwrap())).unwrap()).unwrap().into_rgba8();
+ let x = p.width();
+ let y = p.height();
+ let path = path.with_extension("");
+ let path = path.file_name().unwrap().to_str().unwrap();
+ let mut f = File::create(Path::new(&o).join(n.to_string())).unwrap();
+ f.write_all(&p.into_raw()).unwrap();
+ println!("writing {path:?}");
+ s+= &format!("\tmap.insert(String::from(\"{path}\"), RgbaImage::from_vec({x}, {y}, include_bytes!(concat!(env!(\"OUT_DIR\"), \"/{n}\")).to_vec()).unwrap());\n");
+ n += 1;
+ }
+ }
+ f.write_all(s.as_bytes()).unwrap();
+ f.write_all(b"}").unwrap();
}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..5d56faf
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly"
diff --git a/src/block/distribution.rs b/src/block/distribution.rs
index baa6ee6..06ce5b8 100644
--- a/src/block/distribution.rs
+++ b/src/block/distribution.rs
@@ -8,9 +8,9 @@ use crate::item;
make_simple!(
ConveyorBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>, rot: Rotation| {
+ |_, name, _, ctx: Option<&RenderingContext>, rot: Rotation| {
let ctx = ctx.unwrap(); // we set want_context to true
- Some(tile(ctx, "distribution", "conveyors", name, rot))
+ tile(ctx, name, rot)
},
|_, _, _, buff: &mut DataRead| {
// format:
@@ -31,9 +31,9 @@ make_simple!(
make_simple!(
DuctBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>, rot| {
+ |_, name, _, ctx: Option<&RenderingContext>, rot| {
let ctx = ctx.unwrap();
- Some(tile(ctx, "distribution", "ducts", name, rot))
+ tile(ctx, name, rot)
},
|_, _, _, buff: &mut DataRead| {
// format:
@@ -43,35 +43,25 @@ make_simple!(
true
);
-make_simple!(
- JunctionBlock,
- |_, _, _, _, _, _| None,
- |_, _, _, buff: &mut DataRead| { read_directional_item_buffer(buff) },
- false
-);
-
-make_simple!(SimpleDuctBlock, |_, _, name, _, _, rot: Rotation| {
- let mut base = load("distribution/ducts", "duct-base").unwrap().clone();
- let mut top = load("distribution/ducts", name).unwrap().clone();
+make_simple!(JunctionBlock => |_, _, _, buff: &mut DataRead| { read_directional_item_buffer(buff) });
+make_simple!(SimpleDuctBlock, |_, name, _, _, rot: Rotation| {
+ let mut base = load("duct-base");
+ let mut top = load(name);
top.rotate(rot.rotated(false).count());
base.overlay(&top);
- Some(ImageHolder::from(base))
+ base
});
fn draw_stack(
_: &StackConveyor,
- _: &str,
name: &str,
_: Option<&State>,
ctx: Option<&RenderingContext>,
rot: Rotation,
-) -> Option<ImageHolder> {
+) -> ImageHolder {
let ctx = ctx.unwrap();
let mask = mask(ctx, rot, name);
- // clone to not hold lock
- let edge = load("distribution/stack-conveyors", &format!("{name}-edge"))
- .unwrap()
- .clone();
+ let edge = load(&format!("{name}-edge"));
let edgify = |skip, to: &mut RgbaImage| {
for i in 0..4 {
if i == skip {
@@ -82,45 +72,40 @@ fn draw_stack(
to.overlay(&edge);
}
};
- let gimme = |n: u8| {
- load("distribution/stack-conveyors", &format!("{name}-{n}"))
- .unwrap()
- .clone()
- };
+ let gimme = |n: u8| load(&format!("{name}-{n}"));
let empty = ctx.cross[rot.count() as usize].map_or(true, |(v, _)| v.name != name);
// mindustry says fuck this and just draws the arrow convs in schems but im better than that
- Some(ImageHolder::from(
- if rot.mirrored(true, true).mask() == mask && empty && name != "surge-conveyor" {
- // end
- let mut base = gimme(2);
- edgify(rot.mirrored(true, true).rotated(false).count(), &mut base);
- base
- } else if mask == B0000 && empty {
- // single
- let mut base = gimme(0);
- base.rotate(rot.rotated(false).count());
- edgify(5, &mut base);
- base
- } else if mask == B0000 {
- // input
- let mut base = gimme(1);
- edgify(rot.rotated(false).count(), &mut base);
- base
- } else {
- // directional
- let mut base = gimme(0);
- let going = rot.rotated(false).count();
- base.rotate(going);
- for [r, i] in [[3, 0b1000], [0, 0b0100], [1, 0b0010], [2, 0b0001]] {
- if (mask.into_u8() & i) == 0 && (going != r || empty) {
- let mut edge = edge.clone();
- edge.rotate(r);
- base.overlay(&edge);
- }
+
+ if rot.mirrored(true, true).mask() == mask && empty && name != "surge-conveyor" {
+ // end
+ let mut base = gimme(2);
+ edgify(rot.mirrored(true, true).rotated(false).count(), &mut base);
+ base
+ } else if mask == B0000 && empty {
+ // single
+ let mut base = gimme(0);
+ base.rotate(rot.rotated(false).count());
+ edgify(5, &mut base);
+ base
+ } else if mask == B0000 {
+ // input
+ let mut base = gimme(1);
+ edgify(rot.rotated(false).count(), &mut base);
+ base
+ } else {
+ // directional
+ let mut base = gimme(0);
+ let going = rot.rotated(false).count();
+ base.rotate(going);
+ for [r, i] in [[3, 0b1000], [0, 0b0100], [1, 0b0010], [2, 0b0001]] {
+ if (mask.into_u8() & i) == 0 && (going != r || empty) {
+ let mut edge = edge.clone();
+ edge.rotate(r);
+ base.overlay(&edge);
}
- base
- },
- ))
+ }
+ base
+ }
}
make_simple!(
@@ -231,60 +216,43 @@ impl BlockLogic for ItemBlock {
fn draw(
&self,
- _: &str,
name: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
rot: Rotation,
- ) -> Option<ImageHolder> {
- let mut p = load(
- match name {
- "unloader" => "storage",
- "duct-router" | "duct-unloader" => "distribution/ducts",
- _ => "distribution",
- },
- name,
- )
- .unwrap()
- .clone();
+ ) -> ImageHolder {
+ let mut p = load(name);
if let Some(state) = state {
if let Some(s) = Self::get_state(state) {
- let mut top = load(
- match name {
- "unloader" => "storage",
- _ => "distribution",
- },
- match name {
- "unit-cargo-unload-point" => "unit-cargo-unload-point-top",
- _ => "center",
- },
- )
- .unwrap()
- .clone();
+ let mut top = load(match name {
+ "unit-cargo-unload-point" => "unit-cargo-unload-point-top",
+ "unloader" => "unloader-center",
+ _ => "center",
+ });
p.overlay(top.tint(s.color()));
- return Some(ImageHolder::from(p));
+ return p;
}
}
if matches!(name, "unloader" | "unit-cargo-unload-point") {
- return Some(ImageHolder::from(p));
+ return p;
}
- if matches!(name, "duct-unloader" | "duct-router") {
- let mut null = load("distribution/ducts", "top").unwrap().to_owned();
- null.rotate(rot.rotated(false).count());
- if name == "duct-unloader" {
- let mut top = load("distribution/ducts", "duct-unloader-top")
- .unwrap()
- .to_owned();
- // this rotate call could be omitted if rotation == Right to save a clone
- top.rotate(rot.rotated(false).count());
- null.overlay(&top);
- }
- p.overlay(&null);
- Some(ImageHolder::from(p))
+ if name == "duct-router" {
+ let mut arrow = load("top");
+ arrow.rotate(rot.rotated(false).count());
+ p.overlay(&arrow);
+ p
+ } else if name == "duct-unloader" {
+ let mut top = load("duct-unloader-top");
+ top.rotate(rot.rotated(false).count());
+ p.overlay(&top);
+ let mut arrow = load("top");
+ arrow.rotate(rot.rotated(false).count());
+ p.overlay(&arrow);
+ p
} else {
- let mut null = load("distribution", "cross-full").unwrap().clone();
+ let mut null = load("cross-full");
null.overlay(&p);
- Some(ImageHolder::from(null))
+ null
}
}
@@ -486,6 +454,17 @@ impl BlockLogic for BridgeBlock {
}
Ok(())
}
+
+
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
diff --git a/src/block/drills.rs b/src/block/drills.rs
index 5d8ac30..f46788c 100644
--- a/src/block/drills.rs
+++ b/src/block/drills.rs
@@ -5,35 +5,25 @@ use crate::block::*;
make_simple!(
DrillBlock,
- |_, _, name, _, _, rot: Rotation| {
+ |_, name, _, _, rot: Rotation| {
if matches!(name, "large-plasma-bore" | "plasma-bore") {
- let mut base = load("drills", name).unwrap().clone();
- let top = load("drills", &format!("{name}-top")).unwrap();
- if rot == Rotation::Right {
- base.overlay(&top);
- } else {
- let mut top = top.clone();
- top.rotate(rot.rotated(false).count());
- base.overlay(&top);
- }
- return Some(ImageHolder::from(base));
+ let mut base = load(name);
+ let mut top = load(&format!("{name}-top"));
+ top.rotate(rot.rotated(false).count());
+ base.overlay(&top);
+ return base;
}
- Some(ImageHolder::Borrow(load("drills", name).unwrap()))
+ load(name)
},
- |_, _, _, buff: &mut DataRead| { read_drill(buff) }
+ |_, _, _, buff: &mut DataRead| read_drill(buff)
);
make_simple!(ExtractorBlock);
-make_simple!(WallCrafter, |_, _, _, _, _, rot: Rotation| {
- let mut base = load("drills", "cliff-crusher").unwrap().clone();
- let top = load("drills", "cliff-crusher-top").unwrap();
- if rot == Rotation::Right {
- base.overlay(&top);
- } else {
- let mut top = top.clone();
- top.rotate(rot.rotated(false).count());
- base.overlay(&top);
- }
- Some(ImageHolder::from(base))
+make_simple!(WallCrafter, |_, _, _, _, rot: Rotation| {
+ let mut base = load("cliff-crusher");
+ let mut top = load("cliff-crusher-top");
+ top.rotate(rot.rotated(false).count());
+ base.overlay(&top);
+ base
});
make_register! {
diff --git a/src/block/environment.rs b/src/block/environment.rs
index 168bdfd..fd07bc3 100644
--- a/src/block/environment.rs
+++ b/src/block/environment.rs
@@ -11,15 +11,18 @@ macro_rules! register_env {
$($field => EnvironmentBlock::new($size, true, &[]);)*
);
- make_simple!(EnvironmentBlock, |_, _, name, _, _, _| {
+ make_simple!(EnvironmentBlock, |_, name, _, _, _| {
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
match name {
$($field => {
#[allow(clippy::reversed_empty_ranges)]
- if $variations == 1 { Some(ImageHolder::Borrow(load("environment", $field).unwrap())) }
- else if $variations == 0 { return None }
- else { Some(ImageHolder::Borrow(load("environment", &format!("{}{}", $field, rand.next_range(1usize..$variations))).unwrap())) }
- },)*
+ match $variations {
+ 2..=6 => load(&format!("{}{}", $field, rand.next_range(1usize..$variations))),
+ 1 => load($field),
+ 0 => ImageHolder::from(RgbaImage::new($size * 32, $size * 32)),
+ _ => unreachable!(),
+ }
+ },)*
_ => { unreachable!() }
}
});
diff --git a/src/block/liquid.rs b/src/block/liquid.rs
index 0a8a566..a4cadb6 100644
--- a/src/block/liquid.rs
+++ b/src/block/liquid.rs
@@ -13,24 +13,17 @@ use crate::utils::ImageUtils;
make_simple!(LiquidBlock);
make_simple!(
ConduitBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>, rot| {
+ |_, name, _, ctx: Option<&RenderingContext>, rot| {
let ctx = ctx.unwrap();
let mask = mask(ctx, rot, name);
let (index, rot, flip) = mask2rotations(mask, rot);
- let tile = rotations2tile(
- (index, rot, flip),
- "liquid",
- "conduits",
- &format!("{name}-top"),
- );
- let mut bottom = load("liquid", &format!("conduits/conduit-bottom-{index}"))
- .unwrap()
- .to_owned();
+ let tile = rotations2tile((index, rot, flip), &format!("{name}-top"));
+ let mut bottom = load(&format!("conduit-bottom-{index}"));
flrot(flip, rot, &mut bottom);
bottom.tint(image::Rgb([74, 75, 83]));
bottom.overlay(tile.borrow());
// TODO caps. stopped trying bcz too complex
- Some(ImageHolder::from(bottom))
+ bottom
},
true
);
@@ -122,23 +115,22 @@ impl BlockLogic for FluidBlock {
fn draw(
&self,
- category: &str,
name: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
- ) -> Option<ImageHolder> {
- let mut p = load(category, name).unwrap().clone();
+ ) -> ImageHolder {
+ let mut p = load(name);
if let Some(state) = state {
if let Some(s) = Self::get_state(state) {
- let mut top = load("distribution", "center").unwrap().clone();
+ let mut top = load("center");
p.overlay(top.tint(s.color()));
- return Some(ImageHolder::Own(p));
+ return p;
}
}
- let mut null = load("distribution", "cross-full").unwrap().clone();
+ let mut null = load("cross-full");
null.overlay(&p);
- Some(ImageHolder::Own(null))
+ null
}
/// format:
diff --git a/src/block/logic.rs b/src/block/logic.rs
index ce202bc..9595673 100644
--- a/src/block/logic.rs
+++ b/src/block/logic.rs
@@ -12,8 +12,7 @@ use crate::data::{self, CompressError, DataRead, DataWrite};
make_simple!(LogicBlock);
make_simple!(
- MemoryBlock,
- |_, _, _, _, _, _| None,
+ MemoryBlock =>
|_, _, _, buff: &mut DataRead| {
// format:
// - iterate [`u32`]
@@ -147,12 +146,11 @@ impl BlockLogic for CanvasBlock {
/// i thought about drawing the borders and stuff but it felt like too much work
fn draw(
&self,
- c: &str,
n: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
- ) -> Option<ImageHolder> {
+ ) -> ImageHolder {
if let Some(state) = state {
let state = self.clone_state(state);
let p = state.downcast::<RgbImage>().unwrap();
@@ -169,15 +167,18 @@ impl BlockLogic for CanvasBlock {
.into_rgba8()
.scale((self.size as u32 * 32) - 14)
};
- let mut borders = load(c, n).unwrap().to_owned();
+ let mut borders = load(n);
borders.overlay_at(&p, 7, 7);
- return Some(ImageHolder::from(borders));
+ return borders;
}
- Some(ImageHolder::from(RgbaImage::new(
- self.size as u32 * 32,
- self.size as u32 * 32,
- )))
+ let mut def = RgbaImage::new(self.size as u32 * 32, self.size as u32 * 32);
+ for image::Rgba([r, g, b, _]) in def.pixels_mut() {
+ *r = PALETTE[0][0];
+ *g = PALETTE[0][1];
+ *b = PALETTE[0][2];
+ }
+ ImageHolder::from(def)
}
/// format:
@@ -228,6 +229,16 @@ impl BlockLogic for MessageLogic {
Ok(DynData::Empty)
}
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
+
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty | DynData::String(None) => Ok(Some(Self::create_state(String::new()))),
@@ -323,21 +334,19 @@ impl BlockLogic for SwitchLogic {
fn draw(
&self,
_: &str,
- _: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
- ) -> Option<ImageHolder> {
- let base = load("logic", "switch").unwrap();
+ ) -> ImageHolder {
+ let mut base = load("switch");
if let Some(state) = state {
if *Self::get_state(state) {
- let mut base = base.clone();
- let on = load("logic", "switch-on").unwrap();
+ let on = load("switch-on");
base.overlay(&on);
- return Some(ImageHolder::from(base));
+ return base;
}
}
- Some(ImageHolder::from(base))
+ base
}
}
@@ -394,6 +403,16 @@ fn read_decompressed(buff: &mut DataRead) -> Result<ProcessorState, ProcessorDes
impl BlockLogic for ProcessorLogic {
impl_block!();
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
+
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
diff --git a/src/block/mod.rs b/src/block/mod.rs
index a0120ca..c7feb9a 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -59,14 +59,11 @@ pub trait BlockLogic {
#[allow(unused_variables)]
fn draw(
&self,
- category: &str,
name: &str,
state: Option<&State>,
context: Option<&RenderingContext>,
rot: Rotation,
- ) -> Option<ImageHolder> {
- None
- }
+ ) -> ImageHolder;
fn want_context(&self) -> bool {
false
@@ -163,7 +160,6 @@ impl SerializeError {
/// a block. put it in stuff!
pub struct Block {
- category: Cow<'static, str>,
name: Cow<'static, str>,
pub(crate) logic: BoxAccess<'static, dyn BlockLogic + Sync>,
}
@@ -178,15 +174,10 @@ impl Block {
#[must_use]
/// create a new block
pub const fn new(
- category: Cow<'static, str>,
name: Cow<'static, str>,
logic: BoxAccess<'static, dyn BlockLogic + Sync>,
) -> Self {
- Self {
- category,
- name,
- logic,
- }
+ Self { name, logic }
}
/// this blocks name
@@ -209,18 +200,7 @@ impl Block {
context: Option<&RenderingContext>,
rot: Rotation,
) -> ImageHolder {
- if let Some(p) = self
- .logic
- .as_ref()
- .draw(&self.category, &self.name, state, context, rot)
- {
- return p;
- }
- ImageHolder::Own(read(&self.category, &self.name, self.get_size()))
- }
-
- pub fn has_building(&self) -> bool {
- &self.category != "environment"
+ self.logic.as_ref().draw(&self.name, state, context, rot)
}
/// size.
@@ -448,9 +428,6 @@ macro_rules! make_register {
($($field:literal => $logic:expr;)+) => { paste::paste! {
$(
pub static [<$field:snake:upper>]: $crate::block::Block = $crate::block::Block::new(
- std::borrow::Cow::Borrowed(
- const_str::split!(module_path!(), "::")[2]
- ),
std::borrow::Cow::Borrowed($field), $crate::access::Access::Borrowed(&$logic));
)+
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 4a842b7..20485bb 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -13,12 +13,8 @@ use crate::unit;
use super::BlockRegistry;
make_simple!(SimplePayloadBlock);
-make_simple!(
- PayloadConveyor,
- |_, _, _, _, _, _| None,
- read_payload_conveyor
-);
-// make_simple!(PayloadRouter, |_, _, _, _, _, _| None, read_payload_router);
+make_simple!(PayloadConveyor => read_payload_conveyor);
+// make_simple!(PayloadRouter => read_payload_router);
make_register! {
"payload-conveyor" => PayloadConveyor::new(3, false, cost!(Copper: 10, Graphite: 10));
@@ -70,6 +66,16 @@ impl PayloadBlock {
impl BlockLogic for PayloadBlock {
impl_block!();
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
+
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
@@ -189,8 +195,8 @@ fn read_payload(
UNIT => {
let u = buff.read_u8()?;
let Some(_u) = entity_mapping.get(&u) else {
- return Err(ReadError::Expected("map entry"));
- };
+ return Err(ReadError::Expected("map entry"));
+ };
// unit::Type::try_from(u).unwrap_or(unit::Type::Alpha).read(todo!());
}
_ => return Err(ReadError::Expected("0 | 1")),
diff --git a/src/block/power.rs b/src/block/power.rs
index 566a710..529282e 100644
--- a/src/block/power.rs
+++ b/src/block/power.rs
@@ -10,16 +10,15 @@ make_simple!(NuclearGeneratorBlock => |_, _, _, buff: &mut DataRead| read_nuclea
make_simple!(ImpactReactorBlock => |_, _, _, buff: &mut DataRead| read_impact(buff));
make_simple!(HeaterGeneratorBlock => |_, _, _, buff: &mut DataRead| read_heater(buff));
make_simple!(BatteryBlock);
-make_simple!(DiodeBlock, |_, _, _, _, _, rot: Rotation| {
- let base = load("power", "diode").unwrap();
- if rot == Rotation::Right {
- return Some(ImageHolder::from(base));
- }
- let mut base = base.clone();
- let mut top = load("power", "diode-arrow").unwrap().clone();
- top.rotate(rot.rotated(false).count());
- base.overlay(&top);
- Some(ImageHolder::from(base))
+make_simple!(DiodeBlock, |_, _, _, _, rot: Rotation| {
+ let mut base = load("diode");
+ if rot == Rotation::Right {
+ return base;
+ }
+ let mut top = load("diode-arrow");
+ top.rotate(rot.rotated(false).count());
+ base.overlay(&top);
+ base
});
make_register! {
@@ -130,6 +129,16 @@ impl BlockLogic for ConnectorBlock {
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::Point2Array(Self::get_state(state).clone()))
}
+
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
}
#[derive(Debug, Error)]
@@ -207,6 +216,16 @@ impl BlockLogic for LampBlock {
}
}
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
+
fn clone_state(&self, state: &State) -> State {
let state = Self::get_state(state);
Box::new(Self::create_state(*state))
diff --git a/src/block/production.rs b/src/block/production.rs
index d268e8d..5796ca9 100644
--- a/src/block/production.rs
+++ b/src/block/production.rs
@@ -44,8 +44,7 @@ make_register! {
}
make_simple!(
- ProductionBlock,
- |_, _, _, _, _, _| None,
+ ProductionBlock =>
|_, _, _, buff: &mut DataRead| {
// format:
// - progress: `f32`
@@ -56,8 +55,7 @@ make_simple!(
);
make_simple!(
- HeatCrafter,
- |_, _, _, _, _, _| None,
+ HeatCrafter =>
|_, _, _, buff: &mut DataRead| {
// format:
// - progress: `f32`
diff --git a/src/block/simple.rs b/src/block/simple.rs
index 0f87f50..40cacf5 100644
--- a/src/block/simple.rs
+++ b/src/block/simple.rs
@@ -22,7 +22,7 @@ macro_rules! state_impl {
pub(crate) use state_impl;
-/// draw is called with self, category, name, state, context, rotation
+/// draw is called with self, name, state, context, rotation
/// read is called with build, reg, entity_mapping, buff
macro_rules! make_simple {
($name: ident, $draw: expr, $read: expr, $wants_context: literal) => {
@@ -86,13 +86,13 @@ macro_rules! make_simple {
fn draw(
&self,
- category: &str,
name: &str,
state: Option<&crate::block::State>,
context: Option<&crate::data::renderer::RenderingContext>,
rot: crate::block::Rotation,
- ) -> Option<crate::data::renderer::ImageHolder> {
- $draw(self, category, name, state, context, rot)
+ ) -> crate::data::renderer::ImageHolder {
+ #[allow(clippy::redundant_closure_call)]
+ $draw(self, name, state, context, rot)
}
fn want_context(&self) -> bool {
@@ -106,6 +106,7 @@ macro_rules! make_simple {
entity_mapping: &crate::data::map::EntityMapping,
buff: &mut crate::data::DataRead,
) -> Result<(), crate::data::ReadError> {
+ #[allow(clippy::redundant_closure_call)]
$read(build, reg, entity_mapping, buff)
}
}
@@ -120,12 +121,16 @@ macro_rules! make_simple {
crate::block::simple::make_simple!($name, $draw, $read, false);
};
($name: ident => $read: expr) => {
- crate::block::simple::make_simple!($name, |_, _, _, _, _, _| None, $read);
+ crate::block::simple::make_simple!(
+ $name,
+ |m: &Self, n: &str, _, _, _| crate::data::renderer::read(n, m.get_size()),
+ $read
+ );
};
($name: ident) => {
crate::block::simple::make_simple!(
$name,
- |_, _, _, _, _, _| None,
+ |m: &Self, n: &str, _, _, _| crate::data::renderer::read(n, m.get_size()),
|_, _, _, _| Ok(()),
false
);
diff --git a/src/block/turrets.rs b/src/block/turrets.rs
index 061074c..d1617fc 100644
--- a/src/block/turrets.rs
+++ b/src/block/turrets.rs
@@ -39,20 +39,19 @@ make_register! {
fn draw_turret(
me: &impl BlockLogic,
- _: &str,
name: &str,
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
-) -> Option<ImageHolder> {
+) -> ImageHolder {
let path = match name {
"breach" | "diffuse" | "sublimate" | "titan" | "disperse" | "afflict" | "lustre"
- | "scathe" | "malign" | "smite" => format!("bases/reinforced-block-{}", me.get_size()),
- _ => format!("bases/block-{}", me.get_size()),
+ | "scathe" | "malign" | "smite" => format!("reinforced-block-{}", me.get_size()),
+ _ => format!("block-{}", me.get_size()),
};
- let mut base = load("turrets", &path).unwrap().value().clone();
- base.overlay(load("turrets", name).unwrap().value());
- Some(ImageHolder::from(base))
+ let mut base = load(&path);
+ base.overlay(&load(name));
+ base
}
make_simple!(Turret, draw_turret, |_, _, _, buff: &mut DataRead| {
diff --git a/src/block/units.rs b/src/block/units.rs
index 5bcda05..21a7b3b 100644
--- a/src/block/units.rs
+++ b/src/block/units.rs
@@ -33,86 +33,58 @@ use crate::unit;
// )
// }
-make_simple!(
- ConstructorBlock,
- |me: &Self, _, name, _, _, rot: Rotation| {
- let mut base = load("units", name).unwrap().to_owned();
- let times = rot.rotated(false).count();
- {
- let out = load(
- "payload",
- &match name {
- "additive-reconstructor"
- | "multiplicative-reconstructor"
- | "exponential-reconstructor"
- | "tetrative-reconstructor" => format!("factory-out-{}", me.size),
- _ => format!("factory-out-{}-dark", me.size),
- },
- )
- .unwrap();
- if times != 0 {
- let mut out = out.clone();
- out.rotate(times);
- base.overlay(&out);
- } else {
- base.overlay(&out);
- }
- }
- {
- let input = load(
- "payload",
- &match name {
- "additive-reconstructor"
- | "multiplicative-reconstructor"
- | "exponential-reconstructor"
- | "tetrative-reconstructor" => format!("factory-in-{}", me.size),
- _ => format!("factory-in-{}-dark", me.size),
- },
- )
- .unwrap();
- if times != 0 {
- let mut input = input.clone();
- input.rotate(times);
- base.overlay(&input);
- } else {
- base.overlay(&input);
- }
- }
- // TODO: the context cross is too small
- // for i in 0..4u8 {
- // if let Some((b, rot)) = dbg!(ctx.cross[i as usize]) {
- // if rot.mirrored(true, true) != ctx.rotation && match rot {
- // Rotation::Up => i == 3,
- // Rotation::Right => i == 4,
- // Rotation::Down => i == 0,
- // Rotation::Left => i == 2,
- // } && is_pay(b.name())
- // {
- // let r = unsafe { std::mem::transmute::<u8, Rotation>(i) }
- // .mirrored(true, true)
- // .rotated(false);
- // let mut input = input.clone();
- // input.rotate(r.count());
- // base.overlay(&input);
- // }
- // }
- // }
-
- base.overlay(&load("units", &format!("{name}-top")).unwrap());
-
- if matches!(name, "mech-assembler" | "tank-assembler" | "ship-assembler") {
- let side = load("units", &format!("{name}-side")).unwrap();
- if times != 0 {
- let mut side = side.clone();
- side.rotate(times);
- base.overlay(&side);
- } else {
- base.overlay(&side);
- }
- }
- Some(ImageHolder::from(base))
+make_simple!(ConstructorBlock, |me: &Self, name, _, _, rot: Rotation| {
+ let mut base = load(name);
+ let times = rot.rotated(false).count();
+
+ let mut out = load(&match name {
+ "additive-reconstructor"
+ | "multiplicative-reconstructor"
+ | "exponential-reconstructor"
+ | "tetrative-reconstructor" => format!("factory-out-{}", me.size),
+ _ => format!("factory-out-{}-dark", me.size),
+ });
+ out.rotate(times);
+ base.overlay(&out);
+
+ let mut input = load(&match name {
+ "additive-reconstructor"
+ | "multiplicative-reconstructor"
+ | "exponential-reconstructor"
+ | "tetrative-reconstructor" => format!("factory-in-{}", me.size),
+ _ => format!("factory-in-{}-dark", me.size),
+ });
+ input.rotate(times);
+ base.overlay(&input);
+
+ // TODO: the context cross is too small
+ // for i in 0..4u8 {
+ // if let Some((b, rot)) = dbg!(ctx.cross[i as usize]) {
+ // if rot.mirrored(true, true) != ctx.rotation && match rot {
+ // Rotation::Up => i == 3,
+ // Rotation::Right => i == 4,
+ // Rotation::Down => i == 0,
+ // Rotation::Left => i == 2,
+ // } && is_pay(b.name())
+ // {
+ // let r = unsafe { std::mem::transmute::<u8, Rotation>(i) }
+ // .mirrored(true, true)
+ // .rotated(false);
+ // let mut input = input.clone();
+ // input.rotate(r.count());
+ // base.overlay(&input);
+ // }
+ // }
+ // }
+
+ base.overlay(&load(&format!("{name}-top")));
+ if matches!(name, "mech-assembler" | "tank-assembler" | "ship-assembler") {
+ let mut side = load(&format!("{name}-side"));
+ side.rotate(times);
+ base.overlay(&side);
}
-);
+ base
+});
make_simple!(UnitBlock);
make_simple!(RepairTurret => |_, _, _, buff: &mut DataRead| {
buff.skip(4) // rotation: [`f32`]
@@ -231,45 +203,25 @@ impl BlockLogic for AssemblerBlock {
fn draw(
&self,
- _: &str,
name: &str,
_: Option<&State>,
_: Option<&RenderingContext>,
rot: Rotation,
- ) -> Option<ImageHolder> {
- let mut base = load("units", name).unwrap().to_owned();
- let out = load(
- "payload",
- match name {
- "ground-factory" | "air-factory" | "naval-factory" => "factory-out-3",
- _ => "factory-out-3-dark",
- },
- )
- .unwrap();
- let times = rot.rotated(false).count();
- if times != 0 {
- let mut out = out.clone();
- out.rotate(times);
- base.overlay(&out);
- } else {
- base.overlay(&out);
- }
- base.overlay(
- &load(
- match name {
- "ground-factory" | "air-factory" | "naval-factory" => "payload",
- _ => "units",
- },
- &match name {
- "ground-factory" | "air-factory" | "naval-factory" => {
- format!("factory-top-{}", self.size)
- }
- _ => format!("{name}-top"),
- },
- )
- .unwrap()
- );
- Some(ImageHolder::from(base))
+ ) -> ImageHolder {
+ let mut base = load(name);
+ let mut out = load(match name {
+ "ground-factory" | "air-factory" | "naval-factory" => "factory-out-3",
+ _ => "factory-out-3-dark",
+ });
+ out.rotate(rot.rotated(false).count());
+ base.overlay(&out);
+ base.overlay(&load(&match name {
+ "ground-factory" | "air-factory" | "naval-factory" => {
+ format!("factory-top-{}", self.size)
+ }
+ _ => format!("{name}-top"),
+ }));
+ base
}
/// format:
diff --git a/src/block/walls.rs b/src/block/walls.rs
index 59967ac..6b782a7 100644
--- a/src/block/walls.rs
+++ b/src/block/walls.rs
@@ -2,31 +2,26 @@
use crate::block::simple::*;
use crate::block::*;
use crate::data::dynamic::DynType;
-use crate::data::renderer::{load, read_with, ImageHolder, TOP};
+use crate::data::renderer::{load, read_with};
use tinyrand::{Rand, RandRange, Seeded, StdRand};
use tinyrand_std::clock_seed::ClockSeed;
-make_simple!(WallBlock, |_, _, name, _, _, _| {
+make_simple!(WallBlock, |_, name, _, _, _| {
macro_rules! pick {
($name: literal => load $n: literal) => {{
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
- Some(ImageHolder::from(load(
- "walls",
- &format!("{}{}", $name, rand.next_range(1usize..$n)),
- )))
+ load(&format!("{}{}", $name, rand.next_range(1usize..$n)))
}};
}
match name {
"thruster" => {
- const SFX: &[&str; 1] = &[TOP];
- Some(ImageHolder::from(read_with(
- "turrets", "thruster", SFX, 4u32,
- )))
+ const SFX: &[&str; 1] = &["-top"];
+ read_with("thruster", SFX, 4u32)
}
"scrap-wall" => pick!("scrap-wall" => load 5),
"scrap-wall-large" => pick!("scrap-wall-large" => load 3),
"scrap-wall-huge" => pick!("scrap-wall-huge" => load 3),
- _ => Some(ImageHolder::from(load("walls", name))),
+ _ => load(name),
}
});
@@ -86,6 +81,16 @@ impl DoorBlock {
impl BlockLogic for DoorBlock {
impl_block!();
+ fn draw(
+ &self,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> ImageHolder {
+ read(name, self.size)
+ }
+
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Boolean(false))
}
diff --git a/src/data/autotile.rs b/src/data/autotile.rs
index 3075d4f..76b0e5b 100644
--- a/src/data/autotile.rs
+++ b/src/data/autotile.rs
@@ -2,7 +2,6 @@ use super::renderer::*;
use super::GridPos;
use crate::block::{Block, Rotation};
use bobbin_bits::U4;
-use image::imageops::{flip_horizontal_in_place as flip_h, flip_vertical_in_place as flip_v};
#[cfg(test)]
macro_rules! dir {
@@ -93,19 +92,8 @@ fn print_crosses(v: Vec<Cross<'_>>, height: usize) -> String {
s
}
-pub fn tile(
- ctx: &RenderingContext<'_>,
- category: &str,
- subcategory: &str,
- name: &str,
- rot: Rotation,
-) -> ImageHolder {
- rotations2tile(
- mask2rotations(mask(ctx, rot, name), rot),
- category,
- subcategory,
- name,
- )
+pub fn tile(ctx: &RenderingContext<'_>, name: &str, rot: Rotation) -> ImageHolder {
+ rotations2tile(mask2rotations(mask(ctx, rot, name), rot), name)
}
pub fn mask2rotations(mask: U4, rot: Rotation) -> (u8, u8, u8) {
@@ -221,27 +209,19 @@ pub fn mask2rotations(mask: U4, rot: Rotation) -> (u8, u8, u8) {
pub const FLIP_X: u8 = 1;
pub const FLIP_Y: u8 = 2;
-pub fn flrot(flip: u8, rot: u8, with: &mut RgbaImage) {
+pub fn flrot(flip: u8, rot: u8, with: &mut ImageHolder) {
if (flip & FLIP_X) != 0 {
- flip_h(with);
+ with.flip_h();
}
if (flip & FLIP_Y) != 0 {
- flip_v(with);
+ with.flip_v();
}
with.rotate(rot);
- // let mut from = from.own();
- // from.rotate(rot);
- // ImageHolder::from(from)
}
/// TODO figure out if a flip is cheaper than a rotate_270
-pub fn rotations2tile(
- (index, rot, flip): (u8, u8, u8),
- category: &str,
- subcategory: &str,
- name: &str,
-) -> ImageHolder {
- let mut p = ImageHolder::from(load(category, &format!("{subcategory}/{name}-{index}")));
+pub fn rotations2tile((index, rot, flip): (u8, u8, u8), name: &str) -> ImageHolder {
+ let mut p = load(&format!("{name}-{index}"));
flrot(flip, rot, p.borrow_mut());
p
}
diff --git a/src/data/map.rs b/src/data/map.rs
index f32b039..7478a66 100644
--- a/src/data/map.rs
+++ b/src/data/map.rs
@@ -122,14 +122,6 @@ impl<'l> Tile<'l> {
self.build.as_ref()
}
- /// check if this tile contains a building.
- pub fn has_building(&self) -> bool {
- if let Some(b) = &self.build {
- return b.block.has_building();
- }
- false
- }
-
/// size of this tile
///
/// ._.
@@ -143,16 +135,16 @@ impl<'l> Tile<'l> {
}
pub fn floor_image(&self, context: Option<&RenderingContext>) -> ImageHolder {
- let mut i = self.floor.image(None, context, Rotation::Up).own();
+ let mut i = self.floor.image(None, context, Rotation::Up);
if let Some(ore) = self.ore {
i.overlay(ore.image(None, context, Rotation::Up).borrow());
}
- ImageHolder::from(i)
+ i
}
pub fn build_image(&self, context: Option<&RenderingContext>) -> ImageHolder {
// building covers floore
- let Some(b)= &self.build else {
+ let Some(b) = &self.build else {
unreachable!();
};
b.image(context)
diff --git a/src/data/renderer.rs b/src/data/renderer.rs
index e20b75a..cfd9c9b 100644
--- a/src/data/renderer.rs
+++ b/src/data/renderer.rs
@@ -1,13 +1,9 @@
//! schematic drawing
use dashmap::mapref::one::Ref;
use dashmap::DashMap;
-use image::codecs::png::PngDecoder;
pub(crate) use image::{DynamicImage, RgbaImage};
-use std::io::{BufReader, Cursor};
use std::ops::{Deref, DerefMut};
-use std::path::{Path, PathBuf};
-use std::sync::OnceLock;
-use zip::ZipArchive;
+use std::sync::LazyLock;
pub(crate) use super::autotile::*;
use crate::block::environment::METAL_FLOOR;
@@ -20,13 +16,16 @@ pub(crate) use std::borrow::{Borrow, BorrowMut};
use super::schematic::Schematic;
use super::GridPos;
-type Cache = DashMap<PathBuf, RgbaImage>;
-fn cache() -> &'static Cache {
- CACHE.get_or_init(Cache::new)
-}
+type Cache = DashMap<String, RgbaImage>;
+include!(concat!(env!("OUT_DIR"), "/asset")); // put function from here
+static CACHE: LazyLock<Cache> = LazyLock::new(|| {
+ let mut map = Cache::new();
+ put(&mut map);
+ map
+});
pub enum ImageHolder {
- Borrow(Ref<'static, PathBuf, RgbaImage>),
+ Borrow(Ref<'static, String, RgbaImage>),
Own(RgbaImage),
}
@@ -37,6 +36,14 @@ impl ImageHolder {
Self::Borrow(x) => x.clone(),
}
}
+
+ pub fn rotate(&mut self, times: u8) {
+ if times == 0 {
+ return;
+ }
+ let p: &mut RgbaImage = self.borrow_mut();
+ p.rotate(times);
+ }
}
impl Borrow<RgbaImage> for ImageHolder {
@@ -73,14 +80,8 @@ impl DerefMut for ImageHolder {
}
}
-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 {
+impl From<Ref<'static, String, RgbaImage>> for ImageHolder {
+ fn from(value: Ref<'static, String, RgbaImage>) -> Self {
Self::Borrow(value)
}
}
@@ -91,69 +92,36 @@ impl From<RgbaImage> for ImageHolder {
}
}
-static CACHE: OnceLock<Cache> = OnceLock::new();
-pub(crate) fn load(category: &str, name: &str) -> Option<Ref<'static, PathBuf, RgbaImage>> {
- let key = Path::new(category).join(name);
- use dashmap::mapref::entry::Entry::*;
- Some(match cache().entry(key) {
- Occupied(v) => v.into_ref().downgrade(),
- Vacant(entry) => {
- let mut p = Path::new("blocks").join(category).join(name);
- p.set_extension("png");
- let Some(i) = load_raw(p) else {
- return None;
- };
- entry.insert(i).downgrade()
- }
- })
+pub(crate) fn try_load(name: &str) -> Option<Ref<'static, String, RgbaImage>> {
+ let key = name.to_string();
+ CACHE.get(&key)
}
-#[cfg(not(unix))]
-const P: &str = "target/out";
-#[cfg(unix)]
-const P: &str = "/tmp/mindus-tmp";
-
-fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> {
- let f = std::fs::File::open(Path::new(P).join(f)).ok()?;
- let r = PngDecoder::new(BufReader::new(f)).unwrap();
- let p = DynamicImage::from_decoder(r).unwrap().into_rgba8();
- assert!(p.width() != 0);
- assert!(p.height() != 0);
- Some(p)
+pub(crate) fn load(name: &str) -> ImageHolder {
+ ImageHolder::from(
+ try_load(name)
+ .ok_or_else(|| format!("failed to load {name}"))
+ .unwrap(),
+ )
}
-fn load_zip() {
- if !Path::new(P).exists() {
- let mut zip = ZipArchive::new(Cursor::new(
- include_bytes!(concat!(env!("OUT_DIR"), "/asset")).to_vec(),
- ))
- .unwrap();
- zip.extract(P).unwrap();
- }
-}
-pub const TOP: &str = "-top";
const SUFFIXES: &[&str; 9] = &[
- "-bottom", "-mid", "-base", "", "-left", "-right", TOP, "-over", "-team",
+ "-bottom", "-mid", "-base", "", "-left", "-right", "-top", "-over", "-team",
];
-pub(crate) fn read<S>(category: &str, name: &str, size: S) -> RgbaImage
+pub(crate) fn read<S>(name: &str, size: S) -> ImageHolder
where
S: Into<u32> + Copy,
{
- read_with(category, name, SUFFIXES, size)
+ read_with(name, SUFFIXES, size)
}
-pub(crate) fn read_with<S>(
- category: &str,
- name: &str,
- suffixes: &'static [&'static str],
- size: S,
-) -> RgbaImage
+pub(crate) fn read_with<S>(name: &str, suffixes: &'static [&'static str], size: S) -> ImageHolder
where
S: Into<u32> + Copy,
{
let mut c = RgbaImage::new(size.into() * 32, size.into() * 32);
for suffix in suffixes {
- if let Some(p) = load(category, &format!("{name}{suffix}")) {
+ if let Some(p) = try_load(&format!("{name}{suffix}")) {
if suffix == &"-team" {
c.overlay(p.clone().tint(SHARDED.color()));
continue;
@@ -161,7 +129,7 @@ where
c.overlay(&p);
}
}
- c
+ ImageHolder::from(c)
}
/// trait for renderable objects
@@ -180,7 +148,6 @@ impl Renderable for Schematic<'_> {
/// let output /*: RgbaImage */ = s.render();
/// ```
fn render(&self) -> RgbaImage {
- load_zip();
// fill background
let mut bg = RgbaImage::new(
((self.width + 2) * 32) as u32,
@@ -225,7 +192,6 @@ impl Renderable for Schematic<'_> {
impl Renderable for Map<'_> {
fn render(&self) -> RgbaImage {
- load_zip();
let scale = if self.width + self.height < 2000 {
8
} else {
@@ -291,7 +257,6 @@ impl Renderable for Map<'_> {
#[test]
fn all_blocks() {
- load_zip();
use crate::block::content::Type;
use crate::content::Content;
let reg = crate::block::build_registry();
@@ -308,7 +273,7 @@ fn all_blocks() {
continue;
}
- let t = reg.get(dbg!(t.get_name())).unwrap();
+ let t = reg.get(t.get_name()).unwrap();
t.image(
None,
Some(&RenderingContext {
diff --git a/src/lib.rs b/src/lib.rs
index 59698ff..c2296f7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
//! crate for dealing with mindustry
+#![feature(lazy_cell, slice_as_chunks)]
mod access;
pub mod block;
mod content;
diff --git a/src/utils/image.rs b/src/utils/image.rs
index 1b54d61..6abe314 100644
--- a/src/utils/image.rs
+++ b/src/utils/image.rs
@@ -1,5 +1,5 @@
use fast_image_resize as fr;
-use image::{Rgb, Rgba, RgbaImage};
+use image::{imageops, Rgb, Rgba, RgbaImage};
use std::num::NonZeroU32;
pub trait ImageUtils {
@@ -13,6 +13,10 @@ pub trait ImageUtils {
fn overlay_at(&mut self, with: &Self, x: u32, y: u32) -> &mut Self;
/// rotate
fn rotate(&mut self, times: u8) -> &mut Self;
+ /// flip along the horizontal axis
+ fn flip_h(&mut self) -> &mut Self;
+ /// flip along the vertical axis
+ fn flip_v(&mut self) -> &mut Self;
/// shadow
#[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
fn shadow(&mut self) -> &mut Self;
@@ -78,12 +82,12 @@ impl ImageUtils for RgbaImage {
let local = std::mem::take(self);
let mut own = local.into_raw();
let other = with.as_raw();
- if own.len() % 4 != 0 || other.len() % 4 != 0 {
- unsafe { std::hint::unreachable_unchecked(); }
- }
- for (i, other_pixels) in other.chunks_exact(4).enumerate() {
+ for (i, other_pixels) in unsafe { other.as_chunks_unchecked::<4>() }
+ .iter()
+ .enumerate()
+ {
if other_pixels[3] > 128 {
- let own_pixels = &mut own[i * 4..i * 4 + 4];
+ let own_pixels = unsafe { own.get_unchecked_mut(i * 4..i * 4 + 4) };
own_pixels.copy_from_slice(other_pixels);
}
}
@@ -150,4 +154,16 @@ impl ImageUtils for RgbaImage {
}
self
}
+
+ #[inline(always)]
+ fn flip_h(&mut self) -> &mut Self {
+ imageops::flip_horizontal_in_place(self);
+ self
+ }
+
+ #[inline(always)]
+ fn flip_v(&mut self) -> &mut Self {
+ imageops::flip_vertical_in_place(self);
+ self
+ }
}