1use crate::renderer::RenderContext;
4use crate::utils::{DirtyRegion, RenderError};
5#[cfg(feature = "analysis-integration")]
6use ass_core::analysis::ScriptAnalysis;
7use ass_core::parser::{Event, Script};
8use smallvec::SmallVec;
9
10#[cfg(feature = "nostd")]
11use alloc::{string::String, vec::Vec};
12#[cfg(not(feature = "nostd"))]
13use std::{string::String, vec::Vec};
14
15pub mod animation;
16pub mod compositing;
17pub mod drawing;
18pub mod font_loader;
19pub mod shaping;
20pub mod tag_processor;
21pub mod text_segmenter;
22pub mod transform;
23pub mod validation;
24
25mod build;
26pub use build::SoftwarePipeline;
27
28pub trait Pipeline: Send + Sync {
30 fn prepare_script(
32 &mut self,
33 script: &Script,
34 #[cfg(feature = "analysis-integration")] analysis: Option<&ScriptAnalysis>,
35 #[cfg(not(feature = "analysis-integration"))] _analysis: Option<()>,
36 ) -> Result<(), RenderError>;
37
38 fn script(&self) -> Option<&Script<'_>>;
40
41 fn process_events(
43 &mut self,
44 events: &[&Event],
45 time_cs: u32,
46 context: &RenderContext,
47 ) -> Result<Vec<IntermediateLayer>, RenderError>;
48
49 fn compute_dirty_regions(
51 &self,
52 events: &[&Event],
53 time_cs: u32,
54 prev_time_cs: u32,
55 ) -> Result<Vec<DirtyRegion>, RenderError>;
56}
57
58#[derive(Debug, Clone, Copy)]
60pub enum PipelineStage {
61 Shaping,
63 Drawing,
65 Effects,
67 Compositing,
69}
70
71pub enum IntermediateLayer {
73 Raster(RasterData),
75 Vector(VectorData),
77 Text(TextData),
79}
80
81impl IntermediateLayer {
82 pub fn intersects_region(&self, region: &DirtyRegion) -> bool {
84 match self {
85 Self::Raster(data) => {
86 let layer_bounds = (data.x, data.y, data.x + data.width, data.y + data.height);
87 region.intersects(layer_bounds)
88 }
89 Self::Vector(data) => {
90 if let Some(bounds) = &data.bounds {
91 region.intersects(*bounds)
92 } else {
93 true
94 }
95 }
96 Self::Text(data) => {
97 let approx_bounds = (
98 data.x as u32,
99 data.y as u32,
100 (data.x + 200.0) as u32,
101 (data.y + data.font_size * 1.5) as u32,
102 );
103 region.intersects(approx_bounds)
104 }
105 }
106 }
107}
108
109pub struct RasterData {
111 pub pixels: Vec<u8>,
113 pub x: u32,
115 pub y: u32,
117 pub width: u32,
119 pub height: u32,
121 pub opacity: u8,
123}
124
125pub struct VectorData {
127 pub path: Option<tiny_skia::Path>,
129 pub color: [u8; 4],
131 pub stroke: Option<StrokeInfo>,
133 pub bounds: Option<(u32, u32, u32, u32)>,
135 pub clip: Option<(f32, f32, f32, f32, bool)>,
138 pub blur: f32,
143}
144
145pub struct StrokeInfo {
147 pub color: [u8; 4],
149 pub width: f32,
151}
152
153pub struct TextData {
155 pub text: String,
157 pub font_family: String,
159 pub font_size: f32,
161 pub color: [u8; 4],
163 pub x: f32,
165 pub y: f32,
167 pub effects: SmallVec<[TextEffect; 4]>,
169 pub spacing: f32,
171}
172
173#[derive(Clone, Debug)]
175pub enum TextEffect {
176 Bold,
178 Italic,
180 Underline,
182 Strikethrough,
184 Outline {
187 color: [u8; 4],
188 width_x: f32,
189 width_y: f32,
190 },
191 Shadow {
193 color: [u8; 4],
194 x_offset: f32,
195 y_offset: f32,
196 },
197 Blur { radius: f32 },
199 EdgeBlur { radius: f32 },
201 Karaoke {
204 progress: f32,
205 style: u8,
206 secondary: [u8; 4],
207 },
208 Rotation {
211 x: f32,
212 y: f32,
213 z: f32,
214 origin: Option<(f32, f32)>,
215 },
216 Shear { x: f32, y: f32 },
218 Scale { x: f32, y: f32 },
220 Clip {
222 x1: f32,
223 y1: f32,
224 x2: f32,
225 y2: f32,
226 inverse: bool,
227 },
228 OpaqueBox {
231 color: [u8; 4],
232 padding_x: f32,
233 padding_y: f32,
234 },
235}