rfb_encodings/corre.rs
1// Copyright 2025 Dustin McAfee
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! VNC `CoRRE` (Compact RRE) encoding implementation.
16//!
17//! `CoRRE` is like RRE but uses compact subrectangles with u8 coordinates.
18//! More efficient for small rectangles.
19
20use super::common::{find_subrects, get_background_color, rgba_to_rgb24_pixels};
21use crate::Encoding;
22use bytes::{BufMut, BytesMut};
23
24/// Implements the VNC "`CoRRE`" (Compact RRE) encoding.
25///
26/// `CoRRE` is like RRE but uses compact subrectangles with u8 coordinates.
27/// Format: \[bgColor\]\[nSubrects(u8)\]\[subrect1\]...\[subrectN\]
28/// Each subrect: \[color\]\[x(u8)\]\[y(u8)\]\[w(u8)\]\[h(u8)\]
29pub struct CorRreEncoding;
30
31impl Encoding for CorRreEncoding {
32 #[allow(clippy::cast_possible_truncation)] // CoRRE protocol uses u8 coordinates/dimensions per RFC 6143
33 fn encode(
34 &self,
35 data: &[u8],
36 width: u16,
37 height: u16,
38 _quality: u8,
39 _compression: u8,
40 ) -> BytesMut {
41 // CoRRE format per RFC 6143:
42 // Protocol layer writes: FramebufferUpdateRectHeader + nSubrects count
43 // Encoder writes: bgColor + subrects
44 // Each subrect: color(4) + x(1) + y(1) + w(1) + h(1)
45 let pixels = rgba_to_rgb24_pixels(data);
46 let bg_color = get_background_color(&pixels);
47
48 // Find subrectangles
49 let subrects = find_subrects(&pixels, width as usize, height as usize, bg_color);
50
51 // Encoder output: background color + subrectangle data
52 // Protocol layer will write nSubrects separately
53 let mut buf = BytesMut::with_capacity(4 + subrects.len() * 8);
54 buf.put_u32_le(bg_color); // background pixel value (little-endian)
55
56 // Write subrectangles
57 for subrect in &subrects {
58 buf.put_u32_le(subrect.color); // pixel color (little-endian)
59 buf.put_u8(subrect.x as u8); // x coordinate (u8)
60 buf.put_u8(subrect.y as u8); // y coordinate (u8)
61 buf.put_u8(subrect.w as u8); // width (u8)
62 buf.put_u8(subrect.h as u8); // height (u8)
63 }
64
65 #[cfg(feature = "debug-logging")]
66 {
67 // HEX DUMP: Log the exact bytes being encoded
68 let hex_str: String = buf
69 .iter()
70 .take(32) // Only show first 32 bytes
71 .map(|b| format!("{b:02x}"))
72 .collect::<Vec<String>>()
73 .join(" ");
74 log::info!(
75 "CoRRE encoded {}x{}: {} bytes ({}subrects) = [{}...]",
76 width,
77 height,
78 buf.len(),
79 subrects.len(),
80 hex_str
81 );
82 }
83
84 buf
85 }
86}