rfb_encodings/rre.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 RRE (Rise-and-Run-length Encoding) implementation.
16//!
17//! RRE encodes a rectangle as a background color plus a list of subrectangles
18//! with their own colors. Effective for large solid regions.
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 "RRE" (Rise-and-Run-length Encoding).
25///
26/// RRE encodes a rectangle as a background color plus a list of subrectangles
27/// with their own colors. Format: \[nSubrects(u32)\]\[bgColor\]\[subrect1\]...\[subrectN\]
28/// Each subrect: \[color\]\[x(u16)\]\[y(u16)\]\[w(u16)\]\[h(u16)\]
29pub struct RreEncoding;
30
31impl Encoding for RreEncoding {
32 #[allow(clippy::cast_possible_truncation)] // Subrectangle count limited to image size per VNC protocol
33 fn encode(
34 &self,
35 data: &[u8],
36 width: u16,
37 height: u16,
38 _quality: u8,
39 _compression: u8,
40 ) -> BytesMut {
41 // Convert RGBA to RGB pixels (u32 format: 0RGB)
42 let pixels = rgba_to_rgb24_pixels(data);
43
44 // Find background color (most common pixel)
45 let bg_color = get_background_color(&pixels);
46
47 // Find all subrectangles
48 let subrects = find_subrects(&pixels, width as usize, height as usize, bg_color);
49
50 // Always encode all pixels to avoid data loss
51 // (Even if RRE is inefficient, we must preserve the image correctly)
52 let encoded_size = 4 + 4 + (subrects.len() * (4 + 8)); // header + bg + subrects
53
54 let mut buf = BytesMut::with_capacity(encoded_size);
55
56 // Write header
57 buf.put_u32(subrects.len() as u32); // number of subrectangles (big-endian)
58 buf.put_u32_le(bg_color); // background color in client pixel format (little-endian)
59
60 // Write subrectangles
61 for subrect in subrects {
62 buf.put_u32_le(subrect.color); // pixel in client format (little-endian)
63 buf.put_u16(subrect.x); // protocol coordinates (big-endian)
64 buf.put_u16(subrect.y);
65 buf.put_u16(subrect.w);
66 buf.put_u16(subrect.h);
67 }
68
69 buf
70 }
71}