use alloc::vec::Vec;
use crate::host::display::{PixelFormat as SurfaceFormat, Surface};
use super::proto::{PixelFormat, encoding, rect_header, update_header};
#[derive(Debug)]
pub struct FrameEncoder {
format: PixelFormat,
announced: (u16, u16),
resizable: bool,
last: Vec<u8>,
last_shape: (SurfaceFormat, u32, u32),
}
impl FrameEncoder {
#[must_use]
pub fn new(format: PixelFormat, width: u16, height: u16) -> FrameEncoder {
FrameEncoder {
format,
announced: (width, height),
resizable: false,
last: Vec::new(),
last_shape: (SurfaceFormat::RGBA8888, 0, 0),
}
}
#[must_use]
pub const fn format(&self) -> PixelFormat {
self.format
}
pub fn set_format(&mut self, format: PixelFormat) {
self.format = format;
self.invalidate();
}
pub fn set_encodings(&mut self, list: &[i32]) {
self.resizable = list.contains(&encoding::DESKTOP_SIZE);
}
#[must_use]
pub const fn resizable(&self) -> bool {
self.resizable
}
#[must_use]
pub const fn announced(&self) -> (u16, u16) {
self.announced
}
pub fn invalidate(&mut self) {
self.last.clear();
self.last_shape = (SurfaceFormat::RGBA8888, 0, 0);
}
pub fn update(&mut self, surface: &Surface, incremental: bool) -> Option<Vec<u8>> {
let shape = (surface.format(), surface.width(), surface.height());
let comparable = incremental && !self.last.is_empty() && self.last_shape == shape;
let resized = self.resizable
&& (fits(surface.width()), fits(surface.height())) != self.announced
&& surface.width() > 0
&& surface.height() > 0;
let mut rects: Vec<Vec<u8>> = Vec::new();
if resized {
let (w, h) = (fits(surface.width()), fits(surface.height()));
rects.push(rect_header(0, 0, w, h, encoding::DESKTOP_SIZE).to_vec());
self.announced = (w, h);
}
let (aw, ah) = self.announced;
if comparable && !resized {
for (top, bottom) in self.damaged_bands(surface) {
let height = bottom - top;
let top16 = fits(top);
rects.push(self.raw_rect(surface, 0, top16, aw, fits(height)));
}
if rects.is_empty() {
return None;
}
} else {
rects.push(self.raw_rect(surface, 0, 0, aw, ah));
}
self.remember(surface);
#[allow(clippy::cast_possible_truncation)]
let count = rects.len().min(usize::from(u16::MAX)) as u16;
let mut out = update_header(count).to_vec();
for rect in rects {
out.extend_from_slice(&rect);
}
Some(out)
}
fn damaged_bands(&self, surface: &Surface) -> Vec<(u32, u32)> {
let stride = surface.stride() as usize;
let mut bands = Vec::new();
let mut run: Option<u32> = None;
for y in 0..surface.height() {
let at = y as usize * stride;
let now = surface.row(y).unwrap_or(&[]);
let before = self.last.get(at..at + stride).unwrap_or(&[]);
let same = now == before;
match (same, run) {
(false, None) => run = Some(y),
(true, Some(top)) => {
bands.push((top, y));
run = None;
}
_ => {}
}
}
if let Some(top) = run {
bands.push((top, surface.height()));
}
bands
}
fn raw_rect(&self, surface: &Surface, x: u16, y: u16, width: u16, height: u16) -> Vec<u8> {
let bpp = self.format.bytes_per_pixel();
let mut out = rect_header(x, y, width, height, encoding::RAW).to_vec();
out.reserve(usize::from(width) * usize::from(height) * bpp);
let direct = surface.format() == SurfaceFormat::BGRA8888
&& self.format == PixelFormat::DEFAULT
&& x == 0
&& u32::from(width) == surface.width();
for row in 0..u32::from(height) {
let sy = u32::from(y) + row;
if direct && let Some(bytes) = surface.row(sy) {
out.extend_from_slice(bytes);
continue;
}
for column in 0..u32::from(width) {
let rgb = surface.get(u32::from(x) + column, sy).unwrap_or([0, 0, 0]);
self.format.put(self.format.pack(rgb), &mut out);
}
}
out
}
fn remember(&mut self, surface: &Surface) {
self.last.clear();
self.last.extend_from_slice(surface.pixels());
self.last_shape = (surface.format(), surface.width(), surface.height());
}
}
#[inline]
const fn fits(value: u32) -> u16 {
if value > u16::MAX as u32 {
u16::MAX
} else {
value as u16
}
}
#[cfg(test)]
mod tests {
use super::*;
fn surface(width: u32, height: u32, fill: [u8; 3]) -> Surface {
let mut s = Surface::new(SurfaceFormat::BGRA8888, width, height);
s.fill(fill);
s
}
#[test]
fn the_first_update_is_a_whole_frame_even_when_incremental_was_asked() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
let s = surface(4, 2, [1, 2, 3]);
let update = enc
.update(&s, true)
.expect("a client with nothing gets a frame");
assert_eq!(&update[..4], update_header(1), "one rectangle");
assert_eq!(
update.len(),
4 + 12 + 4 * 2 * 4,
"header, rect header, and 4x2 32-bit pixels"
);
assert_eq!(&update[16..20], [3, 2, 1, 0xff]);
}
#[test]
fn an_unchanged_frame_produces_nothing_incrementally_and_a_frame_otherwise() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
let s = surface(4, 2, [1, 2, 3]);
enc.update(&s, false).expect("the first one");
assert!(enc.update(&s, true).is_none(), "nothing changed");
assert!(
enc.update(&s, false).is_some(),
"a full request is always answered"
);
}
#[test]
fn only_the_changed_rows_are_sent() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 4);
let mut s = surface(4, 4, [0, 0, 0]);
enc.update(&s, false).expect("the first one");
s.put(2, 1, [0xff, 0, 0]);
let update = enc.update(&s, true).expect("row 1 changed");
assert_eq!(&update[..4], update_header(1));
assert_eq!(&update[4..16], rect_header(0, 1, 4, 1, encoding::RAW));
assert_eq!(update.len(), 4 + 12 + 4 * 4);
}
#[test]
fn two_separated_changes_become_two_rectangles() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 5);
let mut s = surface(4, 5, [0, 0, 0]);
enc.update(&s, false).expect("the first one");
s.put(0, 0, [1, 1, 1]);
s.put(0, 4, [1, 1, 1]);
let update = enc.update(&s, true).expect("two bands changed");
assert_eq!(&update[..4], update_header(2));
assert_eq!(&update[4..16], rect_header(0, 0, 4, 1, encoding::RAW));
let second = 16 + 4 * 4;
assert_eq!(
&update[second..second + 12],
rect_header(0, 4, 4, 1, encoding::RAW)
);
}
#[test]
fn a_changed_format_forces_a_whole_frame() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
let s = surface(4, 2, [1, 2, 3]);
enc.update(&s, false).expect("the first one");
assert!(enc.update(&s, true).is_none());
let mut other = PixelFormat::DEFAULT;
other.red_shift = 0;
other.blue_shift = 16;
enc.set_format(other);
let update = enc
.update(&s, true)
.expect("everything it has is wrong now");
assert_eq!(&update[16..20], [1, 2, 3, 0], "R G B in memory now");
}
#[test]
fn a_resize_tells_a_client_that_asked_and_crops_for_one_that_did_not() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
enc.set_encodings(&[encoding::RAW, encoding::DESKTOP_SIZE]);
assert!(enc.resizable());
enc.update(&surface(4, 2, [0, 0, 0]), false).expect("first");
let update = enc
.update(&surface(8, 4, [9, 9, 9]), true)
.expect("resized");
assert_eq!(&update[..4], update_header(2), "DesktopSize, then pixels");
assert_eq!(
&update[4..16],
rect_header(0, 0, 8, 4, encoding::DESKTOP_SIZE)
);
assert_eq!(enc.announced(), (8, 4));
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
enc.set_encodings(&[encoding::RAW]);
assert!(!enc.resizable());
enc.update(&surface(4, 2, [0, 0, 0]), false).expect("first");
let update = enc
.update(&surface(8, 4, [9, 9, 9]), true)
.expect("changed");
assert_eq!(&update[..4], update_header(1));
assert_eq!(&update[4..16], rect_header(0, 0, 4, 2, encoding::RAW));
assert_eq!(
update.len(),
4 + 12 + 4 * 2 * 4,
"still the geometry it was promised"
);
assert_eq!(enc.announced(), (4, 2));
}
#[test]
fn a_shrunken_surface_pads_with_black_rather_than_running_off_the_end() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 4, 2);
let update = enc
.update(&surface(2, 1, [0xff, 0xff, 0xff]), false)
.expect("a frame");
assert_eq!(update.len(), 4 + 12 + 4 * 2 * 4);
let row1 = 16 + 4 * 4;
assert_eq!(&update[row1..row1 + 4], [0, 0, 0, 0]);
}
#[test]
fn an_empty_surface_is_not_a_panic() {
let mut enc = FrameEncoder::new(PixelFormat::DEFAULT, 1, 1);
let update = enc.update(&Surface::empty(), false).expect("a frame");
assert_eq!(update.len(), 4 + 12 + 4);
}
#[test]
fn a_sixteen_bit_client_gets_two_bytes_a_pixel() {
let rgb565 = PixelFormat {
bits_per_pixel: 16,
depth: 16,
big_endian: false,
true_colour: true,
red_max: 31,
green_max: 63,
blue_max: 31,
red_shift: 11,
green_shift: 5,
blue_shift: 0,
};
let mut enc = FrameEncoder::new(rgb565, 2, 2);
let update = enc
.update(&surface(2, 2, [0xff, 0xff, 0xff]), false)
.expect("a frame");
assert_eq!(update.len(), 4 + 12 + 2 * 2 * 2);
assert_eq!(&update[16..18], [0xff, 0xff]);
}
}