1pub fn effect_name_to_index(name: &str) -> Option<usize> {
3 let name = name.to_ascii_lowercase();
4 (0..EFFECT_COUNT).find(|&idx| effect_name(idx).to_ascii_lowercase() == name)
5}
6use egui::Color32;
10
11pub const EFFECT_COUNT: usize = 38;
12
13pub fn effect_name(index: usize) -> &'static str {
14 match index {
15 0 => "Normal",
16 1 => "Invert",
17 2 => "Grayscale",
18 3 => "Plasma (horizontal)",
19 4 => "Plasma (vertical)",
20 5 => "Plasma (circle)",
21 6 => "Plasma (diagonal)",
22 7 => "Rainbow (horizontal)",
23 8 => "Rainbow (vertical)",
24 9 => "Rainbow (circle)",
25 10 => "Rainbow (diagonal)",
26 11 => "Waves (horizontal)",
27 12 => "Waves (vertical)",
28 13 => "Waves (circle)",
29 14 => "Waves (diagonal)",
30 15 => "Noise (horizontal)",
31 16 => "Noise (vertical)",
32 17 => "Noise (circle)",
33 18 => "Noise (diagonal)",
34 19 => "Normal (inverted input)",
36 20 => "Invert (inverted input)",
37 21 => "Grayscale (inverted input)",
38 22 => "Plasma (horizontal, inverted input)",
39 23 => "Plasma (vertical, inverted input)",
40 24 => "Plasma (circle, inverted input)",
41 25 => "Plasma (diagonal, inverted input)",
42 26 => "Rainbow (horizontal, inverted input)",
43 27 => "Rainbow (vertical, inverted input)",
44 28 => "Rainbow (circle, inverted input)",
45 29 => "Rainbow (diagonal, inverted input)",
46 30 => "Waves (horizontal, inverted input)",
47 31 => "Waves (vertical, inverted input)",
48 32 => "Waves (circle, inverted input)",
49 33 => "Waves (diagonal, inverted input)",
50 34 => "Noise (horizontal, inverted input)",
51 35 => "Noise (vertical, inverted input)",
52 36 => "Noise (circle, inverted input)",
53 37 => "Noise (diagonal, inverted input)",
54 _ => "Unknown",
55 }
56}
57
58#[allow(clippy::too_many_arguments)]
59pub fn apply_effect(
60 index: usize,
61 r: u8,
62 g: u8,
63 b: u8,
64 a: u8,
65 x: usize,
66 y: usize,
67 w: usize,
68 h: usize,
69 t: f32,
70 zebra_offset: usize,
71) -> Color32 {
72 let fx = x as f32;
73 let fy = y as f32;
74 let cx = (w as f32) / 2.0;
75 let cy = (h as f32) / 2.0;
76 let dist = ((fx - cx).powi(2) + (fy - cy).powi(2)).sqrt();
77 let diag = (fx + fy) * 0.5;
78 if index >= 19 {
81 if r <= 32 && g <= 32 && b <= 32 {
83 let (r, g, b) = (255, 255, 255);
85 return match index % 19 {
86 0 => Color32::from_rgba_unmultiplied(r, g, b, a),
87 1 => Color32::from_rgba_unmultiplied(255 - r, 255 - g, 255 - b, a),
88 2 => {
89 let avg = ((r as u16 + g as u16 + b as u16) / 3) as u8;
90 Color32::from_rgba_unmultiplied(avg, avg, avg, a)
91 }
92 3 => {
93 let v = (fx * 0.08 + t * 0.12).sin() + (fy * 0.08 + t * 0.15).cos();
94 let v = v * 0.5;
95 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
96 let g = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
97 let b = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
98 Color32::from_rgba_unmultiplied(r, g, b, a)
99 }
100 4 => {
101 let v = (fy * 0.08 + t * 0.12).sin() + (fx * 0.08 + t * 0.15).cos();
102 let v = v * 0.5;
103 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
104 let g = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
105 let b = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
106 Color32::from_rgba_unmultiplied(r, g, b, a)
107 }
108 5 => {
109 let theta = (fy - cy).atan2(fx - cx);
110 let v = (dist * 0.15 + t * 0.12).sin() + (theta * 6.0 + t * 0.2).cos();
111 let v = v * 0.5;
112 let r = ((v * 127.0 + 128.0) as u8)
113 .saturating_add((theta.sin() * 127.0 + 128.0) as u8);
114 let g = ((v * 127.0 + 128.0) as u8)
115 .saturating_add((theta.cos() * 127.0 + 128.0) as u8);
116 let b = ((v * 127.0 + 128.0) as u8)
117 .saturating_add((dist.sin() * 127.0 + 128.0) as u8);
118 Color32::from_rgba_unmultiplied(r, g, b, a)
119 }
120 6 => {
121 let v = (diag * 0.08 + t * 0.12).sin() + (t * 0.15).cos();
122 let v = v * 0.5;
123 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
124 let g = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
125 let b = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
126 Color32::from_rgba_unmultiplied(r, g, b, a)
127 }
128 7 => {
129 let r = (fx * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
130 let g = (fy * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
131 let b = ((fx + fy) * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
132 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
133 }
134 8 => {
135 let r = (fy * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
136 let g = (fx * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
137 let b = ((fx + fy) * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
138 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
139 }
140 9 => {
141 let theta = (fy - cy).atan2(fx - cx);
142 let r = (dist * 0.18 + t * 0.2).sin() * 127.0 + 128.0;
143 let g = (theta * 4.0 + t * 0.3).cos() * 127.0 + 128.0;
144 let b = (dist * 0.09 + theta + t * 0.4).sin() * 127.0 + 128.0;
145 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
146 }
147 10 => {
148 let r = (diag * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
149 let g = (diag * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
150 let b = (diag * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
151 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
152 }
153 11 => {
154 let v = (fx * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
155 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
156 }
157 12 => {
158 let v = (fy * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
159 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
160 }
161 13 => {
162 let v = (dist * 0.25 + t * 0.25).sin() * 127.0 + 128.0;
163 let w = (dist * 0.12 + t * 0.1).cos() * 127.0 + 128.0;
164 Color32::from_rgba_unmultiplied(v as u8, 255 - w as u8, (w as u8) / 2, a)
165 }
166 14 => {
167 let v = (diag * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
168 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
169 }
170 15 => {
171 let seed = (x as u32).wrapping_mul(73856093)
172 ^ (zebra_offset as u32).wrapping_mul(83492791);
173 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
174 Color32::from_rgba_unmultiplied(
175 val,
176 val.wrapping_mul(3),
177 val.wrapping_mul(7),
178 a,
179 )
180 }
181 16 => {
182 let seed = (y as u32).wrapping_mul(19349663)
183 ^ (zebra_offset as u32).wrapping_mul(83492791);
184 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
185 Color32::from_rgba_unmultiplied(
186 val,
187 val.wrapping_mul(3),
188 val.wrapping_mul(7),
189 a,
190 )
191 }
192 17 => {
193 let theta = (fy - cy).atan2(fx - cx);
194 let seed = (dist as u32).wrapping_mul(1234567)
195 ^ (theta.to_bits().wrapping_mul(314159))
196 ^ (zebra_offset as u32).wrapping_mul(83492791);
197 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
198 Color32::from_rgba_unmultiplied(val, val.rotate_left(2), val.rotate_left(4), a)
199 }
200 18 => {
201 let seed = (diag as u32).wrapping_mul(9876543)
202 ^ (zebra_offset as u32).wrapping_mul(83492791);
203 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
204 Color32::from_rgba_unmultiplied(
205 val,
206 val.wrapping_mul(3),
207 val.wrapping_mul(7),
208 a,
209 )
210 }
211 _ => Color32::from_rgba_unmultiplied(255, 255, 255, a),
212 };
213 } else {
214 return Color32::from_rgba_unmultiplied(r, g, b, a);
216 }
217 }
218 let (r, g, b) = (r, g, b);
220 match index % 19 {
221 1 => Color32::from_rgba_unmultiplied(255 - r, 255 - g, 255 - b, a),
222 2 => {
223 let avg = ((r as u16 + g as u16 + b as u16) / 3) as u8;
224 Color32::from_rgba_unmultiplied(avg, avg, avg, a)
225 }
226 3 => {
227 let v = (fx * 0.08 + t * 0.12).sin() + (fy * 0.08 + t * 0.15).cos();
228 let v = v * 0.5;
229 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
230 let g = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
231 let b = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
232 Color32::from_rgba_unmultiplied(r, g, b, a)
233 }
234 4 => {
235 let v = (fy * 0.08 + t * 0.12).sin() + (fx * 0.08 + t * 0.15).cos();
236 let v = v * 0.5;
237 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
238 let g = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
239 let b = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
240 Color32::from_rgba_unmultiplied(r, g, b, a)
241 }
242 5 => {
243 let theta = (fy - cy).atan2(fx - cx);
244 let v = (dist * 0.15 + t * 0.12).sin() + (theta * 6.0 + t * 0.2).cos();
245 let v = v * 0.5;
246 let r = ((v * 127.0 + 128.0) as u8).saturating_add((theta.sin() * 127.0 + 128.0) as u8);
247 let g = ((v * 127.0 + 128.0) as u8).saturating_add((theta.cos() * 127.0 + 128.0) as u8);
248 let b = ((v * 127.0 + 128.0) as u8).saturating_add((dist.sin() * 127.0 + 128.0) as u8);
249 Color32::from_rgba_unmultiplied(r, g, b, a)
250 }
251 6 => {
252 let v = (diag * 0.08 + t * 0.12).sin() + (t * 0.15).cos();
253 let v = v * 0.5;
254 let r = ((v * 127.0 + 128.0) as u8).saturating_add((t as u8) % 128);
255 let g = ((v * 127.0 + 128.0) as u8).saturating_add((x as u8) % 128);
256 let b = ((v * 127.0 + 128.0) as u8).saturating_add((y as u8) % 128);
257 Color32::from_rgba_unmultiplied(r, g, b, a)
258 }
259 7 => {
260 let r = (fx * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
261 let g = (fy * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
262 let b = ((fx + fy) * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
263 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
264 }
265 8 => {
266 let r = (fy * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
267 let g = (fx * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
268 let b = ((fx + fy) * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
269 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
270 }
271 9 => {
272 let theta = (fy - cy).atan2(fx - cx);
273 let r = (dist * 0.18 + t * 0.2).sin() * 127.0 + 128.0;
274 let g = (theta * 4.0 + t * 0.3).cos() * 127.0 + 128.0;
275 let b = (dist * 0.09 + theta + t * 0.4).sin() * 127.0 + 128.0;
276 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
277 }
278 10 => {
279 let r = (diag * 0.12 + t * 0.2).sin() * 127.0 + 128.0;
280 let g = (diag * 0.12 + t * 0.3).cos() * 127.0 + 128.0;
281 let b = (diag * 0.07 + t * 0.4).sin() * 127.0 + 128.0;
282 Color32::from_rgba_unmultiplied(r as u8, g as u8, b as u8, a)
283 }
284 11 => {
285 let v = (fx * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
286 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
287 }
288 12 => {
289 let v = (fy * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
290 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
291 }
292 13 => {
293 let v = (dist * 0.25 + t * 0.25).sin() * 127.0 + 128.0;
294 let w = (dist * 0.12 + t * 0.1).cos() * 127.0 + 128.0;
295 Color32::from_rgba_unmultiplied(v as u8, 255 - w as u8, (w as u8) / 2, a)
296 }
297 14 => {
298 let v = (diag * 0.2 + t * 0.25).sin() * 127.0 + 128.0;
299 Color32::from_rgba_unmultiplied(v as u8, 255 - v as u8, (v as u8) / 2, a)
300 }
301 15 => {
302 let seed =
303 (x as u32).wrapping_mul(73856093) ^ (zebra_offset as u32).wrapping_mul(83492791);
304 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
305 Color32::from_rgba_unmultiplied(val, val.wrapping_mul(3), val.wrapping_mul(7), a)
306 }
307 16 => {
308 let seed =
309 (y as u32).wrapping_mul(19349663) ^ (zebra_offset as u32).wrapping_mul(83492791);
310 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
311 Color32::from_rgba_unmultiplied(val, val.wrapping_mul(3), val.wrapping_mul(7), a)
312 }
313 17 => {
314 let theta = (fy - cy).atan2(fx - cx);
315 let seed = (dist as u32).wrapping_mul(1234567)
316 ^ (theta.to_bits().wrapping_mul(314159))
317 ^ (zebra_offset as u32).wrapping_mul(83492791);
318 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
319 Color32::from_rgba_unmultiplied(val, val.rotate_left(2), val.rotate_left(4), a)
320 }
321 18 => {
322 let seed =
323 (diag as u32).wrapping_mul(9876543) ^ (zebra_offset as u32).wrapping_mul(83492791);
324 let val = ((seed >> 3) ^ (seed << 7)).wrapping_add(seed) as u8;
325 Color32::from_rgba_unmultiplied(val, val.wrapping_mul(3), val.wrapping_mul(7), a)
326 }
327 _ => Color32::from_rgba_unmultiplied(r, g, b, a),
328 }
329}
330
331#[derive(Clone, Copy, PartialEq)]
333pub enum BlendMode {
334 None,
335 FullMode,
336 FullDyn,
337 BlendMode,
338 Random,
339 Diagonal,
340 Checker,
341 Threshold,
342 Intensity,
343 AnyLit,
344 Optimal,
345 OptimalPerFrame,
346 RandomMode,
347 RandomModeMode,
348}
349
350#[allow(clippy::too_many_arguments)]
351pub fn apply_effect_blend(
352 index: usize,
353 r: u8,
354 g: u8,
355 b: u8,
356 a: u8,
357 x: usize,
358 y: usize,
359 w: usize,
360 h: usize,
361 t: f32,
362 zebra_offset: usize,
363 mode: BlendMode,
364) -> Color32 {
365 use getrandom::fill;
366
367 pub fn random_f32() -> f32 {
369 let mut bytes = [0u8; 4];
370 fill(&mut bytes).expect("getrandom failed");
371 let val = u32::from_le_bytes(bytes);
372 (val as f32) / (u32::MAX as f32)
373 }
374
375 pub fn random_range(start: usize, end: usize) -> usize {
377 let mut bytes = [0u8; 8];
378 fill(&mut bytes).expect("getrandom failed");
379 let val = u64::from_le_bytes(bytes);
380 let range = end - start;
381 start + (val as usize % range)
382 }
383 let effect = match mode {
385 BlendMode::FullDyn => {
386 apply_effect(index, r, g, b, a, x, y, w, h, t * 10000.0, zebra_offset)
388 }
389 _ => apply_effect(index, r, g, b, a, x, y, w, h, t, zebra_offset),
390 };
391 let intensity = (r as u16 + g as u16 + b as u16) as f32 / (3.0 * 255.0);
392 let mut alpha = match mode {
393 BlendMode::None => 0.0,
394 BlendMode::FullMode => 1.0, BlendMode::FullDyn => 1.0,
396 BlendMode::BlendMode => intensity.clamp(0.0, 1.0),
397 BlendMode::Random => random_f32(),
398 BlendMode::Diagonal => ((x as f32 + y as f32) / (w as f32 + h as f32)).clamp(0.0, 1.0),
399 BlendMode::Checker => {
400 if (x / 8 + y / 8).is_multiple_of(2) {
401 1.0
402 } else {
403 0.0
404 }
405 }
406 BlendMode::Threshold => {
407 if intensity > 0.5 {
408 1.0
409 } else {
410 0.0
411 }
412 }
413 BlendMode::Intensity => intensity.clamp(0.0, 1.0),
414 BlendMode::AnyLit => {
415 if intensity > 0.0 {
416 1.0
417 } else {
418 0.0
419 }
420 }
421 BlendMode::Optimal | BlendMode::OptimalPerFrame => 1.0, BlendMode::RandomMode => match random_range(0, 5) {
423 0 => random_f32(),
424 1 => ((x as f32 + y as f32) / (w as f32 + h as f32)).clamp(0.0, 1.0),
425 2 => {
426 if (x / 8 + y / 8).is_multiple_of(2) {
427 1.0
428 } else {
429 0.0
430 }
431 }
432 3 => {
433 if intensity > 0.5 {
434 1.0
435 } else {
436 0.0
437 }
438 }
439 _ => intensity.clamp(0.0, 1.0),
440 },
441 BlendMode::RandomModeMode => {
442 let blend_modes = [
444 BlendMode::FullMode,
445 BlendMode::FullDyn,
446 BlendMode::BlendMode,
447 BlendMode::Random,
448 BlendMode::Diagonal,
449 BlendMode::Checker,
450 BlendMode::Threshold,
451 BlendMode::Intensity,
452 BlendMode::AnyLit,
453 BlendMode::RandomMode,
454 ];
456 let selected = blend_modes[random_range(0, blend_modes.len())];
457 return apply_effect_blend(index, r, g, b, a, x, y, w, h, t, zebra_offset, selected);
459 }
460 };
461 if (index == 1 || index == 20) && r == 0 && g == 0 && b == 0 {
463 alpha = 1.0;
464 }
465 let blend = |orig: u8, eff: u8| {
466 ((eff as f32) * alpha + (orig as f32) * (1.0 - alpha))
467 .round()
468 .clamp(0.0, 255.0) as u8
469 };
470 Color32::from_rgba_unmultiplied(
471 blend(r, effect.r()),
472 blend(g, effect.g()),
473 blend(b, effect.b()),
474 a,
475 )
476}