exr/compression/
rle.rs

1use super::*;
2use super::optimize_bytes::*;
3use super::Error;
4use super::Result;
5
6// inspired by  https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfRle.cpp
7
8const MIN_RUN_LENGTH : usize = 3;
9const MAX_RUN_LENGTH : usize = 127;
10
11
12pub fn decompress_bytes(
13    channels: &ChannelList,
14    compressed: ByteVec,
15    rectangle: IntegerBounds,
16    expected_byte_size: usize,
17    pedantic: bool,
18) -> Result<ByteVec> {
19    let mut remaining = compressed.as_slice();
20    let mut decompressed = Vec::with_capacity(expected_byte_size.min(8*2048));
21
22    while !remaining.is_empty() && decompressed.len() != expected_byte_size {
23        let count = take_1(&mut remaining)? as i8 as i32;
24
25        if count < 0 {
26            // take the next '-count' bytes as-is
27            let values = take_n(&mut remaining, (-count) as usize)?;
28            decompressed.extend_from_slice(values);
29        }
30        else {
31            // repeat the next value 'count + 1' times
32            let value = take_1(&mut remaining)?;
33            decompressed.resize(decompressed.len() + count as usize + 1, value);
34        }
35    }
36
37    if pedantic && !remaining.is_empty() {
38        return Err(Error::invalid("data amount"));
39    }
40
41    differences_to_samples(&mut decompressed);
42    interleave_byte_blocks(&mut decompressed);
43    Ok(super::convert_little_endian_to_current(decompressed, channels, rectangle))// TODO no alloc
44}
45
46pub fn compress_bytes(channels: &ChannelList, uncompressed: ByteVec, rectangle: IntegerBounds) -> Result<ByteVec> {
47    // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842
48    let mut data = super::convert_current_to_little_endian(uncompressed, channels, rectangle);// TODO no alloc
49
50    separate_bytes_fragments(&mut data);
51    samples_to_differences(&mut data);
52
53    let mut compressed = Vec::with_capacity(data.len());
54    let mut run_start = 0;
55    let mut run_end = 1;
56
57    while run_start < data.len() {
58        while
59            run_end < data.len()
60                && data[run_start] == data[run_end]
61                && (run_end - run_start) as i32 - 1 < MAX_RUN_LENGTH as i32
62            {
63                run_end += 1;
64            }
65
66        if run_end - run_start >= MIN_RUN_LENGTH {
67            compressed.push(((run_end - run_start) as i32 - 1) as u8);
68            compressed.push(data[run_start]);
69            run_start = run_end;
70
71        } else {
72            while
73                run_end < data.len() && (
74                    (run_end + 1 >= data.len() || data[run_end] != data[run_end + 1])
75                        || (run_end + 2 >= data.len() || data[run_end + 1] != data[run_end + 2])
76                ) && run_end - run_start < MAX_RUN_LENGTH
77                {
78                    run_end += 1;
79                }
80
81            compressed.push((run_start as i32 - run_end as i32) as u8);
82            compressed.extend_from_slice(&data[run_start .. run_end]);
83
84            run_start = run_end;
85            run_end += 1;
86        }
87    }
88
89    Ok(compressed)
90}
91
92fn take_1(slice: &mut &[u8]) -> Result<u8> {
93    if !slice.is_empty() {
94        let result = slice[0];
95        *slice = &slice[1..];
96        Ok(result)
97
98    } else {
99        Err(Error::invalid("compressed data"))
100    }
101}
102
103fn take_n<'s>(slice: &mut &'s [u8], n: usize) -> Result<&'s [u8]> {
104    if n <= slice.len() {
105        let (front, back) = slice.split_at(n);
106        *slice = back;
107        Ok(front)
108
109    } else {
110        Err(Error::invalid("compressed data"))
111    }
112}