mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/utils/image/affine.rs')
-rw-r--r--src/utils/image/affine.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/utils/image/affine.rs b/src/utils/image/affine.rs
new file mode 100644
index 0000000..04a6180
--- /dev/null
+++ b/src/utils/image/affine.rs
@@ -0,0 +1,194 @@
+use super::Image;
+
+/// Rotate a image 180 degrees clockwise.
+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);
+ }
+ }
+ }
+
+ if img.height() % 2 != 0 {
+ 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);
+ }
+ }
+ }
+}
+
+/// # Safety
+///
+/// UB if supplied image rectangular
+unsafe fn transpose<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
+ debug_assert_eq!(img.width(), img.height());
+ let size = img.width();
+ 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,
+ );
+ }
+ }
+ }
+}
+
+/// Rotate a image 90 degrees clockwise.
+/// This is done by first [flipping vertically](flip_v), then [transposing](transpose) the image, to save allocations.
+///
+/// # Safety
+///
+/// UB if the image is not square
+#[inline]
+pub unsafe fn rot_90<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
+ flip_v(img);
+ transpose(img);
+}
+
+/// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise.
+/// [horizontal flip](flip_h), then [transpose].
+///
+/// # Safety
+///
+/// UB if the image is not square
+#[inline]
+pub unsafe fn rot_270<const CHANNELS: usize>(img: &mut Image<&mut [u8], CHANNELS>) {
+ flip_h(img);
+ 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);
+ }
+ }
+ }
+}
+
+/// Flip a image horizontally.
+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);
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::utils::image::img;
+
+ #[test]
+ fn rotate_90() {
+ let mut from = img![
+ [00, 01]
+ [02, 10]
+ ];
+ unsafe { rot_90(&mut from.as_mut()) };
+ assert_eq!(
+ from,
+ img![
+ [02, 00]
+ [10, 01]
+ ]
+ );
+ }
+
+ #[test]
+ fn rotate_180() {
+ let mut from = img![
+ [00, 01]
+ [02, 10]
+ ];
+ rot_180(&mut from.as_mut());
+ assert_eq!(
+ from,
+ img![
+ [10, 02]
+ [01, 00]
+ ]
+ );
+ }
+
+ #[test]
+ fn rotate_270() {
+ let mut from = img![
+ [00, 01]
+ [20, 10]
+ ];
+ unsafe { rot_270(&mut from.as_mut()) };
+ assert_eq!(
+ from,
+ img![
+ [01, 10]
+ [00, 20]
+ ]
+ );
+ }
+
+ #[test]
+ fn flip_vertical() {
+ let mut from = img![
+ [90, 01]
+ [21, 42]
+ ];
+ flip_v(&mut from.as_mut());
+ assert_eq!(
+ from,
+ img![
+ [21, 42]
+ [90, 01]
+ ]
+ )
+ }
+ #[test]
+ fn flip_horizontal() {
+ let mut from = img![
+ [90, 01]
+ [21, 42]
+ ];
+ flip_h(&mut from.as_mut());
+ assert_eq!(
+ from,
+ img![
+ [01, 90]
+ [42, 21]
+ ]
+ )
+ }
+}