use rat_rdp_pdu::codecs::clearcodec::{SHORT_VBAR_CACHE_SIZE, VBAR_CACHE_SIZE};
const VBAR_WRAP: u16 = 32_768;
const SHORT_VBAR_WRAP: u16 = 16_384;
#[derive(Debug, Clone)]
pub struct FullVBar {
pub pixels: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ShortVBar {
pub y_on: u8,
pub pixel_count: u8,
pub pixels: Vec<u8>,
}
pub struct VBarCache {
vbar_storage: Vec<Option<FullVBar>>,
short_vbar_storage: Vec<Option<ShortVBar>>,
vbar_cursor: u16,
short_vbar_cursor: u16,
}
impl VBarCache {
pub fn new() -> Self {
let mut vbar_storage = Vec::with_capacity(VBAR_CACHE_SIZE);
vbar_storage.resize_with(VBAR_CACHE_SIZE, || None);
let mut short_vbar_storage = Vec::with_capacity(SHORT_VBAR_CACHE_SIZE);
short_vbar_storage.resize_with(SHORT_VBAR_CACHE_SIZE, || None);
Self {
vbar_storage,
short_vbar_storage,
vbar_cursor: 0,
short_vbar_cursor: 0,
}
}
pub fn reset(&mut self) {
self.vbar_cursor = 0;
self.short_vbar_cursor = 0;
}
pub fn get_vbar(&self, index: u16) -> Option<&FullVBar> {
self.vbar_storage.get(usize::from(index)).and_then(|slot| slot.as_ref())
}
pub fn get_short_vbar(&self, index: u16) -> Option<&ShortVBar> {
self.short_vbar_storage
.get(usize::from(index))
.and_then(|slot| slot.as_ref())
}
pub fn store_short_vbar(&mut self, short_vbar: ShortVBar) -> u16 {
let index = self.short_vbar_cursor;
self.short_vbar_storage[usize::from(index)] = Some(short_vbar);
self.short_vbar_cursor = (index + 1) % SHORT_VBAR_WRAP;
index
}
pub fn store_vbar(&mut self, vbar: FullVBar) -> u16 {
let index = self.vbar_cursor;
self.vbar_storage[usize::from(index)] = Some(vbar);
self.vbar_cursor = (index + 1) % VBAR_WRAP;
index
}
pub fn reconstruct_full_vbar(
short_vbar: &ShortVBar,
band_height: u16,
bg_blue: u8,
bg_green: u8,
bg_red: u8,
) -> FullVBar {
let height = usize::from(band_height);
let mut pixels = Vec::with_capacity(height * 3);
for _ in 0..usize::from(short_vbar.y_on) {
pixels.push(bg_blue);
pixels.push(bg_green);
pixels.push(bg_red);
}
pixels.extend_from_slice(&short_vbar.pixels);
let bottom_start = usize::from(short_vbar.y_on) + usize::from(short_vbar.pixel_count);
for _ in bottom_start..height {
pixels.push(bg_blue);
pixels.push(bg_green);
pixels.push(bg_red);
}
FullVBar { pixels }
}
}
impl Default for VBarCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_and_retrieve_vbar() {
let mut cache = VBarCache::new();
let vbar = FullVBar {
pixels: vec![0xFF, 0x00, 0x00],
};
let idx = cache.store_vbar(vbar);
assert_eq!(idx, 0);
let retrieved = cache.get_vbar(0).unwrap();
assert_eq!(retrieved.pixels, vec![0xFF, 0x00, 0x00]);
}
#[test]
fn cursor_wraps() {
let mut cache = VBarCache::new();
for i in 0..VBAR_CACHE_SIZE {
let idx = cache.store_vbar(FullVBar {
pixels: vec![u8::try_from(i & 0xFF).unwrap()],
});
assert_eq!(idx, u16::try_from(i).unwrap());
}
let idx = cache.store_vbar(FullVBar { pixels: vec![0xAA] });
assert_eq!(idx, 0);
}
#[test]
fn reconstruct_full_vbar() {
let short = ShortVBar {
y_on: 1,
pixel_count: 2,
pixels: vec![0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00], };
let full = VBarCache::reconstruct_full_vbar(&short, 4, 0xAA, 0xBB, 0xCC);
assert_eq!(full.pixels.len(), 12); assert_eq!(&full.pixels[0..3], &[0xAA, 0xBB, 0xCC]);
assert_eq!(&full.pixels[3..9], &[0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00]);
assert_eq!(&full.pixels[9..12], &[0xAA, 0xBB, 0xCC]);
}
#[test]
fn reset_resets_cursors() {
let mut cache = VBarCache::new();
cache.store_vbar(FullVBar { pixels: vec![0x01] });
cache.store_short_vbar(ShortVBar {
y_on: 0,
pixel_count: 0,
pixels: vec![],
});
assert_eq!(cache.vbar_cursor, 1);
assert_eq!(cache.short_vbar_cursor, 1);
cache.reset();
assert_eq!(cache.vbar_cursor, 0);
assert_eq!(cache.short_vbar_cursor, 0);
}
}