pub mod hextile;
pub mod raw;
pub mod tight;
pub mod zlib;
pub mod zrle;
use crate::error::Result;
use crate::protocol::{
ENCODING_HEXTILE, ENCODING_RAW, ENCODING_TIGHT, ENCODING_ZLIB, ENCODING_ZRLE,
PSEUDO_TIGHT_QUALITY_BASE,
};
use log::info;
pub trait Encoder {
fn encoding_id(&self) -> i32;
#[allow(clippy::too_many_arguments)]
fn encode_rect_into(
&mut self,
pixels: &[u8],
stride: usize,
x: u16,
y: u16,
w: u16,
h: u16,
swap_rb: bool,
out: &mut Vec<u8>,
) -> Result<()>;
}
pub struct EncoderSet {
raw: raw::RawEncoder,
hextile: hextile::HextileEncoder,
zlib: zlib::ZlibCompressor,
zrle: zrle::ZrleEncoder,
tight: tight::TightEncoder,
active: i32,
buf: Vec<u8>,
}
impl Default for EncoderSet {
fn default() -> Self {
Self::new()
}
}
impl EncoderSet {
pub fn new() -> Self {
Self {
raw: raw::RawEncoder,
hextile: hextile::HextileEncoder::new(),
zlib: zlib::ZlibCompressor::new(),
zrle: zrle::ZrleEncoder::new(),
tight: tight::TightEncoder::new(),
active: 0,
buf: Vec::with_capacity(256 * 1024),
}
}
pub fn negotiate(&mut self, client_encodings: &[i32]) {
for &enc in client_encodings {
if (PSEUDO_TIGHT_QUALITY_BASE..=PSEUDO_TIGHT_QUALITY_BASE + 9).contains(&enc) {
let level = (enc - PSEUDO_TIGHT_QUALITY_BASE) as u8;
let quality = (level * 10 + 10).clamp(10, 100);
self.tight.set_quality(quality);
info!("Client requested Tight quality level {} (JPEG quality {})", level, quality);
}
}
for &enc in client_encodings {
match enc {
ENCODING_TIGHT => {
info!("Negotiated encoding: Tight (ID 7)");
self.active = enc;
return;
}
ENCODING_ZRLE => {
info!("Negotiated encoding: ZRLE (ID 16)");
self.active = enc;
return;
}
ENCODING_HEXTILE => {
info!("Negotiated encoding: Hextile (ID 5)");
self.active = enc;
return;
}
ENCODING_ZLIB => {
info!("Negotiated encoding: Zlib (ID 6)");
self.active = enc;
return;
}
ENCODING_RAW => {
info!("Negotiated encoding: Raw (ID 0)");
self.active = enc;
return;
}
_ => continue,
}
}
info!("Negotiated encoding: Raw (fallback)");
self.active = 0;
}
pub fn encoding_id(&self) -> i32 {
self.active
}
#[allow(clippy::too_many_arguments)]
pub fn encode_rect(
&mut self,
pixels: &[u8],
stride: usize,
x: u16,
y: u16,
w: u16,
h: u16,
swap_rb: bool,
) -> Result<&[u8]> {
self.buf.clear();
match self.active {
ENCODING_TIGHT => self
.tight
.encode_rect_into(pixels, stride, x, y, w, h, swap_rb, &mut self.buf)?,
ENCODING_ZRLE => self
.zrle
.encode_rect_into(pixels, stride, x, y, w, h, swap_rb, &mut self.buf)?,
ENCODING_HEXTILE => {
self.hextile
.encode_rect_into(pixels, stride, x, y, w, h, swap_rb, &mut self.buf)?
}
ENCODING_ZLIB => self
.zlib
.encode_rect_into(pixels, stride, x, y, w, h, swap_rb, &mut self.buf)?,
_ => self
.raw
.encode_rect_into(pixels, stride, x, y, w, h, swap_rb, &mut self.buf)?,
}
Ok(&self.buf)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_negotiate_tight() {
let mut encoders = EncoderSet::new();
encoders.negotiate(&[-24, ENCODING_TIGHT, ENCODING_ZRLE, ENCODING_RAW]);
assert_eq!(encoders.encoding_id(), ENCODING_TIGHT);
}
#[test]
fn test_negotiate_zrle_when_tight_absent() {
let mut encoders = EncoderSet::new();
encoders.negotiate(&[ENCODING_ZRLE, ENCODING_RAW]);
assert_eq!(encoders.encoding_id(), ENCODING_ZRLE);
}
#[test]
fn test_negotiate_fallback_raw() {
let mut encoders = EncoderSet::new();
encoders.negotiate(&[999, 888]);
assert_eq!(encoders.encoding_id(), ENCODING_RAW);
}
}