maple_render_core/
input.rs1use 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}