Skip to main content

ass_renderer/debug/player/
render.rs

1use super::PlayerFrame;
2use crate::RenderError;
3use ass_core::parser::Script;
4
5#[cfg(not(feature = "nostd"))]
6use std::time::{Duration, Instant};
7
8#[cfg(feature = "nostd")]
9use alloc::vec::Vec;
10
11impl super::DebugPlayer {
12    pub fn render_current_frame(&mut self) -> Result<PlayerFrame, RenderError> {
13        // Check cache first if enabled
14        if self.cache_enabled {
15            // Round to nearest frame interval for better cache hits
16            let cache_key =
17                (self.current_time_ms / self.frame_interval_ms) * self.frame_interval_ms;
18
19            if let Some(cached_frame) = self.frame_cache.get(&cache_key) {
20                let player_frame = PlayerFrame {
21                    frame: cached_frame.clone(),
22                    timestamp_ms: self.current_time_ms,
23                    render_time: Duration::from_micros(100), // Cached, so very fast
24                    frame_number: (self.current_time_ms / self.frame_interval_ms),
25                };
26
27                if self.show_stats {
28                    println!("📦 Using cached frame for {cache_key}ms");
29                }
30
31                return Ok(player_frame);
32            }
33        }
34
35        let script_content = self
36            .script_content
37            .as_ref()
38            .ok_or_else(|| RenderError::InvalidInput("No script loaded".into()))?;
39
40        let script = Script::parse(script_content)
41            .map_err(|e| RenderError::ParseError(format!("Failed to parse script: {e:?}")))?;
42
43        let start = Instant::now();
44        // Convert milliseconds to centiseconds for the renderer
45        let time_cs = self.current_time_ms / 10;
46        let frame = self.renderer.render_frame(&script, time_cs)?;
47        let render_time = start.elapsed();
48
49        // Cache the frame if enabled
50        if self.cache_enabled {
51            let cache_key =
52                (self.current_time_ms / self.frame_interval_ms) * self.frame_interval_ms;
53
54            // Maintain cache size limit
55            if self.frame_cache.len() >= self.max_cache_size {
56                // Remove oldest entry
57                if let Some(&min_key) = self.frame_cache.keys().min() {
58                    self.frame_cache.remove(&min_key);
59                }
60            }
61
62            self.frame_cache.insert(cache_key, frame.clone());
63        }
64
65        let player_frame = PlayerFrame {
66            frame: frame.clone(),
67            timestamp_ms: self.current_time_ms,
68            render_time,
69            frame_number: (self.current_time_ms / self.frame_interval_ms),
70        };
71
72        if self.show_stats {
73            self.print_frame_stats(&player_frame);
74        }
75
76        if self.save_frames {
77            self.save_frame(&player_frame)?;
78        }
79
80        Ok(player_frame)
81    }
82
83    pub fn update(&mut self, _delta_time: Duration) -> Result<Option<PlayerFrame>, RenderError> {
84        if !self.is_playing {
85            return Ok(None);
86        }
87
88        // Calculate the actual timeline position based on real elapsed time
89        if let Some(start_instant) = self.playback_start_instant {
90            let elapsed_ms = start_instant.elapsed().as_secs_f32() * 1000.0 * self.playback_speed;
91            self.current_time_ms =
92                self.playback_start_time_ms + (elapsed_ms + self.accumulated_time_ms) as u32;
93
94            if self.current_time_ms >= self.end_time_ms {
95                if self.loop_playback {
96                    self.current_time_ms = self.start_time_ms;
97                    self.playback_start_instant = Some(Instant::now());
98                    self.playback_start_time_ms = self.start_time_ms;
99                    self.accumulated_time_ms = 0.0;
100                    println!("🔁 Looping playback");
101                } else {
102                    self.is_playing = false;
103                    self.current_time_ms = self.end_time_ms;
104                    self.playback_start_instant = None;
105                    println!("✅ Playback finished");
106                    return Ok(None);
107                }
108            }
109        }
110
111        self.render_current_frame().map(Some)
112    }
113
114    fn print_frame_stats(&self, player_frame: &PlayerFrame) {
115        let pixels = player_frame.frame.pixels();
116        let mut non_transparent = 0;
117
118        for chunk in pixels.chunks(4) {
119            if chunk.len() == 4 && chunk[3] > 0 {
120                non_transparent += 1;
121            }
122        }
123
124        println!("┌────────────────────────────────────┐");
125        println!(
126            "│ Frame #{:06} @ {:02}:{:02}.{:03}        │",
127            player_frame.frame_number,
128            self.current_time_ms / 60000,
129            (self.current_time_ms / 1000) % 60,
130            self.current_time_ms % 1000
131        );
132        println!("├────────────────────────────────────┤");
133        println!(
134            "│ Render: {:.2}ms                    │",
135            player_frame.render_time.as_secs_f64() * 1000.0
136        );
137        println!("│ Visible pixels: {non_transparent:6}            │");
138        println!(
139            "│ Speed: {:.1}x | Progress: {:3.1}%    │",
140            self.playback_speed,
141            (self.current_time_ms as f32 / self.end_time_ms as f32) * 100.0
142        );
143        println!("└────────────────────────────────────┘");
144    }
145
146    fn save_frame(&self, player_frame: &PlayerFrame) -> Result<(), RenderError> {
147        let path = format!(
148            "{}/frame_{:06}_{:06}ms.png",
149            self.output_dir, player_frame.frame_number, player_frame.timestamp_ms
150        );
151
152        #[cfg(feature = "image")]
153        {
154            use image::{ImageBuffer, Rgba};
155
156            let img = ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(
157                player_frame.frame.width(),
158                player_frame.frame.height(),
159                player_frame.frame.pixels().to_vec(),
160            )
161            .ok_or_else(|| RenderError::BackendError("Failed to create image buffer".into()))?;
162
163            img.save(&path)
164                .map_err(|e| RenderError::BackendError(format!("Failed to save frame: {e}")))?;
165        }
166
167        Ok(())
168    }
169}