1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: MPL-2.0
//! An RGBA image.
use std::ops::Deref;
use crate::prelude::*;
impl Ui {
/// Creates a new [`Image`] with the given size.
///
/// # Arguments
///
/// `width` and `height` are given in points, which is commonly the pixel size of the `1x`
/// scaled image.
///
/// # Panics
///
/// Panics if `width` or `height` are negative or [non-normal](f64::is_normal).
pub fn create_image<'ui>(
&'ui self,
width: f64,
height: f64,
) -> Result<&'ui mut Image, crate::Error> {
let check_dim = |dim: f64| assert!(dim.is_normal() && dim.is_sign_positive());
check_dim(width);
check_dim(height);
// SAFETY: `width` and `height` are probably OK.
// FIXME: what are the upper limits for `width` and `height`?
unsafe { call_libui_new_fn!(ui: self, fn: uiNewImage(width, height) -> Image) }
}
}
impl Image {
unsafe fn from_ptr(_: &Ui, ptr: *mut uiImage) -> Self {
Self { ptr }
}
}
/// An RGBA image.
#[derive(Widget)]
#[widget(handle = "uiImage")]
pub struct Image {
ptr: *mut uiImage,
}
impl Drop for Image {
#[inline]
fn drop(&mut self) {
unsafe { uiFreeImage(self.as_ptr()) };
}
}
impl Image {
pub fn push(&self, repr: Repr) {
let [row_stride, height] = [repr.row_stride, repr.height]
.map(|dim| usize::try_from(dim.as_u32()).unwrap());
// SAFETY: `pixels` must be at least `row_stride * height` bytes.
assert!(repr.pixels.len() >= (row_stride * height));
// SAFETY: `pixels` is dropped at the end of scope, but that's OK as *libui-ng* copies it.
let pixels = repr.pixels.as_mut_ptr().cast();
unsafe {
uiImageAppend(
self.as_ptr(),
pixels,
repr.width.to_libui(),
repr.height.to_libui(),
repr.row_stride.to_libui(),
);
}
}
}
pub enum IntoReprError {
/// The width of the given representation exceeds that allowed by *libui-ng*.
TooWide,
/// The height of the given representation exceeds that allowed by *libui-ng*.
TooTall,
/// The row stride of the given representation exceeds that allowed by *libui-ng*.
StrideTooWide,
}
// FIXME: can we generalize this impl more? Maybe impl on a trait instead of a struct?
#[cfg(feature = "image")]
impl<'a, C> TryFrom<&'a mut image::ImageBuffer<image::Rgba<u8>, C>> for Repr<'a>
where
C: AsRef<[u8]> + Deref<Target = [u8]> + DerefMut,
{
type Error = IntoReprError;
fn try_from(buf: &'a mut image::ImageBuffer<image::Rgba<u8>, C>) -> Result<Self, Self::Error> {
let (width, height) = buf.dimensions();
let [maybe_width, maybe_height] = [width, height].map(|dim| NonNegativeInt::try_from(dim));
let width = maybe_width.map_err(|_| IntoReprError::TooWide)?;
let height = maybe_height.map_err(|_| IntoReprError::TooTall)?;
let row_stride = NonNegativeInt::try_from(buf.as_flat_samples().layout.width_stride)
.map_err(|_| IntoReprError::StrideTooWide)?;
Ok(Self {
pixels: buf.deref_mut(),
width,
height,
row_stride,
})
}
}
pub struct Repr<'a> {
/// An array of 8-bit RGBA components.
pub pixels: &'a mut [u8],
/// The width, in pixels.
pub width: NonNegativeInt,
/// The height, in pixels.
pub height: NonNegativeInt,
/// The row stride, in bytes.
pub row_stride: NonNegativeInt,
}