use crate::utils::RenderError;
use ass_core::parser::{Event, Script, Section};
use super::time_index::TimeIndex;
#[cfg(feature = "nostd")]
use alloc::{collections::BTreeSet, vec::Vec};
#[cfg(not(feature = "nostd"))]
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct EventSelector {
#[cfg(not(feature = "nostd"))]
previous_active: HashSet<usize>,
#[cfg(feature = "nostd")]
previous_active: BTreeSet<usize>,
last_timestamp: Option<u32>,
dirty_regions: Vec<DirtyRegion>,
render_comments: bool,
time_index: Option<TimeIndex>,
}
#[derive(Debug, Clone)]
pub struct DirtyRegion {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
#[derive(Debug)]
pub struct ActiveEvents<'a> {
pub events: Vec<&'a Event<'a>>,
pub newly_active: Vec<usize>,
pub newly_inactive: Vec<usize>,
pub is_dirty: bool,
}
impl EventSelector {
pub fn new() -> Self {
Self {
#[cfg(not(feature = "nostd"))]
previous_active: HashSet::new(),
#[cfg(feature = "nostd")]
previous_active: BTreeSet::new(),
last_timestamp: None,
dirty_regions: Vec::new(),
render_comments: false,
time_index: None,
}
}
pub fn set_render_comments(&mut self, render: bool) {
self.render_comments = render;
}
pub fn select_active<'a>(
&mut self,
script: &'a Script<'a>,
time_cs: u32,
) -> Result<ActiveEvents<'a>, RenderError> {
let mut active_events = Vec::new();
#[cfg(not(feature = "nostd"))]
let mut current_active = HashSet::new();
#[cfg(feature = "nostd")]
let mut current_active = BTreeSet::new();
if let Some(events_section) = script.sections().iter().find_map(|section| {
if let Section::Events(events) = section {
Some(events)
} else {
None
}
}) {
self.ensure_index(events_section);
let index = self
.time_index
.as_ref()
.expect("time index built by ensure_index");
let hi = index
.by_start
.partition_point(|&(start, _, _)| start <= time_cs);
let mut active_idx: Vec<usize> = index.by_start[..hi]
.iter()
.filter(|&&(_, end, _)| end > time_cs)
.map(|&(_, _, idx)| idx)
.collect();
active_idx.sort_unstable();
for idx in active_idx {
active_events.push(&events_section[idx]);
current_active.insert(idx);
}
}
let newly_active: Vec<usize> = current_active
.iter()
.filter(|idx| !self.previous_active.contains(idx))
.cloned()
.collect();
let newly_inactive: Vec<usize> = self
.previous_active
.iter()
.filter(|idx| !current_active.contains(idx))
.cloned()
.collect();
let is_dirty = !newly_active.is_empty()
|| !newly_inactive.is_empty()
|| self.has_animated_events(&active_events, time_cs)
|| self
.last_timestamp
.is_none_or(|last| (time_cs as i32 - last as i32).abs() > 100);
self.previous_active = current_active;
self.last_timestamp = Some(time_cs);
Ok(ActiveEvents {
events: active_events,
newly_active,
newly_inactive,
is_dirty,
})
}
fn ensure_index(&mut self, events: &[Event]) {
let key = (events.as_ptr() as usize, events.len(), self.render_comments);
if self
.time_index
.as_ref()
.is_some_and(|index| index.key == key)
{
return;
}
self.time_index = Some(TimeIndex::build(events, self.render_comments));
}
fn has_animated_events(&self, events: &[&Event], time_cs: u32) -> bool {
for event in events {
let text = event.text;
if text.contains(r"\t(")
|| text.contains(r"\move(")
|| text.contains(r"\fade(")
|| text.contains(r"\fad(")
{
return true;
}
if text.contains(r"\k") || text.contains(r"\K") {
if let Ok(start) = event.start_time_cs() {
if time_cs > start {
return true;
}
}
}
}
false
}
pub fn add_dirty_region(&mut self, x: u32, y: u32, width: u32, height: u32) {
for region in &mut self.dirty_regions {
if Self::regions_overlap(region, x, y, width, height) {
let min_x = region.x.min(x);
let min_y = region.y.min(y);
let max_x = (region.x + region.width).max(x + width);
let max_y = (region.y + region.height).max(y + height);
region.x = min_x;
region.y = min_y;
region.width = max_x - min_x;
region.height = max_y - min_y;
return;
}
}
self.dirty_regions.push(DirtyRegion {
x,
y,
width,
height,
});
}
fn regions_overlap(region: &DirtyRegion, x: u32, y: u32, width: u32, height: u32) -> bool {
!(x >= region.x + region.width
|| x + width <= region.x
|| y >= region.y + region.height
|| y + height <= region.y)
}
pub fn dirty_regions(&self) -> &[DirtyRegion] {
&self.dirty_regions
}
pub fn clear_dirty_regions(&mut self) {
self.dirty_regions.clear();
}
pub fn reset(&mut self) {
self.previous_active.clear();
self.last_timestamp = None;
self.dirty_regions.clear();
}
}
impl Default for EventSelector {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)] pub fn select_active_events<'a>(script: &'a Script<'a>, time_cs: u32) -> Vec<&'a Event<'a>> {
let mut selector = EventSelector::new();
selector
.select_active(script, time_cs)
.map(|active| active.events)
.unwrap_or_default()
}