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
use image::*;

pub trait Overlay<W> {
    /// Overlay with onto 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 onto 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;
}

impl Overlay<RgbImage> for RgbImage {
    fn overlay_at(&mut self, with: &RgbImage, x: u32, y: u32) -> &mut Self {
        for j in 0..with.height() {
            for i in 0..with.width() {
                #[cfg(debug_assertions)]
                {
                    let get = *with.get_pixel(i, j);
                    self.put_pixel(i + x, j + y, get);
                }
                #[cfg(not(debug_assertions))]
                {
                    let get = unsafe { with.unsafe_get_pixel(i, j) };
                    unsafe { self.unsafe_put_pixel(i + x, j + y, get) };
                }
            }
        }
        self
    }
}

impl Overlay<RgbaImage> for RgbImage {
    fn overlay_at(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self {
        for j in 0..with.height() {
            for i in 0..with.width() {
                let get = unsafe { with.unsafe_get_pixel(i, j) };
                // solidity
                if get[3] > 128 {
                    unsafe { self.unsafe_put_pixel(i + x, j + y, Rgb([get[0], get[1], get[2]])) };
                }
            }
        }
        self
    }
}

impl Overlay<RgbaImage> for RgbaImage {
    fn overlay_at(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self {
        for j in 0..with.height() {
            for i in 0..with.width() {
                let get = unsafe { with.unsafe_get_pixel(i, j) };
                if get[3] > 128 {
                    unsafe { self.unsafe_put_pixel(i + x, j + y, get) };
                }
            }
        }
        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 {
        if self.len() % 4 != 0 || with.len() % 4 != 0 {
            unsafe { std::hint::unreachable_unchecked() };
        }
        for (i, other_pixels) in with.array_chunks::<4>().enumerate() {
            if other_pixels[3] > 128 {
                let own_pixels = unsafe { self.get_unchecked_mut(i * 4..i * 4 + 4) };
                own_pixels.copy_from_slice(other_pixels);
            }
        }
        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
    }
}