C++: support for RGB8 in the line by line rendering

Note: this adds a mendatory template parametter to the
(experimental) `render_by_line` function.
I tried to get the PixelType auto-detected from the callback but i
didn't manage
This commit is contained in:
Olivier Goffart 2025-02-19 09:11:10 +01:00 committed by GitHub
parent 48fbf887aa
commit 07803ccd6c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 130 additions and 100 deletions

View file

@ -896,7 +896,8 @@ fn gen_platform(
.with_include("slint_internal.h")
.with_after_include(
r"
namespace slint::cbindgen_private { struct WindowProperties; }
namespace slint::platform { struct Rgb565Pixel; }
namespace slint::cbindgen_private { struct WindowProperties; using slint::platform::Rgb565Pixel; }
",
)
.generate()

View file

@ -271,27 +271,24 @@ void EspPlatform<PixelType>::run_event_loop()
}
#ifdef SLINT_FEATURE_EXPERIMENTAL
} else {
std::unique_ptr<slint::platform::Rgb565Pixel, void (*)(void *)> lb(
static_cast<slint::platform::Rgb565Pixel *>(
heap_caps_malloc((rotated ? size.height : size.width)
* sizeof(slint::platform::Rgb565Pixel),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)),
std::unique_ptr<PixelType, void (*)(void *)> lb(
static_cast<PixelType *>(heap_caps_malloc(
(rotated ? size.height : size.width) * sizeof(PixelType),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)),
heap_caps_free);
m_window->m_renderer.render_by_line([this, &lb](std::size_t line_y,
std::size_t line_start,
std::size_t line_end,
auto &&render_fn) {
std::span<slint::platform::Rgb565Pixel> view { lb.get(),
line_end - line_start };
render_fn(view);
if (byte_swap) {
// Swap endianness to big endian
std::for_each(view.begin(), view.end(),
[](auto &rgbpix) { byte_swap_color(&rgbpix); });
}
esp_lcd_panel_draw_bitmap(panel_handle, line_start, line_y, line_end,
line_y + 1, lb.get());
});
m_window->m_renderer.render_by_line<PixelType>(
[this, &lb](std::size_t line_y, std::size_t line_start,
std::size_t line_end, auto &&render_fn) {
std::span<PixelType> view { lb.get(), line_end - line_start };
render_fn(view);
if (byte_swap) {
// Swap endianness to big endian
std::for_each(view.begin(), view.end(),
[](auto &rgbpix) { byte_swap_color(&rgbpix); });
}
esp_lcd_panel_draw_bitmap(panel_handle, line_start, line_y,
line_end, line_y + 1, lb.get());
});
#endif
}
}

View file

@ -708,27 +708,37 @@ public:
/// a line buffer (as std::span) and invoke the provided fourth parameter (render_fn) with it,
/// to fill it with pixels. After the line buffer is filled with pixels, your implementation is
/// free to flush that line to the screen for display.
template<typename Callback>
///
/// The first template parameter (PixelType) must be specified and can be either Rgb565Pixel or
/// Rgb8Pixel.
template<typename PixelType, typename Callback>
requires requires(Callback callback) {
callback(size_t(0), size_t(0), size_t(0), [&callback](std::span<Rgb565Pixel>) {});
callback(size_t(0), size_t(0), size_t(0), [&callback](std::span<PixelType>) {});
}
PhysicalRegion render_by_line(Callback process_line_callback) const
{
auto r = cbindgen_private::slint_software_renderer_render_by_line_rgb565(
inner,
[](void *process_line_callback_ptr, uintptr_t line, uintptr_t line_start,
uintptr_t line_end, void (*render_fn)(const void *, uint16_t *, std::size_t),
const void *render_fn_data) {
(*reinterpret_cast<Callback *>(process_line_callback_ptr))(
std::size_t(line), std::size_t(line_start), std::size_t(line_end),
[render_fn, render_fn_data](std::span<Rgb565Pixel> line_span) {
render_fn(render_fn_data,
reinterpret_cast<uint16_t *>(line_span.data()),
line_span.size());
});
},
&process_line_callback);
return PhysicalRegion { r };
auto process_line_fn = [](void *process_line_callback_ptr, uintptr_t line,
uintptr_t line_start, uintptr_t line_end,
void (*render_fn)(const void *, PixelType *, std::size_t),
const void *render_fn_data) {
(*reinterpret_cast<Callback *>(process_line_callback_ptr))(
std::size_t(line), std::size_t(line_start), std::size_t(line_end),
[render_fn, render_fn_data](std::span<PixelType> line_span) {
render_fn(render_fn_data, line_span.data(), line_span.size());
});
};
if constexpr (std::is_same_v<PixelType, Rgb565Pixel>) {
return PhysicalRegion { cbindgen_private::slint_software_renderer_render_by_line_rgb565(
inner, process_line_fn, &process_line_callback) };
} else if constexpr (std::is_same_v<PixelType, Rgb8Pixel>) {
return PhysicalRegion { cbindgen_private::slint_software_renderer_render_by_line_rgb8(
inner, process_line_fn, &process_line_callback) };
} else {
static_assert(std::is_same_v<PixelType, Rgba8Pixel>
|| std::is_same_v<PixelType, Rgb565Pixel>,
"Unsupported PixelType. It must be either Rgba8Pixel or Rgb565Pixel");
}
}
# endif

View file

@ -403,6 +403,70 @@ mod software_renderer {
renderer.render(buffer, pixel_stride)
}
#[cfg(feature = "experimental")]
struct LineByLineProcessor<TargetPixel> {
process_line_fn: extern "C" fn(
*mut core::ffi::c_void,
usize,
usize,
usize,
extern "C" fn(*const core::ffi::c_void, *mut TargetPixel, usize),
*const core::ffi::c_void,
),
user_data: *mut core::ffi::c_void,
}
#[cfg(feature = "experimental")]
impl<TargetPixel: i_slint_core::software_renderer::TargetPixel>
i_slint_core::software_renderer::LineBufferProvider for LineByLineProcessor<TargetPixel>
{
type TargetPixel = TargetPixel;
fn process_line(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: impl FnOnce(&mut [TargetPixel]),
) {
self.cpp_process_line(line, range, render_fn);
}
}
#[cfg(feature = "experimental")]
impl<TargetPixel> LineByLineProcessor<TargetPixel> {
fn cpp_process_line<RenderFn: FnOnce(&mut [TargetPixel])>(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: RenderFn,
) {
let mut render_fn = Some(render_fn);
let render_fn_ptr = &mut render_fn as *mut Option<RenderFn> as *const core::ffi::c_void;
extern "C" fn cpp_render_line_callback<
TargetPixel,
RenderFn: FnOnce(&mut [TargetPixel]),
>(
render_fn_ptr: *const core::ffi::c_void,
line_start: *mut TargetPixel,
len: usize,
) {
let line_slice = unsafe { core::slice::from_raw_parts_mut(line_start, len) };
let render_fn =
unsafe { (*(render_fn_ptr as *mut Option<RenderFn>)).take().unwrap() };
render_fn(line_slice);
}
(self.process_line_fn)(
self.user_data,
line,
range.start,
range.end,
cpp_render_line_callback::<TargetPixel, RenderFn>,
render_fn_ptr,
);
}
}
#[cfg(feature = "experimental")]
#[no_mangle]
pub unsafe extern "C" fn slint_software_renderer_render_by_line_rgb565(
@ -412,74 +476,32 @@ mod software_renderer {
usize,
usize,
usize,
extern "C" fn(*const core::ffi::c_void, *mut u16, usize),
extern "C" fn(*const core::ffi::c_void, *mut Rgb565Pixel, usize),
*const core::ffi::c_void,
),
user_data: *mut core::ffi::c_void,
) -> PhysicalRegion {
struct Rgb565Processor {
process_line_fn: extern "C" fn(
*mut core::ffi::c_void,
usize,
usize,
usize,
extern "C" fn(*const core::ffi::c_void, *mut u16, usize),
*const core::ffi::c_void,
),
user_data: *mut core::ffi::c_void,
}
impl i_slint_core::software_renderer::LineBufferProvider for Rgb565Processor {
type TargetPixel = Rgb565Pixel;
fn process_line(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: impl FnOnce(&mut [Rgb565Pixel]),
) {
self.cpp_process_line(line, range, render_fn);
}
}
impl Rgb565Processor {
fn cpp_process_line<RenderFn: FnOnce(&mut [Rgb565Pixel])>(
&mut self,
line: usize,
range: core::ops::Range<usize>,
render_fn: RenderFn,
) {
let mut render_fn = Some(render_fn);
let render_fn_ptr =
&mut render_fn as *mut Option<RenderFn> as *const core::ffi::c_void;
extern "C" fn cpp_render_line_callback<RenderFn: FnOnce(&mut [Rgb565Pixel])>(
render_fn_ptr: *const core::ffi::c_void,
line_start: *mut u16,
len: usize,
) {
let line_slice = unsafe {
core::slice::from_raw_parts_mut(line_start as *mut Rgb565Pixel, len)
};
let render_fn =
unsafe { (*(render_fn_ptr as *mut Option<RenderFn>)).take().unwrap() };
render_fn(line_slice);
}
(self.process_line_fn)(
self.user_data,
line,
range.start,
range.end,
cpp_render_line_callback::<RenderFn>,
render_fn_ptr,
);
}
}
let renderer = &*(r as *const SoftwareRenderer);
let processor = LineByLineProcessor { process_line_fn, user_data };
renderer.render_by_line(processor)
}
let processor = Rgb565Processor { process_line_fn, user_data };
#[cfg(feature = "experimental")]
#[no_mangle]
pub unsafe extern "C" fn slint_software_renderer_render_by_line_rgb8(
r: SoftwareRendererOpaque,
process_line_fn: extern "C" fn(
*mut core::ffi::c_void,
usize,
usize,
usize,
extern "C" fn(*const core::ffi::c_void, *mut Rgb8Pixel, usize),
*const core::ffi::c_void,
),
user_data: *mut core::ffi::c_void,
) -> PhysicalRegion {
let renderer = &*(r as *const SoftwareRenderer);
let processor = LineByLineProcessor { process_line_fn, user_data };
renderer.render_by_line(processor)
}