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: u8 = slice[0];
95 *slice = &slice[1..];
96 Ok(result)
97
98 } else {
99 Err(Error::invalid(message:"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: &[u8], back: &[u8]) = slice.split_at(mid:n);
106 *slice = back;
107 Ok(front)
108
109 } else {
110 Err(Error::invalid(message:"compressed data"))
111 }
112}
113