mindustry logic execution, map- and schematic- parsing and rendering
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use image::*;

pub trait Overlay<W> {
    /// Overlay with => self at coordinates x, y, without blending
    fn overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self;
}

pub trait RepeatNew {
    /// Repeat with over self
    fn repeated(with: &Self, x: u32, y: u32) -> Self;
}

pub trait ImageUtils {
    /// Tint this image with the color
    fn tint(&mut self, color: Rgb<u8>) -> &mut Self;
    /// Overlay with => self (does not blend)
    fn overlay(&mut self, with: &Self) -> &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
    fn shadow(&mut self) -> &mut Self;
    /// silhouette
    fn silhouette(&mut self) -> &mut Self;
    /// scale a image
    fn scale(&self, to: u32) -> Self;
}

macro_rules! unsafe_assert {
    ($cond:expr) => {{
        if !$cond {
            unsafe { std::hint::unreachable_unchecked() }
        }
    }};
}

impl Overlay<RgbImage> for RgbImage {
    fn overlay_at(&mut self, with: &RgbImage, x: u32, y: u32) -> &mut Self {
        unsafe_assert!(with.height() != 0);
        unsafe_assert!(with.width() != 0);
        for j in 0..with.height() {
            for i in 0..with.width() {
                unsafe {
                    let with_index = really_unsafe_index(i, j, with.width()).unchecked_mul(3);
                    let their_px = with.get_unchecked(with_index..with_index.unchecked_add(3));
                    let our_index =
                        really_unsafe_index(i.unchecked_add(x), j.unchecked_add(y), self.width())
                            .unchecked_mul(3);
                    let our_px = self.get_unchecked_mut(our_index..our_index.unchecked_add(3));
                    std::ptr::copy_nonoverlapping(their_px.as_ptr(), our_px.as_mut_ptr(), 3);
                }
            }
        }
        self
    }
}

impl Overlay<RgbaImage> for RgbImage {
    fn overlay_at(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self {
        // TODO NonZeroU32
        unsafe_assert!(with.height() != 0);
        unsafe_assert!(with.width() != 0);
        for j in 0..with.height() {
            for i in 0..with.width() {
                unsafe {
                    let with_index = really_unsafe_index(i, j, with.width()).unchecked_mul(4);
                    // solidity
                    if with.get_unchecked(with_index.unchecked_add(3)) > &128 {
                        let their_px = with.get_unchecked(with_index..with_index.unchecked_add(3));
                        let our_index = really_unsafe_index(
                            i.unchecked_add(x),
                            j.unchecked_add(y),
                            self.width(),
                        )
                        .unchecked_mul(3);
                        let our_px = self.get_unchecked_mut(our_index..our_index.unchecked_add(3));
                        std::ptr::copy_nonoverlapping(their_px.as_ptr(), our_px.as_mut_ptr(), 3);
                    }
                }
            }
        }
        self
    }
}

impl Overlay<RgbaImage> for RgbaImage {
    fn overlay_at(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self {
        unsafe_assert!(with.height() != 0);
        unsafe_assert!(with.width() != 0);
        for j in 0..with.height() {
            for i in 0..with.width() {
                unsafe {
                    let with_index = really_unsafe_index(i, j, with.width()).unchecked_mul(4);
                    let their_px = with.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.get_unchecked_mut(our_index..our_index.unchecked_add(4));
                        std::ptr::copy_nonoverlapping(their_px.as_ptr(), our_px.as_mut_ptr(), 4);
                    }
                }
            }
        }
        self
    }
}

impl RepeatNew for RgbImage {
    fn repeated(with: &Self, x: u32, y: u32) -> Self {
        let mut img = RgbImage::new(x, y); // could probably optimize this a ton but eh
        for x in 0..(x / with.width()) {
            for y in 0..(y / with.height()) {
                img.overlay_at(with, x * with.width(), y * with.height());
            }
        }
        img
    }
}

impl ImageUtils for RgbaImage {
    fn rotate(&mut self, times: u8) -> &mut Self {
        use image::imageops::{rotate180, rotate270, rotate90};
        match times {
            2 => *self = rotate180(self),
            1 => *self = rotate90(self),
            3 => *self = rotate270(self),
            _ => {}
        }
        self
    }

    fn tint(&mut self, color: Rgb<u8>) -> &mut Self {
        let [tr, tg, tb] = [
            color[0] as f32 / 255.0,
            color[1] as f32 / 255.0,
            color[2] as f32 / 255.0,
        ];
        for Rgba([r, g, b, _]) in self.pixels_mut() {
            *r = (*r as f32 * tr) as u8;
            *g = (*g as f32 * tg) as u8;
            *b = (*b as f32 * tb) as u8;
        }
        self
    }

    fn overlay(&mut self, with: &RgbaImage) -> &mut Self {
        debug_assert_eq!(self.width(), with.width());
        debug_assert_eq!(self.height(), with.height());
        if self.len() % 4 != 0 || with.len() % 4 != 0 || with.height() == 0 || with.width() == 0 {
            unsafe { std::hint::unreachable_unchecked() };
        }
        for (i, other_pixels) in with.array_chunks::<4>().enumerate() {
            if other_pixels[3] > 128 {
                unsafe {
                    let own_pixels = self
                        .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,
                    );
                }
            }
        }
        self
    }

    fn scale(&self, to: u32) -> Self {
        imageops::resize(self, to, to, imageops::Nearest)
    }

    fn silhouette(&mut self) -> &mut Self {
        for pixel in self.pixels_mut() {
            if pixel[3] < 128 {
                pixel[2] /= 10;
                pixel[1] /= 10;
                pixel[0] /= 10;
            }
        }
        self
    }

    fn shadow(&mut self) -> &mut Self {
        let mut shadow = self.clone();
        shadow.silhouette();
        let samples = shadow.as_flat_samples_mut();
        blurslice::gaussian_blur_bytes::<4>(
            samples.samples,
            self.width() as usize,
            self.height() as usize,
            9.0,
        )
        .unwrap();
        for x in 0..shadow.width() {
            for y in 0..shadow.height() {
                let Rgba([r, g, b, a]) = self.get_pixel_mut(x, y);
                if *a == 0 {
                    let p = unsafe { shadow.unsafe_get_pixel(x, y) };
                    *r = p[0];
                    *g = p[0];
                    *b = p[0];
                    *a = p[1];
                }
            }
        }
        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
    }
}

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)
}