From b55a58ff385186653593da3bc0193fd94d554990 Mon Sep 17 00:00:00 2001 From: Jovi Hsu Date: Mon, 10 Oct 2022 00:23:33 +0000 Subject: [PATCH] zrle encoding --- webvnc/Cargo.toml | 2 + webvnc/src/canvas.rs | 23 ++- webvnc/src/vnc/mod.rs | 5 +- webvnc/src/vnc/vnc_impl.rs | 145 +++++++++------- webvnc/src/vnc/zlib.rs | 343 +++++++++++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+), 69 deletions(-) create mode 100644 webvnc/src/vnc/zlib.rs diff --git a/webvnc/Cargo.toml b/webvnc/Cargo.toml index c8d9d2b..a143172 100644 --- a/webvnc/Cargo.toml +++ b/webvnc/Cargo.toml @@ -13,6 +13,8 @@ default = ["console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.83" js-sys = "0.3" +flate2 = "1" +byteorder = "1" # bytes="1" # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/webvnc/src/canvas.rs b/webvnc/src/canvas.rs index b34426c..3f30de2 100644 --- a/webvnc/src/canvas.rs +++ b/webvnc/src/canvas.rs @@ -1,6 +1,9 @@ use std::rc::Rc; -use crate::vnc::{ImageData, MouseEventType, Vnc}; +use crate::{ + console_log, log, + vnc::{ImageData, MouseEventType, Vnc}, +}; use wasm_bindgen::prelude::*; use wasm_bindgen::{Clamped, JsCast}; use web_sys::{ @@ -183,12 +186,18 @@ impl Canvas { Clamped(&ri.data), ri.width as u32, ri.height as u32, - ) - .unwrap(); - // ConsoleService::log(&format!( - // "renderring at ({}, {}), width {}, height {}", - // cr.x, cr.y, cr.width, cr.height - // )); + ); + if data.is_err() { + console_log!( + "renderring failed at ({}, {}), width {}, height {}, len {}", + ri.x, + ri.y, + ri.width, + ri.height, + ri.data.len(), + ); + } + let data = data.unwrap(); let _ = self.ctx.put_image_data(&data, ri.x as f64, ri.y as f64); } } diff --git a/webvnc/src/vnc/mod.rs b/webvnc/src/vnc/mod.rs index 59843e6..9f98951 100644 --- a/webvnc/src/vnc/mod.rs +++ b/webvnc/src/vnc/mod.rs @@ -2,6 +2,7 @@ mod des; mod vnc_impl; mod x11cursor; mod x11keyboard; +mod zlib; pub enum MouseEventType { Down, @@ -92,10 +93,10 @@ pub struct StreamReader { #[allow(dead_code)] impl StreamReader { - pub fn new(bufs: Vec>) -> Self { + pub fn new(bufs: Vec>, len: usize) -> Self { Self { inner: bufs, - remain: 0, + remain: len, } } diff --git a/webvnc/src/vnc/vnc_impl.rs b/webvnc/src/vnc/vnc_impl.rs index 22c763e..510328f 100644 --- a/webvnc/src/vnc/vnc_impl.rs +++ b/webvnc/src/vnc/vnc_impl.rs @@ -1,5 +1,5 @@ use super::*; -use super::{des, x11cursor::MouseUtils, x11keyboard, MouseEventType}; +use super::{des, x11cursor::MouseUtils, x11keyboard, zlib, MouseEventType}; use crate::{console_log, log}; const VNC_RFB33: &[u8; 12] = b"RFB 003.003\n"; @@ -25,6 +25,7 @@ pub enum SecurityType { #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(i32)] +#[allow(clippy::upper_case_acronyms)] pub enum VncEncoding { Raw = 0, CopyRect = 1, @@ -71,6 +72,7 @@ pub struct Vnc { padding_rect: Option, outbuf: Vec, outs: Vec, + zlib_decoder: zlib::Decoder, } impl Vnc { @@ -78,18 +80,18 @@ impl Vnc { Self { state: VncState::Init, supported_encodings: vec![ - VncEncoding::Raw, + // VncEncoding::Raw, VncEncoding::CopyRect, // VncEncoding::RRE, // VncEncoding::Hextile, // VncEncoding::TRLE, - // VncEncoding::ZRLE, + VncEncoding::ZRLE, // VncEncoding::CursorPseudo, // VncEncoding::DesktopSizePseudo, ], security_type: SecurityType::Invalid, challenge: [0; 16], - reader: StreamReader::new(Vec::with_capacity(10)), + reader: StreamReader::new(Vec::with_capacity(10), 0), mouse: MouseUtils::new(), require: 12, // the handleshake message length width: 0, @@ -101,24 +103,21 @@ impl Vnc { padding_rect: None, outbuf: Vec::with_capacity(128), outs: Vec::with_capacity(10), + zlib_decoder: zlib::Decoder::new(), } } pub fn do_input(&mut self, input: Vec) { - // ConsoleService::info(&format!( + // console_log!( // "VNC input {}, left {}, require {}", // input.len(), // self.reader.remain(), // self.require - // )); + // ); self.reader.append(input); while self.reader.remain() >= self.require { self.handle_input(); - // ConsoleService::info(&format!( - // "left {}, require {}", - // self.reader.remain(), - // self.require - // )); + // console_log!("left {}, require {}", self.reader.remain(), self.require); } } @@ -471,7 +470,12 @@ impl Vnc { self.read_exact(&mut pfb, 16); // This pixel format will be used unless the client requests a different format using the SetPixelFormat message self.pf = (&pfb).into(); - console_log!("VNC: {}x{}", self.width, self.height); + console_log!( + "VNC: {}x{}\nPixel Format {:#?}", + self.width, + self.height, + self.pf + ); self.name = self.read_string_l32(); self.state = VncState::Connected; self.require = 1; // any message from sever will be handled @@ -506,7 +510,7 @@ impl Vnc { fn handle_framebuffer_update(&mut self) { let _padding = self.read_u8(); self.num_rect_left = self.read_u16(); - // console_log!("VNC: {} rects", self.num_rects_left); + // console_log!("VNC: {} rects", self.num_rect_left); self.require = 12; // the length of the first rectangle hdr self.msg_handling = ServerMessage::FramebufferUpdate; } @@ -546,32 +550,25 @@ impl Vnc { } } } else { + match self.padding_rect.as_ref().unwrap().encoding_type { + 16 => { + if self.require == 4 { + self.require = self.read_u32() as usize; + return; + } + } + _ => (), + } // we now have an entire rectangle let rect = self.padding_rect.take().unwrap(); let mut image_data: Vec = Vec::with_capacity(self.require); + self.read_exact_vec(&mut image_data, self.require); match rect.encoding_type { 0 => { - // for _ in 0..rect.height { - // for _ in 0..rect.width { - // let mut pixel = [0u8; 4]; - // self.read_exact(&mut pixel, 4); - // let b = pixel[0]; - // let g = pixel[1]; - // let r = pixel[2]; - // let a = pixel[3]; - // image_data.extend_from_slice(&[r, g, b, a]); - // } - // } - self.read_exact_vec(&mut image_data, self.require); + // raw let mut y = 0; let mut x = 0; - // for y in 0..rect.height { - // for x in 0..rect.width { - // let idx = (y as usize * rect.width as usize + x as usize) * 4; - // image_data.swap(idx, idx + 2) - // } - // } while y < rect.height { while x < rect.width { let idx = (y as usize * rect.width as usize + x as usize) * 4; @@ -581,21 +578,41 @@ impl Vnc { x = 0; y += 1; } + self.outs.push(VncOutput::RenderImage(ImageData { + type_: rect.encoding_type, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + data: image_data, + })); } 1 => { // copy rectangle - self.read_exact_vec(&mut image_data, 4); + self.outs.push(VncOutput::RenderImage(ImageData { + type_: rect.encoding_type, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + data: image_data, + })); } - _ => unimplemented!(), + 16 => { + // ZRLE + match self.zlib_decoder.decode(&self.pf, &rect, &image_data) { + Ok(images) => { + for i in images { + self.outs.push(VncOutput::RenderImage(i)); + } + } + Err(e) => { + self.outs.push(VncOutput::Err(format!("{:?}", e))); + } + } + } + _ => unimplemented!("Unknow encoding {}", rect.encoding_type), } - self.outs.push(VncOutput::RenderImage(ImageData { - type_: rect.encoding_type, - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height, - data: image_data, - })); self.num_rect_left -= 1; if 0 == self.num_rect_left { self.msg_handling = ServerMessage::None; @@ -713,8 +730,15 @@ impl Vnc { unimplemented!() } - fn handle_zrle_encoding(&mut self, _x: u16, _y: u16, _width: u16, _height: u16) { - unimplemented!() + fn handle_zrle_encoding(&mut self, x: u16, y: u16, width: u16, height: u16) { + self.require = 4; + self.padding_rect = Some(VncRect { + x, + y, + width, + height, + encoding_type: 16, + }) } fn handle_cursor_pseudo_encoding(&mut self, _x: u16, _y: u16, _width: u16, _height: u16) { @@ -739,24 +763,24 @@ impl Vnc { // 1 CARD8 blue-shift // 1 CARD8 padding #[derive(Debug, Clone, Copy, Default)] -struct PixelFormat { +pub struct PixelFormat { // the number of bits used for each pixel value on the wire // 8, 16, 32(usually) only - bits_per_pixel: u8, - depth: u8, + pub bits_per_pixel: u8, + pub depth: u8, // true if multi-byte pixels are interpreted as big endian - big_endian_flag: u8, + pub big_endian_flag: u8, // true then the last six items specify how to extract the red, green and blue intensities from the pixel value - true_color_flag: u8, + pub true_color_flag: u8, // the next three always in big-endian order // no matter how the `big_endian_flag` is set - red_max: u16, - green_max: u16, - blue_max: u16, + pub red_max: u16, + pub green_max: u16, + pub blue_max: u16, // the number of shifts needed to get the red value in a pixel to the least significant bit - red_shift: u8, - green_shift: u8, - blue_shift: u8, + pub red_shift: u8, + pub green_shift: u8, + pub blue_shift: u8, padding_1: u8, padding_2: u8, padding_3: u8, @@ -818,10 +842,11 @@ impl From<&[u8; 16]> for PixelFormat { } } -struct VncRect { - x: u16, - y: u16, - width: u16, - height: u16, - encoding_type: u32, +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VncRect { + pub x: u16, + pub y: u16, + pub width: u16, + pub height: u16, + pub encoding_type: u32, } diff --git a/webvnc/src/vnc/zlib.rs b/webvnc/src/vnc/zlib.rs new file mode 100644 index 0000000..c88e5a6 --- /dev/null +++ b/webvnc/src/vnc/zlib.rs @@ -0,0 +1,343 @@ +/* +Copyright (c) 2016 whitequark + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +use super::vnc_impl::*; +use super::ImageData; +use byteorder::ReadBytesExt; +use std::io::{Read, Result}; + +struct ZlibReader<'a> { + decompressor: flate2::Decompress, + input: &'a [u8], +} + +impl<'a> ZlibReader<'a> { + fn new(decompressor: flate2::Decompress, input: &'a [u8]) -> ZlibReader<'a> { + ZlibReader { + decompressor, + input, + } + } + + fn into_inner(self) -> Result { + if self.input.is_empty() { + Ok(self.decompressor) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "leftover ZRLE byte data", + )) + } + } +} + +impl<'a> Read for ZlibReader<'a> { + fn read(&mut self, output: &mut [u8]) -> std::io::Result { + let in_before = self.decompressor.total_in(); + let out_before = self.decompressor.total_out(); + let result = + self.decompressor + .decompress(self.input, output, flate2::FlushDecompress::None); + let consumed = (self.decompressor.total_in() - in_before) as usize; + let produced = (self.decompressor.total_out() - out_before) as usize; + + self.input = &self.input[consumed..]; + match result { + Ok(flate2::Status::Ok) => Ok(produced), + Ok(flate2::Status::BufError) => Ok(0), + Err(error) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error)), + Ok(flate2::Status::StreamEnd) => Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "ZRLE stream end", + )), + } + } +} + +struct BitReader { + reader: T, + buffer: u8, + position: usize, +} + +impl BitReader { + fn new(reader: T) -> BitReader { + BitReader { + reader, + buffer: 0, + position: 8, + } + } + + fn into_inner(self) -> Result { + if self.position == 8 { + Ok(self.reader) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "leftover ZRLE bit data", + )) + } + } + + fn read_bits(&mut self, count: usize) -> std::io::Result { + assert!(count > 0 && count <= 8); + + if self.position == 8 { + self.buffer = self.reader.read_u8()?; + self.position = 0; + } + + if self.position + count <= 8 { + let shift = 8 - (count + self.position); + let mask = (1 << count) - 1; + let result = (self.buffer >> shift) & mask; + self.position += count; + Ok(result) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "unaligned ZRLE bit read", + )) + } + } + + fn read_bit(&mut self) -> std::io::Result { + Ok(self.read_bits(1)? != 0) + } + + fn align(&mut self) { + self.position = 8; + } +} + +impl Read for BitReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + if self.position == 8 { + self.reader.read(buf) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "unaligned ZRLE byte read", + )) + } + } +} + +pub struct Decoder { + decompressor: Option, +} + +impl Decoder { + pub fn new() -> Decoder { + Decoder { + decompressor: Some(flate2::Decompress::new(/*zlib_header*/ true)), + } + } + + pub fn decode( + &mut self, + format: &PixelFormat, + rect: &VncRect, + input: &[u8], + ) -> Result> { + fn read_run_length(reader: &mut dyn Read) -> Result { + let mut run_length_part; + let mut run_length = 1; + loop { + run_length_part = reader.read_u8()?; + run_length += run_length_part as usize; + if 255 != run_length_part { + break; + } + } + Ok(run_length) + } + + fn copy_true_color( + reader: &mut dyn Read, + pixels: &mut Vec, + pad: bool, + compressed_bpp: usize, + bpp: usize, + ) -> Result<()> { + let mut buf = [255; 4]; + reader.read_exact(&mut buf[pad as usize..pad as usize + compressed_bpp])?; + buf.swap(0, 2); + pixels.extend_from_slice(&buf[..bpp]); + Ok(()) + } + + fn copy_indexed(palette: &[u8], pixels: &mut Vec, bpp: usize, index: u8) { + let start = index as usize * bpp; + pixels.extend_from_slice(&palette[start..start + bpp]) + } + + let mut ret = Vec::new(); + + let bpp = format.bits_per_pixel as usize / 8; + let pixel_mask = (format.red_max as u32) << format.red_shift + | (format.green_max as u32) << format.green_shift + | (format.blue_max as u32) << format.blue_shift; + + let (compressed_bpp, pad_pixel) = + if format.bits_per_pixel == 32 && format.true_color_flag > 0 && format.depth <= 24 { + if pixel_mask & 0x000000ff == 0 { + (3, format.big_endian_flag > 0) + } else if pixel_mask & 0xff000000 == 0 { + (3, format.big_endian_flag == 0) + } else { + (4, false) + } + } else { + (bpp, false) + }; + + let mut palette = Vec::with_capacity(128 * bpp); + let mut reader = BitReader::new(ZlibReader::new(self.decompressor.take().unwrap(), input)); + + let mut y = 0; + while y < rect.height { + let height = if y + 64 > rect.height { + rect.height - y + } else { + 64 + }; + let mut x = 0; + while x < rect.width { + let width = if x + 64 > rect.width { + rect.width - x + } else { + 64 + }; + let pixel_count = height as usize * width as usize; + + let is_rle = reader.read_bit()?; + let palette_size = reader.read_bits(7)?; + palette.truncate(0); + for _ in 0..palette_size { + copy_true_color(&mut reader, &mut palette, pad_pixel, compressed_bpp, bpp)? + } + + let mut pixels = Vec::with_capacity(pixel_count * bpp); + match (is_rle, palette_size) { + (false, 0) => { + // True Color pixels + for _ in 0..pixel_count { + copy_true_color( + &mut reader, + &mut pixels, + pad_pixel, + compressed_bpp, + bpp, + )? + } + } + (false, 1) => { + // Color fill + for _ in 0..pixel_count { + copy_indexed(&palette, &mut pixels, bpp, 0) + } + } + (false, 2) | (false, 3..=4) | (false, 5..=16) => { + // Indexed pixels + let bits_per_index = match palette_size { + 2 => 1, + 3..=4 => 2, + 5..=16 => 4, + _ => unreachable!(), + }; + for _ in 0..height { + for _ in 0..width { + let index = reader.read_bits(bits_per_index)?; + copy_indexed(&palette, &mut pixels, bpp, index) + } + reader.align(); + } + } + (true, 0) => { + // True Color RLE + let mut count = 0; + let mut pixel = Vec::new(); + while count < pixel_count { + pixel.truncate(0); + copy_true_color( + &mut reader, + &mut pixel, + pad_pixel, + compressed_bpp, + bpp, + )?; + let run_length = read_run_length(&mut reader)?; + for _ in 0..run_length { + pixels.extend(&pixel) + } + count += run_length; + } + } + (true, 2..=127) => { + // Indexed RLE + let mut count = 0; + while count < pixel_count { + let longer_than_one = reader.read_bit()?; + let index = reader.read_bits(7)?; + let run_length = if longer_than_one { + read_run_length(&mut reader)? + } else { + 1 + }; + for _ in 0..run_length { + copy_indexed(&palette, &mut pixels, bpp, index); + } + count += run_length; + } + } + _ => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "ZRLE subencoding", + )) + } + } + ret.push(ImageData { + data: pixels, + height, + width, + x: rect.x + x, + y: rect.y + y, + type_: 0, + }); + x += width; + } + y += height; + } + + self.decompressor = Some(reader.into_inner()?.into_inner()?); + Ok(ret) + } +}