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}