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 = geometry
44 .validate()
45 .and_then(|_| {
46 crate::display_list::mapped_bounds(geometry.bounding_box(), transform, self.path)
47 })
48 .and_then(|_| {
49 self.push(DrawCommand::Fill {
50 path: geometry,
51 transform,
52 paint: paint.into(),
53 rule,
54 })
55 });
56 if let Err(error) = &result {
57 self.error = Some(error.clone());
58 }
59 result
60 }
61
62 pub fn new(state: &'a mut ValidationState, path: &'a str, depth: usize) -> Self {
65 Self {
66 commands: Vec::new(),
67 state,
68 path,
69 depth,
70 error: None,
71 }
72 }
73
74 pub fn fill_rect(
76 &mut self,
77 bounds: Rect,
78 paint: impl Into<Paint>,
79 ) -> Result<(), ValidationError> {
80 self.push(DrawCommand::FillRect {
81 bounds,
82 transform: Transform::IDENTITY,
83 paint: paint.into(),
84 })
85 }
86
87 pub fn fill(
92 &mut self,
93 shape: &impl Shape,
94 paint: impl Into<Paint>,
95 rule: FillRule,
96 ) -> Result<(), ValidationError> {
97 if let Some(error) = &self.error {
98 return Err(error.clone());
99 }
100 if let Err(error) = shape.validate() {
101 self.error = Some(error.clone());
102 return Err(error);
103 }
104 if let Err(mut error) = validate_rect(shape.bounding_box(), "", "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 self.push(DrawCommand::Fill {
114 path: Arc::new(shape.path()),
115 transform: Transform::IDENTITY,
116 paint,
117 rule,
118 })
119 }
120
121 pub fn stroke(
127 &mut self,
128 shape: &impl Shape,
129 style: &StrokeStyle,
130 paint: impl Into<Paint>,
131 ) -> Result<(), ValidationError> {
132 if let Some(error) = &self.error {
133 return Err(error.clone());
134 }
135 let path = format!("{}.commands[{}].stroke", self.path, self.commands.len());
136 let result = (|| {
137 style.validate(&path)?;
138 shape.validate()?;
139 validate_rect(shape.bounding_box(), &path, "bounds")?;
140 let geometry = shape.path();
141 for index in 0..geometry.verbs().len() {
142 self.state
143 .enter_with(|| format!("{path}.source.verbs[{index}]"), self.depth)?;
144 }
145 let outline = style.outline(&geometry, &path)?;
146 if outline.verbs().is_empty() {
147 return Ok(());
148 }
149 self.push(DrawCommand::Fill {
150 path: Arc::new(outline),
151 transform: Transform::IDENTITY,
152 paint: paint.into(),
153 rule: FillRule::NonZero,
154 })
155 })();
156 if let Err(error) = &result {
157 self.error = Some(error.clone());
158 }
159 result
160 }
161
162 fn push(&mut self, command: DrawCommand) -> Result<(), ValidationError> {
163 if let Some(error) = &self.error {
164 return Err(error.clone());
165 }
166 let index = self.commands.len();
167 let path = || format!("{}.commands[{index}]", self.path);
168 let (bounds, verbs) = match &command {
169 DrawCommand::ParticleInstances { batch, .. } => (batch.bounds(), batch.len()),
170 DrawCommand::FillRect { bounds, .. } => (*bounds, 0),
171 DrawCommand::Fill { path, .. } => (path.bounding_box(), path.verbs().len()),
172 DrawCommand::PushFilterLayer { .. }
173 | DrawCommand::PushLayer { .. }
174 | DrawCommand::PushRectLayer { .. }
175 | DrawCommand::PushShapeLayer { .. }
176 | DrawCommand::PopLayer
177 | DrawCommand::BeginMask { .. }
178 | DrawCommand::EndMask => {
179 let error = ValidationError::invalid(
180 path(),
181 "commands",
182 "Canvas records local fills, not compiled layer boundaries",
183 );
184 self.error = Some(error.clone());
185 return Err(error);
186 }
187 };
188 if let DrawCommand::FillRect { paint, .. } | DrawCommand::Fill { paint, .. } = &command
189 && let Err(mut error) = paint.validate()
190 {
191 error.path = path();
192 self.error = Some(error.clone());
193 return Err(error);
194 }
195 let result = validate_rect(bounds, "", "bounds")
196 .map_err(|mut error| {
197 error.path = path();
198 error
199 })
200 .and_then(|()| self.state.enter_with(path, self.depth))
201 .and_then(|()| {
202 for verb in 0..verbs {
203 self.state
204 .enter_with(|| format!("{}.verbs[{verb}]", path()), self.depth)?;
205 }
206 Ok(())
207 });
208 if let Err(error) = result {
209 self.error = Some(error.clone());
210 return Err(error);
211 }
212 self.commands.push(command);
213 Ok(())
214 }
215
216 pub fn record(
220 mut self,
221 drawable: &dyn Drawable,
222 context: &DrawContext,
223 ) -> Result<Vec<DrawCommand>, ValidationError> {
224 if let Some(resolved) = drawable.resolve_node(context)? {
225 for command in resolved.record_local(context, crate::ValidationLimits::default())? {
226 self.push(command)?;
227 }
228 return self.finish();
229 }
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 fn finish(self) -> Result<Vec<DrawCommand>, ValidationError> {
245 self.error.map_or(Ok(self.commands), Err)
246 }
247}