gioser

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 660Markdown 302TOML 157RON 312382 Definiciones

⎇ main shared/foreign-lottie/src/runtime/render.rs Rust · 486 líneas · 17.0 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo — 27 crates nuevos (anim, lottie, svg, mesh, image, layer, test, wire-view, widgets rag-sidebar/router/lazy-list/rive-button) y sync de los 95 existentes 93253cc22 · Sergio ·

1// Copyright 2024 the Velato Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use super::Composition;
5use super::model::{Content, Draw, Geometry, GroupTransform, Layer, Shape, fixed};
6use kurbo::{
7    Affine, BezPath, CubicBez, Line, ParamCurve, ParamCurveArclen, PathEl, PathSeg, Point, QuadBez,
8    Rect,
9};
10use peniko::{Fill, Mix};
11use std::mem::swap;
12use std::ops::Range;
13
14pub trait RenderSink {
15    fn push_layer(
16        &mut self,
17        blend: impl Into<peniko::BlendMode>,
18        alpha: f32,
19        transform: Affine,
20        shape: &impl kurbo::Shape,
21    );
22
23    fn push_clip_layer(&mut self, transform: Affine, shape: &impl kurbo::Shape);
24
25    fn pop_layer(&mut self);
26
27    fn draw(
28        &mut self,
29        stroke: Option<&fixed::Stroke>,
30        transform: Affine,
31        brush: &fixed::Brush,
32        shape: &impl kurbo::Shape,
33    );
34}
35
36/// Renders a composition into a scene.
37#[derive(Debug, Default)]
38pub struct Renderer {
39    batch: Batch,
40    mask_elements: Vec<PathEl>,
41}
42
43impl Renderer {
44    /// Creates a new renderer.
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Renders and appends the animation at a given frame to the provided scene.
50    pub fn append(
51        &mut self,
52        animation: &Composition,
53        frame: f64,
54        transform: Affine,
55        alpha: f64,
56        scene: &mut impl RenderSink,
57    ) {
58        self.batch.clear();
59        scene.push_clip_layer(
60            transform,
61            &Rect::new(0.0, 0.0, animation.width as _, animation.height as _),
62        );
63        for layer in animation.layers.iter().rev() {
64            if layer.is_mask {
65                continue;
66            }
67            self.render_layer(
68                animation,
69                &animation.layers,
70                layer,
71                transform,
72                alpha,
73                frame,
74                scene,
75            );
76        }
77        scene.pop_layer();
78    }
79
80    #[expect(clippy::too_many_arguments, reason = "Deferred")]
81    fn render_layer(
82        &mut self,
83        animation: &Composition,
84        layer_set: &[Layer],
85        layer: &Layer,
86        transform: Affine,
87        alpha: f64,
88        frame: f64,
89        scene: &mut impl RenderSink,
90    ) {
91        if !layer.frames.contains(&frame) {
92            return;
93        }
94        let parent_transform = transform;
95        let transform = self.compute_transform(layer_set, layer, parent_transform, frame);
96        let full_rect = Rect::new(0.0, 0.0, animation.width as f64, animation.height as f64);
97        if let Some((mode, mask_index)) = layer.mask_layer {
98            // todo: re-enable masking when it is more understood (and/or if
99            // it's currently supported in vello?) Extra layer to
100            // isolate blending for the mask
101            scene.push_layer(Mix::Normal, 1.0, parent_transform, &full_rect);
102            if let Some(mask) = layer_set.get(mask_index) {
103                self.render_layer(
104                    animation,
105                    layer_set,
106                    mask,
107                    parent_transform,
108                    alpha,
109                    frame,
110                    scene,
111                );
112            }
113            scene.push_layer(mode, 1.0, parent_transform, &full_rect);
114        }
115        let alpha = alpha * layer.opacity.evaluate(frame) / 100.0;
116        for mask in &layer.masks {
117            mask.geometry.evaluate(frame, &mut self.mask_elements);
118            scene.push_clip_layer(transform, &self.mask_elements.as_slice());
119            self.mask_elements.clear();
120        }
121        match &layer.content {
122            Content::None => {}
123            Content::Instance {
124                name,
125                time_remap: _,
126            } => {
127                // TODO: Use time_remap
128                // let frame = time_remap
129                //     .as_ref()
130                //     .map(|tm| tm.evaluate(frame))
131                //     .unwrap_or(frame);
132                if let Some(asset_layers) = animation.assets.get(name) {
133                    let frame = frame / layer.stretch;
134                    let frame_delta = -layer.start_frame / layer.stretch;
135                    for asset_layer in asset_layers.iter().rev() {
136                        if asset_layer.is_mask {
137                            continue;
138                        }
139                        self.render_layer(
140                            animation,
141                            asset_layers,
142                            asset_layer,
143                            transform,
144                            alpha,
145                            frame + frame_delta,
146                            scene,
147                        );
148                    }
149                }
150            }
151            Content::Shape(shapes) => {
152                self.render_shapes(shapes, transform, alpha, frame);
153                self.batch.render(scene);
154                self.batch.clear();
155            }
156        }
157        for _ in 0..layer.masks.len() + (layer.mask_layer.is_some() as usize * 2) {
158            scene.pop_layer();
159        }
160    }
161
162    fn render_shapes(&mut self, shapes: &[Shape], transform: Affine, alpha: f64, frame: f64) {
163        // Keep track of our local top of the geometry stack. Any subsequent
164        // draws are bounded by this.
165        let geometry_start = self.batch.geometries.len();
166        // Also keep track of top of draw stack for repeater evaluation.
167        let draw_start = self.batch.draws.len();
168        // Top to bottom, collect geometries and draws.
169        for shape in shapes {
170            match shape {
171                Shape::Group(shapes, group_transform) => {
172                    let (group_transform, group_alpha) =
173                        if let Some(GroupTransform { transform, opacity }) = group_transform {
174                            (
175                                transform.evaluate(frame).into_owned(),
176                                opacity.evaluate(frame) / 100.0,
177                            )
178                        } else {
179                            (Affine::IDENTITY, 1.0)
180                        };
181                    self.render_shapes(
182                        shapes,
183                        transform * group_transform,
184                        alpha * group_alpha,
185                        frame,
186                    );
187                }
188                Shape::Geometry(geometry) => {
189                    self.batch.push_geometry(geometry, transform, frame);
190                }
191                Shape::Draw(draw) => {
192                    self.batch.push_draw(draw, alpha, geometry_start, frame);
193                }
194                Shape::Repeater(repeater) => {
195                    let repeater = repeater.evaluate(frame);
196                    self.batch
197                        .repeat(repeater.as_ref(), geometry_start, draw_start);
198                }
199                Shape::Trim(trim) => {
200                    let trim = trim.evaluate(frame);
201                    self.batch.apply_trim(trim.as_ref(), geometry_start);
202                }
203            }
204        }
205    }
206
207    /// Computes the transform for a single layer. This currently chases the
208    /// full transform chain each time. If it becomes a bottleneck, we can
209    /// implement caching.
210    fn compute_transform(
211        &self,
212        layer_set: &[Layer],
213        layer: &Layer,
214        global_transform: Affine,
215        frame: f64,
216    ) -> Affine {
217        let mut transform = layer.transform.evaluate(frame).into_owned();
218        let mut parent_index = layer.parent;
219        let mut count = 0_usize;
220        while let Some(index) = parent_index {
221            // We don't check for cycles at import time, so this heuristic
222            // prevents infinite loops.
223            if count >= layer_set.len() {
224                break;
225            }
226            if let Some(parent) = layer_set.get(index) {
227                parent_index = parent.parent;
228                transform = parent.transform.evaluate(frame).into_owned() * transform;
229                count += 1;
230            } else {
231                break;
232            }
233        }
234        global_transform * transform
235    }
236}
237
238#[derive(Clone, Debug)]
239struct DrawData {
240    stroke: Option<fixed::Stroke>,
241    brush: fixed::Brush,
242    alpha: f64,
243    /// Range into `ShapeBatch::geometries`
244    geometry: Range<usize>,
245}
246
247impl DrawData {
248    fn new(draw: &Draw, alpha: f64, geometry: Range<usize>, frame: f64) -> Self {
249        Self {
250            stroke: draw
251                .stroke
252                .as_ref()
253                .map(|stroke| stroke.evaluate(frame).into_owned()),
254            brush: draw.brush.evaluate(1.0, frame).into_owned(),
255            alpha: alpha * draw.opacity.evaluate(frame) / 100.0,
256            geometry,
257        }
258    }
259}
260
261#[derive(Clone, Debug)]
262struct GeometryData {
263    /// Range into `ShapeBatch::elements`
264    elements: Range<usize>,
265    transform: Affine,
266}
267
268#[derive(Debug, Default)]
269struct Batch {
270    elements: Vec<PathEl>,
271    geometries: Vec<GeometryData>,
272    draws: Vec<DrawData>,
273    repeat_geometries: Vec<GeometryData>,
274    repeat_draws: Vec<DrawData>,
275    /// Length of geometries at time of most recent draw. This is
276    /// used to prevent merging into already used geometries.
277    drawn_geometry: usize,
278    trim_elements: Vec<PathEl>,
279}
280
281impl Batch {
282    fn push_geometry(&mut self, geometry: &Geometry, transform: Affine, frame: f64) {
283        // Merge with the previous geometry if possible. There are two
284        // conditions:
285        // 1. The previous geometry has not yet been referenced by a draw
286        // 2. The geometries have the same transform
287        if self.drawn_geometry < self.geometries.len()
288            && self.geometries.last().map(|last| last.transform) == Some(transform)
289        {
290            geometry.evaluate(frame, &mut self.elements);
291            self.geometries.last_mut().unwrap().elements.end = self.elements.len();
292        } else {
293            let start = self.elements.len();
294            geometry.evaluate(frame, &mut self.elements);
295            let end = self.elements.len();
296            self.geometries.push(GeometryData {
297                elements: start..end,
298                transform,
299            });
300        }
301    }
302
303    fn push_draw(&mut self, draw: &Draw, alpha: f64, geometry_start: usize, frame: f64) {
304        self.draws.push(DrawData::new(
305            draw,
306            alpha,
307            geometry_start..self.geometries.len(),
308            frame,
309        ));
310        self.drawn_geometry = self.geometries.len();
311    }
312
313    fn repeat(&mut self, repeater: &fixed::Repeater, geometry_start: usize, draw_start: usize) {
314        // First move the relevant ranges of geometries and draws into side
315        // buffers
316        self.repeat_geometries
317            .extend(self.geometries.drain(geometry_start..));
318        self.repeat_draws.extend(self.draws.drain(draw_start..));
319        // Next, repeat the geometries and apply the offset transform
320        for geometry in self.repeat_geometries.iter() {
321            for i in 0..repeater.copies {
322                let transform = repeater.transform(i);
323                let mut geometry = geometry.clone();
324                geometry.transform *= transform;
325                self.geometries.push(geometry);
326            }
327        }
328        // Finally, repeat the draws, taking into account opacity and the
329        // modified newly repeated geometry ranges
330        let start_alpha = repeater.start_opacity / 100.0;
331        let end_alpha = repeater.end_opacity / 100.0;
332        let delta_alpha = if repeater.copies > 1 {
333            // See note in Skottie: AE does not cover the full opacity range
334            (end_alpha - start_alpha) / repeater.copies as f64
335        } else {
336            0.0
337        };
338        for i in 0..repeater.copies {
339            let alpha = start_alpha + delta_alpha * i as f64;
340            if alpha <= 0.0 {
341                continue;
342            }
343            for mut draw in self.repeat_draws.iter().cloned() {
344                draw.alpha *= alpha;
345                let count = draw.geometry.end - draw.geometry.start;
346                draw.geometry.start =
347                    geometry_start + (draw.geometry.start - geometry_start) * repeater.copies;
348                draw.geometry.end = draw.geometry.start + count * repeater.copies;
349                self.draws.push(draw);
350            }
351        }
352        // Clear the side buffers
353        self.repeat_geometries.clear();
354        self.repeat_draws.clear();
355        // Prevent merging until new geometries are pushed
356        self.drawn_geometry = self.geometries.len();
357    }
358
359    fn apply_trim(&mut self, trim: &fixed::Trim, geometry_start: usize) {
360        let Some((first, second)) = trim.normalized() else {
361            for geometry in &mut self.geometries[geometry_start..] {
362                geometry.elements = 0..0;
363            }
364            return;
365        };
366
367        if first.0 <= 1e-9 && first.1 >= 1.0 - 1e-9 && second.is_none() {
368            return;
369        }
370
371        for geometry in &self.geometries[..geometry_start] {
372            self.trim_elements
373                .extend(self.elements[geometry.elements.clone()].iter().cloned());
374        }
375
376        const ACCURACY: f64 = 0.1;
377
378        for geometry in &mut self.geometries[geometry_start..] {
379            let path: BezPath = self.elements[geometry.elements.clone()]
380                .iter()
381                .cloned()
382                .collect();
383            let segs: Vec<PathSeg> = path.segments().collect();
384            let total_length: f64 = segs.iter().map(|s| s.arclen(ACCURACY)).sum();
385
386            if total_length <= 0.0 || segs.is_empty() {
387                let pos = self.trim_elements.len();
388                geometry.elements = pos..pos;
389                continue;
390            }
391
392            let trim_ranges = [Some(first), second];
393            let mut trimmed = BezPath::new();
394            for range in trim_ranges.into_iter().flatten() {
395                let (norm_start, norm_end) = range;
396                let start_len = norm_start * total_length;
397                let end_len = norm_end * total_length;
398                let mut current_len = 0.0;
399                let mut need_move = true;
400
401                for seg in &segs {
402                    let seg_len = seg.arclen(ACCURACY);
403                    let seg_end = current_len + seg_len;
404
405                    if seg_end < start_len - 1e-9 {
406                        current_len = seg_end;
407                        continue;
408                    }
409                    if current_len > end_len + 1e-9 {
410                        break;
411                    }
412
413                    let t_start = if current_len < start_len {
414                        seg.inv_arclen(start_len - current_len, ACCURACY)
415                            .clamp(0.0, 1.0)
416                    } else {
417                        0.0
418                    };
419                    let t_end = if seg_end > end_len {
420                        seg.inv_arclen(end_len - current_len, ACCURACY)
421                            .clamp(0.0, 1.0)
422                    } else {
423                        1.0
424                    };
425
426                    if t_end > t_start + 1e-9 {
427                        let sub = seg.subsegment(t_start..t_end);
428                        if need_move {
429                            trimmed.move_to(sub.start());
430                            need_move = false;
431                        }
432                        match sub {
433                            PathSeg::Line(l) => trimmed.line_to(l.p1),
434                            PathSeg::Quad(q) => trimmed.quad_to(q.p1, q.p2),
435                            PathSeg::Cubic(c) => trimmed.curve_to(c.p1, c.p2, c.p3),
436                        }
437                    }
438                    current_len = seg_end;
439                }
440            }
441
442            let new_start = self.trim_elements.len();
443            self.trim_elements
444                .extend(trimmed.elements().iter().cloned());
445            geometry.elements = new_start..self.trim_elements.len();
446        }
447
448        let mut offset = 0;
449        for geometry in &mut self.geometries[..geometry_start] {
450            let len = geometry.elements.len();
451            geometry.elements = offset..offset + len;
452            offset += len;
453        }
454
455        swap(&mut self.elements, &mut self.trim_elements);
456        self.trim_elements.clear();
457    }
458
459    fn render(&self, scene: &mut impl RenderSink) {
460        // Process all draws in reverse
461        for draw in self.draws.iter().rev() {
462            // Some nastiness to avoid cloning the brush if unnecessary
463            let modified_brush = if draw.alpha != 1.0 {
464                Some(draw.brush.clone().multiply_alpha(draw.alpha as _))
465            } else {
466                None
467            };
468            let brush = modified_brush.as_ref().unwrap_or(&draw.brush);
469            for geometry in self.geometries[draw.geometry.clone()].iter() {
470                let path = &self.elements[geometry.elements.clone()];
471                let transform = geometry.transform;
472                scene.draw(draw.stroke.as_ref(), transform, brush, &path);
473            }
474        }
475    }
476
477    fn clear(&mut self) {
478        self.elements.clear();
479        self.geometries.clear();
480        self.draws.clear();
481        self.repeat_geometries.clear();
482        self.repeat_draws.clear();
483        self.drawn_geometry = 0;
484        self.trim_elements.clear();
485    }
486}