1#![allow(dead_code)]
9
10use alloc::vec::Vec;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum EasingFunction {
19 Linear,
20 EaseInQuad,
21 EaseOutQuad,
22 EaseInOutQuad,
23 EaseInCubic,
24 EaseOutCubic,
25 EaseInOutCubic,
26 EaseOutBack,
28 EaseOutBounce,
30}
31
32pub fn evaluate_easing_fixed(easing: EasingFunction, t_256: u32) -> u32 {
38 let t = t_256.min(256);
40
41 match easing {
42 EasingFunction::Linear => t,
43
44 EasingFunction::EaseInQuad => {
45 (t * t) >> 8
47 }
48
49 EasingFunction::EaseOutQuad => {
50 let inv = 256 - t;
52 256 - ((inv * inv) >> 8)
53 }
54
55 EasingFunction::EaseInOutQuad => {
56 if t < 128 {
58 (2 * t * t) >> 8
60 } else {
61 let inv = 256 - t;
62 256 - ((2 * inv * inv) >> 8)
63 }
64 }
65
66 EasingFunction::EaseInCubic => {
67 let t2 = (t * t) >> 8;
69 (t2 * t) >> 8
70 }
71
72 EasingFunction::EaseOutCubic => {
73 let inv = 256 - t;
75 let inv2 = (inv * inv) >> 8;
76 let inv3 = (inv2 * inv) >> 8;
77 256 - inv3
78 }
79
80 EasingFunction::EaseInOutCubic => {
81 if t < 128 {
82 let t2 = (t * t) >> 8;
84 let t3 = (t2 * t) >> 8;
85 (4 * t3) >> 8
86 } else {
87 let inv = 256 - t;
88 let inv2 = (inv * inv) >> 8;
89 let inv3 = (inv2 * inv) >> 8;
90 256 - ((4 * inv3) >> 8)
91 }
92 }
93
94 EasingFunction::EaseOutBack => {
95 let inv = 256 - t;
98 let base = 256 - ((inv * inv) >> 8);
99 let mid_dist = if t > 128 { 256 - t } else { t };
102 let bump = (mid_dist * 20) >> 8; if t > 128 {
105 base + bump
106 } else {
107 base
108 }
109 }
110
111 EasingFunction::EaseOutBounce => {
112 if t < 192 {
115 let t_seg = (t * 256) / 192;
118 let inv = 256 - t_seg;
119 let val = (4 * t_seg * inv) >> 8;
120 (val * 256) >> 8
122 } else if t < 240 {
123 let seg_start = 192;
125 let seg_len = 48;
126 let t_seg = ((t - seg_start) * 256) / seg_len;
127 let inv = 256 - t_seg;
128 let bounce_h = 32; let base = 256 - bounce_h;
130 let val = (4 * t_seg * inv) >> 8;
131 base + ((val * bounce_h) >> 8)
132 } else {
133 let seg_start = 240;
135 let seg_len = 16;
136 let t_local = t - seg_start;
137 let t_seg = if seg_len > 0 {
138 (t_local * 256) / seg_len
139 } else {
140 256
141 };
142 let inv = 256 - t_seg;
143 let bounce_h = 8; let base = 256 - bounce_h;
145 let val = (4 * t_seg * inv) >> 8;
146 base + ((val * bounce_h) >> 8)
147 }
148 }
149 }
150}
151
152#[derive(Debug, Clone, Copy)]
158pub enum AnimationProperty {
159 Opacity {
160 from: u8,
161 to: u8,
162 },
163 PositionX {
164 from: i32,
165 to: i32,
166 },
167 PositionY {
168 from: i32,
169 to: i32,
170 },
171 Width {
172 from: u32,
173 to: u32,
174 },
175 Height {
176 from: u32,
177 to: u32,
178 },
179 Scale {
181 from_256: u32,
182 to_256: u32,
183 },
184}
185
186pub struct Animation {
192 pub id: u32,
193 pub window_id: u32,
194 pub property: AnimationProperty,
195 pub easing: EasingFunction,
196 pub duration_ms: u32,
198 pub elapsed_ms: u32,
200 pub completed: bool,
202}
203
204impl Animation {
205 pub fn current_value(&self) -> i64 {
210 if self.completed || self.duration_ms == 0 {
211 return self.target_value();
212 }
213
214 let t_256 = ((self.elapsed_ms as u64 * 256) / self.duration_ms as u64).min(256) as u32;
216 let eased = evaluate_easing_fixed(self.easing, t_256);
217
218 match self.property {
220 AnimationProperty::Opacity { from, to } => {
221 let from_i = from as i64;
222 let to_i = to as i64;
223 from_i + ((to_i - from_i) * eased as i64) / 256
224 }
225 AnimationProperty::PositionX { from, to }
226 | AnimationProperty::PositionY { from, to } => {
227 let from_i = from as i64;
228 let to_i = to as i64;
229 from_i + ((to_i - from_i) * eased as i64) / 256
230 }
231 AnimationProperty::Width { from, to } | AnimationProperty::Height { from, to } => {
232 let from_i = from as i64;
233 let to_i = to as i64;
234 from_i + ((to_i - from_i) * eased as i64) / 256
235 }
236 AnimationProperty::Scale { from_256, to_256 } => {
237 let from_i = from_256 as i64;
238 let to_i = to_256 as i64;
239 from_i + ((to_i - from_i) * eased as i64) / 256
240 }
241 }
242 }
243
244 fn target_value(&self) -> i64 {
246 match self.property {
247 AnimationProperty::Opacity { to, .. } => to as i64,
248 AnimationProperty::PositionX { to, .. } | AnimationProperty::PositionY { to, .. } => {
249 to as i64
250 }
251 AnimationProperty::Width { to, .. } | AnimationProperty::Height { to, .. } => to as i64,
252 AnimationProperty::Scale { to_256, .. } => to_256 as i64,
253 }
254 }
255}
256
257pub struct AnimationManager {
263 animations: Vec<Animation>,
264 next_id: u32,
265}
266
267impl AnimationManager {
268 pub fn new() -> Self {
270 Self {
271 animations: Vec::new(),
272 next_id: 1,
273 }
274 }
275
276 pub fn start(
278 &mut self,
279 window_id: u32,
280 property: AnimationProperty,
281 easing: EasingFunction,
282 duration_ms: u32,
283 ) -> u32 {
284 let id = self.next_id;
285 self.next_id += 1;
286
287 self.animations.push(Animation {
288 id,
289 window_id,
290 property,
291 easing,
292 duration_ms,
293 elapsed_ms: 0,
294 completed: false,
295 });
296
297 id
298 }
299
300 pub fn tick(&mut self, delta_ms: u32) {
302 for anim in &mut self.animations {
303 if anim.completed {
304 continue;
305 }
306 anim.elapsed_ms += delta_ms;
307 if anim.elapsed_ms >= anim.duration_ms {
308 anim.elapsed_ms = anim.duration_ms;
309 anim.completed = true;
310 }
311 }
312 }
313
314 pub fn get_current_value(&self, animation_id: u32) -> Option<i64> {
316 self.animations
317 .iter()
318 .find(|a| a.id == animation_id)
319 .map(|a| a.current_value())
320 }
321
322 pub fn is_complete(&self, animation_id: u32) -> bool {
324 self.animations
325 .iter()
326 .find(|a| a.id == animation_id)
327 .is_none_or(|a| a.completed)
328 }
329
330 pub fn remove_completed(&mut self) {
332 self.animations.retain(|a| !a.completed);
333 }
334
335 pub fn cancel(&mut self, window_id: u32) {
337 self.animations.retain(|a| a.window_id != window_id);
338 }
339
340 pub fn has_active_animations(&self) -> bool {
342 self.animations.iter().any(|a| !a.completed)
343 }
344
345 pub fn active_count(&self) -> usize {
347 self.animations.iter().filter(|a| !a.completed).count()
348 }
349
350 pub fn get_window_animations(&self, window_id: u32) -> Vec<&Animation> {
352 self.animations
353 .iter()
354 .filter(|a| a.window_id == window_id && !a.completed)
355 .collect()
356 }
357}
358
359impl Default for AnimationManager {
360 fn default() -> Self {
361 Self::new()
362 }
363}
364
365pub fn render_shadow(
380 shadow_buffer: &mut [u32],
381 buf_width: u32,
382 width: u32,
383 height: u32,
384 radius: u32,
385 opacity: u8,
386) {
387 let bw = buf_width as usize;
388 let total_w = width + 2 * radius;
389 let total_h = height + 2 * radius;
390 let r = radius as usize;
391 let w = width as usize;
392 let h = height as usize;
393
394 for px in shadow_buffer.iter_mut().take(bw * total_h as usize) {
396 *px = 0;
397 }
398
399 for y in 0..h {
401 for x in 0..w {
402 let px = r + x;
403 let py = r + y;
404 let idx = py * bw + px;
405 if idx < shadow_buffer.len() {
406 shadow_buffer[idx] = opacity as u32;
407 }
408 }
409 }
410
411 if radius == 0 {
413 convert_alpha_to_shadow(shadow_buffer, bw, total_w as usize, total_h as usize);
415 return;
416 }
417
418 let buf_len = bw * total_h as usize;
420 let mut temp = alloc::vec![0u32; buf_len];
421
422 box_blur_h(
424 shadow_buffer,
425 &mut temp,
426 bw,
427 total_w as usize,
428 total_h as usize,
429 r,
430 );
431
432 box_blur_v(
434 &temp,
435 shadow_buffer,
436 bw,
437 total_w as usize,
438 total_h as usize,
439 r,
440 );
441
442 let shadow_copy: Vec<u32> = shadow_buffer[..buf_len].to_vec();
444 box_blur_h(
445 &shadow_copy,
446 shadow_buffer,
447 bw,
448 total_w as usize,
449 total_h as usize,
450 r,
451 );
452
453 convert_alpha_to_shadow(shadow_buffer, bw, total_w as usize, total_h as usize);
455}
456
457fn box_blur_h(src: &[u32], dst: &mut [u32], bw: usize, w: usize, h: usize, radius: usize) {
459 let diam = 2 * radius + 1;
460 for y in 0..h {
461 let row_off = y * bw;
462 let mut sum: u32 = 0;
463
464 for x in 0..=radius.min(w.saturating_sub(1)) {
466 sum += src.get(row_off + x).copied().unwrap_or(0);
467 }
468 for _ in 0..radius.saturating_sub(0) {
470 sum += src.get(row_off).copied().unwrap_or(0);
471 }
472
473 for x in 0..w {
474 let idx = row_off + x;
475 if idx < dst.len() {
476 dst[idx] = sum / diam as u32;
477 }
478
479 let add_x = x + radius + 1;
481 let rem_x = x.wrapping_sub(radius);
482
483 let add_val = if add_x < w {
484 src.get(row_off + add_x).copied().unwrap_or(0)
485 } else {
486 src.get(row_off + w.saturating_sub(1)).copied().unwrap_or(0)
488 };
489
490 let rem_val = if rem_x < w {
491 src.get(row_off + rem_x).copied().unwrap_or(0)
492 } else {
493 src.get(row_off).copied().unwrap_or(0)
494 };
495
496 sum = sum.wrapping_add(add_val).wrapping_sub(rem_val);
497 }
498 }
499}
500
501fn box_blur_v(src: &[u32], dst: &mut [u32], bw: usize, w: usize, h: usize, radius: usize) {
503 let diam = 2 * radius + 1;
504 for x in 0..w {
505 let mut sum: u32 = 0;
506
507 for y in 0..=radius.min(h.saturating_sub(1)) {
509 sum += src.get(y * bw + x).copied().unwrap_or(0);
510 }
511 for _ in 0..radius {
512 sum += src.get(x).copied().unwrap_or(0);
513 }
514
515 for y in 0..h {
516 let idx = y * bw + x;
517 if idx < dst.len() {
518 dst[idx] = sum / diam as u32;
519 }
520
521 let add_y = y + radius + 1;
522 let rem_y = y.wrapping_sub(radius);
523
524 let add_val = if add_y < h {
525 src.get(add_y * bw + x).copied().unwrap_or(0)
526 } else {
527 src.get(h.saturating_sub(1) * bw + x).copied().unwrap_or(0)
528 };
529
530 let rem_val = if rem_y < h {
531 src.get(rem_y * bw + x).copied().unwrap_or(0)
532 } else {
533 src.get(x).copied().unwrap_or(0)
534 };
535
536 sum = sum.wrapping_add(add_val).wrapping_sub(rem_val);
537 }
538 }
539}
540
541fn convert_alpha_to_shadow(buffer: &mut [u32], bw: usize, w: usize, h: usize) {
544 for y in 0..h {
545 for x in 0..w {
546 let idx = y * bw + x;
547 if idx < buffer.len() {
548 let alpha = buffer[idx].min(255);
549 buffer[idx] = alpha << 24; }
551 }
552 }
553}