ul_next/
bitmap.rs

1//! Bitmap container to hold raw pixels data.
2
3use std::{
4    ffi::{c_void, CString},
5    ops::{Deref, DerefMut},
6    path::Path,
7    slice,
8    sync::Arc,
9};
10
11use crate::Library;
12
13/// Errors can occure when creating [`Bitmap`]s
14#[derive(Debug, thiserror::Error)]
15pub enum BitmapError {
16    /// The `Ultralight` library returned a null pointer.
17    #[error("Creation of bitmap failed because Ultralight returned a null pointer")]
18    NullReference,
19    /// The pixels passed to create the [`Bitmap`] does not match the required size.
20    #[error(
21        "Creation of bitmap failed because it required {required} bytes, but got {got} bytes only"
22    )]
23    PixelBufferSizeMismatch { got: usize, required: usize },
24    /// Tried to swap red and blue channels on an unsupported format.
25    #[error("Tried to swap red and blue channels on an unsupported format")]
26    UnsupportedOperationForPixelFormat,
27    /// Could not write bitmap to PNG successfully.
28    #[error("Could not write bitmap to PNG successfully")]
29    FailedPngWrite,
30    /// Could not create bitmap because its empty
31    #[error("Could not create bitmap because its empty")]
32    EmptyBitmap,
33}
34
35type BitmapResult<T> = std::result::Result<T, BitmapError>;
36
37#[derive(Debug, Clone, Copy)]
38/// The supported bitmap formats.
39pub enum BitmapFormat {
40    /// Alpha channel only, 8-bits per pixel.
41    ///
42    /// Encoding: 8-bits per channel, unsigned normalized.
43    ///
44    /// Color-space: Linear (no gamma), alpha-coverage only.
45    A8Unorm = ul_sys::ULBitmapFormat_kBitmapFormat_A8_UNORM as isize,
46
47    /// Blue Green Red Alpha channels, 32-bits per pixel.
48    ///
49    /// Encoding: 8-bits per channel, unsigned normalized.
50    ///
51    /// Color-space: sRGB gamma with premultiplied linear alpha channel.
52    Bgra8UnormSrgb = ul_sys::ULBitmapFormat_kBitmapFormat_BGRA8_UNORM_SRGB as isize,
53}
54
55impl TryFrom<ul_sys::ULBitmapFormat> for BitmapFormat {
56    type Error = ();
57
58    fn try_from(format: ul_sys::ULBitmapFormat) -> Result<Self, Self::Error> {
59        match format {
60            ul_sys::ULBitmapFormat_kBitmapFormat_A8_UNORM => Ok(BitmapFormat::A8Unorm),
61            ul_sys::ULBitmapFormat_kBitmapFormat_BGRA8_UNORM_SRGB => {
62                Ok(BitmapFormat::Bgra8UnormSrgb)
63            }
64            _ => Err(()),
65        }
66    }
67}
68
69impl BitmapFormat {
70    /// Returns the number of bytes per pixel for the specific bitmap format.
71    pub fn bytes_per_pixel(&self) -> u32 {
72        match self {
73            BitmapFormat::A8Unorm => 1,
74            BitmapFormat::Bgra8UnormSrgb => 4,
75        }
76    }
77}
78
79/// An RAII implementation of a “scoped lock” of a pixel buffer for [`Bitmap`].
80/// When this structure is dropped (falls out of scope), the lock will be unlocked.
81///
82/// This struct is created by [`Bitmap::lock_pixels`].
83pub struct PixelsGuard<'a> {
84    lock: &'a mut Bitmap,
85    pixels: &'a mut [u8],
86}
87
88impl<'a> PixelsGuard<'a> {
89    unsafe fn new(lock: &'a mut Bitmap, pixels: &'a mut [u8]) -> PixelsGuard<'a> {
90        PixelsGuard { lock, pixels }
91    }
92}
93
94impl Deref for PixelsGuard<'_> {
95    type Target = [u8];
96
97    fn deref(&self) -> &[u8] {
98        self.pixels
99    }
100}
101
102impl DerefMut for PixelsGuard<'_> {
103    fn deref_mut(&mut self) -> &mut [u8] {
104        self.pixels
105    }
106}
107
108impl Drop for PixelsGuard<'_> {
109    fn drop(&mut self) {
110        unsafe {
111            self.lock.raw_unlock_pixels();
112        }
113    }
114}
115
116/// `Ultralight` Bitmap container.
117pub struct Bitmap {
118    lib: Arc<Library>,
119    internal: ul_sys::ULBitmap,
120    need_to_destroy: bool,
121}
122
123impl Bitmap {
124    pub(crate) unsafe fn from_raw(lib: Arc<Library>, raw: ul_sys::ULBitmap) -> Option<Self> {
125        if raw.is_null() {
126            return None;
127        }
128
129        Some(Bitmap {
130            lib,
131            internal: raw,
132            need_to_destroy: false,
133        })
134    }
135
136    pub(crate) unsafe fn to_ul(&self) -> ul_sys::ULBitmap {
137        self.internal
138    }
139
140    /// Create an empty Bitmap. No pixels will be allocated.
141    pub fn create_empty(lib: Arc<Library>) -> BitmapResult<Self> {
142        let internal = unsafe { lib.ultralight().ulCreateEmptyBitmap() };
143
144        if internal.is_null() {
145            Err(BitmapError::NullReference)
146        } else {
147            Ok(Self {
148                lib,
149                internal,
150                need_to_destroy: true,
151            })
152        }
153    }
154
155    /// Create an aligned Bitmap with a certain configuration. Pixels will be allocated but not
156    /// initialized.
157    ///
158    /// # Arguments
159    /// * `lib` - The ultralight library.
160    /// * `width` - The width of the bitmap.
161    /// * `height` - The height of the bitmap.
162    /// * `format` - The format of the bitmap.
163    pub fn create(
164        lib: Arc<Library>,
165        width: usize,
166        height: usize,
167        format: BitmapFormat,
168    ) -> BitmapResult<Self> {
169        let internal = unsafe {
170            lib.ultralight()
171                .ulCreateBitmap(width as u32, height as u32, format as u32)
172        };
173        if internal.is_null() {
174            Err(BitmapError::NullReference)
175        } else {
176            Ok(Self {
177                lib,
178                internal,
179                need_to_destroy: true,
180            })
181        }
182    }
183
184    /// Create a Bitmap with existing pixels
185    ///
186    /// # Arguments
187    /// * `lib` - The ultralight library.
188    /// * `width` - The width of the bitmap.
189    /// * `height` - The height of the bitmap.
190    /// * `format` - The format of the bitmap.
191    /// * `pixels` - The raw pixels of the bitmap.
192    ///
193    /// The length of the `pixels` slice must be equal to `width * height * format.bytes_per_pixel()`.
194    pub fn create_from_pixels(
195        lib: Arc<Library>,
196        width: u32,
197        height: u32,
198        format: BitmapFormat,
199        pixels: &[u8],
200    ) -> BitmapResult<Self> {
201        let row_bytes = width * format.bytes_per_pixel();
202        let bytes_size = (height * row_bytes) as usize;
203        if pixels.len() != bytes_size {
204            return Err(BitmapError::PixelBufferSizeMismatch {
205                got: pixels.len(),
206                required: bytes_size,
207            });
208        }
209        // This will create a new buffer and copy the pixels into it
210        // NOTE: the constructor allow for row size that is more than the actual row size
211        //       which means that it can support padding, we don't need it for now
212        //       but if needed, we can implement it.
213        let internal = unsafe {
214            lib.ultralight().ulCreateBitmapFromPixels(
215                width,
216                height,
217                format as u32,
218                row_bytes,
219                pixels.as_ptr() as *const c_void,
220                pixels.len(),
221                true,
222            )
223        };
224        if internal.is_null() {
225            Err(BitmapError::NullReference)
226        } else {
227            Ok(Self {
228                lib,
229                internal,
230                need_to_destroy: true,
231            })
232        }
233    }
234
235    /// Create a bitmap from a deep copy of another Bitmap.
236    pub fn copy(&self) -> BitmapResult<Self> {
237        let internal = unsafe { self.lib.ultralight().ulCreateBitmapFromCopy(self.internal) };
238
239        if internal.is_null() {
240            Err(BitmapError::NullReference)
241        } else {
242            Ok(Self {
243                lib: self.lib.clone(),
244                internal,
245                need_to_destroy: true,
246            })
247        }
248    }
249}
250
251impl Bitmap {
252    /// Get the width in pixels.
253    pub fn width(&self) -> u32 {
254        unsafe { self.lib.ultralight().ulBitmapGetWidth(self.internal) }
255    }
256
257    /// Get the height in pixels.
258    pub fn height(&self) -> u32 {
259        unsafe { self.lib.ultralight().ulBitmapGetHeight(self.internal) }
260    }
261
262    /// Get the pixel format.
263    pub fn format(&self) -> BitmapFormat {
264        unsafe { self.lib.ultralight().ulBitmapGetFormat(self.internal) }
265            .try_into()
266            .unwrap()
267    }
268
269    /// Get the number of bytes per pixel.
270    pub fn bpp(&self) -> u32 {
271        unsafe { self.lib.ultralight().ulBitmapGetBpp(self.internal) }
272    }
273
274    /// Get the number of bytes between each row of pixels.
275    ///
276    /// This value is usually calculated as `width() * bytes_per_pixel()` (bpp) but it may be larger
277    /// due to alignment rules in the allocator.
278    pub fn row_bytes(&self) -> u32 {
279        unsafe { self.lib.ultralight().ulBitmapGetRowBytes(self.internal) }
280    }
281
282    /// Get the size in bytes of the pixel buffer.
283    ///
284    /// bytes_size is calculated as `row_bytes() * height()`.
285    pub fn bytes_size(&self) -> usize {
286        unsafe { self.lib.ultralight().ulBitmapGetSize(self.internal) }
287    }
288
289    /// Lock the pixel buffer for reading/writing.
290    ///
291    /// An RAII guard is returned that will unlock the buffer when dropped.
292    pub fn lock_pixels(&mut self) -> Option<PixelsGuard> {
293        let (raw_pixels, size) = unsafe {
294            self.lib.ultralight().ulBitmapLockPixels(self.internal);
295            (
296                self.lib.ultralight().ulBitmapRawPixels(self.internal),
297                self.lib.ultralight().ulBitmapGetSize(self.internal),
298            )
299        };
300
301        if raw_pixels.is_null() {
302            return None;
303        }
304
305        unsafe {
306            let data = slice::from_raw_parts_mut(raw_pixels as _, size);
307            Some(PixelsGuard::new(self, data))
308        }
309    }
310
311    /// Internal unlock the pixel buffer.
312    pub(crate) unsafe fn raw_unlock_pixels(&mut self) {
313        self.lib.ultralight().ulBitmapUnlockPixels(self.internal);
314    }
315
316    /// Whether or not this bitmap is empty (no pixels allocated).
317    pub fn is_empty(&self) -> bool {
318        unsafe { self.lib.ultralight().ulBitmapIsEmpty(self.internal) }
319    }
320
321    /// Reset bitmap pixels to 0.
322    pub fn erase(&self) {
323        unsafe { self.lib.ultralight().ulBitmapErase(self.internal) }
324    }
325
326    /// Write bitmap to a PNG on disk.
327    pub fn write_to_png<P: AsRef<Path>>(&self, path: P) -> BitmapResult<()> {
328        let c_path = CString::new(path.as_ref().to_str().unwrap()).unwrap();
329        let result = unsafe {
330            self.lib
331                .ultralight()
332                .ulBitmapWritePNG(self.internal, c_path.as_ptr())
333        };
334        if result {
335            Ok(())
336        } else {
337            Err(BitmapError::FailedPngWrite)
338        }
339    }
340
341    /// This converts a BGRA bitmap to RGBA bitmap and vice-versa by swapping the red and blue channels.
342    ///
343    /// Only valid if the format is BitmapFormat::BGRA8_UNORM_SRGB
344    pub fn swap_red_blue_channels(&self) -> BitmapResult<()> {
345        if let BitmapFormat::Bgra8UnormSrgb = self.format() {
346            unsafe {
347                self.lib
348                    .ultralight()
349                    .ulBitmapSwapRedBlueChannels(self.internal)
350            }
351            Ok(())
352        } else {
353            Err(BitmapError::UnsupportedOperationForPixelFormat)
354        }
355    }
356}
357
358impl Drop for Bitmap {
359    fn drop(&mut self) {
360        if self.need_to_destroy {
361            unsafe { self.lib.ultralight().ulDestroyBitmap(self.internal) };
362        }
363    }
364}
365
366/// A bitmap object that has an owned pixel buffer.
367///
368/// This is useful for using the raw pixels in any rust code without
369/// binding to the underlying C library.
370///
371/// To create an `Ultralight` bitmap, use [`OwnedBitmap::to_bitmap`].
372pub struct OwnedBitmap {
373    width: u32,
374    height: u32,
375    format: BitmapFormat,
376    bpp: u32,
377    row_bytes: u32,
378    bytes_size: usize,
379    pixels: Option<Vec<u8>>,
380    is_empty: bool,
381}
382
383impl OwnedBitmap {
384    /// Create an [`OwnedBitmap`] from a [`Bitmap`].
385    ///
386    /// This will result in copying all the pixels from the original bitmap.
387    pub fn from_bitmap(bitmap: &mut Bitmap) -> Option<Self> {
388        let width = bitmap.width();
389        let height = bitmap.height();
390        let format = bitmap.format();
391        let bpp = bitmap.bpp();
392        let row_bytes = bitmap.row_bytes();
393        let bytes_size = bitmap.bytes_size();
394        let is_empty = bitmap.is_empty();
395
396        let pixels = bitmap.lock_pixels().map(|v| v.to_vec());
397
398        Some(Self {
399            width,
400            height,
401            format,
402            bpp,
403            row_bytes,
404            bytes_size,
405            pixels,
406            is_empty,
407        })
408    }
409
410    /// Create a [`Bitmap`] from an [`OwnedBitmap`].
411    ///
412    /// This is useful when we need to call `Ultralight` logic that require [`Bitmap`].
413    ///
414    /// This function will copy all the pixels from the owned bitmap.
415    pub fn to_bitmap(&self, lib: Arc<Library>) -> BitmapResult<Bitmap> {
416        if let Some(pixels) = self.pixels.as_ref() {
417            Bitmap::create_from_pixels(lib, self.width, self.height, self.format, pixels.as_slice())
418        } else {
419            Err(BitmapError::EmptyBitmap)
420        }
421    }
422
423    /// Get the width in pixels.
424    pub fn width(&self) -> u32 {
425        self.width
426    }
427
428    /// Get the height in pixels.
429    pub fn height(&self) -> u32 {
430        self.height
431    }
432
433    /// Get the pixel format.
434    pub fn format(&self) -> BitmapFormat {
435        self.format
436    }
437
438    /// Get the number of bytes per pixel.
439    pub fn bpp(&self) -> u32 {
440        self.bpp
441    }
442
443    /// Get the number of bytes between each row of pixels.
444    pub fn row_bytes(&self) -> u32 {
445        self.row_bytes
446    }
447
448    /// Get the size in bytes of the pixel buffer.
449    pub fn bytes_size(&self) -> usize {
450        self.bytes_size
451    }
452
453    /// Get the pixel buffer slice.
454    pub fn pixels(&self) -> Option<&[u8]> {
455        self.pixels.as_deref()
456    }
457
458    /// Get the mutable pixel buffer slice.
459    pub fn pixels_mut(&mut self) -> Option<&mut [u8]> {
460        self.pixels.as_deref_mut()
461    }
462
463    /// Whether or not this bitmap is empty (no pixels allocated).
464    pub fn is_empty(&self) -> bool {
465        self.is_empty
466    }
467}