diff options
Diffstat (limited to 'vendor/exr/src/compression/piz/mod.rs')
-rw-r--r-- | vendor/exr/src/compression/piz/mod.rs | 437 |
1 files changed, 0 insertions, 437 deletions
diff --git a/vendor/exr/src/compression/piz/mod.rs b/vendor/exr/src/compression/piz/mod.rs deleted file mode 100644 index 1d77663..0000000 --- a/vendor/exr/src/compression/piz/mod.rs +++ /dev/null @@ -1,437 +0,0 @@ - - -//! The PIZ compression method is a wavelet compression, -//! based on the PIZ image format, customized for OpenEXR. -// inspired by https://github.com/AcademySoftwareFoundation/openexr/blob/master/OpenEXR/IlmImf/ImfPizCompressor.cpp - -mod huffman; -mod wavelet; - -use crate::prelude::*; -use crate::io::Data; -use crate::meta::attribute::*; -use crate::compression::{ByteVec, Bytes, mod_p}; -use crate::error::{usize_to_i32, usize_to_u16}; -use std::convert::TryFrom; - - -const U16_RANGE: usize = (1_i32 << 16_i32) as usize; -const BITMAP_SIZE: usize = (U16_RANGE as i32 >> 3_i32) as usize; - -#[derive(Debug)] -struct ChannelData { - tmp_start_index: usize, - tmp_end_index: usize, - - resolution: Vec2<usize>, - y_sampling: usize, - samples_per_pixel: usize, -} - - -pub fn decompress( - channels: &ChannelList, - compressed: ByteVec, - rectangle: IntegerBounds, - expected_byte_size: usize, // TODO remove expected byte size as it can be computed with `rectangle.size.area() * channels.bytes_per_pixel` - pedantic: bool -) -> Result<ByteVec> -{ - let expected_u16_count = expected_byte_size / 2; - debug_assert_eq!(expected_byte_size, rectangle.size.area() * channels.bytes_per_pixel); - debug_assert!(!channels.list.is_empty()); - - if compressed.is_empty() { - return Ok(Vec::new()); - } - - debug_assert_ne!(expected_u16_count, 0); - - let mut bitmap = vec![0_u8; BITMAP_SIZE]; // FIXME use bit_vec! - - let mut remaining_input = compressed.as_slice(); - let min_non_zero = u16::read(&mut remaining_input)? as usize; - let max_non_zero = u16::read(&mut remaining_input)? as usize; - - if max_non_zero >= BITMAP_SIZE || min_non_zero >= BITMAP_SIZE { - return Err(Error::invalid("compression data")); - } - - if min_non_zero <= max_non_zero { - u8::read_slice(&mut remaining_input, &mut bitmap[min_non_zero ..= max_non_zero])?; - } - - let (lookup_table, max_value) = reverse_lookup_table_from_bitmap(&bitmap); - - { - let length = i32::read(&mut remaining_input)?; - if pedantic && length as i64 != remaining_input.len() as i64 { - // TODO length might be smaller than remaining?? - return Err(Error::invalid("compression data")); - } - } - - let mut tmp_u16_buffer = huffman::decompress(remaining_input, expected_u16_count)?; - - let mut channel_data: SmallVec<[ChannelData; 6]> = { - let mut tmp_read_index = 0; - - let channel_data = channels.list.iter().map(|channel| { - let channel_data = ChannelData { - tmp_start_index: tmp_read_index, - tmp_end_index: tmp_read_index, - y_sampling: channel.sampling.y(), - resolution: channel.subsampled_resolution(rectangle.size), - samples_per_pixel: channel.sample_type.bytes_per_sample() / SampleType::F16.bytes_per_sample() - }; - - tmp_read_index += channel_data.resolution.area() * channel_data.samples_per_pixel; - channel_data - }).collect(); - - debug_assert_eq!(tmp_read_index, expected_u16_count); - channel_data - }; - - for channel in &channel_data { - let u16_count = channel.resolution.area() * channel.samples_per_pixel; - let u16s = &mut tmp_u16_buffer[channel.tmp_start_index .. channel.tmp_start_index + u16_count]; - - for offset in 0..channel.samples_per_pixel { // if channel is 32 bit, compress interleaved as two 16 bit values - wavelet::decode( - &mut u16s[offset..], - channel.resolution, - Vec2(channel.samples_per_pixel, channel.resolution.x() * channel.samples_per_pixel), - max_value - )?; - } - } - - // Expand the pixel data to their original range - apply_lookup_table(&mut tmp_u16_buffer, &lookup_table); - - // let out_buffer_size = (max_scan_line_size * scan_line_count) + 65536 + 8192; // TODO not use expected byte size? - let mut out = Vec::with_capacity(expected_byte_size); - - for y in rectangle.position.y() .. rectangle.end().y() { - for channel in &mut channel_data { - if mod_p(y, usize_to_i32(channel.y_sampling)) != 0 { - continue; - } - - let u16s_per_line = channel.resolution.x() * channel.samples_per_pixel; - let next_tmp_end_index = channel.tmp_end_index + u16s_per_line; - let values = &tmp_u16_buffer[channel.tmp_end_index .. next_tmp_end_index]; - channel.tmp_end_index = next_tmp_end_index; - - // TODO do not convert endianness for f16-only images - // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842 - // We can support uncompressed data in the machine's native format - // if all image channels are of type HALF, and if the Xdr and the - // native representations of a half have the same size. - u16::write_slice(&mut out, values).expect("write to in-memory failed"); - } - } - - for (previous, current) in channel_data.iter().zip(channel_data.iter().skip(1)) { - debug_assert_eq!(previous.tmp_end_index, current.tmp_start_index); - } - - debug_assert_eq!(channel_data.last().unwrap().tmp_end_index, tmp_u16_buffer.len()); - debug_assert_eq!(out.len(), expected_byte_size); - - // TODO optimize for when all channels are f16! - // we should be able to omit endianness conversions in that case - // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842 - Ok(super::convert_little_endian_to_current(out, channels, rectangle)) -} - - - -pub fn compress( - channels: &ChannelList, - uncompressed: ByteVec, - rectangle: IntegerBounds -) -> Result<ByteVec> -{ - if uncompressed.is_empty() { - return Ok(Vec::new()); - } - - // TODO do not convert endianness for f16-only images - // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842 - let uncompressed = super::convert_current_to_little_endian(uncompressed, channels, rectangle); - let uncompressed = uncompressed.as_slice();// TODO no alloc - - let mut tmp = vec![0_u16; uncompressed.len() / 2 ]; - let mut channel_data: SmallVec<[ChannelData; 6]> = { - let mut tmp_end_index = 0; - - let vec = channels.list.iter().map(|channel| { - let number_samples = channel.subsampled_resolution(rectangle.size); - let byte_size = channel.sample_type.bytes_per_sample() / SampleType::F16.bytes_per_sample(); - let byte_count = byte_size * number_samples.area(); - - let channel = ChannelData { - tmp_end_index, - tmp_start_index: tmp_end_index, - y_sampling: channel.sampling.y(), - resolution: number_samples, - samples_per_pixel: byte_size, - }; - - tmp_end_index += byte_count; - channel - }).collect(); - - debug_assert_eq!(tmp_end_index, tmp.len()); - vec - }; - - let mut remaining_uncompressed_bytes = uncompressed; - for y in rectangle.position.y() .. rectangle.end().y() { - for channel in &mut channel_data { - if mod_p(y, usize_to_i32(channel.y_sampling)) != 0 { continue; } - let u16s_per_line = channel.resolution.x() * channel.samples_per_pixel; - let next_tmp_end_index = channel.tmp_end_index + u16s_per_line; - let target = &mut tmp[channel.tmp_end_index .. next_tmp_end_index]; - channel.tmp_end_index = next_tmp_end_index; - - // TODO do not convert endianness for f16-only images - // see https://github.com/AcademySoftwareFoundation/openexr/blob/3bd93f85bcb74c77255f28cdbb913fdbfbb39dfe/OpenEXR/IlmImf/ImfTiledOutputFile.cpp#L750-L842 - // We can support uncompressed data in the machine's native format - // if all image channels are of type HALF, and if the Xdr and the - // native representations of a half have the same size. - u16::read_slice(&mut remaining_uncompressed_bytes, target).expect("in-memory read failed"); - } - } - - - let (min_non_zero, max_non_zero, bitmap) = bitmap_from_data(&tmp); - let (max_value, table) = forward_lookup_table_from_bitmap(&bitmap); - apply_lookup_table(&mut tmp, &table); - - let mut piz_compressed = Vec::with_capacity(uncompressed.len() / 2); - u16::try_from(min_non_zero)?.write(&mut piz_compressed)?; - u16::try_from(max_non_zero)?.write(&mut piz_compressed)?; - - if min_non_zero <= max_non_zero { - piz_compressed.extend_from_slice(&bitmap[min_non_zero ..= max_non_zero]); - } - - for channel in channel_data { - for offset in 0 .. channel.samples_per_pixel { - wavelet::encode( - &mut tmp[channel.tmp_start_index + offset .. channel.tmp_end_index], - channel.resolution, - Vec2(channel.samples_per_pixel, channel.resolution.x() * channel.samples_per_pixel), - max_value - )?; - } - } - - let huffman_compressed: Vec<u8> = huffman::compress(&tmp)?; - u8::write_i32_sized_slice(&mut piz_compressed, &huffman_compressed).expect("in-memory write failed"); - - Ok(piz_compressed) -} - - -pub fn bitmap_from_data(data: &[u16]) -> (usize, usize, Vec<u8>) { - let mut bitmap = vec![0_u8; BITMAP_SIZE]; - - for value in data { - bitmap[*value as usize >> 3] |= 1 << (*value as u8 & 7); - } - - bitmap[0] = bitmap[0] & !1; // zero is not explicitly stored in the bitmap; we assume that the data always contain zeroes - - let min_index = bitmap.iter().position(|&value| value != 0); - let max_index = min_index.map(|min| // only if min was found - min + bitmap[min..].iter().rposition(|&value| value != 0).expect("[min] not found") - ); - - (min_index.unwrap_or(0), max_index.unwrap_or(0), bitmap) -} - -pub fn forward_lookup_table_from_bitmap(bitmap: &[u8]) -> (u16, Vec<u16>) { - debug_assert_eq!(bitmap.len(), BITMAP_SIZE); - - let mut table = vec![0_u16; U16_RANGE]; - let mut count = 0_usize; - - for (index, entry) in table.iter_mut().enumerate() { - if index == 0 || bitmap[index >> 3] as usize & (1 << (index & 7)) != 0 { - *entry = usize_to_u16(count).unwrap(); - count += 1; - } - } - - (usize_to_u16(count - 1).unwrap(), table) -} - -fn reverse_lookup_table_from_bitmap(bitmap: Bytes<'_>) -> (Vec<u16>, u16) { - let mut table = Vec::with_capacity(U16_RANGE); - - for index in 0 .. U16_RANGE { // cannot use iter because filter removes capacity sizehint - if index == 0 || ((bitmap[index >> 3] as usize & (1 << (index & 7))) != 0) { - table.push(usize_to_u16(index).unwrap()); - } - } - - debug_assert!(!table.is_empty()); - let max_value = usize_to_u16(table.len() - 1).unwrap(); - - // fill remaining up to u16 range - assert!(table.len() <= U16_RANGE); - table.resize(U16_RANGE, 0); - - (table, max_value) -} - -fn apply_lookup_table(data: &mut [u16], table: &[u16]) { - for data in data { - *data = table[*data as usize]; - } -} - -#[cfg(test)] -mod test { - use crate::prelude::*; - use crate::compression::ByteVec; - use crate::compression::piz; - use crate::meta::attribute::*; - - fn test_roundtrip_noise_with(channels: ChannelList, rectangle: IntegerBounds){ - let pixel_bytes: ByteVec = (0 .. 37).map(|_| rand::random()).collect::<Vec<u8>>().into_iter() - .cycle().take(channels.bytes_per_pixel * rectangle.size.area()) - .collect(); - - let compressed = piz::compress(&channels, pixel_bytes.clone(), rectangle).unwrap(); - let decompressed = piz::decompress(&channels, compressed, rectangle, pixel_bytes.len(), true).unwrap(); - - assert_eq!(pixel_bytes, decompressed); - } - - - #[test] - fn roundtrip_any_sample_type(){ - for &sample_type in &[SampleType::F16, SampleType::F32, SampleType::U32] { - let channel = ChannelDescription { - sample_type, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }; - - let channels = ChannelList::new(smallvec![ channel.clone(), channel ]); - - let rectangle = IntegerBounds { - position: Vec2(-30, 100), - size: Vec2(1080, 720), - }; - - test_roundtrip_noise_with(channels, rectangle); - } - } - - #[test] - fn roundtrip_two_channels(){ - let channel = ChannelDescription { - sample_type: SampleType::F16, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }; - - let channel2 = ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }; - - let channels = ChannelList::new(smallvec![ channel, channel2 ]); - - let rectangle = IntegerBounds { - position: Vec2(-3, 1), - size: Vec2(223, 3132), - }; - - test_roundtrip_noise_with(channels, rectangle); - } - - - - #[test] - fn roundtrip_seven_channels(){ - let channels = ChannelList::new(smallvec![ - ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::F16, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::F32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - - ChannelDescription { - sample_type: SampleType::U32, - - name: Default::default(), - quantize_linearly: false, - sampling: Vec2(1,1) - }, - ]); - - let rectangle = IntegerBounds { - position: Vec2(-3, 1), - size: Vec2(1323, 132), - }; - - test_roundtrip_noise_with(channels, rectangle); - } - -}
\ No newline at end of file |