1use super::*;
4
5pub struct Canvas<'a> {
10 commands: Vec<DrawCommand>,
11 state: &'a mut ValidationState,
12 path: &'a str,
13 depth: usize,
14 error: Option<ValidationError>,
15}
16
17impl<'a> Canvas<'a> {
18 pub fn instances(
20 &mut self,
21 batch: Arc<dyn crate::instances::InstanceBatch>,
22 transform: Transform,
23 ) -> Result<(), ValidationError> {
24 let result = (|| {
25 batch.validate()?;
26 crate::display_list::mapped_bounds(batch.bounds(), transform, self.path)?;
27 self.push(DrawCommand::ParticleInstances { batch, transform })
28 })();
29 if let Err(error) = &result {
30 self.error = Some(error.clone());
31 }
32 result
33 }
34
35 pub fn fill_path(
37 &mut self,
38 geometry: Arc<Path2D>,
39 transform: Transform,
40 paint: impl Into<Paint>,
41 rule: FillRule,
42 ) -> Result<(), ValidationError> {
43 let result =
44 crate::display_list::mapped_bounds(geometry.bounding_box(), transform, self.path)
45 .and_then(|_| {
46 self.push(DrawCommand::Fill {
47 path: geometry,
48 transform,
49 paint: paint.into(),
50 rule,
51 })
52 });
53 if let Err(error) = &result {
54 self.error = Some(error.clone());
55 }
56 result
57 }
58
59 pub fn new(state: &'a mut ValidationState, path: &'a str, depth: usize) -> Self {
62 Self {
63 commands: Vec::new(),
64 state,
65 path,
66 depth,
67 error: None,
68 }
69 }
70
71 pub fn fill_rect(
73 &mut self,
74 bounds: Rect,
75 paint: impl Into<Paint>,
76 ) -> Result<(), ValidationError> {
77 self.push(DrawCommand::FillRect {
78 bounds,
79 transform: Transform::IDENTITY,
80 paint: paint.into(),
81 })
82 }
83
84 pub fn fill(
89 &mut self,
90 shape: &impl Shape,
91 paint: impl Into<Paint>,
92 rule: FillRule,
93 ) -> Result<(), ValidationError> {
94 if let Some(error) = &self.error {
95 return Err(error.clone());
96 }
97 let bounds = match shape.bounding_box() {
98 Ok(bounds) => bounds,
99 Err(error) => {
100 self.error = Some(error.clone());
101 return Err(error);
102 }
103 };
104 if let Err(mut error) = validate_rect(bounds, "", "bounds") {
105 error.path = format!("{}.commands[{}]", self.path, self.commands.len());
106 self.error = Some(error.clone());
107 return Err(error);
108 }
109 let paint = paint.into();
110 if let Some(bounds) = shape.as_rect() {
111 return self.fill_rect(bounds, paint);
112 }
113 let path = match shape.path() {
114 Ok(path) => path,
115 Err(error) => {
116 self.error = Some(error.clone());
117 return Err(error);
118 }
119 };
120 self.push(DrawCommand::Fill {
121 path: Arc::new(path),
122 transform: Transform::IDENTITY,
123 paint,
124 rule,
125 })
126 }
127
128 pub fn stroke(
134 &mut self,
135 shape: &impl Shape,
136 style: &StrokeStyle,
137 paint: impl Into<Paint>,
138 ) -> Result<(), ValidationError> {
139 if let Some(error) = &self.error {
140 return Err(error.clone());
141 }
142 let path = format!("{}.commands[{}].stroke", self.path, self.commands.len());
143 let result = (|| {
144 style.validate(&path)?;
145 validate_rect(shape.bounding_box()?, &path, "bounds")?;
146 let geometry = shape.path()?;
147 for index in 0..geometry.verbs().len() {
148 self.state
149 .enter_with(|| format!("{path}.source.verbs[{index}]"), self.depth)?;
150 }
151 let outline = style.outline(&geometry, &path)?;
152 if outline.verbs().is_empty() {
153 return Ok(());
154 }
155 self.push(DrawCommand::Fill {
156 path: Arc::new(outline),
157 transform: Transform::IDENTITY,
158 paint: paint.into(),
159 rule: FillRule::NonZero,
160 })
161 })();
162 if let Err(error) = &result {
163 self.error = Some(error.clone());
164 }
165 result
166 }
167
168 fn push(&mut self, command: DrawCommand) -> Result<(), ValidationError> {
169 if let Some(error) = &self.error {
170 return Err(error.clone());
171 }
172 let index = self.commands.len();
173 let path = || format!("{}.commands[{index}]", self.path);
174 let (bounds, verbs) = match &command {
175 DrawCommand::ParticleInstances { batch, .. } => (batch.bounds(), batch.len()),
176 DrawCommand::FillRect { bounds, .. } => (*bounds, 0),
177 DrawCommand::Fill { path, .. } => (path.bounding_box(), path.verbs().len()),
178 DrawCommand::PushFilterLayer { .. }
179 | DrawCommand::PushLayer { .. }
180 | DrawCommand::PushRectLayer { .. }
181 | DrawCommand::PushShapeLayer { .. }
182 | DrawCommand::PopLayer
183 | DrawCommand::BeginMask { .. }
184 | DrawCommand::EndMask => {
185 let error = ValidationError::invalid(
186 path(),
187 "commands",
188 "Canvas records local fills, not compiled layer boundaries",
189 );
190 self.error = Some(error.clone());
191 return Err(error);
192 }
193 };
194 if let DrawCommand::FillRect { paint, .. } | DrawCommand::Fill { paint, .. } = &command
195 && let Err(mut error) = paint.validate()
196 {
197 error.path = path();
198 self.error = Some(error.clone());
199 return Err(error);
200 }
201 let result = validate_rect(bounds, "", "bounds")
202 .map_err(|mut error| {
203 error.path = path();
204 error
205 })
206 .and_then(|()| self.state.enter_with(path, self.depth))
207 .and_then(|()| {
208 for verb in 0..verbs {
209 self.state
210 .enter_with(|| format!("{}.verbs[{verb}]", path()), self.depth)?;
211 }
212 Ok(())
213 });
214 if let Err(error) = result {
215 self.error = Some(error.clone());
216 return Err(error);
217 }
218 self.commands.push(command);
219 Ok(())
220 }
221
222 pub fn record(
226 mut self,
227 drawable: &dyn Drawable,
228 context: &DrawContext,
229 ) -> Result<Vec<DrawCommand>, ValidationError> {
230 let path = self.path;
231 let result = drawable.draw(context, &mut self);
232 let commands = self.finish()?;
233 result.map_err(|mut error| {
234 error.path = if error.path.is_empty() {
235 format!("{path}.draw")
236 } else {
237 format!("{path}.draw.{}", error.path)
238 };
239 error
240 })?;
241 Ok(commands)
242 }
243
244 pub(crate) fn finish(self) -> Result<Vec<DrawCommand>, ValidationError> {
245 self.error.map_or(Ok(self.commands), Err)
246 }
247}