1#![deny(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4mod damage;
5mod draw;
6mod pipeline;
7pub mod scene;
9pub mod sprites;
10mod style;
11
12use cranpose_render_common::{
13 RenderScene, Renderer, SceneUpdates,
14 text_measure::{CachedFontTextMeasurer, SoftwareTextResources},
15};
16use cranpose_ui::LayoutTree;
17use cranpose_ui_graphics::Size;
18pub use draw::draw_scene;
19pub use scene::{HitRegion, Scene};
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct PixelRect {
25 pub x: u32,
27 pub y: u32,
29 pub width: u32,
31 pub height: u32,
33}
34
35fn pixel_rect(bounds: draw::ClipBounds) -> PixelRect {
36 PixelRect {
37 x: bounds.min_x.max(0) as u32,
38 y: bounds.min_y.max(0) as u32,
39 width: (bounds.max_x - bounds.min_x).max(0) as u32,
40 height: (bounds.max_y - bounds.min_y).max(0) as u32,
41 }
42}
43
44#[derive(Default)]
48pub struct Picture {
49 raster: Option<scene::RasterScene>,
50 spare: scene::RasterScene,
52 width: u32,
53 height: u32,
54 scale: f32,
55 changed: Vec<draw::ClipBounds>,
56}
57
58fn valid_scale(scale: f32) -> f32 {
59 if scale.is_finite() && scale > 0.0 {
60 scale
61 } else {
62 1.0
63 }
64}
65
66pub struct PixelsRenderer {
68 scene: Scene,
69 text: draw::TextRaster,
70 app_context: Option<std::rc::Weak<cranpose_ui::AppContext>>,
72}
73
74impl Default for PixelsRenderer {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl PixelsRenderer {
81 pub fn new() -> Self {
84 Self::with_fonts(&[])
85 }
86
87 pub fn with_fonts(fonts: &[&'static [u8]]) -> Self {
91 Self {
92 scene: Scene::new(),
93 text: draw::TextRaster::new(SoftwareTextResources::from_fonts(fonts)),
94 app_context: None,
95 }
96 }
97
98 pub fn draw(&self, frame: &mut [u8], width: u32, height: u32) {
101 draw::draw_scene_scaled(frame, width, height, &self.scene, &self.text, 1.0);
102 }
103
104 pub fn draw_scaled(&self, frame: &mut [u8], width: u32, height: u32, scale: f32) {
107 draw::draw_scene_scaled(
108 frame,
109 width,
110 height,
111 &self.scene,
112 &self.text,
113 valid_scale(scale),
114 );
115 }
116
117 fn fill_raster_scene(
120 &self,
121 raster: &mut scene::RasterScene,
122 scale: f32,
123 previous: Option<&scene::RasterScene>,
124 ) {
125 match self.scene.graph.as_ref() {
126 Some(graph) => pipeline::fill_raster_scene(
127 raster,
128 graph,
129 self.scene.diagnostics(),
130 scale,
131 previous,
132 ),
133 None => raster.clear(),
134 }
135 }
136
137 pub fn update_picture(
142 &self,
143 picture: &mut Picture,
144 width: u32,
145 height: u32,
146 scale: f32,
147 changed: &mut Vec<PixelRect>,
148 ) {
149 let scale = valid_scale(scale);
150 let mut raster = std::mem::take(&mut picture.spare);
151 self.fill_raster_scene(&mut raster, scale, picture.raster.as_ref());
152 changed.clear();
153 match picture.raster.as_ref() {
154 Some(old)
155 if (picture.width, picture.height, picture.scale) == (width, height, scale) =>
156 {
157 let window = draw::ClipBounds {
158 min_x: 0,
159 min_y: 0,
160 max_x: i32::try_from(width).unwrap_or(i32::MAX),
161 max_y: i32::try_from(height).unwrap_or(i32::MAX),
162 };
163 damage::changed_areas(old, &raster, window, &mut picture.changed);
164 changed.extend(picture.changed.iter().copied().map(pixel_rect));
165 }
166 _ if width > 0 && height > 0 => changed.push(PixelRect {
167 x: 0,
168 y: 0,
169 width,
170 height,
171 }),
172 _ => {}
173 }
174 if let Some(old) = picture.raster.replace(raster) {
175 picture.spare = old;
176 }
177 picture.width = width;
178 picture.height = height;
179 picture.scale = scale;
180 }
181
182 pub fn draw_picture_area(&self, picture: &Picture, area: PixelRect, pixels: &mut [u8]) {
186 let Some(raster) = picture.raster.as_ref() else {
187 return;
188 };
189 let len = area.width as usize * area.height as usize * 4;
190 let Some(pixels) = pixels.get_mut(..len) else {
191 return;
192 };
193 let mut target = draw::Target {
194 pixels,
195 left: i32::try_from(area.x).unwrap_or(i32::MAX),
196 top: i32::try_from(area.y).unwrap_or(i32::MAX),
197 width: area.width,
198 height: area.height,
199 };
200 draw::draw_raster_window(&mut target, raster, self.scene.diagnostics(), &self.text);
201 }
202}
203
204impl PixelsRenderer {
205 pub fn compose_sprites(
209 &self,
210 frame: &mut sprites::SpriteFrame,
211 width: u32,
212 height: u32,
213 scale: f32,
214 ) {
215 let mut scene = std::mem::take(&mut frame.scene);
216 self.fill_raster_scene(&mut scene, valid_scale(scale), None);
217 frame.compose(&scene, width, height, self.scene.diagnostics(), &self.text);
218 frame.scene = scene;
219 }
220}
221
222impl Renderer for PixelsRenderer {
223 type Scene = Scene;
224 type Error = std::convert::Infallible;
225
226 fn attach_app_context_services(&mut self, app_context: &cranpose_ui::AppContext) {
227 app_context.set_text_measurer(CachedFontTextMeasurer::with_text_resources(
228 self.text.resources.clone(),
229 64,
230 ));
231 self.app_context = Some(app_context.downgrade());
232 }
233
234 #[cfg(feature = "installed-fonts")]
235 fn apply_installed_fonts(&mut self, app_context: &cranpose_ui::AppContext) {
236 let Some(resources) = self.text.resources.with_installed_faces() else {
237 return;
238 };
239 self.text = draw::TextRaster::new(resources);
240 let attached = self
241 .app_context
242 .as_ref()
243 .and_then(std::rc::Weak::upgrade)
244 .is_some_and(|attached| std::ptr::eq(&*attached, app_context));
245 if attached {
246 self.attach_app_context_services(app_context);
247 }
248 }
249
250 fn scene(&self) -> &Self::Scene {
251 &self.scene
252 }
253
254 fn scene_mut(&mut self) -> &mut Self::Scene {
255 &mut self.scene
256 }
257
258 fn rebuild_scene(
259 &mut self,
260 layout_tree: &LayoutTree,
261 _viewport: Size,
262 ) -> Result<(), Self::Error> {
263 self.scene.clear();
264 pipeline::render_layout_tree(layout_tree.root(), &mut self.scene);
265 Ok(())
266 }
267
268 fn rebuild_scene_from_applier(
269 &mut self,
270 applier: &mut cranpose_core::MemoryApplier,
271 root: cranpose_core::NodeId,
272 _viewport: Size,
273 ) -> Result<(), Self::Error> {
274 self.scene.rebuild_from_applier(applier, root);
275 Ok(())
276 }
277
278 fn update_scene_from_applier(
279 &mut self,
280 applier: &mut cranpose_core::MemoryApplier,
281 root: cranpose_core::NodeId,
282 _viewport: Size,
283 updates: SceneUpdates<'_>,
284 ) -> Result<(), Self::Error> {
285 self.scene.update_from_applier(applier, root, updates, true);
286 Ok(())
287 }
288
289 fn update_visual_scene_from_applier(
290 &mut self,
291 applier: &mut cranpose_core::MemoryApplier,
292 root: cranpose_core::NodeId,
293 _viewport: Size,
294 updates: SceneUpdates<'_>,
295 ) -> Result<(), Self::Error> {
296 self.scene
297 .update_from_applier(applier, root, updates, false);
298 Ok(())
299 }
300}