ass_renderer/pipeline/transform/
mod.rs1mod parse;
4
5#[cfg(feature = "nostd")]
6use alloc::vec::Vec;
7#[cfg(not(feature = "nostd"))]
8use std::vec::Vec;
9
10use parse::{apply_acceleration_curve, parse_animatable_tags};
11
12#[derive(Debug, Clone)]
14pub struct TransformAnimation {
15 pub start_ms: u32,
17 pub end_ms: u32,
19 pub accel: f32,
21 pub target_tags: Vec<AnimatableTag>,
23}
24
25#[derive(Debug, Clone)]
27pub enum AnimatableTag {
28 FontSize(f32),
30 FontScaleX(f32),
32 FontScaleY(f32),
34 FontSpacing(f32),
36 FontRotationZ(f32),
38 FontRotationX(f32),
40 FontRotationY(f32),
42 PrimaryColor([u8; 4]),
44 SecondaryColor([u8; 4]),
46 OutlineColor([u8; 4]),
48 ShadowColor([u8; 4]),
50 Alpha(u8),
52 BorderWidth(f32),
54 ShadowDepth(f32),
56 Blur(f32),
58}
59
60impl TransformAnimation {
61 pub fn parse(args: &str) -> Option<Self> {
63 let args = args.trim();
70 if !args.starts_with('(') || !args.ends_with(')') {
71 return None;
72 }
73
74 let inner = &args[1..args.len() - 1];
75
76 let tag_start = inner.find('\\')?;
81
82 let params = &inner[..tag_start];
83 let tags = &inner[tag_start..];
84
85 let parts: Vec<&str> = params
87 .trim_end_matches(',')
88 .split(',')
89 .map(|s| s.trim())
90 .collect();
91
92 let (start_ms, end_ms, accel) =
93 if parts.is_empty() || (parts.len() == 1 && parts[0].is_empty()) {
94 (0, 0, 1.0)
96 } else if parts.len() == 1 {
97 if let Ok(val) = parts[0].parse::<f32>() {
99 if val < 10.0 {
100 (0, 0, val)
102 } else {
103 (0, val as u32, 1.0)
105 }
106 } else {
107 (0, 0, 1.0)
108 }
109 } else if parts.len() == 2 {
110 let t1 = parts[0].parse::<u32>().unwrap_or(0);
112 let t2 = parts[1].parse::<u32>().unwrap_or(0);
113 (t1, t2, 1.0)
114 } else {
115 let t1 = parts[0].parse::<u32>().unwrap_or(0);
117 let t2 = parts[1].parse::<u32>().unwrap_or(0);
118 let accel = parts[2].parse::<f32>().unwrap_or(1.0);
120 (t1, t2, accel)
121 };
122
123 let target_tags = parse_animatable_tags(tags);
125
126 if target_tags.is_empty() {
127 return None;
128 }
129
130 Some(TransformAnimation {
131 start_ms,
132 end_ms,
133 accel,
134 target_tags,
135 })
136 }
137
138 pub fn calculate_progress(&self, current_ms: u32, full_duration_ms: u32) -> f32 {
145 let end_ms = if self.end_ms > 0 {
146 self.end_ms
147 } else {
148 full_duration_ms
149 };
150
151 if current_ms <= self.start_ms {
152 return 0.0;
153 }
154 if end_ms <= self.start_ms || current_ms >= end_ms {
155 return 1.0;
156 }
157
158 let linear_progress = (current_ms - self.start_ms) as f32 / (end_ms - self.start_ms) as f32;
159
160 apply_acceleration_curve(linear_progress, self.accel)
162 }
163}
164
165pub fn interpolate_f32(from: f32, to: f32, progress: f32) -> f32 {
167 from + (to - from) * progress
168}
169
170pub fn interpolate_color(from: [u8; 4], to: [u8; 4], progress: f32) -> [u8; 4] {
172 [
173 (from[0] as f32 + (to[0] as f32 - from[0] as f32) * progress) as u8,
174 (from[1] as f32 + (to[1] as f32 - from[1] as f32) * progress) as u8,
175 (from[2] as f32 + (to[2] as f32 - from[2] as f32) * progress) as u8,
176 (from[3] as f32 + (to[3] as f32 - from[3] as f32) * progress) as u8,
177 ]
178}
179
180pub fn interpolate_alpha(from: u8, to: u8, progress: f32) -> u8 {
182 (from as f32 + (to as f32 - from as f32) * progress) as u8
183}