Skip to main content

maple_render_core/
input.rs

1use std::path::Path;
2
3use ab_glyph::{Font, FontRef, PxScale, ScaleFont};
4use image::{Rgba, RgbaImage};
5use imageproc::drawing::draw_text_mut;
6
7use crate::error::{Error, Result};
8
9#[derive(Clone)]
10pub struct TextOptions {
11    pub font_size: f32,
12    pub color: Rgba<u8>,
13    pub background: Rgba<u8>,
14    pub padding: u32,
15}
16
17impl Default for TextOptions {
18    fn default() -> Self {
19        TextOptions {
20            font_size: 72.0,
21            color: Rgba([0, 0, 0, 255]),
22            background: Rgba([255, 255, 255, 255]),
23            padding: 40,
24        }
25    }
26}
27
28#[derive(Clone)]
29pub struct Input {
30    image: RgbaImage,
31    pub layer: u8,
32    pub xs: f64,
33    pub ys: f64,
34    pub xo: f64,
35    pub yo: f64,
36    pub xa: f64,
37    pub ya: f64,
38    pub in_scale: i32,
39    pub in_x0: f64,
40    pub in_y0: f64,
41}
42
43impl Default for Input {
44    fn default() -> Self {
45        Input {
46            image: RgbaImage::new(1, 1),
47            layer: 1,
48            xs: 1.0,
49            ys: 1.0,
50            xo: 0.0,
51            yo: 0.0,
52            xa: 1.0,
53            ya: 0.0,
54            in_scale: 0,
55            in_x0: 0.0,
56            in_y0: 0.0,
57        }
58    }
59}
60
61impl Input {
62    pub fn new() -> Self {
63        Input::default()
64    }
65
66    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
67        Self::load_with_layer(path, 1)
68    }
69
70    pub fn load_with_layer<P: AsRef<Path>>(path: P, layer: u8) -> Result<Self> {
71        let path = path.as_ref();
72
73        if !path.exists() {
74            return Err(Error::FileNotFound(path.to_path_buf()));
75        }
76
77        let img = image::open(path)?;
78        let rgba = img.to_rgba8();
79
80        let mut input = Input { image: rgba, layer, ..Default::default() };
81
82        input.compute_scale_params();
83
84        Ok(input)
85    }
86
87    pub fn from_image(image: RgbaImage, layer: u8) -> Self {
88        let mut input = Input { image, layer, ..Default::default() };
89        input.compute_scale_params();
90        input
91    }
92
93    pub fn from_text(
94        text: &str,
95        layer: u8,
96        options: &TextOptions,
97        font_data: &[u8],
98    ) -> Result<Self> {
99        let font = FontRef::try_from_slice(font_data)
100            .map_err(|e| Error::TextRender(format!("Failed to load font: {}", e)))?;
101
102        let scale = PxScale::from(options.font_size);
103        let scaled_font = font.as_scaled(scale);
104
105        let mut width = 0.0f32;
106        for c in text.chars() {
107            let glyph_id = font.glyph_id(c);
108            width += scaled_font.h_advance(glyph_id);
109        }
110
111        let height = scaled_font.height();
112        let img_width = (width.ceil() as u32).max(1) + options.padding * 2;
113        let img_height = (height.ceil() as u32).max(1) + options.padding * 2;
114
115        let mut image = RgbaImage::from_pixel(img_width, img_height, options.background);
116
117        let x = options.padding as i32;
118        let y = options.padding as i32;
119
120        draw_text_mut(&mut image, options.color, x, y, scale, &font, text);
121
122        Ok(Input::from_image(image, layer))
123    }
124
125    fn compute_scale_params(&mut self) {
126        let w = self.image.width() as i32;
127        let h = self.image.height() as i32;
128
129        self.in_scale = w;
130        self.in_x0 = 0.0;
131        self.in_y0 = 0.0;
132
133        if h > w {
134            self.in_scale = h;
135            self.in_x0 = -((-w + h) as f64) / 2.0;
136        } else {
137            self.in_y0 = -((w - h) as f64) / 2.0;
138        }
139
140        self.xa = 1.0;
141        self.ya = 0.0;
142    }
143
144    pub fn get(&self) -> &RgbaImage {
145        &self.image
146    }
147
148    pub fn get_mut(&mut self) -> &mut RgbaImage {
149        &mut self.image
150    }
151
152    pub fn width(&self) -> u32 {
153        self.image.width()
154    }
155
156    pub fn height(&self) -> u32 {
157        self.image.height()
158    }
159
160    pub fn safe_pixel(&self, x: i32, y: i32) -> Rgba<u8> {
161        let w = self.image.width() as i32;
162        let h = self.image.height() as i32;
163
164        if x < 0 || y < 0 || x >= w || y >= h {
165            Rgba([0, 0, 0, 0])
166        } else {
167            *self.image.get_pixel(x as u32, y as u32)
168        }
169    }
170
171    pub fn set_rotation(&mut self, theta: f64) {
172        self.xa = theta.cos();
173        self.ya = theta.sin();
174    }
175}
176
177#[derive(Clone, Default)]
178pub struct Inputs {
179    data: Vec<Input>,
180}
181
182impl Inputs {
183    pub fn new() -> Self {
184        Inputs { data: Vec::new() }
185    }
186
187    pub fn add(&mut self) -> &mut Input {
188        self.data.push(Input::new());
189        self.data.last_mut().unwrap()
190    }
191
192    pub fn push(&mut self, input: Input) {
193        self.data.push(input);
194    }
195
196    pub fn get(&self) -> &[Input] {
197        &self.data
198    }
199
200    pub fn get_mut(&mut self) -> &mut [Input] {
201        &mut self.data
202    }
203
204    pub fn len(&self) -> usize {
205        self.data.len()
206    }
207
208    pub fn is_empty(&self) -> bool {
209        self.data.is_empty()
210    }
211
212    pub fn iter(&self) -> impl Iterator<Item = &Input> {
213        self.data.iter()
214    }
215
216    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Input> {
217        self.data.iter_mut()
218    }
219}
220
221impl std::ops::Index<usize> for Inputs {
222    type Output = Input;
223
224    fn index(&self, index: usize) -> &Self::Output {
225        &self.data[index]
226    }
227}
228
229impl std::ops::IndexMut<usize> for Inputs {
230    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
231        &mut self.data[index]
232    }
233}