diff --git a/crates/ironrdp-error/src/lib.rs b/crates/ironrdp-error/src/lib.rs index 4b467be6..4318874a 100644 --- a/crates/ironrdp-error/src/lib.rs +++ b/crates/ironrdp-error/src/lib.rs @@ -161,7 +161,7 @@ where } } -/// Temporary compability traits to smooth transition from old style +/// Temporary compatibility traits to smooth transition from old style #[cfg(feature = "std")] #[doc(hidden)] pub mod legacy { diff --git a/crates/ironrdp-graphics/src/color_conversion.rs b/crates/ironrdp-graphics/src/color_conversion.rs index 0d1e47d4..d05187c0 100644 --- a/crates/ironrdp-graphics/src/color_conversion.rs +++ b/crates/ironrdp-graphics/src/color_conversion.rs @@ -18,10 +18,6 @@ fn clip(v: i32) -> u8 { min(max(v, 0), 255) as u8 } -fn clip_i16(v: i16) -> u8 { - min(max(v, 0), 255) as u8 -} - #[derive(Debug)] pub struct YCbCrBuffer<'a> { pub y: &'a [i16], @@ -56,13 +52,6 @@ pub struct YCbCr { pub cr: i16, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct YCoCg { - pub y: u8, - pub co: i8, - pub cg: i8, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Rgb { pub r: u8, @@ -90,19 +79,3 @@ impl From for Rgb { Self { r, g, b } } } - -impl From for Rgb { - fn from(YCoCg { y, co, cg }: YCoCg) -> Self { - let y = i16::from(y); - let co = i16::from(co); - let cg = i16::from(cg); - - let t = y - cg / 2; - - let r = clip_i16(t + co / 2); - let g = clip_i16(y + cg / 2); - let b = clip_i16(t - co / 2); - - Self { r, g, b } - } -} diff --git a/crates/ironrdp-graphics/src/rdp6/bitmap_stream.rs b/crates/ironrdp-graphics/src/rdp6/bitmap_stream.rs index 28e85f43..c2355164 100644 --- a/crates/ironrdp-graphics/src/rdp6/bitmap_stream.rs +++ b/crates/ironrdp-graphics/src/rdp6/bitmap_stream.rs @@ -2,7 +2,7 @@ use ironrdp_pdu::bitmap::rdp6::{BitmapStream as BitmapStreamPdu, ColorPlanes}; use ironrdp_pdu::{decode, PduError}; use thiserror::Error; -use crate::color_conversion::{Rgb, YCoCg}; +use crate::color_conversion::Rgb; use crate::rdp6::rle::{decompress_8bpp_plane, RleError}; #[derive(Debug, Error)] @@ -158,8 +158,6 @@ impl<'a> BitmapStreamDecoderImpl<'a> { } fn write_aycocg_planes_to_rgb24(&self, params: AYCoCgParams, planes: &[u8], dst: &mut Vec) { - // For AYCoCg we need to take color loss level and subsampling into account - let chroma_shift = (params.color_loss_level - 1) as usize; let sample_shift = params.chroma_subsampling as usize; let (y_offset, co_offset, cg_offset) = ( @@ -177,10 +175,10 @@ impl<'a> BitmapStreamDecoderImpl<'a> { let chroma_col = (idx % self.image_width) >> sample_shift; let chroma_idx = chroma_row * self.chroma_width + chroma_col; - let co = (co_plane[chroma_idx] << chroma_shift) as i8; - let cg = (cg_plane[chroma_idx] << chroma_shift) as i8; + let co = co_plane[chroma_idx]; + let cg = cg_plane[chroma_idx]; - let Rgb { r, g, b } = YCoCg { y, co, cg }.into(); + let Rgb { r, g, b } = ycocg_with_cll_to_rgb(params.color_loss_level, y, co, cg); // As described in 3.1.9.1.2 [MS-RDPEGDI], R and B channels are swapped for // AYCoCg when 24-bit image is used (no alpha). We swap them back here @@ -206,7 +204,7 @@ impl<'a> BitmapStreamDecoderImpl<'a> { use_chroma_subsampling, .. } => { - let params = AYCoCgParams { + let params: AYCoCgParams = AYCoCgParams { color_loss_level, chroma_subsampling: use_chroma_subsampling, alpha: self.bitmap.use_alpha, @@ -220,6 +218,32 @@ impl<'a> BitmapStreamDecoderImpl<'a> { } } +/// Perform YCoCg -> RGB conversion with color loss redution (CLL) correction. +fn ycocg_with_cll_to_rgb(cll: u8, y: u8, co: u8, cg: u8) -> Rgb { + // We decrease CLL by 1 to skip division by 2 for co & cg components during computation of + // the following color conversion matrix: + // |R| |1 1/2 -1/2| |Y | + // |G| = |1 0 1/2| * |Co| + // |B| |1 -1/2 -1/2| |Cg| + let chroma_shift = (cll - 1) as usize; + + let clip_i16 = |v: i16| v.max(0).min(255) as u8; + + let co_signed = (co << chroma_shift) as i8; + let cg_signed = (cg << chroma_shift) as i8; + + let y = i16::from(y); + let co = i16::from(co_signed); + let cg = i16::from(cg_signed); + + let t = y - cg; + let r = clip_i16(t + co); + let g = clip_i16(y + cg); + let b = clip_i16(t - co); + + Rgb { r, g, b } +} + impl BitmapStreamDecoder { /// Performs decoding of bitmap stream PDU from `bitmap_data` and writes decoded rgb24 /// image to `dst` buffer. @@ -286,35 +310,35 @@ mod tests { } #[test] - fn decode_64x24_aycocg_rle() { + fn decode_64x64_aycocg_rle() { // AYCoCg (With alpha), RLE, no chroma subsampling assert_decoded_image( - include_bytes!("test_assets/64x24_aycocg_rle.bin"), - include_bytes!("test_assets/64x24_aycocg_rle.bmp"), + include_bytes!("test_assets/64x64_aycocg_rle.bin"), + include_bytes!("test_assets/64x64_aycocg_rle.bmp"), + 64, 64, - 24, ); } #[test] - fn decode_64x24_ycocg_rle_ss() { + fn decode_64x64_ycocg_rle_ss() { // AYCoCg (No alpha), RLE, with chroma subsampling assert_decoded_image( - include_bytes!("test_assets/64x24_ycocg_rle_ss.bin"), - include_bytes!("test_assets/64x24_ycocg_rle_ss.bmp"), + include_bytes!("test_assets/64x64_ycocg_rle_ss.bin"), + include_bytes!("test_assets/64x64_ycocg_rle_ss.bmp"), + 64, 64, - 24, ); } #[test] - fn decode_64x57_ycocg_rle_ss() { + fn decode_64x35_ycocg_rle_ss() { // AYCoCg (No alpha), RLE, with chroma subsampling + odd resolution assert_decoded_image( - include_bytes!("test_assets/64x57_ycocg_rle_ss.bin"), - include_bytes!("test_assets/64x57_ycocg_rle_ss.bmp"), + include_bytes!("test_assets/64x35_ycocg_rle_ss.bin"), + include_bytes!("test_assets/64x35_ycocg_rle_ss.bmp"), 64, - 57, + 35, ); } diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bin deleted file mode 100644 index b5ead214..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bin and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bmp deleted file mode 100644 index 112bbb8c..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_aycocg_rle.bmp and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bin deleted file mode 100644 index ad9b2891..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bin and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bmp deleted file mode 100644 index 3f087c7f..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x24_ycocg_rle_ss.bmp and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bin new file mode 100644 index 00000000..7f0d64fc Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bin differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bmp new file mode 100644 index 00000000..b87630b5 Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x35_ycocg_rle_ss.bmp differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bin deleted file mode 100644 index 1125f1b2..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bin and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bmp deleted file mode 100644 index 55fbc0e9..00000000 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x57_ycocg_rle_ss.bmp and /dev/null differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bin new file mode 100644 index 00000000..46560493 Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bin differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bmp new file mode 100644 index 00000000..0c53eea2 Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_aycocg_rle.bmp differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bin index 0e28acad..2ad571ac 100644 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bin and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bin differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bmp index 4a989c5f..4a08aa52 100644 Binary files a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bmp and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_raw_ss.bmp differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bin b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bin new file mode 100644 index 00000000..a7482b77 Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bin differ diff --git a/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bmp b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bmp new file mode 100644 index 00000000..64379db7 Binary files /dev/null and b/crates/ironrdp-graphics/src/rdp6/test_assets/64x64_ycocg_rle_ss.bmp differ diff --git a/crates/ironrdp-session/src/rfx.rs b/crates/ironrdp-session/src/rfx.rs index 8dd0ecef..745651c7 100644 --- a/crates/ironrdp-session/src/rfx.rs +++ b/crates/ironrdp-session/src/rfx.rs @@ -76,7 +76,7 @@ impl DecodingContext { let channels = channels.ok_or(general_err!("channels header is missing"))?; if channels.0.is_empty() { - return Err(general_err!("no RFX channel annouced")); + return Err(general_err!("no RFX channel announced")); } self.context = context;