summaryrefslogtreecommitdiff
path: root/vendor/exr/src/compression/rle.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/exr/src/compression/rle.rs')
-rw-r--r--vendor/exr/src/compression/rle.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/vendor/exr/src/compression/rle.rs b/vendor/exr/src/compression/rle.rs
new file mode 100644
index 0000000..e96ce72
--- /dev/null
+++ b/vendor/exr/src/compression/rle.rs
@@ -0,0 +1,112 @@
+use super::*;
+use super::optimize_bytes::*;
+use super::Error;
+use super::Result;
+
+// inspired by https://github.com/openexr/openexr/blob/master/OpenEXR/IlmImf/ImfRle.cpp
+
+const MIN_RUN_LENGTH : usize = 3;
+const MAX_RUN_LENGTH : usize = 127;
+
+
+pub fn decompress_bytes(
+ channels: &ChannelList,
+ compressed: ByteVec,
+ rectangle: IntegerBounds,
+ expected_byte_size: usize,
+ pedantic: bool,
+) -> Result<ByteVec> {
+ let mut remaining = compressed.as_slice();
+ let mut decompressed = Vec::with_capacity(expected_byte_size.min(8*2048));
+
+ while !remaining.is_empty() && decompressed.len() != expected_byte_size {
+ let count = take_1(&mut remaining)? as i8 as i32;
+
+ if count < 0 {
+ // take the next '-count' bytes as-is
+ let values = take_n(&mut remaining, (-count) as usize)?;
+ decompressed.extend_from_slice(values);
+ }
+ else {
+ // repeat the next value 'count + 1' times
+ let value = take_1(&mut remaining)?;
+ decompressed.resize(decompressed.len() + count as usize + 1, value);
+ }
+ }
+
+ if pedantic && !remaining.is_empty() {
+ return Err(Error::invalid("data amount"));
+ }
+
+ differences_to_samples(&mut decompressed);
+ interleave_byte_blocks(&mut decompressed);
+ Ok(super::convert_little_endian_to_current(decompressed, channels, rectangle))// TODO no alloc
+}
+
+pub fn compress_bytes(channels: &ChannelList, uncompressed: ByteVec, rectangle: IntegerBounds) -> Result<ByteVec> {
+ // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842
+ let mut data = super::convert_current_to_little_endian(uncompressed, channels, rectangle);// TODO no alloc
+
+ separate_bytes_fragments(&mut data);
+ samples_to_differences(&mut data);
+
+ let mut compressed = Vec::with_capacity(data.len());
+ let mut run_start = 0;
+ let mut run_end = 1;
+
+ while run_start < data.len() {
+ while
+ run_end < data.len()
+ && data[run_start] == data[run_end]
+ && (run_end - run_start) as i32 - 1 < MAX_RUN_LENGTH as i32
+ {
+ run_end += 1;
+ }
+
+ if run_end - run_start >= MIN_RUN_LENGTH {
+ compressed.push(((run_end - run_start) as i32 - 1) as u8);
+ compressed.push(data[run_start]);
+ run_start = run_end;
+
+ } else {
+ while
+ run_end < data.len() && (
+ (run_end + 1 >= data.len() || data[run_end] != data[run_end + 1])
+ || (run_end + 2 >= data.len() || data[run_end + 1] != data[run_end + 2])
+ ) && run_end - run_start < MAX_RUN_LENGTH
+ {
+ run_end += 1;
+ }
+
+ compressed.push((run_start as i32 - run_end as i32) as u8);
+ compressed.extend_from_slice(&data[run_start .. run_end]);
+
+ run_start = run_end;
+ run_end += 1;
+ }
+ }
+
+ Ok(compressed)
+}
+
+fn take_1(slice: &mut &[u8]) -> Result<u8> {
+ if !slice.is_empty() {
+ let result = slice[0];
+ *slice = &slice[1..];
+ Ok(result)
+
+ } else {
+ Err(Error::invalid("compressed data"))
+ }
+}
+
+fn take_n<'s>(slice: &mut &'s [u8], n: usize) -> Result<&'s [u8]> {
+ if n <= slice.len() {
+ let (front, back) = slice.split_at(n);
+ *slice = back;
+ Ok(front)
+
+ } else {
+ Err(Error::invalid("compressed data"))
+ }
+}