1#![crate_name = "sprite_gen"]
2
3use hsl::HSL;
4use randomize::{formulas, PCG32};
5
6#[derive(Debug, Clone, Eq, PartialEq)]
8pub enum MaskValue {
9 Solid,
11 Empty,
13 Body1,
15 Body2,
17}
18
19impl MaskValue {
20 pub fn i8(&self) -> i8 {
21 match self {
22 MaskValue::Solid => -1,
23 MaskValue::Empty => 0,
24 MaskValue::Body1 => 1,
25 MaskValue::Body2 => 2,
26 }
27 }
28}
29
30impl From<MaskValue> for i8 {
31 fn from(from: MaskValue) -> Self {
32 from.i8()
33 }
34}
35
36impl From<i8> for MaskValue {
37 fn from(from: i8) -> Self {
38 match from {
39 -1 => MaskValue::Solid,
40 1 => MaskValue::Body1,
41 2 => MaskValue::Body2,
42 _ => MaskValue::Empty,
43 }
44 }
45}
46
47impl Default for MaskValue {
48 fn default() -> Self {
49 MaskValue::Empty
50 }
51}
52
53#[derive(Debug, Copy, Clone)]
55pub struct Options {
56 pub mirror_x: bool,
58 pub mirror_y: bool,
60 pub colored: bool,
63 pub edge_brightness: f32,
65 pub color_variations: f32,
67 pub brightness_noise: f32,
69 pub saturation: f32,
71 pub seed: u64,
73}
74
75impl Default for Options {
76 fn default() -> Self {
85 Options {
86 mirror_x: false,
87 mirror_y: false,
88 colored: true,
89 edge_brightness: 0.3,
90 color_variations: 0.2,
91 brightness_noise: 0.3,
92 saturation: 0.5,
93 seed: 0,
94 }
95 }
96}
97
98pub fn gen_sprite<T>(mask_buffer: &[T], mask_width: usize, options: Options) -> Vec<u32>
115where
116 T: Into<i8> + Clone,
117{
118 let mask_height = mask_buffer.len() / mask_width;
119
120 let mut mask: Vec<i8> = mask_buffer
122 .iter()
123 .map(|v| std::convert::Into::into(v.clone()))
124 .collect::<_>();
125
126 let mut rng = PCG32::seed(options.seed, 5);
127
128 for val in mask.iter_mut() {
131 if *val == 1 {
132 *val = formulas::f32_closed(rng.next_u32()).round() as i8;
134 } else if *val == 2 {
135 *val = formulas::f32_closed_neg_pos(rng.next_u32()).signum() as i8;
137 }
138 }
139
140 for y in 0..mask_height {
142 for x in 0..mask_width {
143 let index = x + y * mask_width;
144 if mask[index] <= 0 {
145 continue;
146 }
147
148 if y > 0 && mask[index - mask_width] == 0 {
149 mask[index - mask_width] = -1;
150 }
151 if y < mask_height - 1 && mask[index + mask_width] == 0 {
152 mask[index + mask_width] = -1;
153 }
154 if x > 0 && mask[index - 1] == 0 {
155 mask[index - 1] = -1;
156 }
157 if x < mask_width - 1 && mask[index + 1] == 0 {
158 mask[index + 1] = -1;
159 }
160 }
161 }
162
163 let colored: Vec<u32> = if options.colored {
165 color_output(&mask, (mask_width, mask_height), &options, &mut rng)
166 } else {
167 onebit_output(&mask)
168 };
169
170 if options.mirror_x && options.mirror_y {
172 let width = mask_width * 2;
174 let height = mask_height * 2;
175 let mut result = vec![0; width * height];
176
177 for y in 0..mask_height {
178 for x in 0..mask_width {
179 let index = x + y * mask_width;
180 let value = colored[index];
181
182 let index = x + y * width;
183 result[index] = value;
184
185 let index = (width - x - 1) + y * width;
186 result[index] = value;
187
188 let index = x + (height - y - 1) * width;
189 result[index] = value;
190
191 let index = (width - x - 1) + (height - y - 1) * width;
192 result[index] = value;
193 }
194 }
195
196 return result;
197 } else if options.mirror_x {
198 let width = mask_width * 2;
200 let mut result = vec![0; width * mask_height];
201
202 for y in 0..mask_height {
203 for x in 0..mask_width {
204 let index = x + y * mask_width;
205 let value = colored[index];
206
207 let index = x + y * width;
208 result[index] = value;
209
210 let index = (width - x - 1) + y * width;
211 result[index] = value;
212 }
213 }
214
215 return result;
216 } else if options.mirror_y {
217 let height = mask_height * 2;
219 let mut result = vec![0; mask_width * height];
220
221 for y in 0..mask_height {
222 for x in 0..mask_width {
223 let index = x + y * mask_width;
224 let value = colored[index];
225 result[index] = value;
226
227 let index = x + (height - y - 1) * mask_width;
228 result[index] = value;
229 }
230 }
231
232 return result;
233 }
234
235 colored
236}
237
238#[inline]
239fn onebit_output(mask: &[i8]) -> Vec<u32> {
240 mask.iter()
241 .map(|&v| match v {
242 -1 => 0,
243 _ => 0xFF_FF_FF_FF,
244 })
245 .collect()
246}
247
248#[inline]
249fn color_output(
250 mask: &[i8],
251 mask_size: (usize, usize),
252 options: &Options,
253 rng: &mut PCG32,
254) -> Vec<u32> {
255 let mut result = vec![0xFF_FF_FF_FF; mask.len()];
256
257 let is_vertical_gradient = formulas::f32_closed_neg_pos(rng.next_u32()) > 0.0;
258 let saturation = formulas::f32_closed(rng.next_u32()) * options.saturation;
259 let mut hue = formulas::f32_closed(rng.next_u32());
260
261 let variation_check = 1.0 - options.color_variations;
262 let brightness_inv = 1.0 - options.brightness_noise;
263
264 let uv_size = if is_vertical_gradient {
265 (mask_size.1, mask_size.0)
266 } else {
267 mask_size
268 };
269
270 for u in 0..uv_size.0 {
271 let is_new_color = (formulas::f32_closed(rng.next_u32())
273 + formulas::f32_closed(rng.next_u32())
274 + formulas::f32_closed(rng.next_u32()))
275 / 3.0;
276
277 if is_new_color > variation_check {
278 hue = formulas::f32_closed(rng.next_u32());
279 }
280
281 let u_sin = ((u as f32 / uv_size.0 as f32) * std::f32::consts::PI).sin();
282
283 for v in 0..uv_size.1 {
284 let index = if is_vertical_gradient {
285 v + u * mask_size.0
286 } else {
287 u + v * mask_size.0
288 };
289
290 let val = mask[index];
291 if val == 0 {
292 continue;
293 }
294
295 let brightness = u_sin * brightness_inv
296 + formulas::f32_closed(rng.next_u32()) * options.brightness_noise;
297
298 let mut rgb = HSL {
299 h: hue as f64 * 360.0,
300 s: saturation as f64,
301 l: brightness as f64,
302 }
303 .to_rgb();
304
305 if val == -1 {
307 rgb.0 = (rgb.0 as f32 * options.edge_brightness) as u8;
308 rgb.1 = (rgb.1 as f32 * options.edge_brightness) as u8;
309 rgb.2 = (rgb.2 as f32 * options.edge_brightness) as u8;
310 }
311
312 result[index] = ((rgb.0 as u32) << 16) | ((rgb.1 as u32) << 8) | (rgb.2 as u32);
313 }
314 }
315
316 result
317}