ferroui_vello/
framebuffer_render_target.rs1use crate::drawing_context_impl::{CreateInfo, DrawingContextImpl, VelloBackdrop};
2use crate::helpers::pixel_format_helper::{from_premul_rgba, scene_size, to_image, to_premul_rgba};
3use crate::scene::{create_scene_sink, VelloSceneBrush, VelloSceneImage};
4use crate::vello_options::VelloRenderingMode;
5use ferroui_base::platform::surfaces::{IFramebufferPlatformSurface, IFramebufferRenderTarget};
6use ferroui_base::platform::{
7 IDrawingContextImpl, IRenderTarget, PlatformRenderTargetState, RenderTargetDrawingContextProperties,
8 RenderTargetProperties, RenderTargetSceneInfo,
9};
10use std::cell::RefCell;
11use std::rc::Rc;
12
13pub struct FramebufferRenderTarget {
23 use_scaled_drawing: bool,
24 rendering_modes: Vec<VelloRenderingMode>,
25 render_target: RefCell<Option<Rc<dyn IFramebufferRenderTarget>>>,
26 presents_frames: bool,
29}
30
31impl FramebufferRenderTarget {
32 pub fn new(
34 platform_surface: &dyn IFramebufferPlatformSurface,
35 use_scaled_drawing: bool,
36 rendering_modes: Vec<VelloRenderingMode>,
37 ) -> Self {
38 Self {
39 presents_frames: true,
40 ..Self::from_render_target(
41 platform_surface.create_framebuffer_render_target(),
42 use_scaled_drawing,
43 rendering_modes,
44 )
45 }
46 }
47
48 pub fn from_render_target(
51 render_target: Rc<dyn IFramebufferRenderTarget>,
52 use_scaled_drawing: bool,
53 rendering_modes: Vec<VelloRenderingMode>,
54 ) -> Self {
55 Self { use_scaled_drawing, rendering_modes, render_target: RefCell::new(Some(render_target)), presents_frames: false }
56 }
57}
58
59impl IRenderTarget for FramebufferRenderTarget {
60 fn properties(&self) -> RenderTargetProperties {
61 RenderTargetProperties {
62 retains_previous_frame_contents: self
63 .render_target
64 .borrow()
65 .as_ref()
66 .is_some_and(|target| target.retains_frame_contents()),
67 is_suitable_for_direct_rendering: true,
68 }
69 }
70
71 fn create_drawing_context(
72 &self,
73 scene_info: &RenderTargetSceneInfo,
74 ) -> (Box<dyn IDrawingContextImpl>, RenderTargetDrawingContextProperties) {
75 let render_target =
76 self.render_target.borrow().clone().unwrap_or_else(|| panic!("FramebufferRenderTarget has been disposed"));
77
78 let frame_start = if self.presents_frames { crate::perf::frame_start() } else { None };
79
80 let (framebuffer, lock_properties) = render_target.lock(scene_info);
81 let size = framebuffer.size();
82 if size.width <= 0 || size.height <= 0 {
83 panic!("Unable to create a surface with size {}x{}", size.width, size.height);
84 }
85
86 let (format, alpha_format) = (framebuffer.format(), framebuffer.alpha_format());
87 let row_bytes = framebuffer.row_bytes().max(0) as usize;
88 let (width, height) = scene_size(size);
89
90 let sink = create_scene_sink(&self.rendering_modes, width, height);
101 let in_place = sink.rendering_mode() == VelloRenderingMode::Cpu
102 && format == ferroui_base::platform::PixelFormats::RGBA8888
103 && alpha_format == ferroui_base::platform::AlphaFormat::Premul
104 && row_bytes == width as usize * 4;
105
106 let mut has_content = false;
107 framebuffer.with_data(&mut |data| has_content = data.iter().any(|byte| *byte != 0));
108 let backdrop = has_content.then(|| {
109 let framebuffer = framebuffer.clone();
110 VelloBackdrop {
111 composes_over_target: in_place,
112 brush: Box::new(move || {
113 let mut image = None;
114 framebuffer.with_data(&mut |data| {
115 let _perf = crate::perf::scope(crate::perf::Phase::PixelCopy, data.len() as u64);
116 image = Some(to_image(to_premul_rgba(data, row_bytes, size, format, alpha_format), size));
117 });
118 image.map(|image| {
119 VelloSceneBrush::Image(VelloSceneImage {
120 image,
121 x_extend: peniko::Extend::Pad,
122 y_extend: peniko::Extend::Pad,
123 quality: peniko::ImageQuality::Low,
124 alpha: 1.0,
125 })
126 })
127 }),
128 }
129 });
130
131 let properties =
132 RenderTargetDrawingContextProperties { previous_frame_is_retained: lock_properties.previous_frame_is_retained };
133
134 let presented = framebuffer.clone();
135 let create_info = CreateInfo {
136 sink,
137 backdrop,
138 on_finished: Box::new(move |sink| {
139 if in_place {
140 let length = width as usize * height as usize * 4;
141 presented.with_data(&mut |data| sink.render_to_pixels(&mut data[..length]));
142 return;
143 }
144 let mut rgba = vec![0u8; width as usize * height as usize * 4];
145 sink.render_to_pixels(&mut rgba);
146 let _perf = crate::perf::scope(crate::perf::Phase::PixelCopy, rgba.len() as u64);
147 presented.with_data(&mut |data| from_premul_rgba(&rgba, data, row_bytes, size, format, alpha_format));
148 }),
149 scale_drawing_to_dpi: self.use_scaled_drawing,
150 dpi: framebuffer.dpi(),
151 rendering_modes: self.rendering_modes.clone(),
152 };
153
154 let disposables: Vec<Box<dyn FnOnce()>> = vec![Box::new(move || {
155 {
156 let _perf = crate::perf::scope(crate::perf::Phase::Present, 0);
157 framebuffer.dispose();
158 }
159 crate::perf::frame_end(frame_start, "framebuffer", u32::from(width), u32::from(height));
160 })];
161
162 (Box::new(DrawingContextImpl::new(create_info, disposables)), properties)
163 }
164
165 fn platform_render_target_state(&self) -> PlatformRenderTargetState {
166 match &*self.render_target.borrow() {
167 Some(render_target) => render_target.state(),
168 None => PlatformRenderTargetState::DISPOSED,
169 }
170 }
171
172 fn dispose(&self) {
173 if let Some(render_target) = self.render_target.borrow_mut().take() {
174 render_target.dispose();
175 }
176 }
177}