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
use image::{imageops, Rgb, Rgba, RgbaImage};

pub trait ImageUtils {
    /// Tint this image with the color
    fn tint(&mut self, color: Rgb<u8>) -> &mut Self;
    /// Repeat with over self
    fn repeat(&mut self, with: &Self) -> &mut Self;
    /// Overlay with onto self (does not blend)
    fn overlay(&mut self, with: &Self) -> &mut Self;
    /// Overlay with onto self at coordinates x, y, without blending
    fn overlay_at(&mut self, with: &Self, x: u32, y: u32) -> &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
    #[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
    fn shadow(&mut self) -> &mut Self;
    /// silhouette
    #[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
    fn silhouette(&mut self) -> &mut Self;
    /// scale a image
    fn scale(&self, to: u32) -> Self;
}

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 repeat(&mut self, with: &RgbaImage) -> &mut Self {
        for x in 0..(self.width() / with.width()) {
            for y in 0..(self.height() / with.height()) {
                self.overlay_at(with, x * with.width(), y * with.height());
            }
        }
        self
    }

    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() {
                use image::{GenericImage, GenericImageView};
                let get = unsafe { with.unsafe_get_pixel(i, j) };
                if get[3] > 128 {
                    unsafe { self.unsafe_put_pixel(i + x, j + y, get) };
                }
            }
        }
        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)
    }

    #[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
    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
    }

    #[cfg(any(feature = "map_shadow", feature = "schem_shadow"))]
    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 {
                    use image::GenericImageView;
                    // SAFETY: yes
                    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
    }
}