1use std::sync::Arc;
2
3use crate::{Filter, Glyph, NormalizedCoord, Paint, PaintRef, PaintScene, RenderContext};
4use kurbo::{Affine, BezPath, Rect, Shape, Stroke};
5use peniko::{BlendMode, Color, Fill, FontData, Style, StyleRef};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10const DEFAULT_TOLERANCE: f64 = 0.1;
11
12#[derive(Clone, Debug, PartialEq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub enum RenderCommand<Font = FontData, Brush = Paint> {
15 PushLayer(LayerCommand),
19 PushClipLayer(ClipCommand),
23 PopLayer,
25 Stroke(StrokeCommand<Brush>),
27 Fill(FillCommand<Brush>),
29 GlyphRun(GlyphRunCommand<Font, Brush>),
31 BoxShadow(BoxShadowCommand),
33}
34
35impl RenderCommand {
36 fn apply_transform(mut self, transform: Affine) -> Self {
38 match &mut self {
39 RenderCommand::PushLayer(cmd) => cmd.transform = transform * cmd.transform,
40 RenderCommand::PushClipLayer(cmd) => cmd.transform = transform * cmd.transform,
41 RenderCommand::PopLayer => {}
42 RenderCommand::Stroke(cmd) => cmd.transform = transform * cmd.transform,
43 RenderCommand::Fill(cmd) => cmd.transform = transform * cmd.transform,
44 RenderCommand::GlyphRun(cmd) => cmd.transform = transform * cmd.transform,
45 RenderCommand::BoxShadow(cmd) => cmd.transform = transform * cmd.transform,
46 };
47
48 self
49 }
50}
51
52#[derive(Clone, Debug, PartialEq)]
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57pub struct LayerCommand {
58 pub blend: BlendMode,
59 pub alpha: f32,
60 pub transform: Affine,
61 #[cfg_attr(feature = "serde", serde(with = "svg_path"))]
62 pub clip: BezPath, pub filter: Option<Arc<Filter>>,
64 pub backdrop_filter: Option<Arc<Filter>>,
65}
66
67#[derive(Clone, Debug, PartialEq)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72pub struct ClipCommand {
73 pub transform: Affine,
74 #[cfg_attr(feature = "serde", serde(with = "svg_path"))]
75 pub clip: BezPath, }
77
78#[derive(Clone, Debug, PartialEq)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81pub struct StrokeCommand<Brush = Paint> {
82 pub style: Stroke,
83 pub transform: Affine,
84 pub brush: Brush, pub brush_transform: Option<Affine>,
86 #[cfg_attr(feature = "serde", serde(with = "svg_path"))]
87 pub shape: BezPath, }
89
90#[derive(Clone, Debug, PartialEq)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93pub struct FillCommand<Brush = Paint> {
94 pub fill: Fill,
95 pub transform: Affine,
96 pub brush: Brush, pub brush_transform: Option<Affine>,
98 #[cfg_attr(feature = "serde", serde(with = "svg_path"))]
99 pub shape: BezPath, }
101
102#[derive(Clone, Debug, PartialEq)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub struct GlyphRunCommand<Font = FontData, Brush = Paint> {
106 pub font_data: Font,
107 pub font_size: f32,
108 pub hint: bool,
109 pub normalized_coords: Vec<NormalizedCoord>,
110 #[cfg_attr(feature = "serde", serde(default = "Default::default"))]
111 pub embolden: kurbo::Vec2,
112 pub style: Style,
113 pub brush: Brush,
114 pub brush_alpha: f32,
115 pub transform: Affine,
116 pub glyph_transform: Option<Affine>,
117 pub glyphs: Vec<Glyph>,
118}
119
120#[derive(Clone, Debug, PartialEq)]
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
123pub struct BoxShadowCommand {
124 pub transform: Affine,
125 pub rect: Rect,
126 pub brush: Color,
127 pub radius: f64,
128 pub std_dev: f64,
129}
130
131#[derive(Clone, Debug, PartialEq)]
134pub struct Scene {
135 pub tolerance: f64,
136 pub commands: Vec<RenderCommand>,
137}
138
139impl Default for Scene {
140 fn default() -> Self {
141 Self {
142 tolerance: DEFAULT_TOLERANCE,
143 commands: Vec::new(),
144 }
145 }
146}
147
148impl Scene {
149 pub fn new() -> Self {
151 Self::default()
152 }
153
154 pub fn with_tolerance(tolerance: f64) -> Self {
155 Self {
156 tolerance,
157 commands: Vec::new(),
158 }
159 }
160
161 fn convert_paint(&mut self, paint_ref: PaintRef<'_>) -> Paint {
162 match paint_ref {
163 Paint::Solid(color) => Paint::Solid(color),
164 Paint::Gradient(gradient) => Paint::Gradient(gradient.clone()),
165 Paint::Image(image) => Paint::Image(image.to_owned()),
166 Paint::Resource(id) => Paint::Resource(id),
168 Paint::Custom(_) => Paint::Solid(Color::TRANSPARENT),
169 }
170 }
171}
172
173impl RenderContext for Scene {}
174impl PaintScene for Scene {
175 fn reset(&mut self) {
176 self.commands.clear()
177 }
178
179 fn push_layer(
180 &mut self,
181 blend: impl Into<BlendMode>,
182 alpha: f32,
183 transform: Affine,
184 clip: &impl Shape,
185 filter: Option<Arc<Filter>>,
186 backdrop_filter: Option<Arc<Filter>>,
187 ) {
188 let blend = blend.into();
189 let clip = clip.into_path(self.tolerance);
190 let layer = LayerCommand {
191 blend,
192 alpha,
193 transform,
194 clip,
195 filter,
196 backdrop_filter,
197 };
198 self.commands.push(RenderCommand::PushLayer(layer));
199 }
200
201 fn push_clip_layer(&mut self, transform: Affine, clip: &impl Shape) {
202 let clip = clip.into_path(self.tolerance);
203 let layer = ClipCommand { transform, clip };
204 self.commands.push(RenderCommand::PushClipLayer(layer));
205 }
206
207 fn pop_layer(&mut self) {
208 self.commands.push(RenderCommand::PopLayer);
209 }
210
211 fn stroke<'a>(
212 &mut self,
213 style: &Stroke,
214 transform: Affine,
215 paint_ref: impl Into<PaintRef<'a>>,
216 brush_transform: Option<Affine>,
217 shape: &impl Shape,
218 ) {
219 let shape = shape.into_path(self.tolerance);
220 let brush = self.convert_paint(paint_ref.into());
221 let stroke = StrokeCommand {
222 style: style.clone(),
223 transform,
224 brush,
225 brush_transform,
226 shape,
227 };
228 self.commands.push(RenderCommand::Stroke(stroke));
229 }
230
231 fn fill<'a>(
232 &mut self,
233 style: Fill,
234 transform: Affine,
235 paint: impl Into<PaintRef<'a>>,
236 brush_transform: Option<Affine>,
237 shape: &impl Shape,
238 ) {
239 let shape = shape.into_path(self.tolerance);
240 let brush = self.convert_paint(paint.into());
241 let fill = FillCommand {
242 fill: style,
243 transform,
244 brush,
245 brush_transform,
246 shape,
247 };
248 self.commands.push(RenderCommand::Fill(fill));
249 }
250
251 fn draw_glyphs<'a, 's: 'a>(
252 &'a mut self,
253 font: &'a FontData,
254 font_size: f32,
255 hint: bool,
256 normalized_coords: &'a [NormalizedCoord],
257 embolden: kurbo::Vec2,
258 style: impl Into<StyleRef<'a>>,
259 paint_ref: impl Into<PaintRef<'a>>,
260 brush_alpha: f32,
261 transform: Affine,
262 glyph_transform: Option<Affine>,
263 glyphs: impl Iterator<Item = Glyph>,
264 ) {
265 let brush = self.convert_paint(paint_ref.into());
266 let glyph_run = GlyphRunCommand {
267 font_data: font.clone(),
268 font_size,
269 hint,
270 normalized_coords: normalized_coords.to_vec(),
271 embolden,
272 style: style.into().to_owned(),
273 brush,
274 brush_alpha,
275 transform,
276 glyph_transform,
277 glyphs: glyphs.into_iter().collect(),
278 };
279 self.commands.push(RenderCommand::GlyphRun(glyph_run));
280 }
281
282 fn draw_box_shadow(
283 &mut self,
284 transform: Affine,
285 rect: Rect,
286 brush: Color,
287 radius: f64,
288 std_dev: f64,
289 ) {
290 let box_shadow = BoxShadowCommand {
291 transform,
292 rect,
293 brush,
294 radius,
295 std_dev,
296 };
297 self.commands.push(RenderCommand::BoxShadow(box_shadow));
298 }
299
300 fn append_scene(&mut self, scene: Scene, scene_transform: Affine) {
301 self.commands.extend(
302 scene
303 .commands
304 .into_iter()
305 .map(|cmd| cmd.apply_transform(scene_transform)),
306 );
307 }
308}
309
310#[cfg(feature = "serde")]
312mod svg_path {
313 use kurbo::BezPath;
314 use serde::{self, Deserialize, Deserializer, Serializer};
315
316 use crate::svg_path_parser;
317
318 pub fn serialize<S>(path: &BezPath, serializer: S) -> Result<S::Ok, S::Error>
319 where
320 S: Serializer,
321 {
322 serializer.serialize_str(&path.to_svg())
323 }
324
325 pub fn deserialize<'de, D>(deserializer: D) -> Result<BezPath, D::Error>
326 where
327 D: Deserializer<'de>,
328 {
329 let s = String::deserialize(deserializer)?;
330 svg_path_parser::parse_svg_path(&s).map_err(serde::de::Error::custom)
331 }
332}