sar_core/renderer/
resource.rs

1use std::collections::HashMap;
2
3use crate::core::result::Result;
4use image::{imageops, DynamicImage, SubImage};
5
6use crate::core::symbol::SymbolId;
7
8const SYMBOLS_R: &[u8] = include_bytes!("../../assets/symbols_r.png");
9const SYMBOLS_G: &[u8] = include_bytes!("../../assets/symbols_g.png");
10const SYMBOLS_B: &[u8] = include_bytes!("../../assets/symbols_b.png");
11const SYMBOLS_COLOR: &[u8] = include_bytes!("../../assets/symbols_color.png");
12
13const SYMBOL_PIXELS: u32 = 64;
14const SYMBOL_WIDTH_NUM: u32 = 16;
15
16#[derive(Eq, Hash, PartialEq)]
17enum ImageSheet {
18    R,
19    G,
20    B,
21    Color,
22}
23
24pub struct Resource {
25    sheets: HashMap<ImageSheet, DynamicImage>,
26    pub symbol_pixels: u32,
27}
28
29impl Resource {
30    pub fn new() -> Result<Self> {
31        let r = image::load_from_memory(SYMBOLS_R)?;
32        let g = image::load_from_memory(SYMBOLS_G)?;
33        let b = image::load_from_memory(SYMBOLS_B)?;
34        let color = image::load_from_memory(SYMBOLS_COLOR)?;
35
36        let mut sheets = HashMap::new();
37        sheets.insert(ImageSheet::R, r);
38        sheets.insert(ImageSheet::G, g);
39        sheets.insert(ImageSheet::B, b);
40        sheets.insert(ImageSheet::Color, color);
41
42        Ok(Self {
43            sheets,
44            symbol_pixels: SYMBOL_PIXELS,
45        })
46    }
47
48    pub(crate) fn get_image(&self, id: SymbolId) -> Option<Image> {
49        let index = ImageIndex::get(id)?;
50        let sheet = self.sheets.get(&index.sheet)?;
51        let (x, y) = Self::get_coordinates(&index);
52        let image = imageops::crop_imm(sheet, x, y, SYMBOL_PIXELS, SYMBOL_PIXELS);
53        Some(Image::new_with_sheet(image, index))
54    }
55
56    fn get_coordinates(index: &ImageIndex) -> (u32, u32) {
57        let x = index.index % SYMBOL_WIDTH_NUM * SYMBOL_PIXELS;
58        let y = index.index / SYMBOL_WIDTH_NUM * SYMBOL_PIXELS;
59        (x, y)
60    }
61}
62
63pub(crate) enum Image<'a> {
64    R(SubImage<&'a DynamicImage>),
65    G(SubImage<&'a DynamicImage>),
66    B(SubImage<&'a DynamicImage>),
67    Color(SubImage<&'a DynamicImage>),
68}
69
70impl<'a> Image<'a> {
71    pub(crate) fn inner(&self) -> &SubImage<&'a DynamicImage> {
72        match self {
73            Image::R(image) => image,
74            Image::G(image) => image,
75            Image::B(image) => image,
76            Image::Color(image) => image,
77        }
78    }
79}
80
81impl std::fmt::Debug for Image<'_> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            Image::R(_) => write!(f, "R"),
85            Image::G(_) => write!(f, "G"),
86            Image::B(_) => write!(f, "B"),
87            Image::Color(_) => write!(f, "Color"),
88        }
89    }
90}
91
92impl<'a> Image<'a> {
93    fn new_with_sheet(image: SubImage<&'a DynamicImage>, index: ImageIndex) -> Self {
94        match index.sheet {
95            ImageSheet::R => Self::R(image),
96            ImageSheet::G => Self::G(image),
97            ImageSheet::B => Self::B(image),
98            ImageSheet::Color => Self::Color(image),
99        }
100    }
101}
102
103struct ImageIndex {
104    sheet: ImageSheet,
105    index: u32,
106}
107
108impl ImageIndex {
109    fn get(id: SymbolId) -> Option<Self> {
110        match id.id() {
111            id @ 1..=80 => Some(Self {
112                sheet: ImageSheet::R,
113                index: id,
114            }),
115            id @ 240..=480 => {
116                let offset = match id {
117                    305..400 => 240 + 16,
118                    _ if id > 400 => 240 + 16 * 2,
119                    _ => 240,
120                };
121                Some(Self {
122                    sheet: ImageSheet::G,
123                    index: id - offset,
124                })
125            }
126            id @ 481..=720 => {
127                let offset = match id {
128                    561..641 => 480 + 16,
129                    _ if id > 641 => 480 + 16 * 2,
130                    _ => 480,
131                };
132                Some(Self {
133                    sheet: ImageSheet::B,
134                    index: id - offset,
135                })
136            }
137            id @ 721..=768 => Some(Self {
138                sheet: ImageSheet::Color,
139                index: id - 720,
140            }),
141            _ => None,
142        }
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use image::GenericImageView;
149
150    use super::*;
151
152    #[test]
153    fn test_get_image_index() {
154        let index = ImageIndex::get(SymbolId::new(1));
155        assert!(index.is_some());
156    }
157
158    #[test]
159    fn test_get_image() {
160        let resource = Resource::new().unwrap();
161        let image = resource.get_image(SymbolId::new(40)).unwrap();
162        let image2 = resource.get_image(SymbolId::new(41)).unwrap();
163        if let Image::R(image) = image {
164            assert_eq!(image.width(), SYMBOL_PIXELS);
165        } else {
166            panic!("image is not R");
167        }
168        if let Image::R(image) = image2 {
169            assert_eq!(image.width(), SYMBOL_PIXELS);
170        } else {
171            panic!("image2 is not R");
172        }
173    }
174}