Qt style: Use an QImage that is backed by a SharedArray

This way we have one copy less of the data
This commit is contained in:
Olivier Goffart 2020-10-19 11:35:57 +02:00
parent f6edfb5e8e
commit b60567d7f2
4 changed files with 91 additions and 19 deletions

View file

@ -231,7 +231,7 @@ pub enum Resource {
EmbeddedData(super::slice::Slice<'static, u8>),
/// Raw ARGB
#[allow(missing_docs)]
EmbeddedRgbaImage { width: u32, height: u32, data: super::sharedarray::SharedArray<u8> },
EmbeddedRgbaImage { width: u32, height: u32, data: super::sharedarray::SharedArray<u32> },
}
impl Default for Resource {

View file

@ -159,6 +159,9 @@ impl<T: Clone> SharedArray<T> {
size += 1;
self.inner.as_mut().header.size = size;
}
if size == new_capacity {
break;
}
}
}
@ -179,6 +182,45 @@ impl<T: Clone> SharedArray<T> {
self.inner.as_mut().header.size += 1;
}
}
/// Resize the array to the given size.
/// If the array was smaller new elements will be initialized with the value.
/// If the array was bigger, extra elements will be discared
///
/// ```
/// use sixtyfps_corelib::SharedArray;
/// let mut shared_array = SharedArray::<u32>::from_slice(&[1, 2, 3]);
/// shared_array.resize(5, 8);
/// assert_eq!(shared_array.as_slice(), &[1, 2, 3, 8, 8]);
/// shared_array.resize(2, 0);
/// assert_eq!(shared_array.as_slice(), &[1, 2]);
/// ```
pub fn resize(&mut self, new_len: usize, value: T) {
if self.len() == new_len {
return;
}
self.detach(new_len);
// Safety: detach ensured that the array is not shared.
let mut inner = unsafe { self.inner.as_mut() };
if inner.header.size >= new_len {
while inner.header.size > new_len {
inner.header.size -= 1;
// Safety: The array was of size inner.header.size, so there should be an element there
unsafe {
drop(core::ptr::read(inner.data.as_mut_ptr().add(inner.header.size)));
}
}
} else {
while inner.header.size < new_len {
// Safety: The array must have a capacity of at least new_len because of the detach call earlier
unsafe {
core::ptr::write(inner.data.as_mut_ptr().add(inner.header.size), value.clone());
}
inner.header.size += 1;
}
}
}
}
impl<T> Deref for SharedArray<T> {

View file

@ -494,10 +494,10 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
)]
}
Resource::EmbeddedRgbaImage { width, height, data } => {
// Safety: a slice of u32 can be transmuted to a slice of u8
let slice = unsafe { data.as_slice().align_to().1 };
let image = image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw(
*width,
*height,
data.as_slice(),
*width, *height, slice,
)
.unwrap();
smallvec![GLRenderingPrimitivesBuilder::create_image(

View file

@ -42,10 +42,38 @@ macro_rules! get_size {
fn to_resource(image: qttypes::QImage) -> Resource {
let size = image.size();
// Safety: an slice of u8 can be converted to an slice of u32
let data = unsafe { image.data().align_to::<u32>().1 };
Resource::EmbeddedRgbaImage {
width: size.width,
height: size.height,
data: SharedArray::from(image.data()),
data: SharedArray::from_slice(data),
}
}
struct QImageWrapArray {
/// The image reference the array, so the array must outlive the image without being detached or accessed
img: qttypes::QImage,
array: SharedArray<u32>,
}
impl QImageWrapArray {
pub fn new(size: qttypes::QSize, dpr: f32) -> Self {
let mut array = SharedArray::default();
array.resize((size.width * size.height) as usize, 0u32);
let array_ptr = array.as_slice_mut().as_mut_ptr();
let img = cpp!(unsafe [size as "QSize", array_ptr as "uchar*", dpr as "float"] -> qttypes::QImage as "QImage" {
QImage img(array_ptr, size.width(), size.height(), size.width() * 4, QImage::Format_ARGB32_Premultiplied);
img.setDevicePixelRatio(dpr);
return img;
});
QImageWrapArray { img, array }
}
pub fn to_resource(self) -> Resource {
let size = self.img.size();
drop(self.img);
Resource::EmbeddedRgbaImage { width: size.width, height: size.height, data: self.array }
}
}
@ -1140,6 +1168,9 @@ impl Item for NativeScrollView {
) -> HighLevelRenderingPrimitive {
let size: qttypes::QSize = get_size!(self);
let dpr = window.scale_factor();
let mut imgarray = QImageWrapArray::new(size, dpr);
let data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
let left = Self::FIELD_OFFSETS.native_padding_left.apply_pin(self).get();
let right = Self::FIELD_OFFSETS.native_padding_right.apply_pin(self).get();
@ -1151,30 +1182,29 @@ impl Item for NativeScrollView {
width: ((right - left) / dpr) as _,
height: ((bottom - top) / dpr) as _,
};
let mut img = cpp!(unsafe [dpr as "float",size as "QSize", corner_rect as "QRectF"] -> qttypes::QImage as "QImage" {
let img: &mut qttypes::QImage = &mut imgarray.img;
cpp!(unsafe [img as "QImage*", corner_rect as "QRectF"] {
ensure_initialized();
auto [img, rect] = offline_style_rendering_image(size, dpr);
QStyleOptionFrame frameOption;
frameOption.frameShape = QFrame::StyledPanel;
frameOption.lineWidth = 1;
frameOption.midLineWidth = 0;
frameOption.rect = corner_rect.toAlignedRect();
QPainter p(&img);
QPainter p(img);
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, &p, global_widget());
frameOption.rect = QRect(QPoint(), corner_rect.toAlignedRect().topLeft());
qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, &p, global_widget());
return img;
});
let mut draw_scrollbar = |horizontal: bool,
rect: qttypes::QRectF,
value: i32,
page_size: i32,
max: i32,
active_controls: u32,
pressed: bool| {
let draw_scrollbar = |horizontal: bool,
rect: qttypes::QRectF,
value: i32,
page_size: i32,
max: i32,
active_controls: u32,
pressed: bool| {
cpp!(unsafe [
mut img as "QImage",
img as "QImage*",
value as "int",
page_size as "int",
max as "int",
@ -1184,7 +1214,7 @@ impl Item for NativeScrollView {
dpr as "float",
horizontal as "bool"
] {
QPainter p(&img);
QPainter p(img);
auto r = rect.toAlignedRect();
p.translate(r.topLeft()); // There is bugs in the styles if the scrollbar is not in (0,0)
QStyleOptionSlider option;
@ -1232,7 +1262,7 @@ impl Item for NativeScrollView {
data.pressed == 1,
);
return HighLevelRenderingPrimitive::Image { source: to_resource(img) };
return HighLevelRenderingPrimitive::Image { source: imgarray.to_resource() };
}
fn rendering_variables(