ass_renderer/debug/player/
render.rs1use 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 if self.cache_enabled {
15 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), 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 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 if self.cache_enabled {
51 let cache_key =
52 (self.current_time_ms / self.frame_interval_ms) * self.frame_interval_ms;
53
54 if self.frame_cache.len() >= self.max_cache_size {
56 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 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}