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}