zeno/
scratch.rs

1//! Context for reusing dynamic memory allocations.
2
3use super::geometry::{Bounds, BoundsBuilder, Transform};
4use super::path_builder::{PathBuilder, TransformSink};
5use super::path_data::PathData;
6use super::raster::HeapStorage;
7use super::segment::Segment;
8use super::stroke::stroke_with_storage;
9use super::style::{Fill, Style};
10
11use crate::lib::Vec;
12use core::borrow::Borrow;
13
14/// Scratch memory for reusable heap allocations.
15#[derive(Default)]
16pub struct Scratch {
17    pub(super) inner: Inner,
18    pub(super) render: HeapStorage,
19}
20
21impl Scratch {
22    /// Creates a new scratch memory context.
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    /// Applies the style and transform to the path and emits the result to the specified sink.
28    pub fn apply<'a>(
29        &mut self,
30        data: impl PathData,
31        style: impl Into<Style<'a>>,
32        transform: Option<Transform>,
33        sink: &mut impl PathBuilder,
34    ) -> Fill {
35        self.inner.apply(data, &style.into(), transform, sink)
36    }
37
38    /// Computes the bounding box of the path.
39    pub fn bounds<'a>(
40        &mut self,
41        data: impl PathData,
42        style: impl Into<Style<'a>>,
43        transform: Option<Transform>,
44    ) -> Bounds {
45        let style = style.into();
46        let mut bounds = BoundsBuilder::new();
47        self.apply(data, style, transform, &mut bounds);
48        bounds.build()
49    }
50}
51
52#[derive(Default)]
53pub(super) struct Inner {
54    pub segments: Vec<Segment>,
55}
56
57impl Inner {
58    pub fn apply(
59        &mut self,
60        data: impl PathData,
61        style: &Style,
62        transform: Option<Transform>,
63        sink: &mut impl PathBuilder,
64    ) -> Fill {
65        match style {
66            Style::Fill(fill) => {
67                if let Some(transform) = transform {
68                    let mut transform_sink = TransformSink { sink, transform };
69                    data.copy_to(&mut transform_sink);
70                    *fill
71                } else {
72                    data.copy_to(sink);
73                    *fill
74                }
75            }
76            Style::Stroke(stroke) => {
77                if let Some(transform) = transform {
78                    if stroke.scale {
79                        let mut transform_sink = TransformSink { sink, transform };
80                        stroke_with_storage(
81                            data.commands(),
82                            stroke,
83                            &mut transform_sink,
84                            &mut self.segments,
85                        );
86                    } else {
87                        stroke_with_storage(
88                            data.commands()
89                                .map(|cmd| cmd.borrow().transform(&transform)),
90                            stroke,
91                            sink,
92                            &mut self.segments,
93                        );
94                    }
95                } else {
96                    stroke_with_storage(data.commands(), stroke, sink, &mut self.segments);
97                }
98                Fill::NonZero
99            }
100        }
101    }
102}