ironrdp_graphics/rdp6/
rle.rs

1use core::cmp;
2use std::io::{Read as _, Write as _};
3
4use byteorder::ReadBytesExt as _;
5use ironrdp_core::WriteCursor;
6
7/// Maximum possible segment size is 47 (run_length = 2, raw_bytes_count = 15), which is treated as
8/// special mode segment, which repeats last decoded byte in scanline 32 + raw_bytes_count times
9const MAX_DECODED_SEGMENT_SIZE: usize = 47;
10
11#[derive(Debug)]
12pub enum RleDecodeError {
13    ReadCompressedData(std::io::Error),
14    WriteDecompressedData(std::io::Error),
15    InvalidSegmentHeader,
16    SegmentDoNotFitScanline,
17}
18
19impl core::fmt::Display for RleDecodeError {
20    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21        match self {
22            RleDecodeError::ReadCompressedData(_error) => write!(f, "failed to read RLE-compressed data"),
23            RleDecodeError::WriteDecompressedData(_error) => write!(f, "failed to write decompressed data"),
24            RleDecodeError::InvalidSegmentHeader => write!(f, "invalid RLE segment header"),
25            RleDecodeError::SegmentDoNotFitScanline => {
26                write!(f, "decoded scanline segments length exceeds scanline length")
27            }
28        }
29    }
30}
31
32impl core::error::Error for RleDecodeError {
33    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
34        match self {
35            RleDecodeError::ReadCompressedData(error) => Some(error),
36            RleDecodeError::WriteDecompressedData(error) => Some(error),
37            RleDecodeError::InvalidSegmentHeader => None,
38            RleDecodeError::SegmentDoNotFitScanline => None,
39        }
40    }
41}
42
43#[derive(Debug)]
44pub enum RleEncodeError {
45    NotEnoughBytes,
46    BufferTooSmall,
47}
48
49impl core::fmt::Display for RleEncodeError {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        match self {
52            RleEncodeError::NotEnoughBytes => write!(f, "not enough data to compress"),
53            RleEncodeError::BufferTooSmall => write!(f, "destination buffer is too small"),
54        }
55    }
56}
57
58impl core::error::Error for RleEncodeError {}
59
60/// RLE-encoded color plane decoder implementation for RDP6 bitmap stream
61#[derive(Debug)]
62struct RlePlaneDecoder {
63    /// RDP6 performs per-scanline encoding, therefore segment decoder require state reset
64    /// for when each scanline is started (e.g. resetting last decoded byte value to 0)
65    last_decoded_byte: u8,
66
67    width: usize,
68    height: usize,
69
70    decoded_data: [u8; MAX_DECODED_SEGMENT_SIZE],
71    decoded_data_len: usize,
72}
73
74impl RlePlaneDecoder {
75    fn new(width: usize, height: usize) -> Self {
76        Self {
77            last_decoded_byte: 0,
78            width,
79            height,
80            decoded_data: [0; MAX_DECODED_SEGMENT_SIZE],
81            decoded_data_len: 0,
82        }
83    }
84
85    fn decompress_next_segment(&mut self, mut src: &[u8]) -> Result<usize, RleDecodeError> {
86        let control_byte = src.read_u8().map_err(RleDecodeError::ReadCompressedData)?;
87
88        if control_byte == 0 {
89            return Err(RleDecodeError::InvalidSegmentHeader);
90        }
91
92        let rle_bytes_field = control_byte & 0x0F;
93        let raw_bytes_field = (control_byte >> 4) & 0x0F;
94
95        let (run_length, raw_bytes_count) = match rle_bytes_field {
96            1 => (16 + usize::from(raw_bytes_field), 0),
97            2 => (32 + usize::from(raw_bytes_field), 0),
98            rle_control => (usize::from(rle_control), usize::from(raw_bytes_field)),
99        };
100
101        self.decoded_data_len = raw_bytes_count + run_length;
102
103        src.read_exact(&mut self.decoded_data[..raw_bytes_count])
104            .map_err(RleDecodeError::ReadCompressedData)?;
105
106        if raw_bytes_count > 0 {
107            // save last decoded byte for the next segments decoding
108            self.last_decoded_byte = self.decoded_data[raw_bytes_count - 1];
109        }
110
111        self.decoded_data[raw_bytes_count..self.decoded_data_len].fill(self.last_decoded_byte);
112
113        Ok(raw_bytes_count + 1)
114    }
115
116    /// Decodes single RLE-encoded scanline, without performing delta transformation
117    fn decode_scanline(&mut self, src: &[u8], mut dst: &mut [u8]) -> Result<usize, RleDecodeError> {
118        let mut decoded_columns = 0;
119        let mut read_bytes = 0;
120
121        self.last_decoded_byte = 0;
122
123        while decoded_columns < self.width {
124            read_bytes += self.decompress_next_segment(&src[read_bytes..])?;
125
126            if decoded_columns + self.decoded_data_len > self.width {
127                return Err(RleDecodeError::SegmentDoNotFitScanline);
128            }
129
130            dst.write_all(&self.decoded_data[..self.decoded_data_len])
131                .map_err(RleDecodeError::WriteDecompressedData)?;
132
133            decoded_columns += self.decoded_data_len;
134        }
135
136        Ok(read_bytes)
137    }
138
139    /// Performs delta transformation as described in 3.1.9.2.3 of [MS-RDPEGDI]
140    fn resolve_scanline_delta(prev_line: &[u8], current_scanline: &mut [u8]) {
141        assert!(prev_line.len() == current_scanline.len());
142
143        current_scanline
144            .iter_mut()
145            .zip(prev_line.iter())
146            .for_each(|(dst, src)| {
147                let delta = *dst;
148                let value_above = *src;
149
150                let transformed_delta = if delta % 2 == 1 {
151                    255u8.wrapping_sub((delta.wrapping_sub(1)) >> 1)
152                } else {
153                    delta >> 1
154                };
155
156                *dst = value_above.wrapping_add(transformed_delta);
157            });
158    }
159
160    fn decode(mut self, src: &[u8], dst: &mut [u8]) -> Result<usize, RleDecodeError> {
161        let mut read_bytes = 0;
162
163        read_bytes += self.decode_scanline(src, dst)?;
164
165        let (mut prev_scanline, mut dst) = dst.split_at_mut(self.width);
166
167        for _ in 1..self.height {
168            let current_scanline = &mut dst[..self.width];
169
170            read_bytes += self.decode_scanline(&src[read_bytes..], current_scanline)?;
171            Self::resolve_scanline_delta(prev_scanline, current_scanline);
172
173            (prev_scanline, dst) = dst.split_at_mut(self.width);
174        }
175
176        Ok(read_bytes)
177    }
178}
179
180/// Performs decompression of 8bpp color plane into slice.
181/// Slice must have enough space for decompressed data.
182/// Size of data written to dst buffer is exactly equal to `width * height`.
183///
184/// Returns number of bytes consumed from src buffer.
185pub(crate) fn decompress_8bpp_plane(
186    src: &[u8],
187    dst: &mut [u8],
188    width: usize,
189    height: usize,
190) -> Result<usize, RleDecodeError> {
191    RlePlaneDecoder::new(width, height).decode(src, dst)
192}
193
194struct RleEncoderScanlineIterator<I> {
195    inner: core::iter::Enumerate<I>,
196    width: usize,
197    prev_scanline: Vec<u8>,
198}
199
200impl<I: Iterator> RleEncoderScanlineIterator<I> {
201    fn new(width: usize, inner: I) -> Self {
202        Self {
203            width,
204            inner: inner.enumerate(),
205            prev_scanline: vec![0; width],
206        }
207    }
208
209    fn delta_value(prev: u8, next: u8) -> u8 {
210        let mut result = u8::try_from((i16::from(next) - i16::from(prev)) & 0xFF)
211            .expect("masking with 0xFF ensures that the value fits into u8");
212
213        // bit magic from 3.1.9.2.1 of [MS-RDPEGDI].
214        if result < 128 {
215            result <<= 1;
216        } else {
217            result = (255u8.wrapping_sub(result) << 1).wrapping_add(1);
218        }
219
220        result
221    }
222}
223
224impl<I: Iterator<Item = u8>> Iterator for RleEncoderScanlineIterator<I> {
225    type Item = I::Item;
226
227    fn next(&mut self) -> Option<Self::Item> {
228        let (idx, mut next) = self.inner.next()?;
229
230        let prev = core::mem::replace(&mut self.prev_scanline[idx % self.width], next);
231        if idx >= self.width {
232            next = Self::delta_value(prev, next);
233        }
234
235        Some(next)
236    }
237
238    fn size_hint(&self) -> (usize, Option<usize>) {
239        self.inner.size_hint()
240    }
241}
242
243#[derive(Debug)]
244struct RlePlaneEncoder {
245    width: usize,
246    height: usize,
247}
248
249macro_rules! ensure_size {
250    (dst: $buf:ident, size: $expected:expr) => {{
251        let available = $buf.len();
252        let needed = $expected;
253        if !(needed <= available) {
254            return Err(RleEncodeError::BufferTooSmall);
255        }
256    }};
257}
258
259impl RlePlaneEncoder {
260    fn new(width: usize, height: usize) -> Self {
261        Self { width, height }
262    }
263
264    fn encode(&self, mut src: impl Iterator<Item = u8>, dst: &mut WriteCursor<'_>) -> Result<usize, RleEncodeError> {
265        let mut written = 0;
266
267        for _ in 0..self.height {
268            written += self.encode_scanline((&mut src).take(self.width), dst)?;
269        }
270
271        Ok(written)
272    }
273
274    fn encode_scanline(
275        &self,
276        mut src: impl Iterator<Item = u8>,
277        dst: &mut WriteCursor<'_>,
278    ) -> Result<usize, RleEncodeError> {
279        let mut written = 0;
280        let first = src.next().ok_or(RleEncodeError::NotEnoughBytes)?;
281
282        let mut raw = vec![first];
283        let mut seq = (first, 0);
284
285        for byte in src {
286            let (last, count) = seq;
287
288            seq = if byte == last {
289                (byte, count + 1)
290            } else {
291                match count {
292                    3.. => {
293                        written += self.encode_segment(&raw, count, dst)?;
294                        raw.clear();
295                    }
296                    2 => raw.extend_from_slice(&[last, last]),
297                    1 => raw.push(last),
298                    _ => {}
299                }
300
301                raw.push(byte);
302
303                (byte, 0)
304            }
305        }
306
307        let (last, mut count) = seq;
308        if count < 3 {
309            raw.extend(vec![last; count]);
310            count = 0;
311        }
312
313        written += self.encode_segment(&raw, count, dst)?;
314
315        Ok(written)
316    }
317
318    fn encode_segment(&self, mut raw: &[u8], run: usize, dst: &mut WriteCursor<'_>) -> Result<usize, RleEncodeError> {
319        if raw.is_empty() {
320            return Err(RleEncodeError::NotEnoughBytes);
321        }
322
323        let mut extra_bytes = 0;
324
325        while raw.len() > 15 {
326            extra_bytes += self.encode_segment(&raw[0..15], 0, dst)?;
327            raw = &raw[15..];
328        }
329
330        let raw_len = u8::try_from(raw.len()).expect("max value is guaranteed to be 15 due to the prior while loop");
331        let run_capped = u8::try_from(cmp::min(run, 15)).expect("max value is guaranteed to be 15");
332
333        let control = (raw_len << 4) + run_capped;
334
335        ensure_size!(dst: dst, size: raw.len() + 1);
336
337        dst.write_u8(control);
338        dst.write_slice(raw);
339
340        if run > 15 {
341            let last = raw.last().expect("buffer cannot be empty");
342            extra_bytes += self.encode_long_sequence(run - 15, *last, dst)?;
343        }
344
345        Ok(1 + raw.len() + extra_bytes)
346    }
347
348    fn encode_long_sequence(
349        &self,
350        mut run: usize,
351        last: u8,
352        dst: &mut WriteCursor<'_>,
353    ) -> Result<usize, RleEncodeError> {
354        let mut written = 0;
355
356        while run >= 16 {
357            ensure_size!(dst: dst, size: 1);
358
359            let current = u8::try_from(cmp::min(run, MAX_DECODED_SEGMENT_SIZE))
360                .expect("max value is guaranteed to be MAX_DECODED_SEGMENT_SIZE (47)");
361
362            let c_raw_bytes = cmp::min(current / 16, 2);
363            let n_run_length = current - c_raw_bytes * 16;
364
365            let control = (n_run_length << 4) + c_raw_bytes;
366            dst.write_u8(control);
367            written += 1;
368
369            run -= usize::from(current);
370        }
371
372        if run > 0 {
373            match run {
374                short @ 1..=3 => {
375                    written += self.encode_segment(&vec![last; short], 0, dst)?;
376                }
377                long => {
378                    written += self.encode_segment(&[last], long - 1, dst)?;
379                }
380            }
381        }
382
383        Ok(written)
384    }
385}
386
387/// Performs compression of 8bpp color plane pixel stream into a buffer.
388/// Pixel iterator must have at least width * height items.
389/// Destination slice must have enough space for the compressed data.
390///
391/// Returns number of bytes written to the dst buffer.
392pub(crate) fn compress_8bpp_plane(
393    src: impl Iterator<Item = u8>,
394    dst: &mut WriteCursor<'_>,
395    width: usize,
396    height: usize,
397) -> Result<usize, RleEncodeError> {
398    let iter = RleEncoderScanlineIterator::new(width, src);
399    RlePlaneEncoder::new(width, height).encode(iter, dst)
400}
401
402#[cfg(test)]
403#[expect(
404    clippy::needless_raw_strings,
405    reason = "the lint is disable to not interfere with expect! macro"
406)]
407mod tests {
408    use expect_test::expect;
409
410    use super::*;
411
412    /// Performs decompression of 8bpp color plane into vector. Vector will be resized to fit decompressed data.
413    fn decompress(src: &[u8], dst: &mut Vec<u8>, width: usize, height: usize) -> Result<usize, RleDecodeError> {
414        // Ensure dest buffer have enough space for decompressed data
415        dst.resize(width * height, 0);
416
417        decompress_8bpp_plane(src, dst.as_mut_slice(), width, height)
418    }
419
420    fn compress(src: &[u8], dst: &mut [u8], width: usize, height: usize) -> Result<usize, RleEncodeError> {
421        compress_8bpp_plane(src.iter().copied(), &mut WriteCursor::new(dst), width, height)
422    }
423
424    #[test]
425    fn simple_encode() {
426        // Example AAAABBCCCCCD from 3.1.9.2 of [MS-RDPEGDI].
427        let src = [65, 65, 65, 65, 66, 66, 67, 67, 67, 67, 67, 68];
428
429        let width = src.len();
430        let height = 1usize;
431
432        let expected = &[0x13, 65, 0x34, 66, 66, 67, 0x10, 68];
433
434        let mut compressed = vec![0; 255];
435        let len = compress(&src, &mut compressed, width, height).unwrap();
436
437        assert_eq!(&compressed[..len], expected);
438    }
439
440    #[test]
441    fn long_sequence_encode() {
442        // Example from 3.1.9.2.2 of [MS-RDPEGDI].
443        let src = [0x41u8; 100];
444
445        let width = 100usize;
446        let height = 1usize;
447
448        let expected = &[0x1F, 0x41, 0xF2, 0x52];
449
450        let mut compressed = vec![0; 255];
451        let len = compress(&src, &mut compressed, width, height).unwrap();
452
453        assert_eq!(&compressed[..len], expected);
454    }
455
456    #[test]
457    fn multiline_encode() {
458        // Example from 3.1.9.2.1 of [MS-RDPEGDI].
459        let src = [
460            255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
461        ];
462
463        let width = 6usize;
464        let height = 3usize;
465
466        let expected = &[
467            0x13, 0xFF, 0x20, 0xFE, 0xFD, 0x60, 0x01, 0x7D, 0xF5, 0xC2, 0x9A, 0x38, 0x60, 0x01, 0x67, 0x8B, 0xA3, 0x78,
468            0xAF,
469        ];
470
471        let mut compressed = vec![0; 255];
472        let len = compress(&src, &mut compressed, width, height).unwrap();
473
474        assert_eq!(&compressed[..len], expected);
475    }
476
477    #[test]
478    fn long_sequence_decode() {
479        // Example from 3.1.9.2.2 of [MS-RDPEGDI].
480        let src = [0x1F, 0x41, 0xF2, 0x52];
481
482        let width = 100usize;
483        let height = 1usize;
484
485        let expected = &[0x41u8; 100];
486
487        let mut actual = Vec::new();
488        decompress(&src, &mut actual, width, height).unwrap();
489        assert_eq!(actual, expected);
490    }
491
492    #[test]
493    fn multiline_decode() {
494        // Example from 3.1.9.2.3 of [MS-RDPEGDI].
495        let src = [
496            0x13, 0xFF, 0x20, 0xFE, 0xFD, 0x60, 0x01, 0x7D, 0xF5, 0xC2, 0x9A, 0x38, 0x60, 0x01, 0x67, 0x8B, 0xA3, 0x78,
497            0xAF,
498        ];
499
500        let width = 6usize;
501        let height = 3usize;
502
503        let expected = &[
504            255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
505        ];
506
507        let mut actual = Vec::new();
508        decompress(&src, &mut actual, width, height).unwrap();
509        assert_eq!(actual, expected);
510    }
511
512    #[test]
513    fn long_sequence_encode_decode() {
514        // Example from 3.1.9.2.2 of [MS-RDPEGDI].
515        let src = [0x41u8; 100];
516
517        let width = 100usize;
518        let height = 1usize;
519
520        let mut compressed = vec![0; 255];
521        let len = compress(&src, &mut compressed, width, height).unwrap();
522
523        let mut actual = Vec::new();
524        decompress(&compressed[..len], &mut actual, width, height).unwrap();
525
526        assert_eq!(actual.as_slice(), src.as_slice());
527    }
528
529    #[test]
530    fn complex_encode_decode() {
531        let src = [
532            19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 19, 19, 18, 18, 18,
533            18, 18, 18, 18, 18,
534        ];
535
536        let width = src.len();
537        let height = 1usize;
538
539        let mut compressed = vec![0; 255];
540        let len = compress(&src, &mut compressed, width, height).unwrap();
541
542        let mut actual = Vec::new();
543        decompress(&compressed[..len], &mut actual, width, height).unwrap();
544
545        assert_eq!(actual.as_slice(), src.as_slice());
546    }
547
548    #[test]
549    fn multiline_encode_decode() {
550        // Example from 3.1.9.2.3 of [MS-RDPEGDI].
551        let src = [
552            255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
553        ];
554
555        let width = 6usize;
556        let height = 3usize;
557
558        let mut compressed = vec![0; 255];
559        let len = compress(&src, &mut compressed, width, height).unwrap();
560
561        let mut actual = Vec::new();
562        decompress(&compressed[..len], &mut actual, width, height).unwrap();
563
564        assert_eq!(actual.as_slice(), src.as_slice());
565    }
566
567    #[test]
568    fn each_scanline_resets_last_decoded_byte() {
569        let src = [0x17, 0xFF, 0x04, 0x40, 0x01, 0x02, 0x03, 0x04];
570
571        let width = 8usize;
572        let height = 2usize;
573
574        let mut actual = Vec::new();
575
576        let expected = &[
577            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 0, 253, 1,
578        ];
579
580        decompress(&src, &mut actual, width, height).unwrap();
581        assert_eq!(actual, expected);
582    }
583
584    #[test]
585    fn segments_out_of_scanline_produce_error() {
586        let src = [
587            0x18, 0xFF, // Will produce 9 bytes which is out of bounds for 8x2 image
588            0x04, 0x40, 0x01, 0x02, 0x03, 0x04,
589        ];
590
591        let width = 8usize;
592        let height = 2usize;
593
594        let mut actual = Vec::new();
595        expect![[r#"
596            Err(
597                SegmentDoNotFitScanline,
598            )
599        "#]]
600        .assert_debug_eq(&decompress(&src, &mut actual, width, height));
601
602        // Same test, but fail on non-first line
603        let src = [
604            0x17, 0xFF, 0x18, 0xFF, // Will produce 9 bytes which is out of bounds for 8x2 image
605        ];
606
607        let width = 8usize;
608        let height = 2usize;
609
610        let mut actual = Vec::new();
611        expect![[r#"
612            Err(
613                SegmentDoNotFitScanline,
614            )
615        "#]]
616        .assert_debug_eq(&decompress(&src, &mut actual, width, height));
617    }
618
619    #[test]
620    fn insufficient_raw_bytes_handled() {
621        let src = [0x18]; // Actually require 1 more byte
622
623        let width = 8usize;
624        let height = 2usize;
625
626        let mut actual = Vec::new();
627        expect![[r#"
628            Err(
629                ReadCompressedData(
630                    Error {
631                        kind: UnexpectedEof,
632                        message: "failed to fill whole buffer",
633                    },
634                ),
635            )
636        "#]]
637        .assert_debug_eq(&decompress(&src, &mut actual, width, height));
638    }
639
640    #[test]
641    fn empty_buffer_handled() {
642        let src = [];
643
644        let width = 8usize;
645        let height = 2usize;
646
647        let mut actual = Vec::new();
648        expect![[r#"
649            Err(
650                ReadCompressedData(
651                    Error {
652                        kind: UnexpectedEof,
653                        message: "failed to fill whole buffer",
654                    },
655                ),
656            )
657        "#]]
658        .assert_debug_eq(&decompress(&src, &mut actual, width, height));
659    }
660
661    #[test]
662    fn buffer_too_small_encode() {
663        let src = [
664            255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253, 140, 62, 14, 135, 193,
665        ];
666
667        let width = 6usize;
668        let height = 3usize;
669
670        let mut compressed = vec![0; 4];
671
672        expect![[r#"
673            Err(
674                BufferTooSmall,
675            )
676        "#]]
677        .assert_debug_eq(&compress(&src, &mut compressed, width, height));
678    }
679
680    #[test]
681    fn not_enough_bytes_to_encode() {
682        let src = [255, 255, 255, 255, 254, 253, 254, 192, 132, 96, 75, 25, 253];
683
684        let width = 8usize;
685        let height = 3usize;
686
687        let mut compressed = vec![0; 255];
688
689        expect![[r#"
690            Err(
691                NotEnoughBytes,
692            )
693        "#]]
694        .assert_debug_eq(&compress(&src, &mut compressed, width, height));
695    }
696
697    #[test]
698    fn too_small_dest_buffer_handled() {
699        let src = [0x17, 0xFF, 0x04, 0x40, 0x01, 0x02, 0x03, 0x04];
700
701        let width = 8usize;
702        let height = 2usize;
703
704        let mut actual = vec![0u8; 7];
705
706        expect![[r#"
707            Err(
708                WriteDecompressedData(
709                    Error {
710                        kind: WriteZero,
711                        message: "failed to write whole buffer",
712                    },
713                ),
714            )
715        "#]]
716        .assert_debug_eq(&decompress_8bpp_plane(&src, &mut actual, width, height));
717
718        // Check same failure mode, but on non-first line
719    }
720}