ferroui_vello/
surface_render_target.rs1use crate::drawing_context_impl::{CreateInfo, DrawingContextImpl, VelloBackdrop};
2use crate::helpers::image_saving_helper;
3use crate::helpers::pixel_format_helper::{scene_size, to_shared_image};
4use crate::i_drawable_bitmap_impl::IDrawableBitmapImpl;
5use crate::immutable_bitmap::ImmutableBitmap;
6use crate::scene::{create_scene_sink, VelloSceneBrush, VelloSceneImage};
7use crate::vello_options::VelloRenderingMode;
8use ferroui_base::media::imaging::BitmapEncoderOptions;
9use ferroui_base::platform::{IBitmapImpl, IDrawingContextImpl, IDrawingContextLayerImpl, RenderTargetProperties};
10use ferroui_base::{PixelSize, Vector};
11use peniko::{BlendMode, Extend, ImageData, ImageQuality};
12use std::any::Any;
13use std::cell::{Cell, RefCell};
14use std::io::{self, Write};
15use std::rc::Rc;
16
17#[derive(Clone, Default)]
19pub struct SurfaceRenderTargetCreateInfo {
20 pub width: i32,
22
23 pub height: i32,
25
26 pub dpi: Vector,
28
29 pub rendering_modes: Vec<VelloRenderingMode>,
31
32 pub use_scaled_drawing: bool,
34}
35
36struct SurfacePixels {
38 data: Option<std::sync::Arc<Vec<u8>>>,
42 has_content: bool,
45 image: Option<ImageData>,
47}
48
49pub struct SurfaceRenderTarget {
55 use_scaled_drawing: bool,
56 pixels: Rc<RefCell<SurfacePixels>>,
57 rendering_modes: Vec<VelloRenderingMode>,
58 dpi: Vector,
59 pixel_size: PixelSize,
60 version: Rc<Cell<i32>>,
61}
62
63impl SurfaceRenderTarget {
64 pub fn new(create_info: SurfaceRenderTargetCreateInfo) -> Self {
70 let pixel_size = PixelSize::new(create_info.width.max(1), create_info.height.max(1));
71 let (width, height) = scene_size(pixel_size);
72 crate::perf::count(crate::perf::Phase::SurfaceInMemory, u64::from(width) * u64::from(height) * 4);
73
74 Self {
75 use_scaled_drawing: create_info.use_scaled_drawing,
76 pixels: Rc::new(RefCell::new(SurfacePixels {
77 data: Some(std::sync::Arc::new(vec![0u8; width as usize * height as usize * 4])),
78 has_content: false,
79 image: None,
80 })),
81 rendering_modes: create_info.rendering_modes,
82 dpi: create_info.dpi,
83 pixel_size,
84 version: Rc::new(Cell::new(1)),
85 }
86 }
87
88 pub fn properties(&self) -> RenderTargetProperties {
90 RenderTargetProperties::default()
91 }
92
93 pub fn has_render_context_affinity(&self) -> bool {
96 false
97 }
98
99 fn image_of(pixels: &mut SurfacePixels, pixel_size: PixelSize) -> Option<ImageData> {
100 if pixels.image.is_none() {
101 pixels.image = Some(to_shared_image(pixels.data.as_ref()?.clone(), pixel_size));
102 }
103 pixels.image.clone()
104 }
105}
106
107impl IBitmapImpl for SurfaceRenderTarget {
108 fn dpi(&self) -> Vector {
109 self.dpi
110 }
111
112 fn pixel_size(&self) -> PixelSize {
113 self.pixel_size
114 }
115
116 fn version(&self) -> i32 {
117 self.version.get()
118 }
119
120 fn save(&self, stream: &mut dyn Write, options: &BitmapEncoderOptions) -> io::Result<()> {
121 let image = self.image().ok_or_else(|| io::Error::other("SurfaceRenderTarget has been disposed"))?;
122 image_saving_helper::save_image(&image, stream, options)
123 }
124
125 fn dispose(&self) {
126 let mut pixels = self.pixels.borrow_mut();
127 pixels.data = None;
128 pixels.image = None;
129 }
130
131 fn as_any(&self) -> &dyn Any {
132 self
133 }
134}
135
136impl IDrawingContextLayerImpl for SurfaceRenderTarget {
137 fn blit(&self, context: &mut dyn IDrawingContextImpl) {
138 let context = context
139 .as_any_mut()
140 .downcast_mut::<DrawingContextImpl>()
141 .unwrap_or_else(|| panic!("The layer can only be blitted onto a drawing context of the Vello backend"));
142
143 if let Some(image) = self.image() {
144 context.draw_image_in_device_space(image, BlendMode::default());
145 }
146 }
147
148 fn can_blit(&self) -> bool {
149 true
150 }
151
152 fn is_corrupted(&self) -> bool {
153 false
154 }
155
156 fn create_shared_snapshot(&self) -> std::sync::Arc<ferroui_base::platform::SharedBitmapImpl> {
157 let image = self.image().unwrap_or_else(|| panic!("SurfaceRenderTarget has been disposed"));
158 std::sync::Arc::new(ImmutableBitmap::from_image(image))
159 }
160
161 fn create_drawing_context(&self) -> Box<dyn IDrawingContextImpl> {
162 Box::new(self.create_drawing_context_with(Vec::new()))
163 }
164}
165
166impl SurfaceRenderTarget {
167 pub fn has_content(&self) -> bool {
169 self.pixels.borrow().has_content
170 }
171
172 pub fn create_drawing_context_with(&self, disposables: Vec<Box<dyn FnOnce()>>) -> DrawingContextImpl {
176 let (width, height) = scene_size(self.pixel_size);
177 let sink = create_scene_sink(&self.rendering_modes, width, height);
178
179 let backdrop = self.pixels.borrow().has_content.then(|| {
183 let (pixels, pixel_size) = (self.pixels.clone(), self.pixel_size);
184 VelloBackdrop {
185 composes_over_target: sink.rendering_mode() == VelloRenderingMode::Cpu,
186 brush: Box::new(move || {
187 Self::image_of(&mut pixels.borrow_mut(), pixel_size).map(|image| {
188 VelloSceneBrush::Image(VelloSceneImage {
189 image,
190 x_extend: Extend::Pad,
191 y_extend: Extend::Pad,
192 quality: ImageQuality::Low,
193 alpha: 1.0,
194 })
195 })
196 }),
197 }
198 });
199
200 let pixels = self.pixels.clone();
201 let create_info = CreateInfo {
202 sink,
203 backdrop,
204 on_finished: Box::new(move |sink| {
205 let mut pixels = pixels.borrow_mut();
206 pixels.image = None;
207 pixels.has_content = true;
208 if let Some(data) = pixels.data.as_mut() {
209 if std::sync::Arc::strong_count(data) > 1 {
214 crate::perf::count(crate::perf::Phase::PixelCopy, data.len() as u64);
215 }
216 sink.render_to_pixels(&mut std::sync::Arc::make_mut(data)[..]);
217 }
218 }),
219 scale_drawing_to_dpi: self.use_scaled_drawing,
220 dpi: self.dpi,
221 rendering_modes: self.rendering_modes.clone(),
222 };
223
224 let version = self.version.clone();
225 let mut all: Vec<Box<dyn FnOnce()>> = vec![Box::new(move || version.set(version.get() + 1))];
226 all.extend(disposables);
227 DrawingContextImpl::new(create_info, all)
228 }
229}
230
231impl IDrawableBitmapImpl for SurfaceRenderTarget {
232 fn image(&self) -> Option<ImageData> {
233 Self::image_of(&mut self.pixels.borrow_mut(), self.pixel_size)
234 }
235}