Expose the individual rectangle in the dirty region

This commit is contained in:
Olivier Goffart 2024-04-26 13:32:30 +02:00
parent f45bab6a3f
commit 5955d19706
7 changed files with 117 additions and 29 deletions

View file

@ -216,6 +216,8 @@ fn default_config() -> cbindgen::Config {
.iter()
.cloned()
.collect();
config.structure.associated_constants_in_body = true;
config.constant.allow_constexpr = true;
config
}
@ -295,6 +297,7 @@ fn gen_corelib(
"Rect",
"SortOrder",
"BitmapFont",
"PhysicalRegion",
]
.iter()
.chain(items.iter())

View file

@ -225,10 +225,9 @@ void EspPlatform::run_event_loop()
if (buffer1) {
auto region = m_window->m_renderer.render(buffer1.value(),
rotated ? size.height : size.width);
auto o = region.bounding_box_origin();
auto s = region.bounding_box_size();
if (s.width > 0 && s.height > 0) {
if (buffer2) {
if (buffer2) {
auto s = region.bounding_box_size();
if (s.width > 0 && s.height > 0) {
#if SOC_LCD_RGB_SUPPORTED && ESP_IDF_VERSION_MAJOR >= 5
xSemaphoreGive(sem_gui_ready);
xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
@ -241,7 +240,9 @@ void EspPlatform::run_event_loop()
buffer1->data());
std::swap(buffer1, buffer2);
} else {
}
} else {
for (auto [o, s] : region.rectangles()) {
for (int y = o.y; y < o.y + s.height; y++) {
for (int x = o.x; x < o.x + s.width; x++) {
// Swap endianess to big endian

View file

@ -8,6 +8,7 @@
#include <cassert>
#include <cstdint>
#include <utility>
#include <ranges>
struct xcb_connection_t;
struct wl_surface;
@ -564,18 +565,68 @@ public:
/// Returns the size of the bounding box of this region.
PhysicalSize bounding_box_size() const
{
return PhysicalSize({ uint32_t(inner.width), uint32_t(inner.height) });
if (inner.count == 0) {
return PhysicalSize();
}
auto origin = bounding_box_origin();
PhysicalSize size({ .width = uint32_t(inner.rectangles[0].max.x - origin.x),
.height = uint32_t(inner.rectangles[0].max.y - origin.y) });
for (size_t i = 1; i < inner.count; ++i) {
size.width = std::max(size.width, uint32_t(inner.rectangles[i].max.x - origin.x));
size.height = std::max(size.height, uint32_t(inner.rectangles[i].max.y - origin.y));
}
return size;
}
/// Returns the origin of the bounding box of this region.
PhysicalPosition bounding_box_origin() const
{
return PhysicalPosition({ inner.x, inner.y });
if (inner.count == 0) {
return PhysicalPosition();
}
PhysicalPosition origin(
{ .x = inner.rectangles[0].min.x, .y = inner.rectangles[0].min.y });
for (size_t i = 1; i < inner.count; ++i) {
origin.x = std::min<int>(origin.x, inner.rectangles[i].min.x);
origin.y = std::min<int>(origin.y, inner.rectangles[i].min.y);
}
return origin;
}
/// Returns a view on all the rectangles in this region.
/// The returned type is a C++ view over PhysicalRegion::Rect structs.
///
/// It can be used like so:
/// ```cpp
/// for (auto [origin, size] : region.rectangles()) {
/// // Do something with the rect
/// }
/// ```
auto rectangles() const
{
return std::views::counted(inner.rectangles, inner.count)
| std::views::transform([](const auto &box) {
return Rect {
.origin = PhysicalPosition({ .x = box.min.x, .y = box.min.y }),
.size = PhysicalSize({ .width = uint32_t(box.max.x - box.min.x),
.height = uint32_t(box.max.y - box.min.y) })
};
});
}
/// A Rectangle defined with an origin and a size.
/// The PhysicalRegion::rectangles() function returns a view over them
struct Rect
{
/// The origin of the rectangle.
PhysicalPosition origin;
/// The size of the rectangle.
PhysicalSize size;
};
private:
cbindgen_private::types::IntRect inner;
cbindgen_private::PhysicalRegion inner;
friend class SoftwareRenderer;
PhysicalRegion(cbindgen_private::types::IntRect inner) : inner(inner) { }
PhysicalRegion(cbindgen_private::PhysicalRegion inner) : inner(std::move(inner)) { }
};
/// This enum describes which parts of the buffer passed to the SoftwareRenderer may be

View file

@ -32,13 +32,13 @@ using Size2D = Size<T>;
struct LogicalSize : public Size<float>
{
/// Explicitly convert a Size<float> to a LogicalSize
explicit LogicalSize(const Size<float> s) : Size<float>(s) {};
explicit constexpr LogicalSize(const Size<float> s = { 0, 0 }) : Size<float>(s) { }
};
/// A size given in physical pixels.
struct PhysicalSize : public Size<uint32_t>
{
/// Explicitly convert a Size<uint32_t> to a LogicalSize
explicit PhysicalSize(const Size<uint32_t> s) : Size<uint32_t>(s) {};
explicit constexpr PhysicalSize(const Size<uint32_t> s = { 0, 0 }) : Size<uint32_t>(s) { }
};
}

View file

@ -355,8 +355,10 @@ pub unsafe extern "C" fn slint_platform_task_run(event: PlatformTaskOpaque) {
mod software_renderer {
use super::*;
type SoftwareRendererOpaque = *const c_void;
use i_slint_core::graphics::{IntRect, Rgb8Pixel};
use i_slint_core::software_renderer::{RepaintBufferType, Rgb565Pixel, SoftwareRenderer};
use i_slint_core::graphics::Rgb8Pixel;
use i_slint_core::software_renderer::{
PhysicalRegion, RepaintBufferType, Rgb565Pixel, SoftwareRenderer,
};
#[no_mangle]
pub unsafe extern "C" fn slint_software_renderer_new(
@ -383,12 +385,10 @@ mod software_renderer {
buffer: *mut Rgb8Pixel,
buffer_len: usize,
pixel_stride: usize,
) -> IntRect {
) -> PhysicalRegion {
let buffer = core::slice::from_raw_parts_mut(buffer, buffer_len);
let renderer = &*(r as *const SoftwareRenderer);
let r = renderer.render(buffer, pixel_stride);
let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
renderer.render(buffer, pixel_stride)
}
#[no_mangle]
@ -397,12 +397,10 @@ mod software_renderer {
buffer: *mut u16,
buffer_len: usize,
pixel_stride: usize,
) -> IntRect {
) -> PhysicalRegion {
let buffer = core::slice::from_raw_parts_mut(buffer as *mut Rgb565Pixel, buffer_len);
let renderer = &*(r as *const SoftwareRenderer);
let r = renderer.render(buffer, pixel_stride);
let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
renderer.render(buffer, pixel_stride)
}
#[cfg(feature = "experimental")]
@ -418,7 +416,9 @@ mod software_renderer {
*const core::ffi::c_void,
),
user_data: *mut core::ffi::c_void,
) -> IntRect {
) -> PhysicalRegion {
use i_slint_core::software_renderer::PhysicalRegion;
struct Rgb565Processor {
process_line_fn: extern "C" fn(
*mut core::ffi::c_void,
@ -482,9 +482,7 @@ mod software_renderer {
let processor = Rgb565Processor { process_line_fn, user_data };
let r = renderer.render_by_line(processor);
let (orig, size) = (r.bounding_box_origin(), r.bounding_box_size());
i_slint_core::graphics::euclid::rect(orig.x, orig.y, size.width as i32, size.height as i32)
renderer.render_by_line(processor)
}
#[cfg(feature = "experimental")]

View file

@ -198,6 +198,15 @@ pub mod ffi {
y: f32,
}
/// Expand Box2D so that cbindgen can see it.
#[cfg(cbindgen)]
#[repr(C)]
struct Box2D<T, U> {
min: euclid::Point2D<T>,
max: euclid::Point2D<T>,
_unit: std::marker::PhantomData<U>,
}
#[cfg(feature = "std")]
pub use super::path::ffi::*;

View file

@ -207,17 +207,28 @@ pub trait LineBufferProvider {
);
}
#[cfg(not(cbindgen))]
const PHYSICAL_REGION_MAX_SIZE: usize = DirtyRegion::MAX_COUNT;
// cbindgen can't understand associated const correctly, so hardcode the value
#[cfg(cbindgen)]
pub const PHYSICAL_REGION_MAX_SIZE: usize = 3;
const _: () = {
assert!(PHYSICAL_REGION_MAX_SIZE == 3);
assert!(DirtyRegion::MAX_COUNT == 3);
};
/// Represents a rectangular region on the screen, used for partial rendering.
///
/// The region may be composed of multiple sub-regions.
#[derive(Clone, Debug, Default)]
#[repr(C)]
pub struct PhysicalRegion {
rectangles: [euclid::Box2D<i16, PhysicalPx>; DirtyRegion::MAX_COUNT],
rectangles: [euclid::Box2D<i16, PhysicalPx>; PHYSICAL_REGION_MAX_SIZE],
count: usize,
}
impl PhysicalRegion {
fn iter(&self) -> impl Iterator<Item = euclid::Box2D<i16, PhysicalPx>> + '_ {
fn iter_box(&self) -> impl Iterator<Item = euclid::Box2D<i16, PhysicalPx>> + '_ {
(0..self.count).map(|x| self.rectangles[x])
}
@ -242,6 +253,20 @@ impl PhysicalRegion {
let bb = self.bounding_rect();
crate::api::PhysicalPosition { x: bb.origin.x as _, y: bb.origin.y as _ }
}
/// Returns an iterator over the rectangles in this region.
/// Each rectangle is represented by its position and its size.
pub fn iter(
&self,
) -> impl Iterator<Item = (crate::api::PhysicalPosition, crate::api::PhysicalSize)> + '_ {
self.iter_box().map(|r| {
let r = r.to_rect();
(
crate::api::PhysicalPosition { x: r.origin.x as _, y: r.origin.y as _ },
crate::api::PhysicalSize { width: r.width() as _, height: r.height() as _ },
)
})
}
}
/// Computes what are the x ranges that intersects the region for specified y line.
@ -256,7 +281,7 @@ fn region_line_ranges(
) -> Option<i16> {
line_ranges.clear();
let mut next_validity = None::<i16>;
for geom in region.iter() {
for geom in region.iter_box() {
if geom.is_empty() {
continue;
}
@ -921,7 +946,8 @@ impl Scene {
vectors: SceneVectors,
dirty_region: PhysicalRegion,
) -> Self {
let current_line = dirty_region.iter().map(|x| x.min.y_length()).min().unwrap_or_default();
let current_line =
dirty_region.iter_box().map(|x| x.min.y_length()).min().unwrap_or_default();
items.retain(|i| i.pos.y_length() + i.size.height_length() > current_line);
items.sort_unstable_by(compare_scene_item);
let current_items_index = items.partition_point(|i| i.pos.y_length() <= current_line);