mindustry logic execution, map- and schematic- parsing and rendering
-rw-r--r--src/block/distribution.rs39
-rw-r--r--src/block/drills.rs10
-rw-r--r--src/block/payload.rs49
-rw-r--r--src/block/power.rs21
-rw-r--r--src/block/production.rs64
-rw-r--r--src/block/units.rs73
-rw-r--r--src/lib.rs4
-rw-r--r--src/utils/image/affine.rs77
-rw-r--r--src/utils/image/holder.rs7
-rw-r--r--src/utils/image/mod.rs70
-rw-r--r--src/utils/image/overlay.rs69
11 files changed, 242 insertions, 241 deletions
diff --git a/src/block/distribution.rs b/src/block/distribution.rs
index 489d230..97308fe 100644
--- a/src/block/distribution.rs
+++ b/src/block/distribution.rs
@@ -39,12 +39,10 @@ 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!(from name which is ["overflow-duct" "underflow-duct"], s);
- unsafe {
- // SAFETY: any load() is square
- top.rotate(rot.rotated(false).count());
- // SAFETY: same size
- base.overlay(&top);
- }
+ // SAFETY: any load!() is square
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ // SAFETY: same size
+ unsafe { base.overlay(&top) };
base
});
@@ -124,7 +122,9 @@ make_simple!(
SurgeRouter,
|_, _, _, _, r: Rotation, s| {
let mut base = load!("surge-router", s);
- unsafe { base.overlay(load!("top", s).rotate(r.rotated(false).count())) };
+ let mut top = load!("top", s);
+ unsafe { top.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
},
|_, buff: &mut DataRead| buff.skip(2)
@@ -206,17 +206,14 @@ impl BlockLogic for ItemBlock {
unsafe { p.overlay(top.tint(item.color())) };
return p;
}
- match name {
- "duct-router" => {
- unsafe { p.overlay(load!("top", s).rotate(rot.rotated(false).count())) };
- }
- "duct-unloader" => {
- unsafe {
- p.overlay(load!("duct-unloader-top", s).rotate(rot.rotated(false).count()))
- };
- }
- _ => {}
- };
+ if matches!(name, "duct-router" | "duct-unloader") {
+ let mut top = load!(s -> match name {
+ "duct-router" => "top",
+ "duct-unloader" => "duct-unloader-top",
+ });
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ unsafe { p.overlay(&top) };
+ }
p
}
@@ -442,10 +439,8 @@ impl BlockLogic for BridgeBlock {
_ => "reinforced-bridge-conduit-dir",
}
);
- unsafe {
- arrow.rotate(r.rotated(false).count());
- base.overlay(&arrow)
- };
+ unsafe { arrow.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&arrow) };
base
}
// "bridge-conveyor" | "phase-conveyor" | "bridge-conduit" | "phase-conduit" | "payload-mass-driver" | "large-payload-mass-driver"
diff --git a/src/block/drills.rs b/src/block/drills.rs
index 6952157..b042ceb 100644
--- a/src/block/drills.rs
+++ b/src/block/drills.rs
@@ -7,16 +7,18 @@ make_simple!(
|_, name, _, _, rot: Rotation, s| {
let mut base =
load!(from name which is ["large-plasma-bore" | "plasma-bore" | "cliff-crusher"], s);
- unsafe {
- base.overlay(load!(concat "top" => name which is ["large-plasma-bore" | "plasma-bore" | "cliff-crusher"], s).rotate(rot.rotated(false).count()) )
- };
+ let mut top = load!(concat "top" => name which is ["large-plasma-bore" | "plasma-bore" | "cliff-crusher"], s);
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
},
|_, buff: &mut DataRead| read_drill(buff)
);
make_simple!(WallDrillBlock, |_, _, _, _, rot: Rotation, scl| {
let mut base = load!("cliff-crusher", scl);
- unsafe { base.overlay(load!("cliff-crusher-top", scl).rotate(rot.rotated(false).count())) };
+ let mut top = load!("cliff-crusher-top", scl);
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
});
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 9b1970b..f68f8af 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -18,28 +18,30 @@ make_simple!(SimplePayloadBlock, |_, n, _, _, r: Rotation, scl| {
"small-deconstructor" => "factory-in-3",
_ => "factory-in-5",
});
- unsafe {
- base.overlay(r#in.rotate(r.rotated(false).count()))
- .overlay(&load!(scl -> match n {
- "small-deconstructor" => "small-deconstructor-top",
- "deconstructor" => "deconstructor-top",
- _ => "payload-void-top",
- }))
- };
+ unsafe { r#in.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&r#in) };
+ let top = load!(scl -> match n {
+ "small-deconstructor" => "small-deconstructor-top",
+ "deconstructor" => "deconstructor-top",
+ _ => "payload-void-top",
+ });
+ unsafe { base.overlay(&top) };
base
}
// "payload-loader" | "payload-unloader"
_ => {
let mut base = load!(from n which is ["payload-loader" | "payload-unloader"], scl);
let mut input = load!("factory-in-3-dark", scl);
+ unsafe { input.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&input) };
+
let mut output = load!("factory-out-3-dark", scl);
- unsafe {
- base.overlay(input.rotate(r.rotated(false).count()))
- .overlay(output.rotate(r.rotated(false).count()))
- .overlay(
- &load!(concat "top" => n which is ["payload-loader" | "payload-unloader"], scl),
- )
- };
+ unsafe { output.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&output) };
+
+ let top =
+ load!(concat "top" => n which is ["payload-loader" | "payload-unloader"], scl);
+ unsafe { base.overlay(&top) };
base
}
}
@@ -59,10 +61,10 @@ make_simple!(
PayloadRouter,
|_, n, _, _, r: Rotation, s| {
let mut base = load!(from n which is ["payload-router" | "reinforced-payload-router"], s);
- unsafe {
- base.rotate(r.rotated(false).count());
- base.overlay(& load!(concat "over" => n which is ["payload-router" | "reinforced-payload-router"], s));
- }
+ unsafe { base.rotate(r.rotated(false).count()) };
+ let over =
+ load!(concat "over" => n which is ["payload-router" | "reinforced-payload-router"], s);
+ unsafe { base.overlay(&over) };
base
},
read_payload_router
@@ -115,11 +117,10 @@ impl BlockLogic for PayloadBlock {
"large-constructor" => "factory-out-5-dark",
_ => "factory-out-5",
});
- unsafe {
- out.rotate(r.rotated(false).count());
- base.overlay(&out);
- base.overlay(&load!(concat "top" => name which is ["constructor" | "large-constructor" | "payload-source"], s))
- };
+ unsafe { out.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&out) };
+ let top = load!(concat "top" => name which is ["constructor" | "large-constructor" | "payload-source"], s);
+ unsafe { base.overlay(&top) };
base
}
diff --git a/src/block/power.rs b/src/block/power.rs
index 7b04135..d891a8f 100644
--- a/src/block/power.rs
+++ b/src/block/power.rs
@@ -12,15 +12,12 @@ make_simple!(
Neoplasia,
|_, _, _, _, rot: Rotation, scl| {
let mut base = load!("neoplasia-reactor", scl);
- unsafe {
- base.overlay(
- load!(scl -> match rot {
- Rotation::Up | Rotation::Right => "neoplasia-reactor-top1",
- Rotation::Down | Rotation::Left => "neoplasia-reactor-top2",
- })
- .rotate(rot.rotated(false).count()),
- )
- };
+ let mut top = load!(scl -> match rot {
+ Rotation::Up | Rotation::Right => "neoplasia-reactor-top1",
+ Rotation::Down | Rotation::Left => "neoplasia-reactor-top2",
+ });
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
},
|_, buff: &mut DataRead| read_heater(buff)
@@ -31,10 +28,8 @@ make_simple!(DiodeBlock, |_, _, _, _, rot: Rotation, s| {
return base;
}
let mut top = load!("diode-arrow", s);
- unsafe {
- top.rotate(rot.rotated(false).count());
- base.overlay(&top)
- };
+ unsafe { top.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
});
diff --git a/src/block/production.rs b/src/block/production.rs
index bd44518..9e97bf2 100644
--- a/src/block/production.rs
+++ b/src/block/production.rs
@@ -12,22 +12,19 @@ make_simple!(
// electrolyzer exclusive
// ozone <- e(^) -> hydrogen
let mut base = load!("electrolyzer", s);
- unsafe {
- base.overlay(
- load!(s -> match r {
- Rotation::Up | Rotation::Left => "electrolyzer-hydrogen-output1"
- Rotation::Down | Rotation::Right => "electrolyzer-hydrogen-output2"
- })
- .rotate(r.count()),
- );
- base.overlay(
- load!(s -> match r {
- Rotation::Down | Rotation::Right => "electrolyzer-ozone-output1"
- Rotation::Up | Rotation::Left => "electrolyzer-ozone-output2"
- })
- .rotate(r.mirrored(true, true).count()),
- );
- }
+ let mut hydro = load!(s -> match r {
+ Rotation::Up | Rotation::Left => "electrolyzer-hydrogen-output1"
+ Rotation::Down | Rotation::Right => "electrolyzer-hydrogen-output2"
+ });
+ unsafe { hydro.rotate(r.count()) };
+ unsafe { base.overlay(&hydro) };
+
+ let mut ozone = load!(s -> match r {
+ Rotation::Down | Rotation::Right => "electrolyzer-ozone-output1"
+ Rotation::Up | Rotation::Left => "electrolyzer-ozone-output2"
+ });
+ unsafe { ozone.rotate(r.mirrored(true, true).count()) };
+ unsafe { base.overlay(&ozone) };
base
},
|b: &mut Build<'_>, buff: &mut DataRead| {
@@ -44,12 +41,16 @@ make_simple!(
HeatCrafter,
|_, n, _, _, r: Rotation, s| {
let mut base = load!(from n which is ["phase-heater" | "electric-heater" | "oxidation-chamber" | "slag-heater" | "heat-source"], s);
- unsafe {
- base.overlay(match r {
- Rotation::Up | Rotation::Right => load!(concat "top1" => n which is ["phase-heater" | "electric-heater" | "oxidation-chamber" | "slag-heater" | "heat-source"], s),
- Rotation::Down | Rotation::Left => load!(concat "top2" => n which is ["phase-heater" | "electric-heater" | "oxidation-chamber" | "slag-heater" | "heat-source"], s)
- }.rotate(r.rotated(false).count()))
+ let mut top = match r {
+ Rotation::Up | Rotation::Right => {
+ load!(concat "top1" => n which is ["phase-heater" | "electric-heater" | "oxidation-chamber" | "slag-heater" | "heat-source"], s)
+ }
+ Rotation::Down | Rotation::Left => {
+ load!(concat "top2" => n which is ["phase-heater" | "electric-heater" | "oxidation-chamber" | "slag-heater" | "heat-source"], s)
+ }
};
+ unsafe { top.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
},
|_, buff: &mut DataRead| {
@@ -63,18 +64,15 @@ make_simple!(
);
make_simple!(HeatConduit, |_, n, _, _, r: Rotation, s| {
let mut base = load!(from n which is ["heat-router" | "heat-redirector"], s);
- unsafe {
- base.overlay(
- match r {
- Rotation::Up | Rotation::Right => {
- load!(concat "top1" => n which is ["heat-router" | "heat-redirector"], s)
- }
- Rotation::Down | Rotation::Left => {
- load!(concat "top2" => n which is ["heat-router" | "heat-redirector"], s)
- }
- }
- .rotate(r.rotated(false).count()),
- )
+ let mut top = match r {
+ Rotation::Up | Rotation::Right => {
+ load!(concat "top1" => n which is ["heat-router" | "heat-redirector"], s)
+ }
+ Rotation::Down | Rotation::Left => {
+ load!(concat "top2" => n which is ["heat-router" | "heat-redirector"], s)
+ }
};
+ unsafe { top.rotate(r.rotated(false).count()) };
+ unsafe { base.overlay(&top) };
base
});
diff --git a/src/block/units.rs b/src/block/units.rs
index 1fe0ade..97e3b98 100644
--- a/src/block/units.rs
+++ b/src/block/units.rs
@@ -39,16 +39,18 @@ make_simple!(
|_, name, _, _, rot: Rotation, s| {
let mut base =
load!(from name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s);
- unsafe {
- base.overlay(
- match rot {
- Rotation::Up | Rotation::Right => load!(concat "side1" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s),
- Rotation::Down | Rotation::Left => load!(concat "side2" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s)
+ let mut side = match rot {
+ Rotation::Up | Rotation::Right => {
+ load!(concat "side1" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s)
+ }
+ Rotation::Down | Rotation::Left => {
+ load!(concat "side2" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s)
}
- .rotate(rot.rotated(false).count())
- );
- base.overlay(&load!(concat "top" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s))
};
+ unsafe { side.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&side) };
+ let top = load!(concat "top" => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s);
+ unsafe { base.overlay(&top) };
base
},
|_, buff| read_assembler(buff)
@@ -74,15 +76,12 @@ make_simple!(
AssemblerModule,
|_, _, _, _, rot: Rotation, scl| {
let mut base = load!("basic-assembler-module", scl);
- unsafe {
- base.overlay(
- load!(scl -> match rot {
- Rotation::Up | Rotation::Right => "basic-assembler-module-side1",
- _ => "basic-assembler-module-side2",
- })
- .rotate(rot.rotated(false).count()),
- )
- };
+ let mut side = load!(scl -> match rot {
+ Rotation::Up | Rotation::Right => "basic-assembler-module-side1",
+ _ => "basic-assembler-module-side2",
+ });
+ unsafe { side.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&side) };
base
},
|_, buff| read_payload_block(buff)
@@ -160,7 +159,8 @@ impl BlockLogic for ConstructorBlock {
"prime-refabricator" => "factory-out-5-dark",
"tetrative-reconstructor" => "factory-out-9",
});
- unsafe { base.overlay(out.rotate(times)) };
+ unsafe { out.rotate(times) };
+ unsafe { base.overlay(&out) };
let mut r#in = load!(s -> match name {
"additive-reconstructor" => "factory-in-3",
@@ -171,7 +171,8 @@ impl BlockLogic for ConstructorBlock {
"prime-refabricator" => "factory-in-5-dark",
"tetrative-reconstructor" => "factory-in-9",
});
- unsafe { base.overlay(r#in.rotate(times)) };
+ unsafe { r#in.rotate(times) };
+ unsafe { base.overlay(&r#in) };
// TODO: the context cross is too small
// for i in 0..4u8 {
@@ -192,10 +193,8 @@ impl BlockLogic for ConstructorBlock {
// }
// }
// }
-
- unsafe {
- base.overlay(&load!(concat "top" => name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator"], s))
- };
+ let top = load!(concat "top" => name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator"], s);
+ unsafe { base.overlay(&top) };
base
}
@@ -297,21 +296,19 @@ impl BlockLogic for UnitFactory {
s: Scale,
) -> ImageHolder<4> {
let mut base = load!(from name which is ["ground-factory" | "air-factory" | "naval-factory" | "tank-fabricator" | "ship-fabricator" | "mech-fabricator"], s);
- unsafe {
- base.overlay(
- load!(s -> match name {
- "ground-factory" | "air-factory" | "naval-factory" => "factory-out-3",
- _ => "factory-out-3-dark",
- })
- .rotate(rot.rotated(false).count()),
- )
- .overlay(&load!(s -> match name {
- "ground-factory" | "air-factory" | "naval-factory" => "factory-top-3",
- "tank-fabricator" => "tank-fabricator-top",
- "ship-fabricator" => "ship-fabricator-top",
- "mech-fabricator" => "mech-fabricator-top",
- }))
- };
+ let mut out = load!(s -> match name {
+ "ground-factory" | "air-factory" | "naval-factory" => "factory-out-3",
+ _ => "factory-out-3-dark",
+ });
+ unsafe { out.rotate(rot.rotated(false).count()) };
+ unsafe { base.overlay(&out) };
+ let top = load!(s -> match name {
+ "ground-factory" | "air-factory" | "naval-factory" => "factory-top-3",
+ "tank-fabricator" => "tank-fabricator-top",
+ "ship-fabricator" => "ship-fabricator-top",
+ "mech-fabricator" => "mech-fabricator-top",
+ });
+ unsafe { base.overlay(&top) };
base
}
diff --git a/src/lib.rs b/src/lib.rs
index 47d7b00..07de878 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,8 +11,10 @@
test
)]
#![warn(
- clippy::missing_safety_doc,
+ clippy::multiple_unsafe_ops_per_block,
clippy::missing_const_for_fn,
+ clippy::missing_safety_doc,
+ unsafe_op_in_unsafe_fn,
clippy::dbg_macro,
clippy::perf
)]
diff --git a/src/utils/image/affine.rs b/src/utils/image/affine.rs
index 04a6180..6ca78cd 100644
--- a/src/utils/image/affine.rs
+++ b/src/utils/image/affine.rs
@@ -4,15 +4,12 @@ use super::Image;
pub fn rot_180<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
for y in 0..img.height() / 2 {
for x in 0..img.width() {
- // SAFETY: this is safe because it cannot be out of bounds
- unsafe {
- let p = img.pixel(x, y);
- let x2 = img.width() - x - 1;
- let y2 = img.height() - y - 1;
- let p2 = img.pixel(x2, y2);
- img.set_pixel(x, y, p2);
- img.set_pixel(x2, y2, p);
- }
+ let p = unsafe { img.pixel(x, y) };
+ let x2 = img.width() - x - 1;
+ let y2 = img.height() - y - 1;
+ let p2 = unsafe { img.pixel(x2, y2) };
+ unsafe { img.set_pixel(x, y, p2) };
+ unsafe { img.set_pixel(x2, y2, p) };
}
}
@@ -20,15 +17,11 @@ pub fn rot_180<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
let middle = img.height() / 2;
for x in 0..img.width() / 2 {
- // SAFETY: this is safe because it cannot be out of bounds
- unsafe {
- let p = img.pixel(x, middle);
- let x2 = img.width() - x - 1;
-
- let p2 = img.pixel(x2, middle);
- img.set_pixel(x, middle, p2);
- img.set_pixel(x2, middle, p);
- }
+ let p = unsafe { img.pixel(x, middle) };
+ let x2 = img.width() - x - 1;
+ let p2 = unsafe { img.pixel(x2, middle) };
+ unsafe { img.set_pixel(x, middle, p2) };
+ unsafe { img.set_pixel(x2, middle, p) };
}
}
}
@@ -42,10 +35,13 @@ unsafe fn transpose<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>)
for i in 0..size {
for j in i..size {
for c in 0..CHANNELS {
- img.buffer.swap_unchecked(
- (i * size + j) as usize * CHANNELS + c,
- (j * size + i) as usize * CHANNELS + c,
- );
+ // SAFETY: caller gurantees rectangularity
+ unsafe {
+ img.buffer.swap_unchecked(
+ (i * size + j) as usize * CHANNELS + c,
+ (j * size + i) as usize * CHANNELS + c,
+ )
+ };
}
}
}
@@ -60,7 +56,8 @@ unsafe fn transpose<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>)
#[inline]
pub unsafe fn rot_90<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
flip_v(img);
- transpose(img);
+ // SAFETY: caller ensures rectangularity
+ unsafe { transpose(img) };
}
/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
@@ -72,22 +69,22 @@ pub unsafe fn rot_90<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>
#[inline]
pub unsafe fn rot_270<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
flip_h(img);
- transpose(img);
+ // SAFETY: caller ensures rectangularity
+ unsafe { transpose(img) };
}
/// Flip a image vertically.
pub fn flip_v<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
for y in 0..img.height() / 2 {
for x in 0..img.width() {
- // SAFETY: cant overflow
- unsafe {
- let y2 = img.height().unchecked_sub(y).unchecked_sub(1);
- // SAFETY: within bounds
- let p2 = img.pixel(x, y2);
- let p = img.pixel(x, y);
- img.set_pixel(x, y2, p);
- img.set_pixel(x, y, p2);
- }
+ // SAFETY: i think this is unsound if the image is big enough
+ let y2 = unsafe { img.height().unchecked_sub(y) };
+ let y2 = unsafe { y2.unchecked_sub(1) };
+ // SAFETY: within bounds
+ let p2 = unsafe { img.pixel(x, y2) };
+ let p = unsafe { img.pixel(x, y) };
+ unsafe { img.set_pixel(x, y2, p) };
+ unsafe { img.set_pixel(x, y, p2) };
}
}
}
@@ -96,14 +93,12 @@ pub fn flip_v<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
pub fn flip_h<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
for y in 0..img.height() {
for x in 0..img.width() / 2 {
- // SAFETY: This cannot be out of bounds
- unsafe {
- let x2 = img.width().unchecked_sub(x).unchecked_sub(1);
- let p2 = img.pixel(x2, y);
- let p = img.pixel(x, y);
- img.set_pixel(x2, y, p);
- img.set_pixel(x, y, p2);
- }
+ let x2 = unsafe { img.width().unchecked_sub(x) };
+ let x2 = unsafe { x2.unchecked_sub(1) };
+ let p2 = unsafe { img.pixel(x2, y) };
+ let p = unsafe { img.pixel(x, y) };
+ unsafe { img.set_pixel(x2, y, p) };
+ unsafe { img.set_pixel(x, y, p2) };
}
}
}
diff --git a/src/utils/image/holder.rs b/src/utils/image/holder.rs
index 02c9240..4c1b6a5 100644
--- a/src/utils/image/holder.rs
+++ b/src/utils/image/holder.rs
@@ -40,14 +40,15 @@ impl<const CHANNELS: usize> ImageHolder<CHANNELS> {
impl OverlayAt<ImageHolder<4>> for ImageHolder<4> {
unsafe fn overlay_at(&mut self, with: &ImageHolder<4>, x: u32, y: u32) -> &mut Self {
- self.borrow_mut().overlay_at(&with.borrow(), x, y);
+ // SAFETY: this is basically a deref impl, the caller upholds the safety invariants
+ unsafe { self.borrow_mut().overlay_at(&with.borrow(), x, y) };
self
}
}
impl Overlay<ImageHolder<4>> for ImageHolder<4> {
unsafe fn overlay(&mut self, with: &Self) -> &mut Self {
- self.borrow_mut().overlay(&with.borrow());
+ unsafe { self.borrow_mut().overlay(&with.borrow()) };
self
}
}
@@ -63,7 +64,7 @@ impl ImageUtils for ImageHolder<4> {
return self;
}
// borrow mut may clone, so try to avoid
- self.borrow_mut().rotate(times);
+ unsafe { self.borrow_mut().rotate(times) };
self
}
diff --git a/src/utils/image/mod.rs b/src/utils/image/mod.rs
index bbe9a3e..0ef2c69 100644
--- a/src/utils/image/mod.rs
+++ b/src/utils/image/mod.rs
@@ -35,19 +35,19 @@ pub trait ImageUtils {
fn scale(self, to: u32) -> Image<Vec<u8>, 4>;
}
-macro_rules! unsafe_assert {
+macro_rules! assert_unchecked {
($cond:expr) => {{
if !$cond {
#[cfg(debug_assertions)]
+ let _ = ::core::ptr::NonNull::<()>::dangling().as_ref(); // force unsafe wrapping block
+ #[cfg(debug_assertions)]
panic!("assertion failed: {} returned false", stringify!($cond));
#[cfg(not(debug_assertions))]
- unsafe {
- std::hint::unreachable_unchecked()
- };
+ std::hint::unreachable_unchecked()
}
}};
}
-pub(self) use unsafe_assert;
+use assert_unchecked;
impl RepeatNew for Image<&[u8], 4> {
type Output = Image<Vec<u8>, 4>;
@@ -56,7 +56,8 @@ impl RepeatNew for Image<&[u8], 4> {
for x in 0..(x / self.width()) {
for y in 0..(y / self.height()) {
let a: &mut Image<&mut [u8], 4> = &mut img.as_mut();
- a.overlay_at(self, x * self.width(), y * self.height());
+ // SAFETY: caller upholds
+ unsafe { a.overlay_at(self, x * self.width(), y * self.height()) };
}
}
img
@@ -65,20 +66,17 @@ impl RepeatNew for Image<&[u8], 4> {
impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 4> {
unsafe fn overlay(&mut self, with: &Image<&[u8], 4>) -> &mut Self {
- unsafe_assert!(self.width() == with.width());
- unsafe_assert!(self.height() == with.height());
+ // SAFETY: caller upholds these
+ unsafe { assert_unchecked!(self.width() == with.width()) };
+ unsafe { assert_unchecked!(self.height() == with.height()) };
for (i, other_pixels) in with.chunked().enumerate() {
if other_pixels[3] >= 128 {
+ let idx_begin = unsafe { i.unchecked_mul(4) };
+ let idx_end = unsafe { idx_begin.unchecked_add(4) };
+ let own_pixels = unsafe { self.buffer.get_unchecked_mut(idx_begin..idx_end) };
unsafe {
- let own_pixels = self
- .buffer
- .get_unchecked_mut(i.unchecked_mul(4)..i.unchecked_mul(4).unchecked_add(4));
- std::ptr::copy_nonoverlapping(
- other_pixels.as_ptr(),
- own_pixels.as_mut_ptr(),
- 4,
- );
- }
+ std::ptr::copy_nonoverlapping(other_pixels.as_ptr(), own_pixels.as_mut_ptr(), 4)
+ };
}
}
self
@@ -164,9 +162,8 @@ impl ImageUtils for Image<&mut [u8], 4> {
#[inline]
unsafe fn really_unsafe_index(x: u32, y: u32, w: u32) -> usize {
// y * w + x
- (y as usize)
- .unchecked_mul(w as usize)
- .unchecked_add(x as usize)
+ let tmp = unsafe { (y as usize).unchecked_mul(w as usize) };
+ unsafe { tmp.unchecked_add(x as usize) }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -227,15 +224,18 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
pub unsafe fn slice(&self, x: u32, y: u32) -> impl SliceIndex<[u8], Output = [u8]> {
debug_assert!(x < self.width(), "x out of bounds");
debug_assert!(y < self.height(), "y out of bounds");
- let index = really_unsafe_index(x, y, self.width()).unchecked_mul(CHANNELS);
+ let index = unsafe { really_unsafe_index(x, y, self.width()) };
+ let index = unsafe { index.unchecked_mul(CHANNELS) };
debug_assert!(self.buffer.len() > index);
- index..index.unchecked_add(CHANNELS)
+ index..unsafe { index.unchecked_add(CHANNELS) }
}
#[inline]
pub fn chunked(&self) -> impl Iterator<Item = &[u8; CHANNELS]> {
- unsafe_assert!(self.buffer.len() > CHANNELS);
- unsafe_assert!(self.buffer.len() % CHANNELS == 0);
+ // SAFETY: 0 sized images illegal
+ unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
+ // SAFETY: no half pixels!
+ unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
self.buffer.array_chunks::<CHANNELS>()
}
@@ -245,7 +245,9 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
/// Refer to [`slice`]
#[inline]
pub unsafe fn pixel(&self, x: u32, y: u32) -> [u8; CHANNELS] {
- *(self.buffer.get_unchecked(self.slice(x, y)).as_ptr().cast())
+ let idx = unsafe { self.slice(x, y) };
+ let ptr = unsafe { self.buffer.get_unchecked(idx).as_ptr().cast() };
+ unsafe { *ptr }
}
}
@@ -256,18 +258,30 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
/// Refer to [`slice`]
#[inline]
pub unsafe fn pixel_mut(&mut self, x: u32, y: u32) -> &mut [u8] {
- let idx = self.slice(x, y);
- self.buffer.get_unchecked_mut(idx)
+ let idx = unsafe { self.slice(x, y) };
+ unsafe { self.buffer.get_unchecked_mut(idx) }
}
#[inline]
pub fn chunked_mut(&mut self) -> impl Iterator<Item = &mut [u8; CHANNELS]> {
+ // SAFETY: 0 sized images are not allowed
+ unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
+ // SAFETY: buffer cannot have half pixels
+ unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
self.buffer.array_chunks_mut::<CHANNELS>()
}
+ /// Set the pixel at x, y
+ ///
+ /// # Safety
+ ///
+ /// UB if x, y is out of bounds.
#[inline]
pub unsafe fn set_pixel(&mut self, x: u32, y: u32, px: [u8; CHANNELS]) {
- std::ptr::copy_nonoverlapping(px.as_ptr(), self.pixel_mut(x, y).as_mut_ptr(), CHANNELS);
+ // SAFETY: Caller says that x, y is in bounds
+ let out = unsafe { self.pixel_mut(x, y) };
+ // SAFETY: px must be CHANNELS long
+ unsafe { std::ptr::copy_nonoverlapping(px.as_ptr(), out.as_mut_ptr(), CHANNELS) };
}
}
diff --git a/src/utils/image/overlay.rs b/src/utils/image/overlay.rs
index 9cad52e..4406a9b 100644
--- a/src/utils/image/overlay.rs
+++ b/src/utils/image/overlay.rs
@@ -1,4 +1,4 @@
-use super::{really_unsafe_index, unsafe_assert, Image};
+use super::{assert_unchecked, really_unsafe_index, Image};
use std::simd::SimdInt;
use std::simd::SimdPartialOrd;
use std::simd::{simd_swizzle, Simd};
@@ -28,8 +28,8 @@ pub unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) {
let mut srci = 0;
let mut dsti = 0;
while dsti + 16 <= rgb.len() {
- let old: Simd<u8, 16> = Simd::from_slice(rgb.get_unchecked(dsti..dsti + 16));
- let new: Simd<u8, 16> = Simd::from_slice(rgba.get_unchecked(srci..srci + 16));
+ let old: Simd<u8, 16> = Simd::from_slice(unsafe { rgb.get_unchecked(dsti..dsti + 16) });
+ let new: Simd<u8, 16> = Simd::from_slice(unsafe { rgba.get_unchecked(srci..srci + 16) });
let threshold = new.simd_ge(Simd::splat(128)).to_int().cast::<u8>();
let mut mask = simd_swizzle!(
@@ -40,19 +40,17 @@ pub unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) {
let new_rgb = simd_swizzle!(new, [0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0, 0, 0, 0]);
let blended = (new_rgb & mask) | (old & !mask);
- blended.copy_to_slice(rgb.get_unchecked_mut(dsti..dsti + 16));
+ blended.copy_to_slice(unsafe { rgb.get_unchecked_mut(dsti..dsti + 16) });
srci += 16;
dsti += 12;
}
while dsti + 3 <= rgb.len() {
- if *rgba.get_unchecked(srci + 3) >= 128 {
- std::ptr::copy_nonoverlapping(
- rgba.get_unchecked(srci..srci + 3).as_ptr(),
- rgb.get_unchecked_mut(dsti..dsti + 3).as_mut_ptr(),
- 3,
- );
+ if unsafe { *rgba.get_unchecked(srci + 3) } >= 128 {
+ let src = unsafe { rgba.get_unchecked(srci..srci + 3) };
+ let end = unsafe { rgb.get_unchecked_mut(dsti..dsti + 3) };
+ unsafe { std::ptr::copy_nonoverlapping(src.as_ptr(), end.as_mut_ptr(), 3) };
}
srci += 4;
@@ -62,8 +60,9 @@ pub unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) {
impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self {
- unsafe_assert!(x + with.width() <= self.width());
- unsafe_assert!(y + with.height() <= self.height());
+ // SAFETY: caller upholds these
+ unsafe { assert_unchecked!(x + with.width() <= self.width()) };
+ unsafe { assert_unchecked!(y + with.height() <= self.height()) };
for j in 0..with.height() {
let i_x = j as usize * with.width() as usize * 4
..(j as usize + 1) * with.width() as usize * 4;
@@ -72,10 +71,9 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
+ x as usize
+ with.width() as usize)
* 3;
- blit(
- self.buffer.get_unchecked_mut(o_x),
- with.buffer.get_unchecked(i_x),
- )
+ let rgb = unsafe { self.buffer.get_unchecked_mut(o_x) };
+ let rgba = unsafe { with.buffer.get_unchecked(i_x) };
+ unsafe { blit(rgb, rgba) }
}
self
}
@@ -83,19 +81,19 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> {
impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> {
unsafe fn overlay(&mut self, with: &Image<&[u8], 4>) -> &mut Self {
- unsafe_assert!(self.width() == with.width());
- unsafe_assert!(self.height() == with.height());
+ unsafe { assert_unchecked!(self.width() == with.width()) };
+ unsafe { assert_unchecked!(self.height() == with.height()) };
for (i, chunk) in with
.buffer
.chunks_exact(with.width() as usize * 4)
.enumerate()
{
- blit(
+ let rgb = unsafe {
self.buffer.get_unchecked_mut(
i * with.width() as usize * 3..(i + 1) * with.width() as usize * 3,
- ),
- chunk,
- );
+ )
+ };
+ unsafe { blit(rgb, chunk) };
}
self
}
@@ -105,18 +103,21 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> {
unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self {
for j in 0..with.height() {
for i in 0..with.width() {
- let with_index = really_unsafe_index(i, j, with.width()).unchecked_mul(4);
- let their_px = with
- .buffer
- .get_unchecked(with_index..with_index.unchecked_add(4));
- if *their_px.get_unchecked(3) >= 128 {
- let our_index =
- really_unsafe_index(i.unchecked_add(x), j.unchecked_add(y), self.width())
- .unchecked_mul(4);
- let our_px = self
- .buffer
- .get_unchecked_mut(our_index..our_index.unchecked_add(4));
- std::ptr::copy_nonoverlapping(their_px.as_ptr(), our_px.as_mut_ptr(), 4);
+ // TODO: see how many unchecked maths i can get rid of
+ let index_begin = unsafe { really_unsafe_index(i, j, with.width()) };
+ let index_begin = unsafe { index_begin.unchecked_mul(4) };
+ let index_end = unsafe { index_begin.unchecked_add(4) };
+ let their_px = unsafe { with.buffer.get_unchecked(index_begin..index_end) };
+ if unsafe { *their_px.get_unchecked(3) } >= 128 {
+ let x = unsafe { i.unchecked_add(x) };
+ let y = unsafe { j.unchecked_add(y) };
+ let index_begin = unsafe { really_unsafe_index(x, y, self.width()) };
+ let index_begin = unsafe { index_begin.unchecked_mul(4) };
+ let index_end = unsafe { index_begin.unchecked_add(4) };
+ let our_px = unsafe { self.buffer.get_unchecked_mut(index_begin..index_end) };
+ unsafe {
+ std::ptr::copy_nonoverlapping(their_px.as_ptr(), our_px.as_mut_ptr(), 4)
+ };
}
}
}