From 8fd69219baed9f7494498b95a57c04948952b178 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 6 Feb 2025 10:10:19 +0100 Subject: [PATCH 01/24] WIP: Add plumbing for acceleration pixel blending operations on target buffers in a synchronous way --- internal/core/Cargo.toml | 2 + internal/core/software_renderer.rs | 169 +++++++++++++++++++++++++---- 2 files changed, 151 insertions(+), 20 deletions(-) diff --git a/internal/core/Cargo.toml b/internal/core/Cargo.toml index f87d831dd4f..0d0b19c5465 100644 --- a/internal/core/Cargo.toml +++ b/internal/core/Cargo.toml @@ -45,6 +45,8 @@ shared-fontdb = ["i-slint-common/shared-fontdb"] raw-window-handle-06 = ["dep:raw-window-handle-06"] +experimental = [] + default = ["std", "unicode"] [dependencies] diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 7fae4caa959..ba561bae618 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -365,6 +365,59 @@ fn region_line_ranges( next_validity } +mod private_api { + use super::*; + + /// This trait represents access to a buffer of pixels the software renderer can render into, as well + /// as certain operations that the renderer will try to delegate to this trait. Implement these functions + /// to delegate rendering further to hardware-provided 2D acceleration units, such as DMA2D or PXP. + pub trait TargetPixelBuffer { + /// The pixel type the buffer represents. + type Pixel: TargetPixel; + + /// Returns a slice of pixels for the given line. + fn line_slice(&mut self, line_numer: usize) -> &mut [Self::Pixel]; + + /// Returns the number of lines the buffer has. This is typically the height in pixels. + fn num_lines(&self) -> usize; + + /// Fills the buffer with a rectangle at the specified position with the given size and the + /// provided color. Returns true if the operation was successful; false if it could not be + /// implemented and instead the software renderer needs to draw the rectangle. + fn fill_rectangle( + &mut self, + _x: i16, + _y: i16, + _width: i16, + _height: i16, + _color: PremultipliedRgbaColor, + ) -> bool { + false + } + } +} + +#[cfg(feature = "experimental")] +pub use private_api::TargetPixelBuffer; + +struct TargetPixelSlice<'a, T> { + data: &'a mut [T], + pixel_stride: usize, +} + +impl<'a, T: TargetPixel> private_api::TargetPixelBuffer for TargetPixelSlice<'a, T> { + type Pixel = T; + + fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + let offset = line_number * self.pixel_stride; + &mut self.data[offset..offset + self.pixel_stride] + } + + fn num_lines(&self) -> usize { + self.data.len() / self.pixel_stride + } +} + /// A Renderer that do the rendering in software /// /// The renderer can remember what items needs to be redrawn from the previous iteration. @@ -454,6 +507,34 @@ impl SoftwareRenderer { /// Returns the physical dirty region for this frame, excluding the extra_draw_region, /// in the window frame of reference. It is affected by the screen rotation. pub fn render(&self, buffer: &mut [impl TargetPixel], pixel_stride: usize) -> PhysicalRegion { + self.render_buffer_impl(&mut TargetPixelSlice { data: buffer, pixel_stride }) + } + + /// Render the window to the given frame buffer. + /// + /// The renderer uses a cache internally and will only render the part of the window + /// which are dirty. The `extra_draw_region` is an extra region which will also + /// be rendered. (eg: the previous dirty region in case of double buffering) + /// This function returns the region that was rendered. + /// + /// The buffer's line slices need to be wide enough to if the `width` of the screen and the line count the `height`, + /// or the `height` and `width` swapped if the screen is rotated by 90°. + /// + /// Returns the physical dirty region for this frame, excluding the extra_draw_region, + /// in the window frame of reference. It is affected by the screen rotation. + #[cfg(feature = "experimental")] + pub fn render_into_buffer(&self, buffer: &mut impl TargetPixelBuffer) -> PhysicalRegion { + self.render_buffer_impl(buffer) + } + + fn render_buffer_impl( + &self, + buffer: &mut impl private_api::TargetPixelBuffer, + ) -> PhysicalRegion { + let pixel_stride = buffer.line_slice(0).len(); + let num_lines = buffer.num_lines(); + let buffer_pixel_count = num_lines * pixel_stride; + let Some(window) = self.maybe_window_adapter.borrow().as_ref().and_then(|w| w.upgrade()) else { return Default::default(); @@ -471,31 +552,26 @@ impl SoftwareRenderer { window_item.background(), ) } else if rotation.is_transpose() { - (euclid::size2((buffer.len() / pixel_stride) as _, pixel_stride as _), Brush::default()) + (euclid::size2(num_lines as _, pixel_stride as _), Brush::default()) } else { - (euclid::size2(pixel_stride as _, (buffer.len() / pixel_stride) as _), Brush::default()) + (euclid::size2(pixel_stride as _, num_lines as _), Brush::default()) }; if size.is_empty() { return Default::default(); } assert!( if rotation.is_transpose() { - pixel_stride >= size.height as usize && buffer.len() >= (size.width as usize * pixel_stride + size.height as usize) - pixel_stride + pixel_stride >= size.height as usize && buffer_pixel_count >= (size.width as usize * pixel_stride + size.height as usize) - pixel_stride } else { - pixel_stride >= size.width as usize && buffer.len() >= (size.height as usize * pixel_stride + size.width as usize) - pixel_stride + pixel_stride >= size.width as usize && buffer_pixel_count >= (size.height as usize * pixel_stride + size.width as usize) - pixel_stride }, - "buffer of size {} with stride {pixel_stride} is too small to handle a window of size {size:?}", buffer.len() + "buffer of size {} with stride {pixel_stride} is too small to handle a window of size {size:?}", buffer_pixel_count ); let buffer_renderer = SceneBuilder::new( size, factor, window_inner, - RenderToBuffer { - buffer, - stride: pixel_stride, - dirty_range_cache: vec![], - dirty_region: Default::default(), - }, + RenderToBuffer { buffer, dirty_range_cache: vec![], dirty_region: Default::default() }, rotation, ); let mut renderer = self.partial_rendering_state.create_partial_renderer(buffer_renderer); @@ -543,6 +619,7 @@ impl SoftwareRenderer { let mut bg = TargetPixel::background(); // TODO: gradient background + // TODO: Use TargetPixelBuffer::fill_rectangle TargetPixel::blend(&mut bg, background.color().into()); let mut line = 0; while let Some(next) = region_line_ranges( @@ -552,7 +629,7 @@ impl SoftwareRenderer { ) { for l in line..next { for r in &renderer.actual_renderer.processor.dirty_range_cache { - renderer.actual_renderer.processor.buffer[l as usize * pixel_stride..] + renderer.actual_renderer.processor.buffer.line_slice(l as usize) [r.start as usize..r.end as usize] .fill(bg); } @@ -1082,14 +1159,13 @@ trait ProcessScene { fn process_gradient(&mut self, geometry: PhysicalRect, gradient: GradientCommand); } -struct RenderToBuffer<'a, TargetPixel> { - buffer: &'a mut [TargetPixel], - stride: usize, +struct RenderToBuffer<'a, TargetPixelBuffer> { + buffer: &'a mut TargetPixelBuffer, dirty_range_cache: Vec>, dirty_region: PhysicalRegion, } -impl RenderToBuffer<'_, T> { +impl> RenderToBuffer<'_, B> { fn foreach_ranges( &mut self, geometry: &PhysicalRect, @@ -1115,7 +1191,7 @@ impl RenderToBuffer<'_, T> { for l in line..next { f( l, - &mut self.buffer[l as usize * self.stride..][begin as usize..end as usize], + &mut self.buffer.line_slice(l as usize)[begin as usize..end as usize], extra_left_clip, extra_right_clip, ); @@ -1128,6 +1204,42 @@ impl RenderToBuffer<'_, T> { } } + fn foreach_region( + &mut self, + geometry: &PhysicalRect, + mut f: impl FnMut(&mut B, PhysicalRect, i16, i16), + ) { + let mut line = geometry.min_y(); + while let Some(mut next) = + region_line_ranges(&self.dirty_region, line, &mut self.dirty_range_cache) + { + next = next.min(geometry.max_y()); + for r in &self.dirty_range_cache { + if geometry.origin.x >= r.end { + continue; + } + let begin = r.start.max(geometry.origin.x); + let end = r.end.min(geometry.origin.x + geometry.size.width); + if begin >= end { + continue; + } + let extra_left_clip = begin - geometry.origin.x; + let extra_right_clip = geometry.origin.x + geometry.size.width - end; + + let region = PhysicalRect { + origin: PhysicalPoint::new(begin, line), + size: PhysicalSize::new(end - begin, next - line), + }; + + f(&mut self.buffer, region, extra_left_clip, extra_right_clip); + } + if next == geometry.max_y() { + break; + } + line = next; + } + } + fn process_texture_impl(&mut self, geometry: PhysicalRect, texture: SceneTexture<'_>) { self.foreach_ranges(&geometry, |line, buffer, extra_left_clip, extra_right_clip| { draw_functions::draw_texture_line( @@ -1142,7 +1254,9 @@ impl RenderToBuffer<'_, T> { } } -impl ProcessScene for RenderToBuffer<'_, T> { +impl> ProcessScene + for RenderToBuffer<'_, B> +{ fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture<'static>) { self.process_texture_impl(geometry, texture) } @@ -1153,8 +1267,23 @@ impl ProcessScene for RenderToBuffer<'_, T> { } fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) { - self.foreach_ranges(&geometry, |_line, buffer, _extra_left_clip, _extra_right_clip| { - TargetPixel::blend_slice(buffer, color); + self.foreach_region(&geometry, |buffer, rect, _extra_left_clip, _extra_right_clip| { + if !buffer.fill_rectangle( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + color, + ) { + let begin = rect.min_x(); + let end = rect.max_x(); + for l in rect.min_y()..rect.max_y() { + ::blend_slice( + &mut buffer.line_slice(l as usize)[begin as usize..end as usize], + color, + ) + } + } }); } From 580cc3a74150f307d9669e2077e4c9fec7d88cba Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 6 Feb 2025 20:04:28 +0100 Subject: [PATCH 02/24] WIP: Start wrapping TargetPIxelBuffer in C++ --- api/cpp/Cargo.toml | 2 +- api/cpp/include/slint-platform.h | 77 ++++++++++++++++ api/cpp/platform.rs | 151 ++++++++++++++++++++++++++++++- 3 files changed, 228 insertions(+), 2 deletions(-) diff --git a/api/cpp/Cargo.toml b/api/cpp/Cargo.toml index 7972c615cd3..71acb7b8f43 100644 --- a/api/cpp/Cargo.toml +++ b/api/cpp/Cargo.toml @@ -46,7 +46,7 @@ system-testing = ["i-slint-backend-selector/system-testing"] std = ["i-slint-core/default", "i-slint-core/image-default-formats", "i-slint-backend-selector"] freestanding = ["i-slint-core/libm", "i-slint-core/unsafe-single-threaded"] -experimental = [] +experimental = ["i-slint-core/experimental"] default = ["std", "backend-winit", "renderer-femtovg", "backend-qt"] diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 844f54f83af..b0c5ace79c3 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -538,6 +538,20 @@ struct Rgb565Pixel friend bool operator==(const Rgb565Pixel &lhs, const Rgb565Pixel &rhs) = default; }; +# ifdef SLINT_FEATURE_EXPERIMENTAL +template +struct TargetPixelBuffer +{ + virtual ~TargetPixelBuffer() { } + + virtual std::span line_slice(std::size_t line_number) = 0; + virtual std::size_t num_lines() = 0; + + virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, + const RgbaColor &premultiplied_color) = 0; +}; +# endif + /// Slint's software renderer. /// /// To be used as a template parameter of the WindowAdapter. @@ -680,6 +694,69 @@ class SoftwareRenderer : public AbstractRenderer return PhysicalRegion { r }; } +# ifdef SLINT_FEATURE_EXPERIMENTAL + PhysicalRegion render(TargetPixelBuffer *buffer) const + { + cbindgen_private::CppRgb8TargetPixelBuffer buffer_wrapper { + .user_data = buffer, + .line_slice = + [](void *self, uintptr_t line_number, Rgb8Pixel **slice_ptr, + uintptr_t *slice_len) { + auto *buffer = reinterpret_cast *>(self); + auto slice = buffer->line_slice(line_number); + *slice_ptr = slice.data(); + *slice_len = slice.size(); + }, + .num_lines = + [](void *self) { + auto *buffer = reinterpret_cast *>(self); + return buffer->num_lines(); + }, + .fill_rectangle = + [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, uint8_t red, + uint8_t green, uint8_t blue, uint8_t alpha) { + auto *buffer = reinterpret_cast *>(self); + return buffer->fill_rectangle( + x, y, width, height, + Color::from_argb_uint8(alpha, red, green, blue)); + } + }; + auto r = + cbindgen_private::slint_software_renderer_render_accel_rgb8(inner, &buffer_wrapper); + return PhysicalRegion { r }; + } + PhysicalRegion render(TargetPixelBuffer *buffer) const + { + cbindgen_private::CppRgb565TargetPixelBuffer buffer_wrapper { + .user_data = buffer, + .line_slice = + [](void *self, uintptr_t line_number, uint16_t **slice_ptr, + uintptr_t *slice_len) { + auto *buffer = reinterpret_cast *>(self); + auto slice = buffer->line_slice(line_number); + *slice_ptr = reinterpret_cast(slice.data()); + *slice_len = slice.size(); + }, + .num_lines = + [](void *self) { + auto *buffer = reinterpret_cast *>(self); + return buffer->num_lines(); + }, + .fill_rectangle = + [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, uint8_t red, + uint8_t green, uint8_t blue, uint8_t alpha) { + auto *buffer = reinterpret_cast *>(self); + return buffer->fill_rectangle( + x, y, width, height, + Color::from_argb_uint8(alpha, red, green, blue)); + } + }; + auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, + &buffer_wrapper); + return PhysicalRegion { r }; + } +# endif + /// Render the window scene into an RGB 565 encoded pixel buffer /// /// The buffer must be at least as large as the associated slint::Window diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index d771e0f4bbf..0e80a9e1e14 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -356,10 +356,139 @@ mod software_renderer { type SoftwareRendererOpaque = *const c_void; use i_slint_core::graphics::{IntRect, Rgb8Pixel}; use i_slint_core::software_renderer::{ - PhysicalRegion, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, + PhysicalRegion, PremultipliedRgbaColor, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, + TargetPixelBuffer, }; use i_slint_core::SharedVector; + type CppTargetPixelBufferUserData = *mut c_void; + + #[repr(C)] + pub struct CppRgb8TargetPixelBuffer { + user_data: CppTargetPixelBufferUserData, + line_slice: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + usize, + slice_ptr: &mut *mut Rgb8Pixel, + slice_len: *mut usize, + ), + num_lines: unsafe extern "C" fn(CppTargetPixelBufferUserData) -> usize, + fill_rectangle: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + i16, + i16, + i16, + i16, + u8, + u8, + u8, + u8, + ) -> bool, + } + + impl TargetPixelBuffer for CppRgb8TargetPixelBuffer { + type Pixel = Rgb8Pixel; + + fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + unsafe { + let mut data = core::ptr::null_mut(); + let mut len = 0; + (self.line_slice)(self.user_data, line_number, &mut data, &mut len); + core::slice::from_raw_parts_mut(data, len) + } + } + + fn num_lines(&self) -> usize { + unsafe { (self.num_lines)(self.user_data) } + } + + fn fill_rectangle( + &mut self, + x: i16, + y: i16, + width: i16, + height: i16, + color: PremultipliedRgbaColor, + ) -> bool { + unsafe { + (self.fill_rectangle)( + self.user_data, + x, + y, + width, + height, + color.red, + color.green, + color.blue, + color.alpha, + ) + } + } + } + + #[repr(C)] + pub struct CppRgb565TargetPixelBuffer { + user_data: CppTargetPixelBufferUserData, + line_slice: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + usize, + slice_ptr: &mut *mut u16, + slice_len: *mut usize, + ), + num_lines: unsafe extern "C" fn(CppTargetPixelBufferUserData) -> usize, + fill_rectangle: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + i16, + i16, + i16, + i16, + u8, + u8, + u8, + u8, + ) -> bool, + } + + impl TargetPixelBuffer for CppRgb565TargetPixelBuffer { + type Pixel = Rgb565Pixel; + + fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + unsafe { + let mut data = core::ptr::null_mut(); + let mut len = 0; + (self.line_slice)(self.user_data, line_number, &mut data, &mut len); + core::slice::from_raw_parts_mut(data as *mut Rgb565Pixel, len) + } + } + + fn num_lines(&self) -> usize { + unsafe { (self.num_lines)(self.user_data) } + } + + fn fill_rectangle( + &mut self, + x: i16, + y: i16, + width: i16, + height: i16, + color: PremultipliedRgbaColor, + ) -> bool { + unsafe { + (self.fill_rectangle)( + self.user_data, + x, + y, + width, + height, + color.red, + color.green, + color.blue, + color.alpha, + ) + } + } + } + #[no_mangle] pub unsafe extern "C" fn slint_software_renderer_new( buffer_age: u32, @@ -391,6 +520,26 @@ mod software_renderer { renderer.render(buffer, pixel_stride) } + #[cfg(feature = "experimental")] + #[no_mangle] + pub unsafe extern "C" fn slint_software_renderer_render_accel_rgb8( + r: SoftwareRendererOpaque, + buffer: *mut CppRgb8TargetPixelBuffer, + ) -> PhysicalRegion { + let renderer = &*(r as *const SoftwareRenderer); + unsafe { renderer.render_into_buffer(&mut *buffer) } + } + + #[cfg(feature = "experimental")] + #[no_mangle] + pub unsafe extern "C" fn slint_software_renderer_render_accel_rgb565( + r: SoftwareRendererOpaque, + buffer: *mut CppRgb565TargetPixelBuffer, + ) -> PhysicalRegion { + let renderer = &*(r as *const SoftwareRenderer); + unsafe { renderer.render_into_buffer(&mut *buffer) } + } + #[no_mangle] pub unsafe extern "C" fn slint_software_renderer_render_rgb565( r: SoftwareRendererOpaque, From 612cdf91cfd81cbccbd3ab93028605d04fdde0ad Mon Sep 17 00:00:00 2001 From: Sam Cristall Date: Tue, 11 Feb 2025 16:08:57 -0700 Subject: [PATCH 03/24] Add draw_texture/process_texture and use accelerated fill_rectangle for background draw --- api/cpp/include/slint-platform.h | 29 +++++++ api/cpp/platform.rs | 122 +++++++++++++++++++++++++++++ internal/core/software_renderer.rs | 91 ++++++++++++++------- 3 files changed, 214 insertions(+), 28 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index b0c5ace79c3..6cd404225f2 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -549,6 +549,11 @@ struct TargetPixelBuffer virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, const RgbaColor &premultiplied_color) = 0; + virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, + const uint8_t *src, uint16_t src_stride, int16_t src_width, + int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, + uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, + uint32_t colorize, uint8_t alpha, uint8_t rotation) = 0; }; # endif @@ -719,6 +724,18 @@ class SoftwareRenderer : public AbstractRenderer return buffer->fill_rectangle( x, y, width, height, Color::from_argb_uint8(alpha, red, green, blue)); + }, + .draw_texture = + [](void *self, int16_t x, int16_t y, int16_t width, int16_t handle, + const uint8_t *src, uint16_t src_stride, int16_t src_width, + int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, + uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, + uint8_t alpha, uint8_t rotation) { + auto *buffer = reinterpret_cast *>(self); + return buffer->draw_texture(x, y, width, handle, src, src_stride, src_width, + src_height, src_pixel_format, src_dx, src_dy, + src_off_x, src_off_y, colorize, alpha, + rotation); } }; auto r = @@ -749,6 +766,18 @@ class SoftwareRenderer : public AbstractRenderer return buffer->fill_rectangle( x, y, width, height, Color::from_argb_uint8(alpha, red, green, blue)); + }, + .draw_texture = + [](void *self, int16_t x, int16_t y, int16_t width, int16_t handle, + const uint8_t *src, uint16_t src_stride, int16_t src_width, + int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, + uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, + uint8_t alpha, uint8_t rotation) { + auto *buffer = reinterpret_cast *>(self); + return buffer->draw_texture(x, y, width, handle, src, src_stride, src_width, + src_height, src_pixel_format, src_dx, src_dy, + src_off_x, src_off_y, colorize, alpha, + rotation); } }; auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 0e80a9e1e14..291511f3745 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -384,6 +384,25 @@ mod software_renderer { u8, u8, ) -> bool, + draw_texture: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + i16, + i16, + i16, + i16, + *const u8, + u16, + i16, + i16, + u8, + u16, + u16, + u16, + u16, + u32, + u8, + u8, + ) -> bool, } impl TargetPixelBuffer for CppRgb8TargetPixelBuffer { @@ -424,6 +443,48 @@ mod software_renderer { ) } } + + fn draw_texture( + &mut self, + x: i16, + y: i16, + width: i16, + height: i16, + src: *const u8, + src_stride: u16, + src_width: i16, + src_height: i16, + src_pixel_format: u8, + dx: u16, + dy: u16, + off_x: u16, + off_y: u16, + colorize: u32, + alpha: u8, + rotation: u8, + ) -> bool { + unsafe { + (self.draw_texture)( + self.user_data, + x, + y, + width, + height, + src, + src_stride, + src_width, + src_height, + src_pixel_format, + dx, + dy, + off_x, + off_y, + colorize, + alpha, + rotation, + ) + } + } } #[repr(C)] @@ -447,6 +508,25 @@ mod software_renderer { u8, u8, ) -> bool, + draw_texture: unsafe extern "C" fn( + CppTargetPixelBufferUserData, + i16, + i16, + i16, + i16, + *const u8, + u16, + i16, + i16, + u8, + u16, + u16, + u16, + u16, + u32, + u8, + u8, + ) -> bool, } impl TargetPixelBuffer for CppRgb565TargetPixelBuffer { @@ -487,6 +567,48 @@ mod software_renderer { ) } } + + fn draw_texture( + &mut self, + x: i16, + y: i16, + width: i16, + height: i16, + src: *const u8, + src_stride: u16, + src_width: i16, + src_height: i16, + src_pixel_format: u8, + dx: u16, + dy: u16, + off_x: u16, + off_y: u16, + colorize: u32, + alpha: u8, + rotation: u8, + ) -> bool { + unsafe { + (self.draw_texture)( + self.user_data, + x, + y, + width, + height, + src, + src_stride, + src_width, + src_height, + src_pixel_format, + dx, + dy, + off_x, + off_y, + colorize, + alpha, + rotation, + ) + } + } } #[no_mangle] diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index ba561bae618..d308c6781e8 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -394,6 +394,31 @@ mod private_api { ) -> bool { false } + + /// Draw a texture into the buffer at the specified position with the given size and + /// colorized if needed. Returns true if the operation was successful; false if it could not be + /// implemented and instead the software renderer needs to draw the texture + fn draw_texture( + &mut self, + _x: i16, + _y: i16, + _width: i16, + _height: i16, + _src: *const u8, + _src_stride: u16, + _src_width: i16, + _src_height: i16, + _src_pixel_format: u8, + _src_dx: u16, + _src_dy: u16, + _src_off_x: u16, + _src_off_y: u16, + _colorize: u32, + _alpha: u8, + _rotation: u8, + ) -> bool { + false + } } } @@ -617,27 +642,14 @@ impl SoftwareRenderer { }; drop(i); - let mut bg = TargetPixel::background(); + let mut bg = PremultipliedRgbaColor::background(); // TODO: gradient background - // TODO: Use TargetPixelBuffer::fill_rectangle - TargetPixel::blend(&mut bg, background.color().into()); - let mut line = 0; - while let Some(next) = region_line_ranges( - &dirty_region, - line, - &mut renderer.actual_renderer.processor.dirty_range_cache, - ) { - for l in line..next { - for r in &renderer.actual_renderer.processor.dirty_range_cache { - renderer.actual_renderer.processor.buffer.line_slice(l as usize) - [r.start as usize..r.end as usize] - .fill(bg); - } - } - line = next; - } - + PremultipliedRgbaColor::blend(&mut bg, background.color().into()); renderer.actual_renderer.processor.dirty_region = dirty_region.clone(); + renderer.actual_renderer.processor.process_rectangle( + PhysicalRect::from_size(euclid::Size2D::new(size.width, size.height)), + bg, + ); for (component, origin) in components { crate::item_rendering::render_component_items( @@ -1241,15 +1253,38 @@ impl> RenderToBuffe } fn process_texture_impl(&mut self, geometry: PhysicalRect, texture: SceneTexture<'_>) { - self.foreach_ranges(&geometry, |line, buffer, extra_left_clip, extra_right_clip| { - draw_functions::draw_texture_line( - &geometry, - PhysicalLength::new(line), - &texture, - buffer, - extra_left_clip, - extra_right_clip, - ); + self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { + if !buffer.draw_texture( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + texture.data.as_ptr(), + texture.pixel_stride, + texture.source_size().width, + texture.source_size().height, + texture.format as u8, + texture.extra.dx.0, + texture.extra.dy.0, + texture.extra.off_x.0, + texture.extra.off_y.0, + texture.extra.colorize.as_argb_encoded(), + texture.extra.alpha, + texture.extra.rotation as u8, + ) { + let begin = rect.min_x(); + let end = rect.max_x(); + for l in rect.min_y()..rect.max_y() { + draw_functions::draw_texture_line( + &geometry, + PhysicalLength::new(l), + &texture, + &mut buffer.line_slice(l as usize)[begin as usize..end as usize], + extra_left_clip, + extra_right_clip, + ); + } + } }); } } From 40b7820c32b23a358aa7ddb0cd4f384f67debf9a Mon Sep 17 00:00:00 2001 From: Sam Cristall Date: Wed, 12 Feb 2025 15:40:21 -0700 Subject: [PATCH 04/24] Add span_y to draw_texture to fix partial texture draws --- api/cpp/include/slint-platform.h | 22 +++++++++++----------- api/cpp/platform.rs | 6 ++++++ internal/core/software_renderer.rs | 2 ++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 6cd404225f2..9c4c3ba4e51 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -549,7 +549,7 @@ struct TargetPixelBuffer virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, const RgbaColor &premultiplied_color) = 0; - virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, + virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const uint8_t *src, uint16_t src_stride, int16_t src_width, int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, @@ -726,15 +726,15 @@ class SoftwareRenderer : public AbstractRenderer Color::from_argb_uint8(alpha, red, green, blue)); }, .draw_texture = - [](void *self, int16_t x, int16_t y, int16_t width, int16_t handle, - const uint8_t *src, uint16_t src_stride, int16_t src_width, + [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, + int16_t span_y, const uint8_t *src, uint16_t src_stride, int16_t src_width, int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, uint8_t alpha, uint8_t rotation) { auto *buffer = reinterpret_cast *>(self); - return buffer->draw_texture(x, y, width, handle, src, src_stride, src_width, - src_height, src_pixel_format, src_dx, src_dy, - src_off_x, src_off_y, colorize, alpha, + return buffer->draw_texture(x, y, width, height, span_y, src, src_stride, + src_width, src_height, src_pixel_format, src_dx, + src_dy, src_off_x, src_off_y, colorize, alpha, rotation); } }; @@ -768,15 +768,15 @@ class SoftwareRenderer : public AbstractRenderer Color::from_argb_uint8(alpha, red, green, blue)); }, .draw_texture = - [](void *self, int16_t x, int16_t y, int16_t width, int16_t handle, - const uint8_t *src, uint16_t src_stride, int16_t src_width, + [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, + int16_t span_y, const uint8_t *src, uint16_t src_stride, int16_t src_width, int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, uint8_t alpha, uint8_t rotation) { auto *buffer = reinterpret_cast *>(self); - return buffer->draw_texture(x, y, width, handle, src, src_stride, src_width, - src_height, src_pixel_format, src_dx, src_dy, - src_off_x, src_off_y, colorize, alpha, + return buffer->draw_texture(x, y, width, height, span_y, src, src_stride, + src_width, src_height, src_pixel_format, src_dx, + src_dy, src_off_x, src_off_y, colorize, alpha, rotation); } }; diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 291511f3745..5d55cba94cf 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -390,6 +390,7 @@ mod software_renderer { i16, i16, i16, + i16, *const u8, u16, i16, @@ -450,6 +451,7 @@ mod software_renderer { y: i16, width: i16, height: i16, + span_y: i16, src: *const u8, src_stride: u16, src_width: i16, @@ -470,6 +472,7 @@ mod software_renderer { y, width, height, + span_y, src, src_stride, src_width, @@ -514,6 +517,7 @@ mod software_renderer { i16, i16, i16, + i16, *const u8, u16, i16, @@ -574,6 +578,7 @@ mod software_renderer { y: i16, width: i16, height: i16, + span_y: i16, src: *const u8, src_stride: u16, src_width: i16, @@ -594,6 +599,7 @@ mod software_renderer { y, width, height, + span_y, src, src_stride, src_width, diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index d308c6781e8..55bd3cabb77 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -404,6 +404,7 @@ mod private_api { _y: i16, _width: i16, _height: i16, + _span_y: i16, _src: *const u8, _src_stride: u16, _src_width: i16, @@ -1259,6 +1260,7 @@ impl> RenderToBuffe rect.origin.y, rect.size.width, rect.size.height, + geometry.origin.y, texture.data.as_ptr(), texture.pixel_stride, texture.source_size().width, From 4d2e9806b8f8f1ee09c6215d9d80d0b709b54d88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 09:16:48 +0100 Subject: [PATCH 05/24] Change TargetPixelBuffer's draw_texture function to take a data structure instead of many arguments --- api/cpp/cbindgen.rs | 2 +- api/cpp/include/slint-platform.h | 75 ++++++++++---- api/cpp/platform.rs | 98 ++++++++----------- internal/compiler/generator/rust.rs | 8 +- internal/core/graphics/image.rs | 28 +++--- internal/core/software_renderer.rs | 76 +++++++++----- .../core/software_renderer/draw_functions.rs | 16 +-- internal/core/software_renderer/scene.rs | 14 +-- 8 files changed, 183 insertions(+), 134 deletions(-) diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index c08cf37f839..8e5ec8701f1 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -897,7 +897,7 @@ fn gen_platform( .with_after_include( r" namespace slint::platform { struct Rgb565Pixel; } -namespace slint::cbindgen_private { struct WindowProperties; using slint::platform::Rgb565Pixel; } +namespace slint::cbindgen_private { struct WindowProperties; using slint::platform::Rgb565Pixel; using slint::cbindgen_private::types::TexturePixelFormat; } ", ) .generate() diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 9c4c3ba4e51..87b75d2ba55 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -539,6 +539,27 @@ struct Rgb565Pixel }; # ifdef SLINT_FEATURE_EXPERIMENTAL + +using cbindgen_private::types::TexturePixelFormat; + +/// This structure describes the properties of a texture for blending with +/// TargetPixelBuffer::draw_texture(). +struct Texture +{ + /// A reference to the pixel bytes of the texture. These bytes are in the format specified by + /// `pixel_format`. + std::span bytes; + /// The pixel format of the texture. + TexturePixelFormat pixel_format; + uint16_t pixel_stride; + uint16_t width; + uint16_t height; + uint16_t delta_x; + uint16_t delta_y; + uint16_t source_offset_x; + uint16_t source_offset_y; +}; + template struct TargetPixelBuffer { @@ -550,10 +571,8 @@ struct TargetPixelBuffer virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, const RgbaColor &premultiplied_color) = 0; virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, - const uint8_t *src, uint16_t src_stride, int16_t src_width, - int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, - uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, - uint32_t colorize, uint8_t alpha, uint8_t rotation) = 0; + const Texture &texture, uint32_t colorize, uint8_t alpha, + uint8_t rotation) = 0; }; # endif @@ -727,15 +746,23 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - int16_t span_y, const uint8_t *src, uint16_t src_stride, int16_t src_width, - int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, - uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, - uint8_t alpha, uint8_t rotation) { + int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, + uint32_t colorize, uint8_t alpha, uint8_t rotation) { auto *buffer = reinterpret_cast *>(self); - return buffer->draw_texture(x, y, width, height, span_y, src, src_stride, - src_width, src_height, src_pixel_format, src_dx, - src_dy, src_off_x, src_off_y, colorize, alpha, - rotation); + Texture texture { + .bytes = std::span { internal_texture->bytes, + internal_texture->bytes_len }, + .pixel_format = internal_texture->pixel_format, + .pixel_stride = internal_texture->pixel_stride, + .width = internal_texture->width, + .height = internal_texture->height, + .delta_x = internal_texture->delta_x, + .delta_y = internal_texture->delta_y, + .source_offset_x = internal_texture->source_offset_x, + .source_offset_y = internal_texture->source_offset_y, + }; + return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, + alpha, rotation); } }; auto r = @@ -769,15 +796,23 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - int16_t span_y, const uint8_t *src, uint16_t src_stride, int16_t src_width, - int16_t src_height, uint8_t src_pixel_format, uint16_t src_dx, - uint16_t src_dy, uint16_t src_off_x, uint16_t src_off_y, uint32_t colorize, - uint8_t alpha, uint8_t rotation) { + int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, + uint32_t colorize, uint8_t alpha, uint8_t rotation) { auto *buffer = reinterpret_cast *>(self); - return buffer->draw_texture(x, y, width, height, span_y, src, src_stride, - src_width, src_height, src_pixel_format, src_dx, - src_dy, src_off_x, src_off_y, colorize, alpha, - rotation); + Texture texture { + .bytes = std::span { internal_texture->bytes, + internal_texture->bytes_len }, + .pixel_format = internal_texture->pixel_format, + .pixel_stride = internal_texture->pixel_stride, + .width = internal_texture->width, + .height = internal_texture->height, + .delta_x = internal_texture->delta_x, + .delta_y = internal_texture->delta_y, + .source_offset_x = internal_texture->source_offset_x, + .source_offset_y = internal_texture->source_offset_y, + }; + return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, + alpha, rotation); } }; auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 5d55cba94cf..fc162de2173 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -357,12 +357,26 @@ mod software_renderer { use i_slint_core::graphics::{IntRect, Rgb8Pixel}; use i_slint_core::software_renderer::{ PhysicalRegion, PremultipliedRgbaColor, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, - TargetPixelBuffer, + TargetPixelBuffer, Texture, TexturePixelFormat, }; use i_slint_core::SharedVector; type CppTargetPixelBufferUserData = *mut c_void; + #[repr(C)] + pub struct CppInternalTexture { + pub bytes: *const u8, + pub bytes_len: usize, + pub pixel_format: TexturePixelFormat, + pub pixel_stride: u16, + pub width: u16, + pub height: u16, + pub delta_x: u16, + pub delta_y: u16, + pub source_offset_x: u16, + pub source_offset_y: u16, + } + #[repr(C)] pub struct CppRgb8TargetPixelBuffer { user_data: CppTargetPixelBufferUserData, @@ -391,15 +405,7 @@ mod software_renderer { i16, i16, i16, - *const u8, - u16, - i16, - i16, - u8, - u16, - u16, - u16, - u16, + &CppInternalTexture, u32, u8, u8, @@ -452,15 +458,7 @@ mod software_renderer { width: i16, height: i16, span_y: i16, - src: *const u8, - src_stride: u16, - src_width: i16, - src_height: i16, - src_pixel_format: u8, - dx: u16, - dy: u16, - off_x: u16, - off_y: u16, + texture: Texture<'_>, colorize: u32, alpha: u8, rotation: u8, @@ -473,15 +471,18 @@ mod software_renderer { width, height, span_y, - src, - src_stride, - src_width, - src_height, - src_pixel_format, - dx, - dy, - off_x, - off_y, + &CppInternalTexture { + bytes: texture.bytes.as_ptr(), + bytes_len: texture.bytes.len(), + pixel_format: texture.pixel_format, + pixel_stride: texture.pixel_stride, + width: texture.width, + height: texture.height, + delta_x: texture.delta_x, + delta_y: texture.delta_y, + source_offset_x: texture.source_offset_x, + source_offset_y: texture.source_offset_y, + }, colorize, alpha, rotation, @@ -518,15 +519,7 @@ mod software_renderer { i16, i16, i16, - *const u8, - u16, - i16, - i16, - u8, - u16, - u16, - u16, - u16, + &CppInternalTexture, u32, u8, u8, @@ -579,15 +572,7 @@ mod software_renderer { width: i16, height: i16, span_y: i16, - src: *const u8, - src_stride: u16, - src_width: i16, - src_height: i16, - src_pixel_format: u8, - dx: u16, - dy: u16, - off_x: u16, - off_y: u16, + texture: Texture<'_>, colorize: u32, alpha: u8, rotation: u8, @@ -600,15 +585,18 @@ mod software_renderer { width, height, span_y, - src, - src_stride, - src_width, - src_height, - src_pixel_format, - dx, - dy, - off_x, - off_y, + &CppInternalTexture { + bytes: texture.bytes.as_ptr(), + bytes_len: texture.bytes.len(), + pixel_format: texture.pixel_format, + pixel_stride: texture.pixel_stride, + width: texture.width, + height: texture.height, + delta_x: texture.delta_x, + delta_y: texture.delta_y, + source_offset_x: texture.source_offset_x, + source_offset_y: texture.source_offset_y, + }, colorize, alpha, rotation, diff --git a/internal/compiler/generator/rust.rs b/internal/compiler/generator/rust.rs index 663b27a4699..e9ad4edb039 100644 --- a/internal/compiler/generator/rust.rs +++ b/internal/compiler/generator/rust.rs @@ -65,12 +65,12 @@ impl quote::ToTokens for crate::embedded_resources::PixelFormat { fn to_tokens(&self, tokens: &mut TokenStream) { use crate::embedded_resources::PixelFormat::*; let tks = match self { - Rgb => quote!(sp::PixelFormat::Rgb), - Rgba => quote!(sp::PixelFormat::Rgba), + Rgb => quote!(sp::TexturePixelFormat::Rgb), + Rgba => quote!(sp::TexturePixelFormat::Rgba), RgbaPremultiplied => { - quote!(sp::PixelFormat::RgbaPremultiplied) + quote!(sp::TexturePixelFormat::RgbaPremultiplied) } - AlphaMap(_) => quote!(sp::PixelFormat::AlphaMap), + AlphaMap(_) => quote!(sp::TexturePixelFormat::AlphaMap), }; tokens.extend(tks); } diff --git a/internal/core/graphics/image.rs b/internal/core/graphics/image.rs index f7ebf629452..fe4127fe545 100644 --- a/internal/core/graphics/image.rs +++ b/internal/core/graphics/image.rs @@ -224,8 +224,8 @@ impl PartialEq for SharedImageBuffer { #[repr(u8)] #[derive(Clone, PartialEq, Debug, Copy)] -/// The pixel format of a StaticTexture -pub enum PixelFormat { +/// The pixel format used for textures. +pub enum TexturePixelFormat { /// red, green, blue. 24bits. Rgb, /// Red, green, blue, alpha. 32bits. @@ -241,15 +241,15 @@ pub enum PixelFormat { SignedDistanceField, } -impl PixelFormat { +impl TexturePixelFormat { /// The number of bytes in a pixel pub fn bpp(self) -> usize { match self { - PixelFormat::Rgb => 3, - PixelFormat::Rgba => 4, - PixelFormat::RgbaPremultiplied => 4, - PixelFormat::AlphaMap => 1, - PixelFormat::SignedDistanceField => 1, + TexturePixelFormat::Rgb => 3, + TexturePixelFormat::Rgba => 4, + TexturePixelFormat::RgbaPremultiplied => 4, + TexturePixelFormat::AlphaMap => 1, + TexturePixelFormat::SignedDistanceField => 1, } } } @@ -261,7 +261,7 @@ pub struct StaticTexture { /// The position and size of the texture within the image pub rect: IntRect, /// The pixel format of this texture - pub format: PixelFormat, + pub format: TexturePixelFormat, /// The color, for the alpha map ones pub color: crate::Color, /// index in the data array @@ -435,7 +435,7 @@ impl ImageInner { let slice = &mut slice[(rect.min_y() + y) * stride..][rect.x_range()]; let source = &ts.data[t.index + y * rect.width() * t.format.bpp()..]; match t.format { - PixelFormat::Rgb => { + TexturePixelFormat::Rgb => { let mut iter = source.chunks_exact(3).map(|p| Rgba8Pixel { r: p[0], g: p[1], @@ -444,7 +444,7 @@ impl ImageInner { }); slice.fill_with(|| iter.next().unwrap()); } - PixelFormat::RgbaPremultiplied => { + TexturePixelFormat::RgbaPremultiplied => { let mut iter = source.chunks_exact(4).map(|p| Rgba8Pixel { r: p[0], g: p[1], @@ -453,7 +453,7 @@ impl ImageInner { }); slice.fill_with(|| iter.next().unwrap()); } - PixelFormat::Rgba => { + TexturePixelFormat::Rgba => { let mut iter = source.chunks_exact(4).map(|p| { let a = p[3]; Rgba8Pixel { @@ -465,7 +465,7 @@ impl ImageInner { }); slice.fill_with(|| iter.next().unwrap()); } - PixelFormat::AlphaMap => { + TexturePixelFormat::AlphaMap => { let col = t.color.to_argb_u8(); let mut iter = source.iter().map(|p| { let a = *p as u32 * col.alpha as u32; @@ -478,7 +478,7 @@ impl ImageInner { }); slice.fill_with(|| iter.next().unwrap()); } - PixelFormat::SignedDistanceField => { + TexturePixelFormat::SignedDistanceField => { todo!("converting from a signed distance field to an image") } }; diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 55bd3cabb77..9266fc90e0f 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -18,9 +18,7 @@ pub use self::minimal_software_window::MinimalSoftwareWindow; use self::scene::*; use crate::api::PlatformError; use crate::graphics::rendering_metrics_collector::{RefreshMode, RenderingMetricsCollector}; -use crate::graphics::{ - BorderRadius, PixelFormat, Rgba8Pixel, SharedImageBuffer, SharedPixelBuffer, -}; +use crate::graphics::{BorderRadius, Rgba8Pixel, SharedImageBuffer, SharedPixelBuffer}; use crate::item_rendering::{ CachedRenderingData, DirtyRegion, PartialRenderingState, RenderBorderRectangle, RenderImage, RenderRectangle, @@ -368,6 +366,36 @@ fn region_line_ranges( mod private_api { use super::*; + pub use crate::graphics::TexturePixelFormat; + + /// This structure describes the properties of a texture for blending with [`TargetPixelBuffer::draw_texture`]. + #[allow(dead_code)] + #[non_exhaustive] + pub struct Texture<'a> { + /// A reference to the pixel bytes of the texture. These bytes are in the format specified by `pixel_format`. + pub bytes: &'a [u8], + /// The pixel format of the texture. + pub pixel_format: TexturePixelFormat, + /// The number of pixels per horizontal line of the texture. + pub pixel_stride: u16, + /// The width of the texture in pixels. + pub width: u16, + /// The height of the texture in pixels. + pub height: u16, + /// The delta to apply to the source x coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. + pub delta_x: u16, + /// The delta to apply to the source y coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. + pub delta_y: u16, + /// The offset within the texture to start reading pixels from in the x direction. The + /// offset is specified in 8:8 fixed point format. + pub source_offset_x: u16, + /// The offset within the texture to start reading pixels from in the y direction. The + /// offset is specified in 8:8 fixed point format. + pub source_offset_y: u16, + } + /// This trait represents access to a buffer of pixels the software renderer can render into, as well /// as certain operations that the renderer will try to delegate to this trait. Implement these functions /// to delegate rendering further to hardware-provided 2D acceleration units, such as DMA2D or PXP. @@ -405,15 +433,7 @@ mod private_api { _width: i16, _height: i16, _span_y: i16, - _src: *const u8, - _src_stride: u16, - _src_width: i16, - _src_height: i16, - _src_pixel_format: u8, - _src_dx: u16, - _src_dy: u16, - _src_off_x: u16, - _src_off_y: u16, + _src_texture: Texture<'_>, _colorize: u32, _alpha: u8, _rotation: u8, @@ -424,7 +444,10 @@ mod private_api { } #[cfg(feature = "experimental")] -pub use private_api::TargetPixelBuffer; +pub use private_api::{TargetPixelBuffer, Texture, TexturePixelFormat}; + +#[cfg(not(feature = "experimental"))] +use private_api::TexturePixelFormat; struct TargetPixelSlice<'a, T> { data: &'a mut [T], @@ -1261,15 +1284,17 @@ impl> RenderToBuffe rect.size.width, rect.size.height, geometry.origin.y, - texture.data.as_ptr(), - texture.pixel_stride, - texture.source_size().width, - texture.source_size().height, - texture.format as u8, - texture.extra.dx.0, - texture.extra.dy.0, - texture.extra.off_x.0, - texture.extra.off_y.0, + private_api::Texture { + bytes: texture.data, + pixel_format: texture.format, + pixel_stride: texture.pixel_stride, + width: texture.source_size().width as u16, + height: texture.source_size().height as u16, + delta_x: texture.extra.dx.0, + delta_y: texture.extra.dy.0, + source_offset_x: texture.extra.off_x.0, + source_offset_y: texture.extra.off_y.0, + }, texture.extra.colorize.as_argb_encoded(), texture.extra.alpha, texture.extra.rotation as u8, @@ -1545,7 +1570,8 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { let bpp = t.format.bpp(); let color = if colorize.alpha() > 0 { colorize } else { t.color }; - let alpha = if colorize.alpha() > 0 || t.format == PixelFormat::AlphaMap { + let alpha = if colorize.alpha() > 0 || t.format == TexturePixelFormat::AlphaMap + { color.alpha() as u16 * global_alpha_u16 / 255 } else { global_alpha_u16 @@ -1719,7 +1745,7 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { SceneTexture { data, pixel_stride, - format: PixelFormat::AlphaMap, + format: TexturePixelFormat::AlphaMap, extra: SceneTextureExtra { colorize: color, // color already is mixed with global alpha @@ -1750,7 +1776,7 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { SceneTexture { data, pixel_stride, - format: PixelFormat::SignedDistanceField, + format: TexturePixelFormat::SignedDistanceField, extra: SceneTextureExtra { colorize: color, // color already is mixed with global alpha diff --git a/internal/core/software_renderer/draw_functions.rs b/internal/core/software_renderer/draw_functions.rs index acb19e42cbe..07666f790da 100644 --- a/internal/core/software_renderer/draw_functions.rs +++ b/internal/core/software_renderer/draw_functions.rs @@ -7,7 +7,7 @@ //! on the line buffer use super::{Fixed, PhysicalLength, PhysicalRect}; -use crate::graphics::{PixelFormat, Rgb8Pixel}; +use crate::graphics::{Rgb8Pixel, TexturePixelFormat}; use crate::lengths::{PointLengths, SizeLengths}; use crate::Color; use derive_more::{Add, Mul, Sub}; @@ -183,7 +183,7 @@ pub(super) fn draw_texture_line( fn fetch_blend_pixel( line_buffer: &mut [impl TargetPixel], - format: PixelFormat, + format: TexturePixelFormat, data: &[u8], alpha: u8, color: Color, @@ -191,10 +191,10 @@ pub(super) fn draw_texture_line( mut pos: impl FnMut(usize) -> (usize, u8, u8), ) { match format { - PixelFormat::Rgb => { + TexturePixelFormat::Rgb => { for pix in line_buffer { let pos = pos(3).0; - let p = &data[pos..pos + 3]; + let p: &[u8] = &data[pos..pos + 3]; if alpha == 0xff { *pix = TargetPixel::from_rgb(p[0], p[1], p[2]); } else { @@ -204,7 +204,7 @@ pub(super) fn draw_texture_line( } } } - PixelFormat::Rgba => { + TexturePixelFormat::Rgba => { if color.alpha() == 0 { for pix in line_buffer { let pos = pos(4).0; @@ -231,7 +231,7 @@ pub(super) fn draw_texture_line( } } } - PixelFormat::RgbaPremultiplied => { + TexturePixelFormat::RgbaPremultiplied => { if color.alpha() > 0 { for pix in line_buffer { let pos = pos(4).0; @@ -267,7 +267,7 @@ pub(super) fn draw_texture_line( } } } - PixelFormat::AlphaMap => { + TexturePixelFormat::AlphaMap => { for pix in line_buffer { let pos = pos(1).0; let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( @@ -279,7 +279,7 @@ pub(super) fn draw_texture_line( pix.blend(c); } } - PixelFormat::SignedDistanceField => { + TexturePixelFormat::SignedDistanceField => { const RANGE: i32 = 6; let factor = (362 * 256 / delta.0) * RANGE; // 362 ≃ 255 * sqrt(2) for pix in line_buffer { diff --git a/internal/core/software_renderer/scene.rs b/internal/core/software_renderer/scene.rs index 68c62f7416b..cecde03ec5d 100644 --- a/internal/core/software_renderer/scene.rs +++ b/internal/core/software_renderer/scene.rs @@ -7,7 +7,7 @@ use super::{ Fixed, PhysicalBorderRadius, PhysicalLength, PhysicalPoint, PhysicalRect, PhysicalRegion, PhysicalSize, PremultipliedRgbaColor, RenderingRotation, }; -use crate::graphics::{PixelFormat, SharedImageBuffer}; +use crate::graphics::{SharedImageBuffer, TexturePixelFormat}; use crate::lengths::{PointLengths as _, SizeLengths as _}; use crate::Color; use alloc::rc::Rc; @@ -284,7 +284,7 @@ pub enum SceneCommand { pub struct SceneTexture<'a> { /// This should have a size so that the entire slice is ((height - 1) * pixel_stride + width) * bpp pub data: &'a [u8], - pub format: PixelFormat, + pub format: TexturePixelFormat, /// number of pixels between two lines in the source pub pixel_stride: u16, @@ -294,7 +294,7 @@ pub struct SceneTexture<'a> { impl SceneTexture<'_> { pub fn source_size(&self) -> PhysicalSize { let mut len = self.data.len(); - if self.format == PixelFormat::SignedDistanceField { + if self.format == TexturePixelFormat::SignedDistanceField { len -= 1; } else { len /= self.format.bpp(); @@ -355,27 +355,27 @@ impl SharedBufferCommand { SharedBufferData::SharedImage(SharedImageBuffer::RGB8(b)) => SceneTexture { data: &b.as_bytes()[start * 3..end * 3], pixel_stride: stride as u16, - format: PixelFormat::Rgb, + format: TexturePixelFormat::Rgb, extra: self.extra, }, SharedBufferData::SharedImage(SharedImageBuffer::RGBA8(b)) => SceneTexture { data: &b.as_bytes()[start * 4..end * 4], pixel_stride: stride as u16, - format: PixelFormat::Rgba, + format: TexturePixelFormat::Rgba, extra: self.extra, }, SharedBufferData::SharedImage(SharedImageBuffer::RGBA8Premultiplied(b)) => { SceneTexture { data: &b.as_bytes()[start * 4..end * 4], pixel_stride: stride as u16, - format: PixelFormat::RgbaPremultiplied, + format: TexturePixelFormat::RgbaPremultiplied, extra: self.extra, } } SharedBufferData::AlphaMap { data, width } => SceneTexture { data: &data[start..end], pixel_stride: *width, - format: PixelFormat::AlphaMap, + format: TexturePixelFormat::AlphaMap, extra: self.extra, }, } From a33605a730d724aa5fb70f3726be7e4fad7415b6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 17:16:09 +0100 Subject: [PATCH 06/24] Expose screen rotation in the TargetPixelBuffer's draw_texture as integral angle --- api/cpp/include/slint-platform.h | 10 +++++----- api/cpp/platform.rs | 16 ++++++++-------- internal/core/software_renderer.rs | 6 +++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 87b75d2ba55..4b0be6745b0 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -572,7 +572,7 @@ struct TargetPixelBuffer const RgbaColor &premultiplied_color) = 0; virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const Texture &texture, uint32_t colorize, uint8_t alpha, - uint8_t rotation) = 0; + int screen_rotation_degrees) = 0; }; # endif @@ -747,7 +747,7 @@ class SoftwareRenderer : public AbstractRenderer .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, - uint32_t colorize, uint8_t alpha, uint8_t rotation) { + uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { auto *buffer = reinterpret_cast *>(self); Texture texture { .bytes = std::span { internal_texture->bytes, @@ -762,7 +762,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_y = internal_texture->source_offset_y, }; return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, - alpha, rotation); + alpha, screen_rotation_degrees); } }; auto r = @@ -797,7 +797,7 @@ class SoftwareRenderer : public AbstractRenderer .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, - uint32_t colorize, uint8_t alpha, uint8_t rotation) { + uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { auto *buffer = reinterpret_cast *>(self); Texture texture { .bytes = std::span { internal_texture->bytes, @@ -812,7 +812,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_y = internal_texture->source_offset_y, }; return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, - alpha, rotation); + alpha, screen_rotation_degrees); } }; auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index fc162de2173..5a1eace2d52 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -356,8 +356,8 @@ mod software_renderer { type SoftwareRendererOpaque = *const c_void; use i_slint_core::graphics::{IntRect, Rgb8Pixel}; use i_slint_core::software_renderer::{ - PhysicalRegion, PremultipliedRgbaColor, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, - TargetPixelBuffer, Texture, TexturePixelFormat, + PhysicalRegion, PremultipliedRgbaColor, RenderingRotation, RepaintBufferType, Rgb565Pixel, + SoftwareRenderer, TargetPixelBuffer, Texture, TexturePixelFormat, }; use i_slint_core::SharedVector; @@ -408,7 +408,7 @@ mod software_renderer { &CppInternalTexture, u32, u8, - u8, + i32, ) -> bool, } @@ -461,7 +461,7 @@ mod software_renderer { texture: Texture<'_>, colorize: u32, alpha: u8, - rotation: u8, + rotation: RenderingRotation, ) -> bool { unsafe { (self.draw_texture)( @@ -485,7 +485,7 @@ mod software_renderer { }, colorize, alpha, - rotation, + rotation.angle() as i32, ) } } @@ -522,7 +522,7 @@ mod software_renderer { &CppInternalTexture, u32, u8, - u8, + i32, ) -> bool, } @@ -575,7 +575,7 @@ mod software_renderer { texture: Texture<'_>, colorize: u32, alpha: u8, - rotation: u8, + rotation: RenderingRotation, ) -> bool { unsafe { (self.draw_texture)( @@ -599,7 +599,7 @@ mod software_renderer { }, colorize, alpha, - rotation, + rotation.angle() as i32, ) } } diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 9266fc90e0f..8c8940ae40d 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -80,7 +80,7 @@ impl RenderingRotation { matches!(self, Self::Rotate90 | Self::Rotate180) } /// Angle of the rotation in degrees - fn angle(self) -> f32 { + pub fn angle(self) -> f32 { match self { RenderingRotation::NoRotation => 0., RenderingRotation::Rotate90 => 90., @@ -436,7 +436,7 @@ mod private_api { _src_texture: Texture<'_>, _colorize: u32, _alpha: u8, - _rotation: u8, + _rotation: RenderingRotation, ) -> bool { false } @@ -1297,7 +1297,7 @@ impl> RenderToBuffe }, texture.extra.colorize.as_argb_encoded(), texture.extra.alpha, - texture.extra.rotation as u8, + texture.extra.rotation, ) { let begin = rect.min_x(); let end = rect.max_x(); From 8d17ad87606a30692688c04d5a590e12b72966a3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 17:20:13 +0100 Subject: [PATCH 07/24] Fix the build with the experimental feature turned off --- api/cpp/platform.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 5a1eace2d52..32ff11e3421 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -356,14 +356,20 @@ mod software_renderer { type SoftwareRendererOpaque = *const c_void; use i_slint_core::graphics::{IntRect, Rgb8Pixel}; use i_slint_core::software_renderer::{ - PhysicalRegion, PremultipliedRgbaColor, RenderingRotation, RepaintBufferType, Rgb565Pixel, - SoftwareRenderer, TargetPixelBuffer, Texture, TexturePixelFormat, + PhysicalRegion, RepaintBufferType, Rgb565Pixel, SoftwareRenderer, }; use i_slint_core::SharedVector; + #[cfg(feature = "experimental")] + use i_slint_core::software_renderer::{ + PremultipliedRgbaColor, RenderingRotation, TargetPixelBuffer, Texture, TexturePixelFormat, + }; + + #[cfg(feature = "experimental")] type CppTargetPixelBufferUserData = *mut c_void; #[repr(C)] + #[cfg(feature = "experimental")] pub struct CppInternalTexture { pub bytes: *const u8, pub bytes_len: usize, @@ -378,6 +384,7 @@ mod software_renderer { } #[repr(C)] + #[cfg(feature = "experimental")] pub struct CppRgb8TargetPixelBuffer { user_data: CppTargetPixelBufferUserData, line_slice: unsafe extern "C" fn( @@ -412,6 +419,7 @@ mod software_renderer { ) -> bool, } + #[cfg(feature = "experimental")] impl TargetPixelBuffer for CppRgb8TargetPixelBuffer { type Pixel = Rgb8Pixel; @@ -491,6 +499,7 @@ mod software_renderer { } } + #[cfg(feature = "experimental")] #[repr(C)] pub struct CppRgb565TargetPixelBuffer { user_data: CppTargetPixelBufferUserData, @@ -526,6 +535,7 @@ mod software_renderer { ) -> bool, } + #[cfg(feature = "experimental")] impl TargetPixelBuffer for CppRgb565TargetPixelBuffer { type Pixel = Rgb565Pixel; From bd3c11fddcd2e7313d48d6efe6a5ce2350b4391b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 17:27:27 +0100 Subject: [PATCH 08/24] Some C++ docs for TargetPixelBuffer --- api/cpp/include/slint-platform.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 4b0be6745b0..1fc1ca5b778 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -560,16 +560,29 @@ struct Texture uint16_t source_offset_y; }; +/// Abstract base class for a target pixel buffer where certain drawing operations can be delegated. +/// Use this to implement support for hardware accelerators such as DMA2D, PPA, or PXP on +/// Microcontrollers. template struct TargetPixelBuffer { virtual ~TargetPixelBuffer() { } + /// Returns a span of pixels for the specified line number. virtual std::span line_slice(std::size_t line_number) = 0; + /// Returns the number of lines in the buffer. This is the height of the buffer in pixels. virtual std::size_t num_lines() = 0; + /// Fill a rectangle at the specified pixel coordinates with the given color. Return true + /// if the operation succeeded; false otherwise; virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, const RgbaColor &premultiplied_color) = 0; + + /// Draw a portion of provided texture to the specified pixel coordinates. This may + /// be called multiple times for a given texture, for example when clipping. Typically + /// y is identicaly with span_y, but when clipping, span_y refers to the y coordinate of + /// the texture if it's unclipped. Each pixel of the texture is to be blended with the given + /// colorize color as well as the alpha value. virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const Texture &texture, uint32_t colorize, uint8_t alpha, int screen_rotation_degrees) = 0; From d2edf0526d8083657d53e796b8c4560382b24b4e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 17:29:12 +0100 Subject: [PATCH 09/24] Improve API for C++ TargetPixelBuffer::draw_texture's blending color --- api/cpp/include/slint-platform.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 1fc1ca5b778..fff01e7b7d4 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -584,8 +584,8 @@ struct TargetPixelBuffer /// the texture if it's unclipped. Each pixel of the texture is to be blended with the given /// colorize color as well as the alpha value. virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, - const Texture &texture, uint32_t colorize, uint8_t alpha, - int screen_rotation_degrees) = 0; + const Texture &texture, const RgbaColor &colorize, + uint8_t alpha, int screen_rotation_degrees) = 0; }; # endif @@ -774,8 +774,9 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, - alpha, screen_rotation_degrees); + return buffer->draw_texture(x, y, width, height, span_y, texture, + Color::from_argb_encoded(colorize), alpha, + screen_rotation_degrees); } }; auto r = @@ -824,8 +825,9 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, span_y, texture, colorize, - alpha, screen_rotation_degrees); + return buffer->draw_texture(x, y, width, height, span_y, texture, + Color::from_argb_encoded(colorize), alpha, + screen_rotation_degrees); } }; auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, From 1694349a978fd806e10758232c91cac662b8bf8d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 17:43:18 +0100 Subject: [PATCH 10/24] Missing docs fix --- api/cpp/include/slint-platform.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index fff01e7b7d4..7adbdc881f6 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -732,6 +732,7 @@ class SoftwareRenderer : public AbstractRenderer } # ifdef SLINT_FEATURE_EXPERIMENTAL + /// Renders into the given TargetPixelBuffer. PhysicalRegion render(TargetPixelBuffer *buffer) const { cbindgen_private::CppRgb8TargetPixelBuffer buffer_wrapper { @@ -783,6 +784,8 @@ class SoftwareRenderer : public AbstractRenderer cbindgen_private::slint_software_renderer_render_accel_rgb8(inner, &buffer_wrapper); return PhysicalRegion { r }; } + + /// Renders into the given TargetPixelBuffer. PhysicalRegion render(TargetPixelBuffer *buffer) const { cbindgen_private::CppRgb565TargetPixelBuffer buffer_wrapper { From 64764dfea2c78c3eba59261b1f66cbf7f8028b80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 18:04:27 +0100 Subject: [PATCH 11/24] More docs --- api/cpp/include/slint-platform.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 7adbdc881f6..d6bb8e47f58 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -551,12 +551,23 @@ struct Texture std::span bytes; /// The pixel format of the texture. TexturePixelFormat pixel_format; + /// The number of pixels per line. uint16_t pixel_stride; + /// The width of the texture in pixels. uint16_t width; + /// The height of the texture in pixels. uint16_t height; + /// The delta to apply to the source x coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. uint16_t delta_x; + /// The delta to apply to the source y coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. uint16_t delta_y; + /// The offset within the texture to start reading pixels from in the x direction. The + /// offset is specified in 8:8 fixed point format. uint16_t source_offset_x; + /// The offset within the texture to start reading pixels from in the y direction. The + /// offset is specified in 8:8 fixed point format. uint16_t source_offset_y; }; From e4b143e8368371ad1f8177e33fb60e886ad665cb Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 17:06:22 +0000 Subject: [PATCH 12/24] [autofix.ci] apply automated fixes --- api/cpp/include/slint-platform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index d6bb8e47f58..27eb25bf151 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -558,7 +558,7 @@ struct Texture /// The height of the texture in pixels. uint16_t height; /// The delta to apply to the source x coordinate between pixels when drawing the texture. - /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. uint16_t delta_x; /// The delta to apply to the source y coordinate between pixels when drawing the texture. /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. @@ -567,7 +567,7 @@ struct Texture /// offset is specified in 8:8 fixed point format. uint16_t source_offset_x; /// The offset within the texture to start reading pixels from in the y direction. The - /// offset is specified in 8:8 fixed point format. + /// offset is specified in 8:8 fixed point format. uint16_t source_offset_y; }; From b72c507ee60921cd59ec1fe20e25d610892301ff Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 18:59:49 +0100 Subject: [PATCH 13/24] Fix invalid reinterpret_cast in render to target pixel buffer method for Rgb565Pixel --- api/cpp/include/slint-platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 27eb25bf151..9bc5ca81262 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -826,7 +826,7 @@ class SoftwareRenderer : public AbstractRenderer [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { - auto *buffer = reinterpret_cast *>(self); + auto *buffer = reinterpret_cast *>(self); Texture texture { .bytes = std::span { internal_texture->bytes, internal_texture->bytes_len }, From 06db58909b9eed49357ad5e566f34682582bcfba Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 19:01:32 +0100 Subject: [PATCH 14/24] Fix docs about fixed point format for source texture offsets --- api/cpp/include/slint-platform.h | 4 ++-- internal/core/software_renderer.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 9bc5ca81262..8b41cceebb3 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -564,10 +564,10 @@ struct Texture /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. uint16_t delta_y; /// The offset within the texture to start reading pixels from in the x direction. The - /// offset is specified in 8:8 fixed point format. + /// offset is specified in 12:4 fixed point format. uint16_t source_offset_x; /// The offset within the texture to start reading pixels from in the y direction. The - /// offset is specified in 8:8 fixed point format. + /// offset is specified in 12:4 fixed point format. uint16_t source_offset_y; }; diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 8c8940ae40d..29be80c8bf6 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -389,10 +389,10 @@ mod private_api { /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. pub delta_y: u16, /// The offset within the texture to start reading pixels from in the x direction. The - /// offset is specified in 8:8 fixed point format. + /// offset is specified in 12:4 fixed point format. pub source_offset_x: u16, /// The offset within the texture to start reading pixels from in the y direction. The - /// offset is specified in 8:8 fixed point format. + /// offset is specified in 12:4 fixed point format. pub source_offset_y: u16, } From dc214abf977ff80abc6afbdec2ec734a0e872a76 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 20 Feb 2025 19:02:41 +0100 Subject: [PATCH 15/24] Fix build for C++ generated code for embedded textures --- internal/compiler/generator/cpp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/compiler/generator/cpp.rs b/internal/compiler/generator/cpp.rs index 4928aeed7a7..21160eeb08e 100644 --- a/internal/compiler/generator/cpp.rs +++ b/internal/compiler/generator/cpp.rs @@ -942,7 +942,7 @@ fn embed_resource( init: Some(format!( "{{ .rect = {{ {r_x}, {r_y}, {r_w}, {r_h} }}, - .format = slint::cbindgen_private::types::PixelFormat::{format}, + .format = slint::cbindgen_private::types::TexturePixelFormat::{format}, .color = {color}, .index = 0, }}" From 43bccd9d45b33cb0218fc2805f231a459e5a2b63 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 10:07:53 +0100 Subject: [PATCH 16/24] Simplify accelerated texture and rectangle fills There's no need to split the areas to span lines. --- api/cpp/include/slint-platform.h | 18 ++++---- api/cpp/platform.rs | 6 --- internal/core/software_renderer.rs | 68 +++++++++++++++--------------- 3 files changed, 41 insertions(+), 51 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 8b41cceebb3..408fa047a56 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -589,12 +589,10 @@ struct TargetPixelBuffer virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, const RgbaColor &premultiplied_color) = 0; - /// Draw a portion of provided texture to the specified pixel coordinates. This may - /// be called multiple times for a given texture, for example when clipping. Typically - /// y is identicaly with span_y, but when clipping, span_y refers to the y coordinate of - /// the texture if it's unclipped. Each pixel of the texture is to be blended with the given - /// colorize color as well as the alpha value. - virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, + /// Draw a portion of provided texture to the specified pixel coordinates. + /// Each pixel of the texture is to be blended with the given colorize color as well as the + /// alpha value. + virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, const Texture &texture, const RgbaColor &colorize, uint8_t alpha, int screen_rotation_degrees) = 0; }; @@ -771,7 +769,7 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, + const cbindgen_private::CppInternalTexture *internal_texture, uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { auto *buffer = reinterpret_cast *>(self); Texture texture { @@ -786,7 +784,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, span_y, texture, + return buffer->draw_texture(x, y, width, height, texture, Color::from_argb_encoded(colorize), alpha, screen_rotation_degrees); } @@ -824,7 +822,7 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, + const cbindgen_private::CppInternalTexture *internal_texture, uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { auto *buffer = reinterpret_cast *>(self); Texture texture { @@ -839,7 +837,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, span_y, texture, + return buffer->draw_texture(x, y, width, height, texture, Color::from_argb_encoded(colorize), alpha, screen_rotation_degrees); } diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index 32ff11e3421..b5d9418c48e 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -411,7 +411,6 @@ mod software_renderer { i16, i16, i16, - i16, &CppInternalTexture, u32, u8, @@ -465,7 +464,6 @@ mod software_renderer { y: i16, width: i16, height: i16, - span_y: i16, texture: Texture<'_>, colorize: u32, alpha: u8, @@ -478,7 +476,6 @@ mod software_renderer { y, width, height, - span_y, &CppInternalTexture { bytes: texture.bytes.as_ptr(), bytes_len: texture.bytes.len(), @@ -527,7 +524,6 @@ mod software_renderer { i16, i16, i16, - i16, &CppInternalTexture, u32, u8, @@ -581,7 +577,6 @@ mod software_renderer { y: i16, width: i16, height: i16, - span_y: i16, texture: Texture<'_>, colorize: u32, alpha: u8, @@ -594,7 +589,6 @@ mod software_renderer { y, width, height, - span_y, &CppInternalTexture { bytes: texture.bytes.as_ptr(), bytes_len: texture.bytes.len(), diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 29be80c8bf6..ba267825963 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -432,7 +432,6 @@ mod private_api { _y: i16, _width: i16, _height: i16, - _span_y: i16, _src_texture: Texture<'_>, _colorize: u32, _alpha: u8, @@ -1277,28 +1276,27 @@ impl> RenderToBuffe } fn process_texture_impl(&mut self, geometry: PhysicalRect, texture: SceneTexture<'_>) { - self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { - if !buffer.draw_texture( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - geometry.origin.y, - private_api::Texture { - bytes: texture.data, - pixel_format: texture.format, - pixel_stride: texture.pixel_stride, - width: texture.source_size().width as u16, - height: texture.source_size().height as u16, - delta_x: texture.extra.dx.0, - delta_y: texture.extra.dy.0, - source_offset_x: texture.extra.off_x.0, - source_offset_y: texture.extra.off_y.0, - }, - texture.extra.colorize.as_argb_encoded(), - texture.extra.alpha, - texture.extra.rotation, - ) { + if !self.buffer.draw_texture( + geometry.origin.x, + geometry.origin.y, + geometry.size.width, + geometry.size.height, + private_api::Texture { + bytes: texture.data, + pixel_format: texture.format, + pixel_stride: texture.pixel_stride, + width: texture.source_size().width as u16, + height: texture.source_size().height as u16, + delta_x: texture.extra.dx.0, + delta_y: texture.extra.dy.0, + source_offset_x: texture.extra.off_x.0, + source_offset_y: texture.extra.off_y.0, + }, + texture.extra.colorize.as_argb_encoded(), + texture.extra.alpha, + texture.extra.rotation, + ) { + self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { let begin = rect.min_x(); let end = rect.max_x(); for l in rect.min_y()..rect.max_y() { @@ -1311,8 +1309,8 @@ impl> RenderToBuffe extra_right_clip, ); } - } - }); + }); + } } } @@ -1329,14 +1327,14 @@ impl> ProcessScene } fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) { - self.foreach_region(&geometry, |buffer, rect, _extra_left_clip, _extra_right_clip| { - if !buffer.fill_rectangle( - rect.origin.x, - rect.origin.y, - rect.size.width, - rect.size.height, - color, - ) { + if !self.buffer.fill_rectangle( + geometry.origin.x, + geometry.origin.y, + geometry.size.width, + geometry.size.height, + color, + ) { + self.foreach_region(&geometry, |buffer, rect, _extra_left_clip, _extra_right_clip| { let begin = rect.min_x(); let end = rect.max_x(); for l in rect.min_y()..rect.max_y() { @@ -1345,8 +1343,8 @@ impl> ProcessScene color, ) } - } - }); + }) + }; } fn process_rounded_rectangle(&mut self, geometry: PhysicalRect, rr: RoundedRectangle) { From 1e6e1cfaf278a52768ba6eee78fb1549789b286a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 10:12:58 +0100 Subject: [PATCH 17/24] Separate the target pixel buffer trait and related types into a separate module --- internal/core/software_renderer.rs | 93 ++----------------- .../software_renderer/target_pixel_buffer.rs | 79 ++++++++++++++++ 2 files changed, 87 insertions(+), 85 deletions(-) create mode 100644 internal/core/software_renderer/target_pixel_buffer.rs diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index ba267825963..d9aaa4319a3 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -363,97 +363,20 @@ fn region_line_ranges( next_validity } -mod private_api { - use super::*; - - pub use crate::graphics::TexturePixelFormat; - - /// This structure describes the properties of a texture for blending with [`TargetPixelBuffer::draw_texture`]. - #[allow(dead_code)] - #[non_exhaustive] - pub struct Texture<'a> { - /// A reference to the pixel bytes of the texture. These bytes are in the format specified by `pixel_format`. - pub bytes: &'a [u8], - /// The pixel format of the texture. - pub pixel_format: TexturePixelFormat, - /// The number of pixels per horizontal line of the texture. - pub pixel_stride: u16, - /// The width of the texture in pixels. - pub width: u16, - /// The height of the texture in pixels. - pub height: u16, - /// The delta to apply to the source x coordinate between pixels when drawing the texture. - /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. - pub delta_x: u16, - /// The delta to apply to the source y coordinate between pixels when drawing the texture. - /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. - pub delta_y: u16, - /// The offset within the texture to start reading pixels from in the x direction. The - /// offset is specified in 12:4 fixed point format. - pub source_offset_x: u16, - /// The offset within the texture to start reading pixels from in the y direction. The - /// offset is specified in 12:4 fixed point format. - pub source_offset_y: u16, - } - - /// This trait represents access to a buffer of pixels the software renderer can render into, as well - /// as certain operations that the renderer will try to delegate to this trait. Implement these functions - /// to delegate rendering further to hardware-provided 2D acceleration units, such as DMA2D or PXP. - pub trait TargetPixelBuffer { - /// The pixel type the buffer represents. - type Pixel: TargetPixel; - - /// Returns a slice of pixels for the given line. - fn line_slice(&mut self, line_numer: usize) -> &mut [Self::Pixel]; - - /// Returns the number of lines the buffer has. This is typically the height in pixels. - fn num_lines(&self) -> usize; - - /// Fills the buffer with a rectangle at the specified position with the given size and the - /// provided color. Returns true if the operation was successful; false if it could not be - /// implemented and instead the software renderer needs to draw the rectangle. - fn fill_rectangle( - &mut self, - _x: i16, - _y: i16, - _width: i16, - _height: i16, - _color: PremultipliedRgbaColor, - ) -> bool { - false - } - - /// Draw a texture into the buffer at the specified position with the given size and - /// colorized if needed. Returns true if the operation was successful; false if it could not be - /// implemented and instead the software renderer needs to draw the texture - fn draw_texture( - &mut self, - _x: i16, - _y: i16, - _width: i16, - _height: i16, - _src_texture: Texture<'_>, - _colorize: u32, - _alpha: u8, - _rotation: RenderingRotation, - ) -> bool { - false - } - } -} +mod target_pixel_buffer; #[cfg(feature = "experimental")] -pub use private_api::{TargetPixelBuffer, Texture, TexturePixelFormat}; +pub use target_pixel_buffer::{TargetPixelBuffer, Texture, TexturePixelFormat}; #[cfg(not(feature = "experimental"))] -use private_api::TexturePixelFormat; +use target_pixel_buffer::TexturePixelFormat; struct TargetPixelSlice<'a, T> { data: &'a mut [T], pixel_stride: usize, } -impl<'a, T: TargetPixel> private_api::TargetPixelBuffer for TargetPixelSlice<'a, T> { +impl<'a, T: TargetPixel> target_pixel_buffer::TargetPixelBuffer for TargetPixelSlice<'a, T> { type Pixel = T; fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { @@ -577,7 +500,7 @@ impl SoftwareRenderer { fn render_buffer_impl( &self, - buffer: &mut impl private_api::TargetPixelBuffer, + buffer: &mut impl target_pixel_buffer::TargetPixelBuffer, ) -> PhysicalRegion { let pixel_stride = buffer.line_slice(0).len(); let num_lines = buffer.num_lines(); @@ -1200,7 +1123,7 @@ struct RenderToBuffer<'a, TargetPixelBuffer> { dirty_region: PhysicalRegion, } -impl> RenderToBuffer<'_, B> { +impl> RenderToBuffer<'_, B> { fn foreach_ranges( &mut self, geometry: &PhysicalRect, @@ -1281,7 +1204,7 @@ impl> RenderToBuffe geometry.origin.y, geometry.size.width, geometry.size.height, - private_api::Texture { + target_pixel_buffer::Texture { bytes: texture.data, pixel_format: texture.format, pixel_stride: texture.pixel_stride, @@ -1314,7 +1237,7 @@ impl> RenderToBuffe } } -impl> ProcessScene +impl> ProcessScene for RenderToBuffer<'_, B> { fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture<'static>) { diff --git a/internal/core/software_renderer/target_pixel_buffer.rs b/internal/core/software_renderer/target_pixel_buffer.rs new file mode 100644 index 00000000000..9414ce06dae --- /dev/null +++ b/internal/core/software_renderer/target_pixel_buffer.rs @@ -0,0 +1,79 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 + +use super::*; + +pub use crate::graphics::TexturePixelFormat; + +/// This structure describes the properties of a texture for blending with [`TargetPixelBuffer::draw_texture`]. +#[allow(dead_code)] +#[non_exhaustive] +pub struct Texture<'a> { + /// A reference to the pixel bytes of the texture. These bytes are in the format specified by `pixel_format`. + pub bytes: &'a [u8], + /// The pixel format of the texture. + pub pixel_format: TexturePixelFormat, + /// The number of pixels per horizontal line of the texture. + pub pixel_stride: u16, + /// The width of the texture in pixels. + pub width: u16, + /// The height of the texture in pixels. + pub height: u16, + /// The delta to apply to the source x coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. + pub delta_x: u16, + /// The delta to apply to the source y coordinate between pixels when drawing the texture. + /// This is used when scaling the texture. The delta is specified in 8:8 fixed point format. + pub delta_y: u16, + /// The offset within the texture to start reading pixels from in the x direction. The + /// offset is specified in 12:4 fixed point format. + pub source_offset_x: u16, + /// The offset within the texture to start reading pixels from in the y direction. The + /// offset is specified in 12:4 fixed point format. + pub source_offset_y: u16, +} + +/// This trait represents access to a buffer of pixels the software renderer can render into, as well +/// as certain operations that the renderer will try to delegate to this trait. Implement these functions +/// to delegate rendering further to hardware-provided 2D acceleration units, such as DMA2D or PXP. +pub trait TargetPixelBuffer { + /// The pixel type the buffer represents. + type Pixel: TargetPixel; + + /// Returns a slice of pixels for the given line. + fn line_slice(&mut self, line_numer: usize) -> &mut [Self::Pixel]; + + /// Returns the number of lines the buffer has. This is typically the height in pixels. + fn num_lines(&self) -> usize; + + /// Fills the buffer with a rectangle at the specified position with the given size and the + /// provided color. Returns true if the operation was successful; false if it could not be + /// implemented and instead the software renderer needs to draw the rectangle. + fn fill_rectangle( + &mut self, + _x: i16, + _y: i16, + _width: i16, + _height: i16, + _color: PremultipliedRgbaColor, + ) -> bool { + false + } + + /// Draw a texture into the buffer at the specified position with the given size and + /// colorized if needed. Returns true if the operation was successful; false if it could not be + /// implemented and instead the software renderer needs to draw the texture + fn draw_texture( + &mut self, + _x: i16, + _y: i16, + _width: i16, + _height: i16, + _src_texture: Texture<'_>, + _colorize: u32, + _alpha: u8, + _rotation: RenderingRotation, + ) -> bool { + false + } +} From 2a33efdb0fb833ea5dd949892705c3444dff32b4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 11:06:36 +0100 Subject: [PATCH 18/24] Fix window background fill Don't blend the background but fill it when going through ProcessScene. --- api/cpp/cbindgen.rs | 1 + api/cpp/include/slint-platform.h | 27 +++++---- api/cpp/platform.rs | 15 ++++- internal/core/graphics.rs | 15 +++++ internal/core/software_renderer.rs | 59 ++++++++++++------- .../software_renderer/target_pixel_buffer.rs | 4 +- 6 files changed, 88 insertions(+), 33 deletions(-) diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index 8e5ec8701f1..9fead07d2e1 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -327,6 +327,7 @@ fn gen_corelib( "SortOrder", "BitmapFont", "PhysicalRegion", + "CompositionMode", ] .iter() .chain(items.iter()) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index 408fa047a56..d910ae17dd4 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -540,6 +540,7 @@ struct Rgb565Pixel # ifdef SLINT_FEATURE_EXPERIMENTAL +using cbindgen_private::CompositionMode; using cbindgen_private::types::TexturePixelFormat; /// This structure describes the properties of a texture for blending with @@ -587,14 +588,16 @@ struct TargetPixelBuffer /// Fill a rectangle at the specified pixel coordinates with the given color. Return true /// if the operation succeeded; false otherwise; virtual bool fill_rectangle(int16_t x, int16_t y, int16_t width, int16_t height, - const RgbaColor &premultiplied_color) = 0; + const RgbaColor &premultiplied_color, + CompositionMode composition_mode) = 0; /// Draw a portion of provided texture to the specified pixel coordinates. /// Each pixel of the texture is to be blended with the given colorize color as well as the /// alpha value. virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, const Texture &texture, const RgbaColor &colorize, - uint8_t alpha, int screen_rotation_degrees) = 0; + uint8_t alpha, int screen_rotation_degrees, + CompositionMode composition_mode) = 0; }; # endif @@ -761,16 +764,18 @@ class SoftwareRenderer : public AbstractRenderer }, .fill_rectangle = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, uint8_t red, - uint8_t green, uint8_t blue, uint8_t alpha) { + uint8_t green, uint8_t blue, uint8_t alpha, + CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); return buffer->fill_rectangle( x, y, width, height, - Color::from_argb_uint8(alpha, red, green, blue)); + Color::from_argb_uint8(alpha, red, green, blue), composition_mode); }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, const cbindgen_private::CppInternalTexture *internal_texture, - uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { + uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees, + CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); Texture texture { .bytes = std::span { internal_texture->bytes, @@ -786,7 +791,7 @@ class SoftwareRenderer : public AbstractRenderer }; return buffer->draw_texture(x, y, width, height, texture, Color::from_argb_encoded(colorize), alpha, - screen_rotation_degrees); + screen_rotation_degrees, composition_mode); } }; auto r = @@ -814,16 +819,18 @@ class SoftwareRenderer : public AbstractRenderer }, .fill_rectangle = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, uint8_t red, - uint8_t green, uint8_t blue, uint8_t alpha) { + uint8_t green, uint8_t blue, uint8_t alpha, + CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); return buffer->fill_rectangle( x, y, width, height, - Color::from_argb_uint8(alpha, red, green, blue)); + Color::from_argb_uint8(alpha, red, green, blue), composition_mode); }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, const cbindgen_private::CppInternalTexture *internal_texture, - uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees) { + uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees, + CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); Texture texture { .bytes = std::span { internal_texture->bytes, @@ -839,7 +846,7 @@ class SoftwareRenderer : public AbstractRenderer }; return buffer->draw_texture(x, y, width, height, texture, Color::from_argb_encoded(colorize), alpha, - screen_rotation_degrees); + screen_rotation_degrees, composition_mode); } }; auto r = cbindgen_private::slint_software_renderer_render_accel_rgb565(inner, diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index b5d9418c48e..bcbf3ceaf48 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -362,7 +362,8 @@ mod software_renderer { #[cfg(feature = "experimental")] use i_slint_core::software_renderer::{ - PremultipliedRgbaColor, RenderingRotation, TargetPixelBuffer, Texture, TexturePixelFormat, + CompositionMode, PremultipliedRgbaColor, RenderingRotation, TargetPixelBuffer, Texture, + TexturePixelFormat, }; #[cfg(feature = "experimental")] @@ -404,6 +405,7 @@ mod software_renderer { u8, u8, u8, + CompositionMode, ) -> bool, draw_texture: unsafe extern "C" fn( CppTargetPixelBufferUserData, @@ -415,6 +417,7 @@ mod software_renderer { u32, u8, i32, + CompositionMode, ) -> bool, } @@ -442,6 +445,7 @@ mod software_renderer { width: i16, height: i16, color: PremultipliedRgbaColor, + composition_mode: CompositionMode, ) -> bool { unsafe { (self.fill_rectangle)( @@ -454,6 +458,7 @@ mod software_renderer { color.green, color.blue, color.alpha, + composition_mode, ) } } @@ -468,6 +473,7 @@ mod software_renderer { colorize: u32, alpha: u8, rotation: RenderingRotation, + composition_mode: CompositionMode, ) -> bool { unsafe { (self.draw_texture)( @@ -491,6 +497,7 @@ mod software_renderer { colorize, alpha, rotation.angle() as i32, + composition_mode, ) } } @@ -517,6 +524,7 @@ mod software_renderer { u8, u8, u8, + CompositionMode, ) -> bool, draw_texture: unsafe extern "C" fn( CppTargetPixelBufferUserData, @@ -528,6 +536,7 @@ mod software_renderer { u32, u8, i32, + CompositionMode, ) -> bool, } @@ -555,6 +564,7 @@ mod software_renderer { width: i16, height: i16, color: PremultipliedRgbaColor, + composition_mode: CompositionMode, ) -> bool { unsafe { (self.fill_rectangle)( @@ -567,6 +577,7 @@ mod software_renderer { color.green, color.blue, color.alpha, + composition_mode, ) } } @@ -581,6 +592,7 @@ mod software_renderer { colorize: u32, alpha: u8, rotation: RenderingRotation, + composition_mode: CompositionMode, ) -> bool { unsafe { (self.draw_texture)( @@ -604,6 +616,7 @@ mod software_renderer { colorize, alpha, rotation.angle() as i32, + composition_mode, ) } } diff --git a/internal/core/graphics.rs b/internal/core/graphics.rs index 981553c4a00..8d9b2567671 100644 --- a/internal/core/graphics.rs +++ b/internal/core/graphics.rs @@ -214,6 +214,21 @@ impl From for RequestedGraphicsAPI { } } +/// This enum describes the how pixels from a source are merged with the pixels in a destination image. +/// This is a sub-set of the standard [Porter-Duff](https://en.wikipedia.org/wiki/Alpha_compositing) modes. +#[repr(u8)] +#[allow(dead_code)] +#[derive(Default, Copy, Clone, Debug)] +#[non_exhaustive] +pub enum CompositionMode { + /// Only pixels from the source target are drawn. + Source, + /// The source is placed over the destination. + #[default] + SourceOver, + // TODO: maybe add more modes (e.g. xor, plus darker, etc.) +} + /// Internal module for use by cbindgen and the C++ platform API layer. #[cfg(feature = "ffi")] pub mod ffi { diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index d9aaa4319a3..10a4edb18e1 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -366,10 +366,10 @@ fn region_line_ranges( mod target_pixel_buffer; #[cfg(feature = "experimental")] -pub use target_pixel_buffer::{TargetPixelBuffer, Texture, TexturePixelFormat}; +pub use target_pixel_buffer::{CompositionMode, TargetPixelBuffer, Texture, TexturePixelFormat}; #[cfg(not(feature = "experimental"))] -use target_pixel_buffer::TexturePixelFormat; +use target_pixel_buffer::{CompositionMode, TexturePixelFormat}; struct TargetPixelSlice<'a, T> { data: &'a mut [T], @@ -592,9 +592,10 @@ impl SoftwareRenderer { // TODO: gradient background PremultipliedRgbaColor::blend(&mut bg, background.color().into()); renderer.actual_renderer.processor.dirty_region = dirty_region.clone(); - renderer.actual_renderer.processor.process_rectangle( + renderer.actual_renderer.processor.process_rectangle_impl( PhysicalRect::from_size(euclid::Size2D::new(size.width, size.height)), bg, + CompositionMode::Source, ); for (component, origin) in components { @@ -1218,6 +1219,7 @@ impl> Rende texture.extra.colorize.as_argb_encoded(), texture.extra.alpha, texture.extra.rotation, + CompositionMode::default(), ) { self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { let begin = rect.min_x(); @@ -1235,40 +1237,55 @@ impl> Rende }); } } -} - -impl> ProcessScene - for RenderToBuffer<'_, B> -{ - fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture<'static>) { - self.process_texture_impl(geometry, texture) - } - - fn process_shared_image_buffer(&mut self, geometry: PhysicalRect, buffer: SharedBufferCommand) { - let texture = buffer.as_texture(); - self.process_texture_impl(geometry, texture); - } - fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) { + fn process_rectangle_impl( + &mut self, + geometry: PhysicalRect, + color: PremultipliedRgbaColor, + composition_mode: CompositionMode, + ) { if !self.buffer.fill_rectangle( geometry.origin.x, geometry.origin.y, geometry.size.width, geometry.size.height, color, + composition_mode, ) { self.foreach_region(&geometry, |buffer, rect, _extra_left_clip, _extra_right_clip| { let begin = rect.min_x(); let end = rect.max_x(); for l in rect.min_y()..rect.max_y() { - ::blend_slice( - &mut buffer.line_slice(l as usize)[begin as usize..end as usize], - color, - ) + match composition_mode { + CompositionMode::Source => buffer.line_slice(l as usize) + [begin as usize..end as usize] + .fill(::from_rgb(color.red, color.green, color.blue)), + CompositionMode::SourceOver => ::blend_slice( + &mut buffer.line_slice(l as usize)[begin as usize..end as usize], + color, + ), + } } }) }; } +} + +impl> ProcessScene + for RenderToBuffer<'_, B> +{ + fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture<'static>) { + self.process_texture_impl(geometry, texture) + } + + fn process_shared_image_buffer(&mut self, geometry: PhysicalRect, buffer: SharedBufferCommand) { + let texture = buffer.as_texture(); + self.process_texture_impl(geometry, texture); + } + + fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) { + self.process_rectangle_impl(geometry, color, CompositionMode::default()); + } fn process_rounded_rectangle(&mut self, geometry: PhysicalRect, rr: RoundedRectangle) { self.foreach_ranges(&geometry, |line, buffer, extra_left_clip, extra_right_clip| { diff --git a/internal/core/software_renderer/target_pixel_buffer.rs b/internal/core/software_renderer/target_pixel_buffer.rs index 9414ce06dae..b1f5d6346d3 100644 --- a/internal/core/software_renderer/target_pixel_buffer.rs +++ b/internal/core/software_renderer/target_pixel_buffer.rs @@ -3,7 +3,7 @@ use super::*; -pub use crate::graphics::TexturePixelFormat; +pub use crate::graphics::{CompositionMode, TexturePixelFormat}; /// This structure describes the properties of a texture for blending with [`TargetPixelBuffer::draw_texture`]. #[allow(dead_code)] @@ -56,6 +56,7 @@ pub trait TargetPixelBuffer { _width: i16, _height: i16, _color: PremultipliedRgbaColor, + _composition_mode: CompositionMode, ) -> bool { false } @@ -73,6 +74,7 @@ pub trait TargetPixelBuffer { _colorize: u32, _alpha: u8, _rotation: RenderingRotation, + _composition_mode: CompositionMode, ) -> bool { false } From 59b67479afadf683921260f85b1d7fbbf31d20e7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 15:24:06 +0100 Subject: [PATCH 19/24] Simplify background color passing --- internal/core/software_renderer.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 10a4edb18e1..98356d1bc72 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -588,13 +588,11 @@ impl SoftwareRenderer { }; drop(i); - let mut bg = PremultipliedRgbaColor::background(); // TODO: gradient background - PremultipliedRgbaColor::blend(&mut bg, background.color().into()); renderer.actual_renderer.processor.dirty_region = dirty_region.clone(); renderer.actual_renderer.processor.process_rectangle_impl( PhysicalRect::from_size(euclid::Size2D::new(size.width, size.height)), - bg, + background.color().into(), CompositionMode::Source, ); From dce2900ed62a9e99439bf714f762b16cad8969e3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 15:25:14 +0100 Subject: [PATCH 20/24] Prospective fix for background fill --- internal/core/software_renderer.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 98356d1bc72..22cc690c96a 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -1255,9 +1255,12 @@ impl> Rende let end = rect.max_x(); for l in rect.min_y()..rect.max_y() { match composition_mode { - CompositionMode::Source => buffer.line_slice(l as usize) - [begin as usize..end as usize] - .fill(::from_rgb(color.red, color.green, color.blue)), + CompositionMode::Source => { + let mut fill_col = T::background(); + T::blend(&mut fill_col, color); + buffer.line_slice(l as usize)[begin as usize..end as usize] + .fill(fill_col) + } CompositionMode::SourceOver => ::blend_slice( &mut buffer.line_slice(l as usize)[begin as usize..end as usize], color, From 3e22d7db0277d97c0e14d135d5d4a9e1a0d51317 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 21:00:26 +0100 Subject: [PATCH 21/24] Revert "Simplify accelerated texture and rectangle fills" This reverts commit 43bccd9d45b33cb0218fc2805f231a459e5a2b63. --- api/cpp/include/slint-platform.h | 18 ++--- api/cpp/platform.rs | 6 ++ internal/core/software_renderer.rs | 69 ++++++++++++------- .../software_renderer/target_pixel_buffer.rs | 1 + 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/api/cpp/include/slint-platform.h b/api/cpp/include/slint-platform.h index d910ae17dd4..b3b01f80a7c 100644 --- a/api/cpp/include/slint-platform.h +++ b/api/cpp/include/slint-platform.h @@ -591,10 +591,12 @@ struct TargetPixelBuffer const RgbaColor &premultiplied_color, CompositionMode composition_mode) = 0; - /// Draw a portion of provided texture to the specified pixel coordinates. - /// Each pixel of the texture is to be blended with the given colorize color as well as the - /// alpha value. - virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, + /// Draw a portion of provided texture to the specified pixel coordinates. This may + /// be called multiple times for a given texture, for example when clipping. Typically + /// y is identicaly with span_y, but when clipping, span_y refers to the y coordinate of + /// the texture if it's unclipped. Each pixel of the texture is to be blended with the given + /// colorize color as well as the alpha value. + virtual bool draw_texture(int16_t x, int16_t y, int16_t width, int16_t height, int16_t span_y, const Texture &texture, const RgbaColor &colorize, uint8_t alpha, int screen_rotation_degrees, CompositionMode composition_mode) = 0; @@ -773,7 +775,7 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - const cbindgen_private::CppInternalTexture *internal_texture, + int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees, CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); @@ -789,7 +791,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, texture, + return buffer->draw_texture(x, y, width, height, span_y, texture, Color::from_argb_encoded(colorize), alpha, screen_rotation_degrees, composition_mode); } @@ -828,7 +830,7 @@ class SoftwareRenderer : public AbstractRenderer }, .draw_texture = [](void *self, int16_t x, int16_t y, int16_t width, int16_t height, - const cbindgen_private::CppInternalTexture *internal_texture, + int16_t span_y, const cbindgen_private::CppInternalTexture *internal_texture, uint32_t colorize, uint8_t alpha, int32_t screen_rotation_degrees, CompositionMode composition_mode) { auto *buffer = reinterpret_cast *>(self); @@ -844,7 +846,7 @@ class SoftwareRenderer : public AbstractRenderer .source_offset_x = internal_texture->source_offset_x, .source_offset_y = internal_texture->source_offset_y, }; - return buffer->draw_texture(x, y, width, height, texture, + return buffer->draw_texture(x, y, width, height, span_y, texture, Color::from_argb_encoded(colorize), alpha, screen_rotation_degrees, composition_mode); } diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index bcbf3ceaf48..b9089192579 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -413,6 +413,7 @@ mod software_renderer { i16, i16, i16, + i16, &CppInternalTexture, u32, u8, @@ -469,6 +470,7 @@ mod software_renderer { y: i16, width: i16, height: i16, + span_y: i16, texture: Texture<'_>, colorize: u32, alpha: u8, @@ -482,6 +484,7 @@ mod software_renderer { y, width, height, + span_y, &CppInternalTexture { bytes: texture.bytes.as_ptr(), bytes_len: texture.bytes.len(), @@ -532,6 +535,7 @@ mod software_renderer { i16, i16, i16, + i16, &CppInternalTexture, u32, u8, @@ -588,6 +592,7 @@ mod software_renderer { y: i16, width: i16, height: i16, + span_y: i16, texture: Texture<'_>, colorize: u32, alpha: u8, @@ -601,6 +606,7 @@ mod software_renderer { y, width, height, + span_y, &CppInternalTexture { bytes: texture.bytes.as_ptr(), bytes_len: texture.bytes.len(), diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 22cc690c96a..251aa3fb5bc 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -1198,28 +1198,29 @@ impl> Rende } fn process_texture_impl(&mut self, geometry: PhysicalRect, texture: SceneTexture<'_>) { - if !self.buffer.draw_texture( - geometry.origin.x, - geometry.origin.y, - geometry.size.width, - geometry.size.height, - target_pixel_buffer::Texture { - bytes: texture.data, - pixel_format: texture.format, - pixel_stride: texture.pixel_stride, - width: texture.source_size().width as u16, - height: texture.source_size().height as u16, - delta_x: texture.extra.dx.0, - delta_y: texture.extra.dy.0, - source_offset_x: texture.extra.off_x.0, - source_offset_y: texture.extra.off_y.0, - }, - texture.extra.colorize.as_argb_encoded(), - texture.extra.alpha, - texture.extra.rotation, - CompositionMode::default(), - ) { - self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { + self.foreach_region(&geometry, |buffer, rect, extra_left_clip, extra_right_clip| { + if !buffer.draw_texture( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + geometry.origin.y, + target_pixel_buffer::Texture { + bytes: texture.data, + pixel_format: texture.format, + pixel_stride: texture.pixel_stride, + width: texture.source_size().width as u16, + height: texture.source_size().height as u16, + delta_x: texture.extra.dx.0, + delta_y: texture.extra.dy.0, + source_offset_x: texture.extra.off_x.0, + source_offset_y: texture.extra.off_y.0, + }, + texture.extra.colorize.as_argb_encoded(), + texture.extra.alpha, + texture.extra.rotation, + CompositionMode::default(), + ) { let begin = rect.min_x(); let end = rect.max_x(); for l in rect.min_y()..rect.max_y() { @@ -1232,8 +1233,8 @@ impl> Rende extra_right_clip, ); } - }); - } + } + }); } fn process_rectangle_impl( @@ -1285,7 +1286,25 @@ impl> Proce } fn process_rectangle(&mut self, geometry: PhysicalRect, color: PremultipliedRgbaColor) { - self.process_rectangle_impl(geometry, color, CompositionMode::default()); + self.foreach_region(&geometry, |buffer, rect, _extra_left_clip, _extra_right_clip| { + if !buffer.fill_rectangle( + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, + color, + CompositionMode::default(), + ) { + let begin = rect.min_x(); + let end = rect.max_x(); + for l in rect.min_y()..rect.max_y() { + ::blend_slice( + &mut buffer.line_slice(l as usize)[begin as usize..end as usize], + color, + ) + } + } + }); } fn process_rounded_rectangle(&mut self, geometry: PhysicalRect, rr: RoundedRectangle) { diff --git a/internal/core/software_renderer/target_pixel_buffer.rs b/internal/core/software_renderer/target_pixel_buffer.rs index b1f5d6346d3..362bdf25faf 100644 --- a/internal/core/software_renderer/target_pixel_buffer.rs +++ b/internal/core/software_renderer/target_pixel_buffer.rs @@ -70,6 +70,7 @@ pub trait TargetPixelBuffer { _y: i16, _width: i16, _height: i16, + _span_y: i16, _src_texture: Texture<'_>, _colorize: u32, _alpha: u8, From eee61abc2847190e34627569a7e01b11f28a61d7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 21:03:39 +0100 Subject: [PATCH 22/24] Rename TargetPixelBuffer::Pixel to ::TargetPixel --- api/cpp/platform.rs | 8 ++++---- internal/core/software_renderer.rs | 10 ++++++---- internal/core/software_renderer/target_pixel_buffer.rs | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/api/cpp/platform.rs b/api/cpp/platform.rs index b9089192579..22fc202f59c 100644 --- a/api/cpp/platform.rs +++ b/api/cpp/platform.rs @@ -424,9 +424,9 @@ mod software_renderer { #[cfg(feature = "experimental")] impl TargetPixelBuffer for CppRgb8TargetPixelBuffer { - type Pixel = Rgb8Pixel; + type TargetPixel = Rgb8Pixel; - fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + fn line_slice(&mut self, line_number: usize) -> &mut [Self::TargetPixel] { unsafe { let mut data = core::ptr::null_mut(); let mut len = 0; @@ -546,9 +546,9 @@ mod software_renderer { #[cfg(feature = "experimental")] impl TargetPixelBuffer for CppRgb565TargetPixelBuffer { - type Pixel = Rgb565Pixel; + type TargetPixel = Rgb565Pixel; - fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + fn line_slice(&mut self, line_number: usize) -> &mut [Self::TargetPixel] { unsafe { let mut data = core::ptr::null_mut(); let mut len = 0; diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 251aa3fb5bc..c3f0c292167 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -377,9 +377,9 @@ struct TargetPixelSlice<'a, T> { } impl<'a, T: TargetPixel> target_pixel_buffer::TargetPixelBuffer for TargetPixelSlice<'a, T> { - type Pixel = T; + type TargetPixel = T; - fn line_slice(&mut self, line_number: usize) -> &mut [Self::Pixel] { + fn line_slice(&mut self, line_number: usize) -> &mut [Self::TargetPixel] { let offset = line_number * self.pixel_stride; &mut self.data[offset..offset + self.pixel_stride] } @@ -1122,7 +1122,9 @@ struct RenderToBuffer<'a, TargetPixelBuffer> { dirty_region: PhysicalRegion, } -impl> RenderToBuffer<'_, B> { +impl> + RenderToBuffer<'_, B> +{ fn foreach_ranges( &mut self, geometry: &PhysicalRect, @@ -1273,7 +1275,7 @@ impl> Rende } } -impl> ProcessScene +impl> ProcessScene for RenderToBuffer<'_, B> { fn process_texture(&mut self, geometry: PhysicalRect, texture: SceneTexture<'static>) { diff --git a/internal/core/software_renderer/target_pixel_buffer.rs b/internal/core/software_renderer/target_pixel_buffer.rs index 362bdf25faf..868c37c27f9 100644 --- a/internal/core/software_renderer/target_pixel_buffer.rs +++ b/internal/core/software_renderer/target_pixel_buffer.rs @@ -38,10 +38,10 @@ pub struct Texture<'a> { /// to delegate rendering further to hardware-provided 2D acceleration units, such as DMA2D or PXP. pub trait TargetPixelBuffer { /// The pixel type the buffer represents. - type Pixel: TargetPixel; + type TargetPixel: TargetPixel; /// Returns a slice of pixels for the given line. - fn line_slice(&mut self, line_numer: usize) -> &mut [Self::Pixel]; + fn line_slice(&mut self, line_numer: usize) -> &mut [Self::TargetPixel]; /// Returns the number of lines the buffer has. This is typically the height in pixels. fn num_lines(&self) -> usize; From 44d5690eab4909c5ac9f58f4d0c610b74ce0318d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 21:06:38 +0100 Subject: [PATCH 23/24] Simplify RenderToBuffer declaration --- internal/core/software_renderer.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index c3f0c292167..b26f6dc6a5d 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -1122,13 +1122,11 @@ struct RenderToBuffer<'a, TargetPixelBuffer> { dirty_region: PhysicalRegion, } -impl> - RenderToBuffer<'_, B> -{ +impl RenderToBuffer<'_, B> { fn foreach_ranges( &mut self, geometry: &PhysicalRect, - mut f: impl FnMut(i16, &mut [T], i16, i16), + mut f: impl FnMut(i16, &mut [B::TargetPixel], i16, i16), ) { let mut line = geometry.min_y(); while let Some(mut next) = @@ -1259,12 +1257,12 @@ impl> for l in rect.min_y()..rect.max_y() { match composition_mode { CompositionMode::Source => { - let mut fill_col = T::background(); - T::blend(&mut fill_col, color); + let mut fill_col = B::TargetPixel::background(); + B::TargetPixel::blend(&mut fill_col, color); buffer.line_slice(l as usize)[begin as usize..end as usize] .fill(fill_col) } - CompositionMode::SourceOver => ::blend_slice( + CompositionMode::SourceOver => ::blend_slice( &mut buffer.line_slice(l as usize)[begin as usize..end as usize], color, ), From d832e766f331965f134a462800f5792828116d7f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Feb 2025 21:09:41 +0100 Subject: [PATCH 24/24] Simplify foreach_ranges by re-using foreach_region --- internal/core/software_renderer.rs | 40 ++++++++---------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index b26f6dc6a5d..9ae03fa1501 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -1128,37 +1128,17 @@ impl RenderToBuffer<'_, B> { geometry: &PhysicalRect, mut f: impl FnMut(i16, &mut [B::TargetPixel], i16, i16), ) { - let mut line = geometry.min_y(); - while let Some(mut next) = - region_line_ranges(&self.dirty_region, line, &mut self.dirty_range_cache) - { - next = next.min(geometry.max_y()); - for r in &self.dirty_range_cache { - if geometry.origin.x >= r.end { - continue; - } - let begin = r.start.max(geometry.origin.x); - let end = r.end.min(geometry.origin.x + geometry.size.width); - if begin >= end { - continue; - } - let extra_left_clip = begin - geometry.origin.x; - let extra_right_clip = geometry.origin.x + geometry.size.width - end; - - for l in line..next { - f( - l, - &mut self.buffer.line_slice(l as usize)[begin as usize..end as usize], - extra_left_clip, - extra_right_clip, - ); - } - } - if next == geometry.max_y() { - break; + self.foreach_region(geometry, |buffer, rect, extra_left_clip, extra_right_clip| { + for l in rect.min_y()..rect.max_y() { + f( + l, + &mut buffer.line_slice(l as usize) + [rect.min_x() as usize..rect.max_x() as usize], + extra_left_clip, + extra_right_clip, + ); } - line = next; - } + }); } fn foreach_region(