1use crate::filter_effects::Filter;
7#[cfg(feature = "text")]
8use crate::glyph::{GlyphRenderer, GlyphRunBuilder, GlyphType, PreparedGlyph};
9use crate::kurbo::{Affine, BezPath, Rect, Stroke};
10use crate::mask::Mask;
11use crate::paint::PaintType;
12#[cfg(feature = "text")]
13use crate::peniko::FontData;
14use crate::peniko::{BlendMode, Fill};
15use crate::strip::Strip;
16use crate::strip_generator::StripStorage;
17use alloc::vec::Vec;
18
19#[derive(Debug, Default)]
21pub struct CachedStrips {
22 strip_storage: StripStorage,
24 strip_start_indices: Vec<usize>,
26}
27
28impl CachedStrips {
29 pub fn new(strip_storage: StripStorage, strip_start_indices: Vec<usize>) -> Self {
31 Self {
32 strip_storage,
33 strip_start_indices,
34 }
35 }
36
37 pub fn clear(&mut self) {
39 self.strip_storage.clear();
40 self.strip_start_indices.clear();
41 }
42
43 pub fn is_empty(&self) -> bool {
45 self.strip_storage.is_empty() && self.strip_start_indices.is_empty()
46 }
47
48 pub fn strip_count(&self) -> usize {
50 self.strip_storage.strips.len()
51 }
52
53 pub fn alpha_count(&self) -> usize {
55 self.strip_storage.alphas.len()
56 }
57
58 pub fn strips(&self) -> &[Strip] {
60 &self.strip_storage.strips
61 }
62
63 pub fn alphas(&self) -> &[u8] {
65 &self.strip_storage.alphas
66 }
67
68 pub fn strip_start_indices(&self) -> &[usize] {
70 &self.strip_start_indices
71 }
72
73 pub fn take(&mut self) -> (StripStorage, Vec<usize>) {
75 let strip_storage = core::mem::take(&mut self.strip_storage);
76 let strip_start_indices = core::mem::take(&mut self.strip_start_indices);
77 (strip_storage, strip_start_indices)
78 }
79}
80
81#[derive(Debug)]
83pub struct Recording {
84 commands: Vec<RenderCommand>,
86 cached_strips: CachedStrips,
88 transform: Affine,
90}
91
92#[derive(Debug, Clone)]
94pub struct PushLayerCommand {
95 pub clip_path: Option<BezPath>,
97 pub blend_mode: Option<BlendMode>,
99 pub opacity: Option<f32>,
101 pub mask: Option<Mask>,
103 pub filter: Option<Filter>,
105}
106
107#[derive(Debug)]
109pub enum RenderCommand {
110 FillPath(BezPath),
112 StrokePath(BezPath),
114 FillRect(Rect),
116 StrokeRect(Rect),
118 SetTransform(Affine),
120 SetFillRule(Fill),
122 SetStroke(Stroke),
124 PushLayer(PushLayerCommand),
126 PopLayer,
128 SetPaint(PaintType),
130 SetPaintTransform(Affine),
132 ResetPaintTransform,
134 SetFilterEffect(Filter),
136 ResetFilterEffect,
138 #[cfg(feature = "text")]
140 FillOutlineGlyph((BezPath, Affine)),
141 #[cfg(feature = "text")]
143 StrokeOutlineGlyph((BezPath, Affine)),
144}
145
146impl Recording {
147 pub fn new() -> Self {
149 Self {
150 commands: Vec::new(),
151 cached_strips: CachedStrips::default(),
152 transform: Affine::IDENTITY,
153 }
154 }
155
156 pub(crate) fn set_transform(&mut self, transform: Affine) {
158 self.transform = transform;
159 }
160
161 pub fn commands(&self) -> &[RenderCommand] {
163 &self.commands
164 }
165
166 pub fn command_count(&self) -> usize {
168 self.commands.len()
169 }
170
171 pub fn has_cached_strips(&self) -> bool {
173 !self.cached_strips.is_empty()
174 }
175
176 pub fn strip_count(&self) -> usize {
178 self.cached_strips.strip_count()
179 }
180
181 pub fn alpha_count(&self) -> usize {
183 self.cached_strips.alpha_count()
184 }
185
186 pub fn get_cached_strips(&self) -> (&[Strip], &[u8]) {
188 (self.cached_strips.strips(), self.cached_strips.alphas())
189 }
190
191 pub fn take_cached_strips(&mut self) -> (StripStorage, Vec<usize>) {
193 self.cached_strips.take()
194 }
195
196 pub fn get_strip_start_indices(&self) -> &[usize] {
198 self.cached_strips.strip_start_indices()
199 }
200
201 pub fn clear(&mut self) {
203 self.commands.clear();
204 self.cached_strips.clear();
205 self.transform = Affine::IDENTITY;
206 }
207
208 pub(crate) fn add_command(&mut self, command: RenderCommand) {
210 self.commands.push(command);
211 }
212
213 pub fn set_cached_strips(
215 &mut self,
216 strip_storage: StripStorage,
217 strip_start_indices: Vec<usize>,
218 ) {
219 self.cached_strips = CachedStrips::new(strip_storage, strip_start_indices);
220 }
221}
222
223impl Default for Recording {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229pub trait Recordable {
265 fn record<F>(&mut self, recording: &mut Recording, f: F)
280 where
281 F: FnOnce(&mut Recorder<'_>);
282
283 fn prepare_recording(&mut self, recording: &mut Recording);
300
301 fn execute_recording(&mut self, recording: &Recording);
325}
326
327#[derive(Debug)]
329pub struct Recorder<'a> {
330 recording: &'a mut Recording,
332
333 #[cfg(feature = "text")]
334 glyph_caches: Option<crate::glyph::GlyphCaches>,
335}
336
337impl<'a> Recorder<'a> {
338 pub fn new(
340 recording: &'a mut Recording,
341 transform: Affine,
342 #[cfg(feature = "text")] glyph_caches: crate::glyph::GlyphCaches,
343 ) -> Self {
344 let mut s = Self {
345 recording,
346 #[cfg(feature = "text")]
347 glyph_caches: Some(glyph_caches),
348 };
349 s.set_transform(transform);
351 s
352 }
353
354 pub fn fill_path(&mut self, path: &BezPath) {
356 self.recording
357 .add_command(RenderCommand::FillPath(path.clone()));
358 }
359
360 pub fn stroke_path(&mut self, path: &BezPath) {
362 self.recording
363 .add_command(RenderCommand::StrokePath(path.clone()));
364 }
365
366 pub fn fill_rect(&mut self, rect: &Rect) {
368 self.recording.add_command(RenderCommand::FillRect(*rect));
369 }
370
371 pub fn stroke_rect(&mut self, rect: &Rect) {
373 self.recording.add_command(RenderCommand::StrokeRect(*rect));
374 }
375
376 pub fn set_transform(&mut self, transform: Affine) {
378 self.recording.set_transform(transform);
379 self.recording
380 .add_command(RenderCommand::SetTransform(transform));
381 }
382
383 pub fn set_fill_rule(&mut self, fill_rule: Fill) {
385 self.recording
386 .add_command(RenderCommand::SetFillRule(fill_rule));
387 }
388
389 pub fn set_stroke(&mut self, stroke: Stroke) {
391 self.recording.add_command(RenderCommand::SetStroke(stroke));
392 }
393
394 pub fn set_paint(&mut self, paint: impl Into<PaintType>) {
396 self.recording
397 .add_command(RenderCommand::SetPaint(paint.into()));
398 }
399
400 pub fn set_paint_transform(&mut self, paint_transform: Affine) {
402 self.recording
403 .add_command(RenderCommand::SetPaintTransform(paint_transform));
404 }
405
406 pub fn reset_paint_transform(&mut self) {
408 self.recording
409 .add_command(RenderCommand::ResetPaintTransform);
410 }
411
412 pub fn set_filter_effect(&mut self, filter: Filter) {
414 self.recording
415 .add_command(RenderCommand::SetFilterEffect(filter));
416 }
417
418 pub fn reset_filter_effect(&mut self) {
420 self.recording.add_command(RenderCommand::ResetFilterEffect);
421 }
422
423 pub fn push_layer(
425 &mut self,
426 clip_path: Option<&BezPath>,
427 blend_mode: Option<BlendMode>,
428 opacity: Option<f32>,
429 mask: Option<Mask>,
430 filter: Option<Filter>,
431 ) {
432 self.recording
433 .add_command(RenderCommand::PushLayer(PushLayerCommand {
434 clip_path: clip_path.cloned(),
435 blend_mode,
436 opacity,
437 mask,
438 filter,
439 }));
440 }
441
442 pub fn push_clip_layer(&mut self, clip_path: &BezPath) {
444 self.push_layer(Some(clip_path), None, None, None, None);
445 }
446
447 pub fn push_filter_layer(&mut self, filter: Filter) {
451 self.push_layer(None, None, None, None, Some(filter));
452 }
453
454 pub fn pop_layer(&mut self) {
456 self.recording.add_command(RenderCommand::PopLayer);
457 }
458
459 #[cfg(feature = "text")]
461 pub fn glyph_run(&mut self, font: &FontData) -> GlyphRunBuilder<'_, Self> {
462 GlyphRunBuilder::new(font.clone(), self.recording.transform, self)
463 }
464}
465
466#[cfg(feature = "text")]
467impl GlyphRenderer for Recorder<'_> {
468 fn fill_glyph(&mut self, glyph: PreparedGlyph<'_>) {
469 match glyph.glyph_type {
470 GlyphType::Outline(outline_glyph) => {
471 if !outline_glyph.path.is_empty() {
472 self.recording.add_command(RenderCommand::FillOutlineGlyph((
473 outline_glyph.path.clone(),
474 glyph.transform,
475 )));
476 }
477 }
478
479 _ => {
480 unimplemented!("Recording glyphs of type {:?}", glyph.glyph_type);
481 }
482 }
483 }
484
485 fn stroke_glyph(&mut self, glyph: PreparedGlyph<'_>) {
486 match glyph.glyph_type {
487 GlyphType::Outline(outline_glyph) => {
488 if !outline_glyph.path.is_empty() {
489 self.recording
490 .add_command(RenderCommand::StrokeOutlineGlyph((
491 outline_glyph.path.clone(),
492 glyph.transform,
493 )));
494 }
495 }
496 _ => {
497 unimplemented!("Recording glyphs of type {:?}", glyph.glyph_type);
498 }
499 }
500 }
501
502 fn restore_glyph_caches(&mut self, caches: crate::glyph::GlyphCaches) {
503 self.glyph_caches = Some(caches);
504 }
505 fn take_glyph_caches(&mut self) -> crate::glyph::GlyphCaches {
506 self.glyph_caches.take().unwrap_or_default()
507 }
508}