| 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 | |