ul_next/surface.rs
1//! Offscreen pixel buffer surface.
2//!
3//! `Surface`s are used only when the [`View`](crate::view::View) is not accelerated.
4
5use std::{
6 ops::{Deref, DerefMut},
7 sync::Arc,
8};
9
10use crate::{rect::Rect, Library};
11
12/// An RAII implementation of a “scoped lock” of a pixel buffer for [`Surface`].
13/// When this structure is dropped (falls out of scope), the lock will be unlocked.
14///
15/// This struct is created by [`Surface::lock_pixels`].
16pub struct PixelsGuard<'a> {
17 lock: &'a mut Surface,
18 pixels: &'a mut [u8],
19}
20
21impl<'a> PixelsGuard<'a> {
22 unsafe fn new(lock: &'a mut Surface, pixels: &'a mut [u8]) -> PixelsGuard<'a> {
23 PixelsGuard { lock, pixels }
24 }
25}
26
27impl Deref for PixelsGuard<'_> {
28 type Target = [u8];
29
30 fn deref(&self) -> &[u8] {
31 self.pixels
32 }
33}
34
35impl DerefMut for PixelsGuard<'_> {
36 fn deref_mut(&mut self) -> &mut [u8] {
37 self.pixels
38 }
39}
40
41impl Drop for PixelsGuard<'_> {
42 fn drop(&mut self) {
43 unsafe {
44 self.lock.raw_unlock_pixels();
45 }
46 }
47}
48
49/// Offscreen pixel buffer surface. (Premultiplied BGRA 32-bit format)
50///
51/// When using the CPU renderer, each View is painted to its own Surface.
52///
53// TODO: add support and remove comments below (note //)
54/// **NOTE: Custom Surface is currently not support in this Rust wrapper**
55//
56// You can provide your own Surface implementation to make the renderer paint directly to a block
57// of memory controlled by you (this is useful for lower-latency uploads to GPU memory or other
58// platform-specific bitmaps).
59//
60// A default Surface implementation, `BitmapSurface`, is automatically provided by the library when
61// you call [`Renderer::create`](crate::renderer::Renderer::create)
62// without defining a custom `SurfaceFactory`.
63//
64// To provide your own custom Surface implementation, you should implement the
65// `SurfaceFactory` trait, and pass the struct to `platform::set_surface_definition`
66// before calling [`Renderer::create`](crate::renderer::Renderer::create) or
67// [`App::new`](crate::app::App::new).
68pub struct Surface {
69 lib: Arc<Library>,
70 internal: ul_sys::ULSurface,
71}
72
73impl Surface {
74 /// Helper internal function to allow getting a reference to a managed
75 /// surface.
76 pub(crate) unsafe fn from_raw(lib: Arc<Library>, raw: ul_sys::ULSurface) -> Self {
77 Self { lib, internal: raw }
78 }
79}
80
81impl Surface {
82 /// Get the width (in pixels).
83 pub fn width(&self) -> u32 {
84 unsafe { self.lib.ultralight().ulSurfaceGetWidth(self.internal) }
85 }
86
87 /// Get the height (in pixels).
88 pub fn height(&self) -> u32 {
89 unsafe { self.lib.ultralight().ulSurfaceGetHeight(self.internal) }
90 }
91
92 /// Get the number of bytes between each row of pixels.
93 ///
94 /// usually `width * 4`
95 pub fn row_bytes(&self) -> u32 {
96 unsafe { self.lib.ultralight().ulSurfaceGetRowBytes(self.internal) }
97 }
98
99 /// Get the size in bytes of the pixel buffer.
100 ///
101 /// bytes_size is calculated as `row_bytes() * height()`.
102 pub fn bytes_size(&self) -> usize {
103 unsafe { self.lib.ultralight().ulSurfaceGetSize(self.internal) }
104 }
105
106 /// Lock the pixel buffer for reading/writing.
107 ///
108 /// An RAII guard is returned that will unlock the buffer when dropped.
109 //
110 // this takes `&mut` even though its not needed to lock the structure,
111 // so that you can't resize or modify while its locked.
112 pub fn lock_pixels(&mut self) -> Option<PixelsGuard> {
113 let raw_locked_pixels = unsafe { self.lib.ultralight().ulSurfaceLockPixels(self.internal) };
114 if raw_locked_pixels.is_null() {
115 return None;
116 }
117
118 let size = self.bytes_size();
119 unsafe {
120 let data = std::slice::from_raw_parts_mut(raw_locked_pixels as _, size);
121 Some(PixelsGuard::new(self, data))
122 }
123 }
124
125 /// Internal unlock the pixel buffer.
126 pub(crate) unsafe fn raw_unlock_pixels(&self) {
127 self.lib.ultralight().ulSurfaceUnlockPixels(self.internal)
128 }
129
130 /// Resize the pixel buffer to a certain width and height (both in pixels).
131 pub fn resize(&self, width: u32, height: u32) {
132 unsafe {
133 self.lib
134 .ultralight()
135 .ulSurfaceResize(self.internal, width, height)
136 }
137 }
138
139 /// Set the dirty bounds to a certain value.
140 ///
141 /// This is called after the Renderer paints to an area of the pixel buffer.
142 /// (The new value will be joined with the existing dirty_bounds())
143 pub fn set_dirty_bounds(&self, bounds: Rect<i32>) {
144 unsafe {
145 self.lib
146 .ultralight()
147 .ulSurfaceSetDirtyBounds(self.internal, bounds.into())
148 }
149 }
150
151 /// Get the dirty bounds.
152 ///
153 /// This value can be used to determine which portion of the pixel buffer has been updated since
154 /// the last call to [`clear_dirty_bounds`](Surface::clear_dirty_bounds).
155 ///
156 /// The general algorithm to determine if a Surface needs display is:
157 /// ```rust,ignore
158 /// if !surface.dirty_bounds().is_empty() {
159 /// // Surface pixels are dirty and needs display.
160 /// // Cast Surface to native Surface and use it here (pseudo code)
161 /// display_surface(surface);
162 ///
163 /// // Once you're done, clear the dirty bounds:
164 /// surface.clear_dirty_bounds();
165 /// }
166 /// ```
167 pub fn dirty_bounds(&self) -> Rect<i32> {
168 unsafe {
169 self.lib
170 .ultralight()
171 .ulSurfaceGetDirtyBounds(self.internal)
172 .into()
173 }
174 }
175
176 /// Clear the dirty bounds.
177 ///
178 /// You should call this after you're done displaying the Surface.
179 pub fn clear_dirty_bounds(&self) {
180 unsafe {
181 self.lib
182 .ultralight()
183 .ulSurfaceClearDirtyBounds(self.internal)
184 }
185 }
186
187 //pub fn user_data(&self) -> *mut std::ffi::c_void {
188 // unsafe { ul_sys::ulSurfaceGetUserData(self.internal) }
189 //}
190}