mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 22:54:36 +00:00
MCU: Render line by line in a line buffer
This commit is contained in:
parent
cd9db61545
commit
20f41a681b
2 changed files with 198 additions and 18 deletions
|
@ -9,6 +9,7 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
|
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use embedded_graphics::pixelcolor::Rgb888;
|
use embedded_graphics::pixelcolor::Rgb888;
|
||||||
|
@ -21,15 +22,15 @@ use sixtyfps_corelib::{Color, ImageInner};
|
||||||
|
|
||||||
pub fn render_window_frame<T: DrawTarget<Color = Rgb888>>(
|
pub fn render_window_frame<T: DrawTarget<Color = Rgb888>>(
|
||||||
runtime_window: Rc<sixtyfps_corelib::window::Window>,
|
runtime_window: Rc<sixtyfps_corelib::window::Window>,
|
||||||
|
background: Rgb888,
|
||||||
display: &mut T,
|
display: &mut T,
|
||||||
) where
|
) where
|
||||||
T::Error: std::fmt::Debug,
|
T::Error: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let size = display.bounding_box().size;
|
let size = display.bounding_box().size;
|
||||||
let scene = prepare_scene(runtime_window, SizeF::new(size.width as _, size.height as _));
|
let mut scene = prepare_scene(runtime_window, SizeF::new(size.width as _, size.height as _));
|
||||||
// TODO: process the scene to render line by line.
|
|
||||||
// for now, just draw them
|
/*for item in scene.future_items {
|
||||||
for item in scene.items {
|
|
||||||
match item.command {
|
match item.command {
|
||||||
SceneCommand::Rectangle { color } => {
|
SceneCommand::Rectangle { color } => {
|
||||||
embedded_graphics::primitives::Rectangle {
|
embedded_graphics::primitives::Rectangle {
|
||||||
|
@ -67,23 +68,199 @@ pub fn render_window_frame<T: DrawTarget<Color = Rgb888>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
let mut line_buffer = vec![background; size.width as usize];
|
||||||
|
while scene.current_line < size.height as u16 {
|
||||||
|
line_buffer.fill(background);
|
||||||
|
let line = scene.process_line();
|
||||||
|
for span in line.spans.iter().rev() {
|
||||||
|
match span.command {
|
||||||
|
SceneCommand::Rectangle { color } => {
|
||||||
|
let alpha = color.alpha();
|
||||||
|
if alpha == u8::MAX {
|
||||||
|
line_buffer[(span.x) as usize..(span.x + span.width) as usize]
|
||||||
|
.fill(to_rgb888_color_discard_alpha(color))
|
||||||
|
} else {
|
||||||
|
for pix in
|
||||||
|
&mut line_buffer[(span.x) as usize..(span.x + span.width) as usize]
|
||||||
|
{
|
||||||
|
let a = (u8::MAX - alpha) as u16;
|
||||||
|
let b = alpha as u16;
|
||||||
|
*pix = Rgb888::new(
|
||||||
|
((pix.r() as u16 * a + color.red() as u16 * b) >> 8) as u8,
|
||||||
|
((pix.g() as u16 * a + color.green() as u16 * b) >> 8) as u8,
|
||||||
|
((pix.b() as u16 * a + color.blue() as u16 * b) >> 8) as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SceneCommand::Texture {
|
||||||
|
data,
|
||||||
|
format,
|
||||||
|
stride,
|
||||||
|
source_width,
|
||||||
|
source_height,
|
||||||
|
color,
|
||||||
|
} => {
|
||||||
|
let sx = span.width as f32 / source_width as f32;
|
||||||
|
let sy = span.height as f32 / source_height as f32;
|
||||||
|
let bpp = bpp(format) as usize;
|
||||||
|
let y = line.line - span.y;
|
||||||
|
|
||||||
|
for (x, pix) in line_buffer[(span.x) as usize..(span.x + span.width) as usize]
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let pos = ((y as f32 / sy) as usize * stride as usize)
|
||||||
|
+ (x as f32 / sx) as usize * bpp;
|
||||||
|
*pix = match format {
|
||||||
|
PixelFormat::Rgb => {
|
||||||
|
Rgb888::new(data[pos + 0], data[pos + 1], data[pos + 2])
|
||||||
|
}
|
||||||
|
PixelFormat::Rgba => {
|
||||||
|
if color.alpha() == 0 {
|
||||||
|
let a = (u8::MAX - data[pos + 3]) as u16;
|
||||||
|
let b = data[pos + 3] as u16;
|
||||||
|
Rgb888::new(
|
||||||
|
((pix.r() as u16 * a + data[pos + 0] as u16 * b) >> 8)
|
||||||
|
as u8,
|
||||||
|
((pix.g() as u16 * a + data[pos + 1] as u16 * b) >> 8)
|
||||||
|
as u8,
|
||||||
|
((pix.b() as u16 * a + data[pos + 2] as u16 * b) >> 8)
|
||||||
|
as u8,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let a = (u8::MAX - data[pos + 3]) as u16;
|
||||||
|
let b = data[pos + 3] as u16;
|
||||||
|
Rgb888::new(
|
||||||
|
((pix.r() as u16 * a + color.red() as u16 * b) >> 8) as u8,
|
||||||
|
((pix.g() as u16 * a + color.green() as u16 * b) >> 8)
|
||||||
|
as u8,
|
||||||
|
((pix.b() as u16 * a + color.blue() as u16 * b) >> 8) as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PixelFormat::AlphaMap => {
|
||||||
|
let a = (u8::MAX - data[pos]) as u16;
|
||||||
|
let b = data[pos] as u16;
|
||||||
|
Rgb888::new(
|
||||||
|
((pix.r() as u16 * a + color.red() as u16 * b) >> 8) as u8,
|
||||||
|
((pix.g() as u16 * a + color.green() as u16 * b) >> 8) as u8,
|
||||||
|
((pix.b() as u16 * a + color.blue() as u16 * b) >> 8) as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display
|
||||||
|
.fill_contiguous(
|
||||||
|
&embedded_graphics::primitives::Rectangle::new(
|
||||||
|
Point::new(0, line.line as i32),
|
||||||
|
Size::new(size.width, 1),
|
||||||
|
),
|
||||||
|
line_buffer.iter().copied(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Scene {
|
struct Scene {
|
||||||
items: Vec<SceneItem>,
|
/// the next line to be processed
|
||||||
|
current_line: u16,
|
||||||
|
|
||||||
|
/// Element that have `y > current_line`
|
||||||
|
/// They must be sorted by `y` in reverse order (bottom to top)
|
||||||
|
/// then by `z` top to bottom
|
||||||
|
future_items: Vec<SceneItem>,
|
||||||
|
|
||||||
|
/// The items that overlap with the current line, sorted by z top to bottom
|
||||||
|
current_items: VecDeque<SceneItem>,
|
||||||
|
|
||||||
|
/// Some staging buffer of scene item
|
||||||
|
next_items: VecDeque<SceneItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
fn new(mut items: Vec<SceneItem>) -> Self {
|
||||||
|
items.sort_by(|a, b| compare_scene_item(a, b).reverse());
|
||||||
|
Self {
|
||||||
|
future_items: items,
|
||||||
|
current_line: 0,
|
||||||
|
current_items: Default::default(),
|
||||||
|
next_items: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will generate a LineCommand for the current_line, remove all items that are done from the items
|
||||||
|
fn process_line(&mut self) -> LineCommand {
|
||||||
|
let mut command = vec![];
|
||||||
|
// Take the next element from current_items or future_items
|
||||||
|
loop {
|
||||||
|
let a_next_z =
|
||||||
|
self.future_items.last().filter(|i| i.y == self.current_line).map(|i| i.z);
|
||||||
|
let b_next_z = self.current_items.front().map(|i| i.z);
|
||||||
|
let item = match (a_next_z, b_next_z) {
|
||||||
|
(Some(a), Some(b)) => {
|
||||||
|
if a > b {
|
||||||
|
self.future_items.pop()
|
||||||
|
} else {
|
||||||
|
self.current_items.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(_), None) => self.future_items.pop(),
|
||||||
|
(None, Some(_)) => self.current_items.pop_front(),
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
let item = item.unwrap();
|
||||||
|
if item.y + item.height > self.current_line + 1 {
|
||||||
|
self.next_items.push_back(item.clone());
|
||||||
|
}
|
||||||
|
command.push(item);
|
||||||
|
}
|
||||||
|
core::mem::swap(&mut self.next_items, &mut self.current_items);
|
||||||
|
let line = self.current_line;
|
||||||
|
self.current_line += 1;
|
||||||
|
LineCommand { spans: command, line }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
struct SceneItem {
|
struct SceneItem {
|
||||||
x: u16,
|
x: u16,
|
||||||
y: u16,
|
y: u16,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
// this is the order of the item from which it is in the item tree
|
// this is the order of the item from which it is in the item tree
|
||||||
// z: u16,
|
z: u16,
|
||||||
command: SceneCommand,
|
command: SceneCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LineCommand {
|
||||||
|
line: u16,
|
||||||
|
// Fixme: we need to process these so we do not draw items under opaque regions
|
||||||
|
spans: Vec<SceneItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_scene_item(a: &SceneItem, b: &SceneItem) -> std::cmp::Ordering {
|
||||||
|
// First, order by line (top to bottom)
|
||||||
|
match a.y.partial_cmp(&b.y) {
|
||||||
|
None | Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
Some(ord) => return ord,
|
||||||
|
}
|
||||||
|
// Then by the reverse z (front to back)
|
||||||
|
match a.z.partial_cmp(&b.z) {
|
||||||
|
None | Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
Some(ord) => return ord.reverse(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// anything else, we don't care
|
||||||
|
core::cmp::Ordering::Equal
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
enum SceneCommand {
|
enum SceneCommand {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: Color,
|
color: Color,
|
||||||
|
@ -109,7 +286,7 @@ fn prepare_scene(runtime_window: Rc<sixtyfps_corelib::window::Window>, size: Siz
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Scene { items: prepare_scene.items }
|
Scene::new(prepare_scene.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PrepareScene {
|
struct PrepareScene {
|
||||||
|
@ -228,10 +405,11 @@ impl sixtyfps_corelib::item_rendering::ItemRenderer for PrepareScene {
|
||||||
// FIXME: gradients
|
// FIXME: gradients
|
||||||
let color = rect.background().color();
|
let color = rect.background().color();
|
||||||
if color.alpha() > 0 {
|
if color.alpha() > 0 {
|
||||||
self.new_scene_item(
|
if let Some(r) =
|
||||||
geom.inflate(-border, -border),
|
geom.inflate(-border, -border).intersection(&self.current_state.clip)
|
||||||
SceneCommand::Rectangle { color },
|
{
|
||||||
);
|
}
|
||||||
|
self.new_scene_item(r, SceneCommand::Rectangle { color });
|
||||||
}
|
}
|
||||||
if border > 0.01 {
|
if border > 0.01 {
|
||||||
// FIXME: radius
|
// FIXME: radius
|
||||||
|
@ -373,7 +551,7 @@ fn bpp(format: PixelFormat) -> u16 {
|
||||||
PixelFormat::AlphaMap => 1,
|
PixelFormat::AlphaMap => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
fn to_color(data: &[u8], format: PixelFormat, color: Color) -> Rgb888 {
|
fn to_color(data: &[u8], format: PixelFormat, color: Color) -> Rgb888 {
|
||||||
match format {
|
match format {
|
||||||
PixelFormat::Rgba if color.alpha() > 0 => Rgb888::new(
|
PixelFormat::Rgba if color.alpha() > 0 => Rgb888::new(
|
||||||
|
@ -389,4 +567,8 @@ fn to_color(data: &[u8], format: PixelFormat, color: Color) -> Rgb888 {
|
||||||
((color.blue() as u16 * data[0] as u16) >> 8) as u8,
|
((color.blue() as u16 * data[0] as u16) >> 8) as u8,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub fn to_rgb888_color_discard_alpha(col: Color) -> Rgb888 {
|
||||||
|
Rgb888::new(col.red(), col.green(), col.blue())
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,7 +242,9 @@ impl WinitWindow for SimulatorWindow {
|
||||||
let mut display: SimulatorDisplay<embedded_graphics::pixelcolor::Rgb888> =
|
let mut display: SimulatorDisplay<embedded_graphics::pixelcolor::Rgb888> =
|
||||||
SimulatorDisplay::new(Size { width: size.width, height: size.height });
|
SimulatorDisplay::new(Size { width: size.width, height: size.height });
|
||||||
|
|
||||||
display.clear(to_rgb888_color_discard_alpha(self.background_color.get())).unwrap();
|
let background =
|
||||||
|
crate::renderer::to_rgb888_color_discard_alpha(self.background_color.get());
|
||||||
|
display.clear(background).unwrap();
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
{
|
{
|
||||||
|
@ -263,7 +265,7 @@ impl WinitWindow for SimulatorWindow {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::renderer::render_window_frame(runtime_window, &mut display);
|
crate::renderer::render_window_frame(runtime_window, background, &mut display);
|
||||||
|
|
||||||
let output_image = display
|
let output_image = display
|
||||||
.to_rgb_output_image(&embedded_graphics_simulator::OutputSettings::default());
|
.to_rgb_output_image(&embedded_graphics_simulator::OutputSettings::default());
|
||||||
|
@ -321,7 +323,3 @@ impl WinitWindow for SimulatorWindow {
|
||||||
}
|
}
|
||||||
fn set_icon(&self, _icon: sixtyfps_corelib::graphics::Image) {}
|
fn set_icon(&self, _icon: sixtyfps_corelib::graphics::Image) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_rgb888_color_discard_alpha(col: Color) -> Rgb888 {
|
|
||||||
Rgb888::new(col.red(), col.green(), col.blue())
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue