/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/compression/rle.rs
Line | Count | Source |
1 | | use super::{optimize_bytes::*, Error, Result, *}; |
2 | | |
3 | | // inspired by https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfRle.cpp |
4 | | |
5 | | const MIN_RUN_LENGTH: usize = 3; |
6 | | const MAX_RUN_LENGTH: usize = 127; |
7 | | |
8 | 0 | pub fn decompress_bytes( |
9 | 0 | channels: &ChannelList, |
10 | 0 | compressed_le: ByteVec, |
11 | 0 | rectangle: IntegerBounds, |
12 | 0 | expected_byte_size: usize, |
13 | 0 | pedantic: bool, |
14 | 0 | ) -> Result<ByteVec> { |
15 | 0 | let mut decompressed_le = unpack_rle_tokens(&compressed_le, expected_byte_size, pedantic)?; |
16 | 0 | differences_to_samples(&mut decompressed_le); |
17 | 0 | interleave_byte_blocks(&mut decompressed_le); |
18 | 0 | super::convert_little_endian_to_current(decompressed_le, channels, rectangle) |
19 | | // TODO no alloc |
20 | 0 | } |
21 | | |
22 | | /// Shared by this compression method and DWA's RLE scheme (both port OpenEXR's |
23 | | /// `internal_rle_decompress`, see `compression::dwa`) - kept separate from |
24 | | /// `decompress_bytes` because DWA does not apply the delta prediction / |
25 | | /// byte-block interleaving done there. |
26 | 0 | pub(super) fn unpack_rle_tokens( |
27 | 0 | compressed_le: &[u8], |
28 | 0 | expected_byte_size: usize, |
29 | 0 | pedantic: bool, |
30 | 0 | ) -> Result<ByteVec> { |
31 | 0 | let mut remaining_le = compressed_le; |
32 | 0 | let mut decompressed_le = Vec::with_capacity(expected_byte_size.min(8 * 2048)); |
33 | | |
34 | 0 | while !remaining_le.is_empty() && decompressed_le.len() != expected_byte_size { |
35 | 0 | let count = take_1(&mut remaining_le)? as i8 as i32; |
36 | | |
37 | 0 | if count < 0 { |
38 | | // take the next '-count' bytes as-is |
39 | 0 | let values = take_n(&mut remaining_le, -count as usize)?; |
40 | 0 | decompressed_le.extend_from_slice(values); |
41 | | } else { |
42 | | // repeat the next value 'count + 1' times |
43 | 0 | let value = take_1(&mut remaining_le)?; |
44 | 0 | decompressed_le.resize(decompressed_le.len() + (count as usize) + 1, value); |
45 | | } |
46 | | } |
47 | | |
48 | 0 | if pedantic && !remaining_le.is_empty() { |
49 | 0 | return Err(Error::invalid("data amount")); |
50 | 0 | } |
51 | | |
52 | 0 | Ok(decompressed_le) |
53 | 0 | } |
54 | | |
55 | 0 | pub fn compress_bytes( |
56 | 0 | channels: &ChannelList, |
57 | 0 | uncompressed_ne: ByteVec, |
58 | 0 | rectangle: IntegerBounds, |
59 | 0 | ) -> Result<ByteVec> { |
60 | | // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842 |
61 | 0 | let mut data_le = |
62 | 0 | super::convert_current_to_little_endian(uncompressed_ne, channels, rectangle)?; // TODO no alloc |
63 | | |
64 | 0 | separate_bytes_fragments(&mut data_le); |
65 | 0 | samples_to_differences(&mut data_le); |
66 | | |
67 | 0 | Ok(pack_rle_tokens(&data_le)) |
68 | 0 | } |
69 | | |
70 | | /// Shared by this compression method and DWA's RLE section. This only emits |
71 | | /// the byte-oriented RLE token stream; callers are responsible for any byte |
72 | | /// prediction, byte interleaving, or zlib wrapping required by their format. |
73 | 0 | pub(super) fn pack_rle_tokens(data_le: &[u8]) -> ByteVec { |
74 | 0 | let mut compressed_le = Vec::with_capacity(data_le.len()); |
75 | 0 | let mut run_start = 0; |
76 | 0 | let mut run_end = 1; |
77 | | |
78 | 0 | while run_start < data_le.len() { |
79 | 0 | while run_end < data_le.len() |
80 | 0 | && data_le[run_start] == data_le[run_end] |
81 | 0 | && ((run_end - run_start) as i32) - 1 < (MAX_RUN_LENGTH as i32) |
82 | 0 | { |
83 | 0 | run_end += 1; |
84 | 0 | } |
85 | | |
86 | 0 | if run_end - run_start >= MIN_RUN_LENGTH { |
87 | 0 | compressed_le.push((((run_end - run_start) as i32) - 1) as u8); |
88 | 0 | compressed_le.push(data_le[run_start]); |
89 | 0 | run_start = run_end; |
90 | 0 | } else { |
91 | 0 | while run_end < data_le.len() |
92 | 0 | && (run_end + 1 >= data_le.len() |
93 | 0 | || data_le[run_end] != data_le[run_end + 1] |
94 | 0 | || run_end + 2 >= data_le.len() |
95 | 0 | || data_le[run_end + 1] != data_le[run_end + 2]) |
96 | 0 | && run_end - run_start < MAX_RUN_LENGTH |
97 | 0 | { |
98 | 0 | run_end += 1; |
99 | 0 | } |
100 | | |
101 | 0 | compressed_le.push(((run_start as i32) - (run_end as i32)) as u8); |
102 | 0 | compressed_le.extend_from_slice(&data_le[run_start..run_end]); |
103 | | |
104 | 0 | run_start = run_end; |
105 | 0 | run_end += 1; |
106 | | } |
107 | | } |
108 | | |
109 | 0 | compressed_le |
110 | 0 | } |
111 | | |
112 | 0 | fn take_1(slice: &mut &[u8]) -> Result<u8> { |
113 | 0 | if !slice.is_empty() { |
114 | 0 | let result = slice[0]; |
115 | 0 | *slice = &slice[1..]; |
116 | 0 | Ok(result) |
117 | | } else { |
118 | 0 | Err(Error::invalid("compressed data")) |
119 | | } |
120 | 0 | } |
121 | | |
122 | 0 | fn take_n<'s>(slice: &mut &'s [u8], n: usize) -> Result<&'s [u8]> { |
123 | 0 | if n <= slice.len() { |
124 | 0 | let (front, back) = slice.split_at(n); |
125 | 0 | *slice = back; |
126 | 0 | Ok(front) |
127 | | } else { |
128 | 0 | Err(Error::invalid("compressed data")) |
129 | | } |
130 | 0 | } |