reflexo_vec2canvas/
incr.rs

1use tiny_skia as sk;
2
3use reflexo::{
4    error::prelude::*,
5    vector::{
6        incr::IncrDocClient,
7        ir::{ImmutStr, Module, Page, Rect},
8        vm::RenderVm,
9    },
10};
11
12use crate::{set_transform, CanvasDevice, CanvasOp, CanvasPage, CanvasTask, DefaultExportFeature};
13
14/// Incremental pass from vector to canvas
15pub struct IncrVec2CanvasPass {
16    /// Canvas's pixel per point
17    pub pixel_per_pt: f32,
18    /// Fills background color with a css color string
19    /// Default is white.
20    ///
21    /// Note: If the string is empty, the background is transparent.
22    pub fill: ImmutStr,
23    /// Holds a sequence of canvas pages that are rendered
24    pub pages: Vec<CanvasPage>,
25}
26
27impl Default for IncrVec2CanvasPass {
28    fn default() -> Self {
29        Self {
30            pixel_per_pt: 3.,
31            fill: "#ffffff".into(),
32            pages: vec![],
33        }
34    }
35}
36
37impl IncrVec2CanvasPass {
38    /// Interprets the changes in the given module and pages.
39    pub fn interpret_changes(&mut self, module: &Module, pages: &[Page]) {
40        // render the document
41        let mut t = CanvasTask::<DefaultExportFeature>::default();
42
43        let mut ct = t.fork_canvas_render_task(module);
44
45        let pages: Vec<CanvasPage> = pages
46            .iter()
47            .enumerate()
48            .map(|(idx, Page { content, size })| {
49                if idx < self.pages.len() && self.pages[idx].content == *content {
50                    return self.pages[idx].clone();
51                }
52
53                CanvasPage {
54                    content: *content,
55                    elem: ct.render_item(content),
56                    size: *size,
57                }
58            })
59            .collect();
60
61        self.pages = pages;
62    }
63
64    /// Flushes a page to the canvas with the given transform.
65    pub async fn flush_page(&mut self, idx: usize, canvas: &dyn CanvasDevice, ts: sk::Transform) {
66        let pg = &self.pages[idx];
67
68        if !set_transform(canvas, ts) {
69            return;
70        }
71        canvas.set_fill_style_str(self.fill.as_ref());
72        canvas.fill_rect(0., 0., pg.size.x.0 as f64, pg.size.y.0 as f64);
73
74        pg.elem.realize(ts, canvas).await;
75    }
76}
77
78/// Maintains the state of the incremental rendering a canvas at client side
79#[derive(Default)]
80pub struct IncrCanvasDocClient {
81    /// State of converting vector to canvas
82    pub vec2canvas: IncrVec2CanvasPass,
83
84    /// Expected exact state of the current DOM.
85    /// Initially it is None meaning no any page is rendered.
86    pub doc_view: Option<Vec<Page>>,
87}
88
89impl IncrCanvasDocClient {
90    /// Reset the state of the incremental rendering.
91    pub fn reset(&mut self) {}
92
93    /// Set canvas's pixel per point
94    pub fn set_pixel_per_pt(&mut self, pixel_per_pt: f32) {
95        self.vec2canvas.pixel_per_pt = pixel_per_pt;
96    }
97
98    /// Set canvas's background color
99    pub fn set_fill(&mut self, fill: ImmutStr) {
100        self.vec2canvas.fill = fill;
101    }
102
103    fn patch_delta(&mut self, kern: &IncrDocClient) {
104        if let Some(layout) = &kern.layout {
105            let pages = layout.pages(&kern.doc.module);
106            if let Some(pages) = pages {
107                self.vec2canvas
108                    .interpret_changes(pages.module(), pages.pages());
109            }
110        }
111    }
112
113    /// Render a specific page of the document in the given window.
114    pub async fn render_page_in_window(
115        &mut self,
116        kern: &mut IncrDocClient,
117        canvas: &dyn CanvasDevice,
118        idx: usize,
119        _rect: Rect,
120    ) -> Result<()> {
121        self.patch_delta(kern);
122
123        if idx >= self.vec2canvas.pages.len() {
124            Err(error_once!("Renderer.OutofPageRange", idx: idx))?;
125        }
126
127        let s = self.vec2canvas.pixel_per_pt;
128        let ts = sk::Transform::from_scale(s, s);
129        self.vec2canvas.flush_page(idx, canvas, ts).await;
130
131        Ok(())
132    }
133}