feat(init,display): loader streams ELF segments (any size, kills half-built proc); window pixels RGBA8 premul (tiny-skia native), canvas mapped plain memory + pixels_mut/touch
This commit is contained in:
@@ -5,12 +5,10 @@
|
||||
//! front.
|
||||
|
||||
use crate::syscall;
|
||||
use crate::window::Window;
|
||||
use types::address::{ByteOffset, VirtualAddress};
|
||||
use types::address::VirtualAddress;
|
||||
use types::bounded::CapSlot;
|
||||
use types::display::window::{self as w, PIXEL_BYTES};
|
||||
use types::display::{Color, Dimension, ScreenCoord};
|
||||
use types::mapping::PagePerms;
|
||||
|
||||
/// Where an app's canvas is mapped.
|
||||
const CANVAS_VA: VirtualAddress = VirtualAddress::from_raw(0x0100_0000);
|
||||
@@ -67,11 +65,13 @@ struct Damage {
|
||||
y1: u16,
|
||||
}
|
||||
|
||||
/// A window's pixels, mapped here. Draw with the `Framebuffer` methods (and
|
||||
/// `raster`); `flush` presents everything drawn since the last one.
|
||||
/// A window's pixels, mapped here as plain memory. Draw with the
|
||||
/// `Framebuffer` methods (and `raster`), or on the bytes themselves
|
||||
/// (`pixels_mut`, then `touch`); `flush` presents everything changed since
|
||||
/// the last one.
|
||||
pub struct Canvas {
|
||||
window: CapSlot,
|
||||
pixels: Window,
|
||||
pixels: &'static mut [u8],
|
||||
width: Dimension,
|
||||
height: Dimension,
|
||||
damage: Option<Damage>,
|
||||
@@ -84,7 +84,7 @@ impl Canvas {
|
||||
syscall::Received::Message { words, cap: Some(buffer), .. } if words[0] == w::OK => (words, buffer),
|
||||
_ => return None,
|
||||
};
|
||||
let pixels = crate::window::map(buffer, CANVAS_VA, PagePerms::ReadWrite).ok()?;
|
||||
let pixels = crate::window::map_buffer(buffer, CANVAS_VA).ok()?;
|
||||
let (width, height) = (Dimension::new(words[1] as u16), Dimension::new(words[2] as u16));
|
||||
// The display says how big; the mapping says how much is there.
|
||||
(width.raw() as usize * height.raw() as usize * PIXEL_BYTES <= pixels.len())
|
||||
@@ -95,12 +95,19 @@ impl Canvas {
|
||||
self.window
|
||||
}
|
||||
|
||||
fn offset(&self, x: u16, y: u16) -> ByteOffset {
|
||||
ByteOffset::new((y as usize * self.width.raw() as usize + x as usize) * PIXEL_BYTES)
|
||||
/// The pixels, `width * height` of them, each the bytes R, G, B, A.
|
||||
pub fn pixels_mut(&mut self) -> &mut [u8] {
|
||||
let n = self.width.raw() as usize * self.height.raw() as usize * PIXEL_BYTES;
|
||||
&mut self.pixels[..n]
|
||||
}
|
||||
|
||||
/// Marks `x..x+w`, `y..y+h` (already clipped) as changed.
|
||||
fn touch(&mut self, x: u16, y: u16, w: u16, h: u16) {
|
||||
fn offset(&self, x: u16, y: u16) -> usize {
|
||||
(y as usize * self.width.raw() as usize + x as usize) * PIXEL_BYTES
|
||||
}
|
||||
|
||||
/// Marks a rectangle as changed, for the next `flush`; clipped.
|
||||
pub fn touch(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension) {
|
||||
let Some((x, y, w, h)) = self.clip(x, y, w, h) else { return };
|
||||
let (x1, y1) = (x + w, y + h);
|
||||
self.damage = Some(match self.damage {
|
||||
None => Damage { x0: x, y0: y, x1, y1 },
|
||||
@@ -112,7 +119,15 @@ impl Canvas {
|
||||
fn clip(&self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension) -> Option<(u16, u16, u16, u16)> {
|
||||
let (cw, ch) = (self.width.raw(), self.height.raw());
|
||||
let (x, y) = (x.raw(), y.raw());
|
||||
(x < cw && y < ch).then(|| (x, y, w.raw().min(cw - x), h.raw().min(ch - y)))
|
||||
(x < cw && y < ch && w.raw() > 0 && h.raw() > 0).then(|| (x, y, w.raw().min(cw - x), h.raw().min(ch - y)))
|
||||
}
|
||||
|
||||
/// Fills `len` pixels from byte `at` with `color`.
|
||||
fn fill(&mut self, at: usize, len: usize, color: Color) {
|
||||
let px = color.to_rgba();
|
||||
for chunk in self.pixels[at..at + len * PIXEL_BYTES].chunks_exact_mut(PIXEL_BYTES) {
|
||||
chunk.copy_from_slice(&px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,28 +141,24 @@ impl port_framebuffer::Framebuffer for Canvas {
|
||||
}
|
||||
|
||||
fn write_pixel(&mut self, x: ScreenCoord, y: ScreenCoord, color: Color) {
|
||||
if let Some((x, y, _, _)) = self.clip(x, y, Dimension::new(1), Dimension::new(1)) {
|
||||
self.pixels.write(self.offset(x, y), color.raw());
|
||||
self.touch(x, y, 1, 1);
|
||||
}
|
||||
self.fill_rect(x, y, Dimension::new(1), Dimension::new(1), color);
|
||||
}
|
||||
|
||||
fn write_row(&mut self, x: ScreenCoord, y: ScreenCoord, row: &[Color]) {
|
||||
let Some((x, y, w, _)) = self.clip(x, y, Dimension::new(row.len().min(u16::MAX as usize) as u16), Dimension::new(1)) else { return };
|
||||
let mut run = [0u32; 64];
|
||||
for (i, chunk) in row[..w as usize].chunks(run.len()).enumerate() {
|
||||
for (slot, c) in run.iter_mut().zip(chunk) {
|
||||
*slot = c.raw();
|
||||
}
|
||||
self.pixels.write_u32s(self.offset(x + (i * 64) as u16, y), &run[..chunk.len()]);
|
||||
let len = Dimension::new(row.len().min(u16::MAX as usize) as u16);
|
||||
let Some((cx, cy, w, _)) = self.clip(x, y, len, Dimension::new(1)) else { return };
|
||||
let at = self.offset(cx, cy);
|
||||
for (chunk, c) in self.pixels[at..at + w as usize * PIXEL_BYTES].chunks_exact_mut(PIXEL_BYTES).zip(row) {
|
||||
chunk.copy_from_slice(&c.to_rgba());
|
||||
}
|
||||
self.touch(x, y, w, 1);
|
||||
self.touch(x, y, len, Dimension::new(1));
|
||||
}
|
||||
|
||||
fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) {
|
||||
let Some((x, y, w, h)) = self.clip(x, y, w, h) else { return };
|
||||
for row in y..y + h {
|
||||
self.pixels.fill_u32(self.offset(x, row), w as usize, color.raw());
|
||||
let Some((cx, cy, cw, ch)) = self.clip(x, y, w, h) else { return };
|
||||
for row in cy..cy + ch {
|
||||
let at = self.offset(cx, row);
|
||||
self.fill(at, cw as usize, color);
|
||||
}
|
||||
self.touch(x, y, w, h);
|
||||
}
|
||||
@@ -165,14 +176,14 @@ impl port_framebuffer::Framebuffer for Canvas {
|
||||
}
|
||||
|
||||
fn scroll_up(&mut self, top: ScreenCoord, height: Dimension, by: Dimension, fill: Color) {
|
||||
let Some((_, top, _, height)) = self.clip(ScreenCoord::new(0), top, self.width, height) else { return };
|
||||
let by = by.raw().min(height);
|
||||
let row = self.width.raw() as usize * PIXEL_BYTES;
|
||||
let kept = (height - by) as usize * row;
|
||||
self.pixels.copy_within(self.offset(0, top + by), self.offset(0, top), kept);
|
||||
let width = self.width;
|
||||
let Some((_, top, _, height)) = self.clip(ScreenCoord::new(0), top, width, height) else { return };
|
||||
let by = by.raw().min(height);
|
||||
let kept = (height - by) as usize * width.raw() as usize * PIXEL_BYTES;
|
||||
let (from, to) = (self.offset(0, top + by), self.offset(0, top));
|
||||
self.pixels.copy_within(from..from + kept, to);
|
||||
self.fill_rect(ScreenCoord::new(0), ScreenCoord::new(top + height - by), width, Dimension::new(by), fill);
|
||||
self.touch(0, top, width.raw(), height);
|
||||
self.touch(ScreenCoord::new(0), ScreenCoord::new(top), width, Dimension::new(height));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user