Skip to main content

cranpose_render_pixels/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod draw;
4mod pipeline;
5pub mod scene;
6pub mod sprites;
7pub mod style;
8
9use cranpose_render_common::{
10    RenderScene, Renderer, SceneUpdates,
11    text_measure::{CachedFontTextMeasurer, SoftwareTextResources},
12};
13use cranpose_ui::LayoutTree;
14use cranpose_ui_graphics::Size;
15pub use draw::draw_scene;
16pub use scene::{HitRegion, Scene};
17
18#[derive(Debug)]
19pub enum PixelsRendererError {
20    Layout(String),
21}
22
23pub struct PixelsRenderer {
24    scene: Scene,
25    text_resources: SoftwareTextResources,
26}
27
28impl Default for PixelsRenderer {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl PixelsRenderer {
35    pub fn new() -> Self {
36        Self {
37            scene: Scene::new(),
38            text_resources: SoftwareTextResources::default(),
39        }
40    }
41
42    pub fn draw(&self, frame: &mut [u8], width: u32, height: u32) {
43        draw::draw_scene_with_text_resources(
44            frame,
45            width,
46            height,
47            &self.scene,
48            &self.text_resources,
49        );
50    }
51
52    /// Rasterizes logical scene coordinates at `scale` device pixels per point.
53    /// Hit regions stay in logical coordinates. Invalid scales use 1.0.
54    pub fn draw_scaled(&self, frame: &mut [u8], width: u32, height: u32, scale: f32) {
55        let scale = if scale.is_finite() && scale > 0.0 {
56            scale
57        } else {
58            1.0
59        };
60        draw::draw_scene_scaled(
61            frame,
62            width,
63            height,
64            &self.scene,
65            &self.text_resources,
66            scale,
67        );
68    }
69}
70
71impl PixelsRenderer {
72    /// The scene at `scale` device pixels per point as sprites for a GPU
73    /// compositor, into `frame`, which keeps its textures from the frames
74    /// before. Invalid scales use 1.0.
75    pub fn compose_sprites(
76        &self,
77        frame: &mut sprites::SpriteFrame,
78        width: u32,
79        height: u32,
80        scale: f32,
81    ) {
82        let scale = if scale.is_finite() && scale > 0.0 {
83            scale
84        } else {
85            1.0
86        };
87        let scene = match self.scene.graph.as_ref() {
88            Some(graph) => pipeline::build_raster_scene(graph, self.scene.diagnostics(), scale),
89            None => scene::RasterScene::new(),
90        };
91        frame.compose(
92            &scene,
93            width,
94            height,
95            self.scene.diagnostics(),
96            &self.text_resources,
97        );
98    }
99}
100
101impl Renderer for PixelsRenderer {
102    type Scene = Scene;
103    type Error = PixelsRendererError;
104
105    fn attach_app_context_services(&mut self, app_context: &cranpose_ui::AppContext) {
106        app_context.set_text_measurer(CachedFontTextMeasurer::with_text_resources(
107            self.text_resources.clone(),
108            64,
109        ));
110    }
111
112    fn scene(&self) -> &Self::Scene {
113        &self.scene
114    }
115
116    fn scene_mut(&mut self) -> &mut Self::Scene {
117        &mut self.scene
118    }
119
120    fn rebuild_scene(
121        &mut self,
122        layout_tree: &LayoutTree,
123        _viewport: Size,
124    ) -> Result<(), Self::Error> {
125        self.scene.clear();
126        pipeline::render_layout_tree(layout_tree.root(), &mut self.scene);
127        Ok(())
128    }
129
130    fn rebuild_scene_from_applier(
131        &mut self,
132        applier: &mut cranpose_core::MemoryApplier,
133        root: cranpose_core::NodeId,
134        _viewport: Size,
135    ) -> Result<(), Self::Error> {
136        self.scene.rebuild_from_applier(applier, root);
137        Ok(())
138    }
139
140    fn update_scene_from_applier(
141        &mut self,
142        applier: &mut cranpose_core::MemoryApplier,
143        root: cranpose_core::NodeId,
144        _viewport: Size,
145        updates: SceneUpdates<'_>,
146    ) -> Result<(), Self::Error> {
147        self.scene.update_from_applier(applier, root, updates, true);
148        Ok(())
149    }
150
151    fn update_visual_scene_from_applier(
152        &mut self,
153        applier: &mut cranpose_core::MemoryApplier,
154        root: cranpose_core::NodeId,
155        _viewport: Size,
156        updates: SceneUpdates<'_>,
157    ) -> Result<(), Self::Error> {
158        self.scene
159            .update_from_applier(applier, root, updates, false);
160        Ok(())
161    }
162}