ass_renderer/renderer/
event_selector.rs1use crate::utils::RenderError;
4use ass_core::parser::{Event, Script, Section};
5
6use super::time_index::TimeIndex;
7
8#[cfg(feature = "nostd")]
9use alloc::{collections::BTreeSet, vec::Vec};
10#[cfg(not(feature = "nostd"))]
11use std::collections::HashSet;
12
13#[derive(Debug, Clone)]
15pub struct EventSelector {
16 #[cfg(not(feature = "nostd"))]
18 previous_active: HashSet<usize>,
19 #[cfg(feature = "nostd")]
20 previous_active: BTreeSet<usize>,
21
22 last_timestamp: Option<u32>,
24
25 dirty_regions: Vec<DirtyRegion>,
27
28 render_comments: bool,
30
31 time_index: Option<TimeIndex>,
35}
36
37#[derive(Debug, Clone)]
39pub struct DirtyRegion {
40 pub x: u32,
42 pub y: u32,
44 pub width: u32,
46 pub height: u32,
48}
49
50#[derive(Debug)]
52pub struct ActiveEvents<'a> {
53 pub events: Vec<&'a Event<'a>>,
55 pub newly_active: Vec<usize>,
57 pub newly_inactive: Vec<usize>,
59 pub is_dirty: bool,
61}
62
63impl EventSelector {
64 pub fn new() -> Self {
66 Self {
67 #[cfg(not(feature = "nostd"))]
68 previous_active: HashSet::new(),
69 #[cfg(feature = "nostd")]
70 previous_active: BTreeSet::new(),
71 last_timestamp: None,
72 dirty_regions: Vec::new(),
73 render_comments: false,
78 time_index: None,
79 }
80 }
81
82 pub fn set_render_comments(&mut self, render: bool) {
84 self.render_comments = render;
85 }
86
87 pub fn select_active<'a>(
96 &mut self,
97 script: &'a Script<'a>,
98 time_cs: u32,
99 ) -> Result<ActiveEvents<'a>, RenderError> {
100 let mut active_events = Vec::new();
101 #[cfg(not(feature = "nostd"))]
102 let mut current_active = HashSet::new();
103 #[cfg(feature = "nostd")]
104 let mut current_active = BTreeSet::new();
105
106 if let Some(events_section) = script.sections().iter().find_map(|section| {
111 if let Section::Events(events) = section {
112 Some(events)
113 } else {
114 None
115 }
116 }) {
117 self.ensure_index(events_section);
118 let index = self
119 .time_index
120 .as_ref()
121 .expect("time index built by ensure_index");
122 let hi = index
123 .by_start
124 .partition_point(|&(start, _, _)| start <= time_cs);
125 let mut active_idx: Vec<usize> = index.by_start[..hi]
126 .iter()
127 .filter(|&&(_, end, _)| end > time_cs)
128 .map(|&(_, _, idx)| idx)
129 .collect();
130 active_idx.sort_unstable();
131 for idx in active_idx {
132 active_events.push(&events_section[idx]);
133 current_active.insert(idx);
134 }
135 }
136
137 let newly_active: Vec<usize> = current_active
139 .iter()
140 .filter(|idx| !self.previous_active.contains(idx))
141 .cloned()
142 .collect();
143
144 let newly_inactive: Vec<usize> = self
145 .previous_active
146 .iter()
147 .filter(|idx| !current_active.contains(idx))
148 .cloned()
149 .collect();
150
151 let is_dirty = !newly_active.is_empty()
153 || !newly_inactive.is_empty()
154 || self.has_animated_events(&active_events, time_cs)
155 || self
156 .last_timestamp
157 .is_none_or(|last| (time_cs as i32 - last as i32).abs() > 100);
158
159 self.previous_active = current_active;
161 self.last_timestamp = Some(time_cs);
162
163 Ok(ActiveEvents {
164 events: active_events,
165 newly_active,
166 newly_inactive,
167 is_dirty,
168 })
169 }
170
171 fn ensure_index(&mut self, events: &[Event]) {
178 let key = (events.as_ptr() as usize, events.len(), self.render_comments);
179 if self
180 .time_index
181 .as_ref()
182 .is_some_and(|index| index.key == key)
183 {
184 return;
185 }
186
187 self.time_index = Some(TimeIndex::build(events, self.render_comments));
188 }
189
190 fn has_animated_events(&self, events: &[&Event], time_cs: u32) -> bool {
192 for event in events {
193 let text = event.text;
194 if text.contains(r"\t(")
196 || text.contains(r"\move(")
197 || text.contains(r"\fade(")
198 || text.contains(r"\fad(")
199 {
200 return true;
201 }
202 if text.contains(r"\k") || text.contains(r"\K") {
204 if let Ok(start) = event.start_time_cs() {
205 if time_cs > start {
206 return true;
207 }
208 }
209 }
210 }
211 false
212 }
213
214 pub fn add_dirty_region(&mut self, x: u32, y: u32, width: u32, height: u32) {
216 for region in &mut self.dirty_regions {
218 if Self::regions_overlap(region, x, y, width, height) {
219 let min_x = region.x.min(x);
221 let min_y = region.y.min(y);
222 let max_x = (region.x + region.width).max(x + width);
223 let max_y = (region.y + region.height).max(y + height);
224 region.x = min_x;
225 region.y = min_y;
226 region.width = max_x - min_x;
227 region.height = max_y - min_y;
228 return;
229 }
230 }
231
232 self.dirty_regions.push(DirtyRegion {
233 x,
234 y,
235 width,
236 height,
237 });
238 }
239
240 fn regions_overlap(region: &DirtyRegion, x: u32, y: u32, width: u32, height: u32) -> bool {
242 !(x >= region.x + region.width
243 || x + width <= region.x
244 || y >= region.y + region.height
245 || y + height <= region.y)
246 }
247
248 pub fn dirty_regions(&self) -> &[DirtyRegion] {
250 &self.dirty_regions
251 }
252
253 pub fn clear_dirty_regions(&mut self) {
255 self.dirty_regions.clear();
256 }
257
258 pub fn reset(&mut self) {
260 self.previous_active.clear();
261 self.last_timestamp = None;
262 self.dirty_regions.clear();
263 }
264}
265
266impl Default for EventSelector {
267 fn default() -> Self {
268 Self::new()
269 }
270}
271
272#[allow(dead_code)] pub fn select_active_events<'a>(script: &'a Script<'a>, time_cs: u32) -> Vec<&'a Event<'a>> {
275 let mut selector = EventSelector::new();
276 selector
277 .select_active(script, time_cs)
278 .map(|active| active.events)
279 .unwrap_or_default()
280}