1 | use super::*;
|
2 | use super::optimize_bytes::*;
|
3 | use super::Error;
|
4 | use super::Result;
|
5 |
|
6 | // inspired by https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfRle.cpp
|
7 |
|
8 | const MIN_RUN_LENGTH : usize = 3;
|
9 | const MAX_RUN_LENGTH : usize = 127;
|
10 |
|
11 |
|
12 | pub 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 |
|
46 | pub 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 |
|
92 | fn 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 |
|
103 | fn 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 | |