mindustry logic execution, map- and schematic- parsing and rendering
resize aot
bendn 2023-07-29
parent 1a86c06 · commit fc0158e
-rw-r--r--Cargo.toml3
-rw-r--r--build.rs59
-rw-r--r--src/block/distribution.rs55
-rw-r--r--src/block/drills.rs14
-rw-r--r--src/block/environment.rs8
-rw-r--r--src/block/liquid.rs17
-rw-r--r--src/block/logic.rs45
-rw-r--r--src/block/mod.rs6
-rw-r--r--src/block/payload.rs3
-rw-r--r--src/block/power.rs12
-rw-r--r--src/block/simple.rs7
-rw-r--r--src/block/turrets.rs5
-rw-r--r--src/block/units.rs76
-rw-r--r--src/block/walls.rs11
-rw-r--r--src/data/autotile.rs8
-rw-r--r--src/data/map.rs14
-rw-r--r--src/data/renderer.rs112
-rw-r--r--src/data/schematic.rs9
-rw-r--r--src/lib.rs2
-rw-r--r--src/utils/image.rs31
20 files changed, 296 insertions, 201 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 4c62287..8b6af4d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "1.4.6"
+version = "1.4.7"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)"
authors = [
@@ -21,7 +21,6 @@ image = { version = "0.24", features = ["png"], default-features = false }
color-hex = "0.2"
tinyrand = "0.5"
tinyrand-std = "0.5"
-fast_image_resize = "2.7"
thiserror = "1.0"
bobbin-bits = "0.1"
blurslice = { version = "0.1", optional = true }
diff --git a/build.rs b/build.rs
index f772a82..a023dc5 100644
--- a/build.rs
+++ b/build.rs
@@ -13,25 +13,60 @@ fn main() {
println!("cargo:rerun-if-changed=assets/");
println!("cargo:rerun-if-changed=build.rs");
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"phf::phf_map! {").unwrap();
- let mut s = String::new(); // idk write_all / write wasnt working
+ let o = Path::new(&o);
+ let mut full = File::create(o.join("full.rs")).unwrap();
+ // let mut half = File::create(o.join("half.rs")).unwrap();
+ let mut quar = File::create(o.join("quar.rs")).unwrap();
+ let mut eigh = File::create(o.join("eigh.rs")).unwrap();
+ let mut n = 2usize;
+ for mut f in [&full, &eigh, &quar] {
+ f.write_all(b"phf::phf_map! {\n").unwrap();
+ }
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!("\t\"{path}\" => r!(LazyLock::new(|| RgbaImage::from_vec({x}, {y}, include_bytes!(concat!(env!(\"OUT_DIR\"), \"/{n}\")).to_vec()).unwrap())),\n");
+ macro_rules! writ {
+ ($ext:ident / $scale:literal) => {
+ let mut buf = File::create(o.join(n.to_string() + "-" + stringify!($ext))).unwrap();
+ let new = if $scale == 1 {
+ p.clone()
+ } else {
+ // boulders
+ let (mx, my) = if p.width() + p.height() == 48+48 {
+ (32, 32)
+ // vents
+ } else if path.contains("vent") {
+ (32, 32)
+ } else {
+ (p.height(), p.width())
+ };
+ image::imageops::resize(
+ &p,
+ mx / $scale,
+ my / $scale,
+ image::imageops::Nearest,
+ )
+ };
+ let x = new.width();
+ let y = new.height();
+ buf.write_all(&new.into_raw()).unwrap();
+ writeln!($ext,
+ r#" "{path}" => r!(LazyLock::new(|| RgbaImage::from_vec({x}, {y}, include_bytes!(concat!(env!("OUT_DIR"), "/{n}-{}")).to_vec()).unwrap())),"#,
+ stringify!($ext)
+ ).unwrap();
+ };
+ }
+ writ!(full / 1);
+ // writ!(half + 0.5);
+ writ!(quar / 4);
+ writ!(eigh / 8);
n += 1;
}
}
- f.write_all(s.as_bytes()).unwrap();
- f.write_all(b"}").unwrap();
+ for mut f in [full, eigh, quar] {
+ f.write_all(b"}").unwrap();
+ }
}
diff --git a/src/block/distribution.rs b/src/block/distribution.rs
index 06ce5b8..aaa9528 100644
--- a/src/block/distribution.rs
+++ b/src/block/distribution.rs
@@ -8,10 +8,7 @@ use crate::item;
make_simple!(
ConveyorBlock,
- |_, name, _, ctx: Option<&RenderingContext>, rot: Rotation| {
- let ctx = ctx.unwrap(); // we set want_context to true
- tile(ctx, name, rot)
- },
+ |_, name, _, ctx: Option<&RenderingContext>, rot, s| tile(ctx.unwrap(), name, rot, s),
|_, _, _, buff: &mut DataRead| {
// format:
// - amount: `i32`
@@ -31,10 +28,7 @@ make_simple!(
make_simple!(
DuctBlock,
- |_, name, _, ctx: Option<&RenderingContext>, rot| {
- let ctx = ctx.unwrap();
- tile(ctx, name, rot)
- },
+ |_, name, _, ctx: Option<&RenderingContext>, rot, s| tile(ctx.unwrap(), name, rot, s),
|_, _, _, buff: &mut DataRead| {
// format:
// - rec_dir: `i8`
@@ -43,10 +37,10 @@ make_simple!(
true
);
-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);
+make_simple!(JunctionBlock => |_, _, _, buff| { read_directional_item_buffer(buff) });
+make_simple!(SimpleDuctBlock, |_, name, _, _, rot: Rotation, s| {
+ let mut base = load("duct-base", s);
+ let mut top = load(name, s);
top.rotate(rot.rotated(false).count());
base.overlay(&top);
base
@@ -58,10 +52,11 @@ fn draw_stack(
_: Option<&State>,
ctx: Option<&RenderingContext>,
rot: Rotation,
+ s: Scale,
) -> ImageHolder {
let ctx = ctx.unwrap();
let mask = mask(ctx, rot, name);
- let edge = load(&format!("{name}-edge"));
+ let edge = load(&format!("{name}-edge"), s);
let edgify = |skip, to: &mut RgbaImage| {
for i in 0..4 {
if i == skip {
@@ -72,7 +67,7 @@ fn draw_stack(
to.overlay(&edge);
}
};
- let gimme = |n: u8| load(&format!("{name}-{n}"));
+ let gimme = |n: u8| load(&format!("{name}-{n}"), s);
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
@@ -220,16 +215,20 @@ impl BlockLogic for ItemBlock {
state: Option<&State>,
_: Option<&RenderingContext>,
rot: Rotation,
+ s: Scale,
) -> ImageHolder {
- let mut p = load(name);
+ let mut p = load(name, s);
if let Some(state) = state {
- if let Some(s) = Self::get_state(state) {
- 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()));
+ if let Some(item) = Self::get_state(state) {
+ let mut top = load(
+ match name {
+ "unit-cargo-unload-point" => "unit-cargo-unload-point-top",
+ "unloader" => "unloader-center",
+ _ => "center",
+ },
+ s,
+ );
+ p.overlay(top.tint(item.color()));
return p;
}
}
@@ -237,20 +236,20 @@ impl BlockLogic for ItemBlock {
return p;
}
if name == "duct-router" {
- let mut arrow = load("top");
+ let mut arrow = load("top", s);
arrow.rotate(rot.rotated(false).count());
p.overlay(&arrow);
p
} else if name == "duct-unloader" {
- let mut top = load("duct-unloader-top");
+ let mut top = load("duct-unloader-top", s);
top.rotate(rot.rotated(false).count());
p.overlay(&top);
- let mut arrow = load("top");
+ let mut arrow = load("top", s);
arrow.rotate(rot.rotated(false).count());
p.overlay(&arrow);
p
} else {
- let mut null = load("cross-full");
+ let mut null = load("cross-full", s);
null.overlay(&p);
null
}
@@ -455,15 +454,15 @@ impl BlockLogic for BridgeBlock {
Ok(())
}
-
fn draw(
&self,
name: &str,
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
}
diff --git a/src/block/drills.rs b/src/block/drills.rs
index f46788c..8bd6960 100644
--- a/src/block/drills.rs
+++ b/src/block/drills.rs
@@ -5,22 +5,22 @@ use crate::block::*;
make_simple!(
DrillBlock,
- |_, name, _, _, rot: Rotation| {
+ |_, name, _, _, rot: Rotation, s| {
if matches!(name, "large-plasma-bore" | "plasma-bore") {
- let mut base = load(name);
- let mut top = load(&format!("{name}-top"));
+ let mut base = load(name, s);
+ let mut top = load(&format!("{name}-top"), s);
top.rotate(rot.rotated(false).count());
base.overlay(&top);
return base;
}
- load(name)
+ load(name, s)
},
|_, _, _, buff: &mut DataRead| read_drill(buff)
);
make_simple!(ExtractorBlock);
-make_simple!(WallCrafter, |_, _, _, _, rot: Rotation| {
- let mut base = load("cliff-crusher");
- let mut top = load("cliff-crusher-top");
+make_simple!(WallCrafter, |_, _, _, _, rot: Rotation, s| {
+ let mut base = load("cliff-crusher", s);
+ let mut top = load("cliff-crusher-top", s);
top.rotate(rot.rotated(false).count());
base.overlay(&top);
base
diff --git a/src/block/environment.rs b/src/block/environment.rs
index ef38266..5e73499 100644
--- a/src/block/environment.rs
+++ b/src/block/environment.rs
@@ -11,15 +11,15 @@ macro_rules! register_env {
$($field => EnvironmentBlock::new($size, true, &[]);)*
);
- make_simple!(EnvironmentBlock, |_, name, _, _, _| {
+ make_simple!(EnvironmentBlock, |_, name, _, _, _, s| {
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
match name {
$($field => {
#[allow(clippy::reversed_empty_ranges)]
match $variations {
- 2..=6 => load(&format!("{}{}", $field, rand.next_range(1usize..$variations))),
- 1 => load($field),
- 0 => ImageHolder::from(RgbaImage::new($size * 32, $size * 32)),
+ 2..=6 => load(&format!("{}{}", $field, rand.next_range(1usize..$variations)), s),
+ 1 => load($field, s),
+ 0 => ImageHolder::from(RgbaImage::new(s * $size, s * $size)),
_ => unreachable!(),
}
},)*
diff --git a/src/block/liquid.rs b/src/block/liquid.rs
index a4cadb6..4bb8106 100644
--- a/src/block/liquid.rs
+++ b/src/block/liquid.rs
@@ -13,12 +13,12 @@ use crate::utils::ImageUtils;
make_simple!(LiquidBlock);
make_simple!(
ConduitBlock,
- |_, name, _, ctx: Option<&RenderingContext>, rot| {
+ |_, name, _, ctx: Option<&RenderingContext>, rot, s| {
let ctx = ctx.unwrap();
let mask = mask(ctx, rot, name);
let (index, rot, flip) = mask2rotations(mask, rot);
- let tile = rotations2tile((index, rot, flip), &format!("{name}-top"));
- let mut bottom = load(&format!("conduit-bottom-{index}"));
+ let tile = rotations2tile((index, rot, flip), &format!("{name}-top"), s);
+ let mut bottom = load(&format!("conduit-bottom-{index}"), s);
flrot(flip, rot, &mut bottom);
bottom.tint(image::Rgb([74, 75, 83]));
bottom.overlay(tile.borrow());
@@ -119,16 +119,17 @@ impl BlockLogic for FluidBlock {
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- let mut p = load(name);
+ let mut p = load(name, s);
if let Some(state) = state {
- if let Some(s) = Self::get_state(state) {
- let mut top = load("center");
- p.overlay(top.tint(s.color()));
+ if let Some(liq) = Self::get_state(state) {
+ let mut top = load("center", s);
+ p.overlay(top.tint(liq.color()));
return p;
}
}
- let mut null = load("cross-full");
+ let mut null = load("cross-full", s);
null.overlay(&p);
null
}
diff --git a/src/block/logic.rs b/src/block/logic.rs
index 9595673..b2f2fb1 100644
--- a/src/block/logic.rs
+++ b/src/block/logic.rs
@@ -150,29 +150,33 @@ impl BlockLogic for CanvasBlock {
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
if let Some(state) = state {
let state = self.clone_state(state);
let p = state.downcast::<RgbImage>().unwrap();
- // SAFETY: canvas_size cannot be 0, so width & height musnt be 0, and size cannot be 0
- let p = unsafe {
- DynamicImage::from(
- RgbImage::from_raw(
- self.canvas_size as u32,
- self.canvas_size as u32,
- p.into_raw(),
- )
- .unwrap(),
- )
- .into_rgba8()
- .scale((self.size as u32 * 32) - 14)
+ let offset = match s {
+ Scale::Full => 7,
+ // Scale::Half => 3,
+ Scale::Quarter => 2,
+ Scale::Eigth => 1,
};
- let mut borders = load(n);
- borders.overlay_at(&p, 7, 7);
+ let p = DynamicImage::from(
+ RgbImage::from_raw(
+ self.canvas_size as u32,
+ self.canvas_size as u32,
+ p.into_raw(),
+ )
+ .unwrap(),
+ )
+ .into_rgba8()
+ .scale((s * self.size as u32) - offset * 2);
+ let mut borders = load(n, s);
+ borders.overlay_at(&p, offset, offset);
return borders;
}
- let mut def = RgbaImage::new(self.size as u32 * 32, self.size as u32 * 32);
+ let mut def = RgbaImage::new(s * self.size as u32, s * self.size as u32);
for image::Rgba([r, g, b, _]) in def.pixels_mut() {
*r = PALETTE[0][0];
*g = PALETTE[0][1];
@@ -235,8 +239,9 @@ impl BlockLogic for MessageLogic {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
@@ -337,11 +342,12 @@ impl BlockLogic for SwitchLogic {
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- let mut base = load("switch");
+ let mut base = load("switch", s);
if let Some(state) = state {
if *Self::get_state(state) {
- let on = load("switch-on");
+ let on = load("switch-on", s);
base.overlay(&on);
return base;
}
@@ -409,8 +415,9 @@ impl BlockLogic for ProcessorLogic {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
diff --git a/src/block/mod.rs b/src/block/mod.rs
index c7feb9a..c1440c5 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -63,6 +63,7 @@ pub trait BlockLogic {
state: Option<&State>,
context: Option<&RenderingContext>,
rot: Rotation,
+ scale: Scale,
) -> ImageHolder;
fn want_context(&self) -> bool {
@@ -199,8 +200,11 @@ impl Block {
state: Option<&State>,
context: Option<&RenderingContext>,
rot: Rotation,
+ scale: Scale,
) -> ImageHolder {
- self.logic.as_ref().draw(&self.name, state, context, rot)
+ self.logic
+ .as_ref()
+ .draw(&self.name, state, context, rot, scale)
}
/// size.
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 20485bb..c9961a1 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -72,8 +72,9 @@ impl BlockLogic for PayloadBlock {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
diff --git a/src/block/power.rs b/src/block/power.rs
index 529282e..2ef86f5 100644
--- a/src/block/power.rs
+++ b/src/block/power.rs
@@ -10,12 +10,12 @@ 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 mut base = load("diode");
+make_simple!(DiodeBlock, |_, _, _, _, rot: Rotation, s| {
+ let mut base = load("diode",s);
if rot == Rotation::Right {
return base;
}
- let mut top = load("diode-arrow");
+ let mut top = load("diode-arrow",s);
top.rotate(rot.rotated(false).count());
base.overlay(&top);
base
@@ -136,8 +136,9 @@ impl BlockLogic for ConnectorBlock {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
}
@@ -222,8 +223,9 @@ impl BlockLogic for LampBlock {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
fn clone_state(&self, state: &State) -> State {
diff --git a/src/block/simple.rs b/src/block/simple.rs
index 40cacf5..20a9f22 100644
--- a/src/block/simple.rs
+++ b/src/block/simple.rs
@@ -90,9 +90,10 @@ macro_rules! make_simple {
state: Option<&crate::block::State>,
context: Option<&crate::data::renderer::RenderingContext>,
rot: crate::block::Rotation,
+ scale: crate::data::renderer::Scale,
) -> crate::data::renderer::ImageHolder {
#[allow(clippy::redundant_closure_call)]
- $draw(self, name, state, context, rot)
+ $draw(self, name, state, context, rot, scale)
}
fn want_context(&self) -> bool {
@@ -123,14 +124,14 @@ macro_rules! make_simple {
($name: ident => $read: expr) => {
crate::block::simple::make_simple!(
$name,
- |m: &Self, n: &str, _, _, _| crate::data::renderer::read(n, m.get_size()),
+ |m: &Self, n, _, _, _, s| crate::data::renderer::read(n, m.get_size(), s),
$read
);
};
($name: ident) => {
crate::block::simple::make_simple!(
$name,
- |m: &Self, n: &str, _, _, _| crate::data::renderer::read(n, m.get_size()),
+ |m: &Self, n, _, _, _, s| crate::data::renderer::read(n, m.get_size(), s),
|_, _, _, _| Ok(()),
false
);
diff --git a/src/block/turrets.rs b/src/block/turrets.rs
index d1617fc..6510d5c 100644
--- a/src/block/turrets.rs
+++ b/src/block/turrets.rs
@@ -43,14 +43,15 @@ fn draw_turret(
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
let path = match name {
"breach" | "diffuse" | "sublimate" | "titan" | "disperse" | "afflict" | "lustre"
| "scathe" | "malign" | "smite" => format!("reinforced-block-{}", me.get_size()),
_ => format!("block-{}", me.get_size()),
};
- let mut base = load(&path);
- base.overlay(&load(name));
+ let mut base = load(&path, s);
+ base.overlay(&load(name, s));
base
}
diff --git a/src/block/units.rs b/src/block/units.rs
index 21a7b3b..536446a 100644
--- a/src/block/units.rs
+++ b/src/block/units.rs
@@ -33,27 +33,38 @@ use crate::unit;
// )
// }
-make_simple!(ConstructorBlock, |me: &Self, name, _, _, rot: Rotation| {
- let mut base = load(name);
+make_simple!(ConstructorBlock, |me: &Self,
+ name,
+ _,
+ _,
+ rot: Rotation,
+ s| {
+ let mut base = load(name, s);
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),
- });
+ 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),
+ },
+ s,
+ );
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),
- });
+ 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),
+ },
+ s,
+ );
input.rotate(times);
base.overlay(&input);
@@ -77,9 +88,9 @@ make_simple!(ConstructorBlock, |me: &Self, name, _, _, rot: Rotation| {
// }
// }
- base.overlay(&load(&format!("{name}-top")));
+ base.overlay(&load(&format!("{name}-top"), s));
if matches!(name, "mech-assembler" | "tank-assembler" | "ship-assembler") {
- let mut side = load(&format!("{name}-side"));
+ let mut side = load(&format!("{name}-side"), s);
side.rotate(times);
base.overlay(&side);
}
@@ -207,20 +218,27 @@ impl BlockLogic for AssemblerBlock {
_: Option<&State>,
_: Option<&RenderingContext>,
rot: Rotation,
+ s: Scale,
) -> 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",
- });
+ let mut base = load(name, s);
+ let mut out = load(
+ match name {
+ "ground-factory" | "air-factory" | "naval-factory" => "factory-out-3",
+ _ => "factory-out-3-dark",
+ },
+ s,
+ );
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.overlay(&load(
+ &match name {
+ "ground-factory" | "air-factory" | "naval-factory" => {
+ format!("factory-top-{}", self.size)
+ }
+ _ => format!("{name}-top"),
+ },
+ s,
+ ));
base
}
diff --git a/src/block/walls.rs b/src/block/walls.rs
index 6b782a7..7ae1d22 100644
--- a/src/block/walls.rs
+++ b/src/block/walls.rs
@@ -6,22 +6,22 @@ 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, _, _, _, s| {
macro_rules! pick {
($name: literal => load $n: literal) => {{
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
- load(&format!("{}{}", $name, rand.next_range(1usize..$n)))
+ load(&format!("{}{}", $name, rand.next_range(1usize..$n)), s)
}};
}
match name {
"thruster" => {
const SFX: &[&str; 1] = &["-top"];
- read_with("thruster", SFX, 4u32)
+ read_with("thruster", SFX, 4u32, s)
}
"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),
- _ => load(name),
+ _ => load(name, s),
}
});
@@ -87,8 +87,9 @@ impl BlockLogic for DoorBlock {
_: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
+ s: Scale,
) -> ImageHolder {
- read(name, self.size)
+ read(name, self.size, s)
}
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
diff --git a/src/data/autotile.rs b/src/data/autotile.rs
index 76b0e5b..a79195e 100644
--- a/src/data/autotile.rs
+++ b/src/data/autotile.rs
@@ -92,8 +92,8 @@ fn print_crosses(v: Vec<Cross<'_>>, height: usize) -> String {
s
}
-pub fn tile(ctx: &RenderingContext<'_>, name: &str, rot: Rotation) -> ImageHolder {
- rotations2tile(mask2rotations(mask(ctx, rot, name), rot), name)
+pub fn tile(ctx: &RenderingContext<'_>, name: &str, rot: Rotation, s: Scale) -> ImageHolder {
+ rotations2tile(mask2rotations(mask(ctx, rot, name), rot), name, s)
}
pub fn mask2rotations(mask: U4, rot: Rotation) -> (u8, u8, u8) {
@@ -220,8 +220,8 @@ pub fn flrot(flip: u8, rot: u8, with: &mut ImageHolder) {
}
/// TODO figure out if a flip is cheaper than a rotate_270
-pub fn rotations2tile((index, rot, flip): (u8, u8, u8), name: &str) -> ImageHolder {
- let mut p = load(&format!("{name}-{index}"));
+pub fn rotations2tile((index, rot, flip): (u8, u8, u8), name: &str, scale: Scale) -> ImageHolder {
+ let mut p = load(&format!("{name}-{index}"), scale);
flrot(flip, rot, p.borrow_mut());
p
}
diff --git a/src/data/map.rs b/src/data/map.rs
index 7478a66..355aaf3 100644
--- a/src/data/map.rs
+++ b/src/data/map.rs
@@ -134,20 +134,20 @@ impl<'l> Tile<'l> {
1
}
- pub fn floor_image(&self, context: Option<&RenderingContext>) -> ImageHolder {
- let mut i = self.floor.image(None, context, Rotation::Up);
+ pub fn floor_image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder {
+ let mut i = self.floor.image(None, context, Rotation::Up, s);
if let Some(ore) = self.ore {
- i.overlay(ore.image(None, context, Rotation::Up).borrow());
+ i.overlay(ore.image(None, context, Rotation::Up, s).borrow());
}
i
}
- pub fn build_image(&self, context: Option<&RenderingContext>) -> ImageHolder {
+ pub fn build_image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder {
// building covers floore
let Some(b) = &self.build else {
unreachable!();
};
- b.image(context)
+ b.image(context, s)
}
}
@@ -243,9 +243,9 @@ impl<'l> Build<'l> {
}
}
- pub fn image(&self, context: Option<&RenderingContext>) -> ImageHolder {
+ pub fn image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder {
self.block
- .image(self.state.as_ref(), context, self.rotation)
+ .image(self.state.as_ref(), context, self.rotation, s)
}
pub fn name(&self) -> &str {
diff --git a/src/data/renderer.rs b/src/data/renderer.rs
index b1b2e0a..de64e3f 100644
--- a/src/data/renderer.rs
+++ b/src/data/renderer.rs
@@ -20,8 +20,11 @@ macro_rules! r {
}};
}
-type Cache = phf::Map<&'static str, &'static LazyLock<RgbaImage>>;
-static CACHE: Cache = include!(concat!(env!("OUT_DIR"), "/asset"));
+type Images = phf::Map<&'static str, &'static LazyLock<RgbaImage>>;
+static FULL: Images = include!(concat!(env!("OUT_DIR"), "/full.rs"));
+// static HALF: Images = include!(concat!(env!("OUT_DIR"), "/half.rs"));
+static QUAR: Images = include!(concat!(env!("OUT_DIR"), "/quar.rs"));
+static EIGH: Images = include!(concat!(env!("OUT_DIR"), "/eigh.rs"));
pub enum ImageHolder {
Borrow(&'static RgbaImage),
@@ -91,14 +94,44 @@ impl From<RgbaImage> for ImageHolder {
}
}
-pub(crate) fn try_load(name: &str) -> Option<&'static RgbaImage> {
+#[derive(Debug, Copy, Clone)]
+pub enum Scale {
+ Full,
+ // Half,
+ Quarter,
+ Eigth,
+}
+
+impl Scale {
+ fn px(self) -> u8 {
+ match self {
+ Self::Full => 32,
+ Self::Quarter => 32 / 4,
+ Self::Eigth => 32 / 8,
+ }
+ }
+}
+
+impl std::ops::Mul<u32> for Scale {
+ type Output = u32;
+ fn mul(self, rhs: u32) -> u32 {
+ self.px() as u32 * rhs
+ }
+}
+
+pub(crate) fn try_load(name: &str, scale: Scale) -> Option<&'static RgbaImage> {
let key = name.to_string();
- CACHE.get(&key).map(|v| LazyLock::force(v))
+ match scale {
+ Scale::Quarter => QUAR.get(&key).map(|v| LazyLock::force(v)),
+ Scale::Eigth => EIGH.get(&key).map(|v| LazyLock::force(v)),
+ Scale::Full => FULL.get(&key).map(|v| LazyLock::force(v)),
+ // Scale::Half => HALF.get(&key).map(|v| LazyLock::force(v)),
+ }
}
-pub(crate) fn load(name: &str) -> ImageHolder {
+pub(crate) fn load(name: &str, scale: Scale) -> ImageHolder {
ImageHolder::from(
- try_load(name)
+ try_load(name, scale)
.ok_or_else(|| format!("failed to load {name}"))
.unwrap(),
)
@@ -107,20 +140,28 @@ pub(crate) fn load(name: &str) -> ImageHolder {
const SUFFIXES: &[&str; 9] = &[
"-bottom", "-mid", "-base", "", "-left", "-right", "-top", "-over", "-team",
];
-pub(crate) fn read<S>(name: &str, size: S) -> ImageHolder
+pub(crate) fn read<S>(name: &str, size: S, scale: Scale) -> ImageHolder
where
S: Into<u32> + Copy,
{
- read_with(name, SUFFIXES, size)
+ read_with(name, SUFFIXES, size, scale)
}
-pub(crate) fn read_with<S>(name: &str, suffixes: &'static [&'static str], size: S) -> ImageHolder
+pub(crate) fn read_with<S>(
+ name: &str,
+ suffixes: &'static [&'static str],
+ size: S,
+ scale: Scale,
+) -> ImageHolder
where
S: Into<u32> + Copy,
{
- let mut c = RgbaImage::new(size.into() * 32, size.into() * 32);
+ let mut c = RgbaImage::new(
+ size.into() * scale.px() as u32,
+ size.into() * scale.px() as u32,
+ );
for suffix in suffixes {
- if let Some(p) = try_load(&format!("{name}{suffix}")) {
+ if let Some(p) = try_load(&format!("{name}{suffix}"), scale) {
if suffix == &"-team" {
c.overlay(p.clone().tint(SHARDED.color()));
continue;
@@ -152,7 +193,11 @@ impl Renderable for Schematic<'_> {
((self.width + 2) * 32) as u32,
((self.height + 2) * 32) as u32,
);
- bg.repeat(METAL_FLOOR.image(None, None, Rotation::Up).borrow());
+ bg.repeat(
+ METAL_FLOOR
+ .image(None, None, Rotation::Up, Scale::Full)
+ .borrow(),
+ );
let mut canvas = RgbaImage::new(
((self.width + 2) * 32) as u32,
((self.height + 2) * 32) as u32,
@@ -174,8 +219,12 @@ 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(
- tile.image(ctx.as_ref(), tile.get_rotation().unwrap_or(Rotation::Up))
- .borrow(),
+ tile.image(
+ ctx.as_ref(),
+ tile.get_rotation().unwrap_or(Rotation::Up),
+ Scale::Full,
+ )
+ .borrow(),
(x + 1) * 32,
(y + 1) * 32,
);
@@ -192,12 +241,12 @@ impl Renderable for Schematic<'_> {
impl Renderable for Map<'_> {
fn render(&self) -> RgbaImage {
let scale = if self.width + self.height < 2000 {
- 8
+ Scale::Quarter
} else {
- 4
+ Scale::Eigth
};
- let mut floor = RgbaImage::new(self.width as u32 * scale, self.height as u32 * scale);
- let mut top = RgbaImage::new(self.width as u32 * scale, self.height as u32 * scale);
+ let mut floor = RgbaImage::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);
for (x, y, j, tile) in self.tiles.iter().enumerate().map(|(j, t)| {
(
(j % self.width),
@@ -208,12 +257,11 @@ impl Renderable for Map<'_> {
)
}) {
// draw the floor first.
- floor.overlay_at(
- // SAFETY: [`load_raw`] forces nonzero image size
- unsafe { &tile.floor_image(None).own().scale(scale) },
- x as u32 * scale,
- y as u32 * scale,
- );
+ let img = tile.floor_image(None, scale);
+ // println!("draw {tile:?} ({x}, {y}) + {scale:?}");
+ // assert_eq!(img.width(), scale.px() as u32);
+ // assert_eq!(img.height(), scale.px() as u32);
+ floor.overlay_at(&img, scale * x as u32, scale * y as u32);
if let Some(build) = tile.build() {
let s = build.block.get_size();
let x = x - ((s - 1) / 2) as usize;
@@ -233,17 +281,10 @@ impl Renderable for Map<'_> {
};
Some(rctx)
})();
- top.overlay_at(
- // SAFETY: tile.size can never be 0, and [`load_raw`] forces nonzero.
- unsafe {
- &tile
- .build_image(ctx.as_ref())
- .own()
- .scale(tile.size() as u32 * scale)
- },
- x as u32 * scale,
- y as u32 * scale,
- );
+ let img = tile.build_image(ctx.as_ref(), scale);
+ // assert_eq!(img.width(), scale * build.block.get_size() as u32);
+ // assert_eq!(img.height(), scale * build.block.get_size() as u32);
+ top.overlay_at(&img, scale * x as u32, scale * y as u32);
}
}
#[cfg(feature = "map_shadow")]
@@ -284,6 +325,7 @@ fn all_blocks() {
},
}),
Rotation::Up,
+ Scale::Quarter,
);
}
}
diff --git a/src/data/schematic.rs b/src/data/schematic.rs
index 5c34bcb..1e3e513 100644
--- a/src/data/schematic.rs
+++ b/src/data/schematic.rs
@@ -59,8 +59,13 @@ impl<'l> Placement<'l> {
}
/// draws this placement in particular
- pub fn image(&self, context: Option<&RenderingContext>, rot: Rotation) -> ImageHolder {
- self.block.image(self.get_state(), context, rot)
+ pub fn image(
+ &self,
+ context: Option<&RenderingContext>,
+ rot: Rotation,
+ s: Scale,
+ ) -> ImageHolder {
+ self.block.image(self.get_state(), context, rot, s)
}
/// set the state
diff --git a/src/lib.rs b/src/lib.rs
index c2296f7..2d2430b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
//! crate for dealing with mindustry
-#![feature(lazy_cell, slice_as_chunks)]
+#![feature(lazy_cell, array_chunks)]
mod access;
pub mod block;
mod content;
diff --git a/src/utils/image.rs b/src/utils/image.rs
index 6abe314..8291c7d 100644
--- a/src/utils/image.rs
+++ b/src/utils/image.rs
@@ -1,6 +1,4 @@
-use fast_image_resize as fr;
use image::{imageops, Rgb, Rgba, RgbaImage};
-use std::num::NonZeroU32;
pub trait ImageUtils {
/// Tint this image with the color
@@ -24,9 +22,7 @@ pub trait ImageUtils {
#[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
fn silhouette(&mut self) -> &mut Self;
/// scale a image
- ///
- /// SAFETY: to and width and height cannot be 0.
- unsafe fn scale(self, to: u32) -> Self;
+ fn scale(&self, to: u32) -> Self;
}
impl ImageUtils for RgbaImage {
@@ -82,10 +78,8 @@ impl ImageUtils for RgbaImage {
let local = std::mem::take(self);
let mut own = local.into_raw();
let other = with.as_raw();
- for (i, other_pixels) in unsafe { other.as_chunks_unchecked::<4>() }
- .iter()
- .enumerate()
- {
+ assert!(own.len() % 4 == 0 && other.len() % 4 == 0);
+ for (i, other_pixels) in other.array_chunks::<4>().enumerate() {
if other_pixels[3] > 128 {
let own_pixels = unsafe { own.get_unchecked_mut(i * 4..i * 4 + 4) };
own_pixels.copy_from_slice(other_pixels);
@@ -95,23 +89,8 @@ impl ImageUtils for RgbaImage {
self
}
- unsafe fn scale(self, to: u32) -> Self {
- debug_assert_ne!(to, 0);
- debug_assert_ne!(self.width(), 0);
- debug_assert_ne!(self.height(), 0);
- let to = NonZeroU32::new_unchecked(to);
- let src = fr::Image::from_vec_u8(
- NonZeroU32::new_unchecked(self.width()),
- NonZeroU32::new_unchecked(self.height()),
- self.into_vec(),
- fr::PixelType::U8x4,
- )
- .unwrap();
- let mut dst = fr::Image::new(to, to, fr::PixelType::U8x4);
- fr::Resizer::new(fr::ResizeAlg::Nearest)
- .resize(&src.view(), &mut dst.view_mut())
- .unwrap();
- RgbaImage::from_raw(to.get(), to.get(), dst.into_vec()).unwrap()
+ fn scale(&self, to: u32) -> Self {
+ imageops::resize(self, to, to, imageops::Nearest)
}
#[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]