From 996bbbe0b75b92e8fe7dc6aee858c67af84d26be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kurt=20K=C3=BChnert?= Date: Wed, 12 Oct 2022 09:26:11 +0200 Subject: [PATCH] changed decode to return bytes instead of u16s --- Cargo.toml | 3 - examples/basic.rs | 8 +- examples/benchmark.rs | 18 ++--- examples/dual_channel.rs | 7 +- src/decode.rs | 153 ++++++++++++++++++++++++--------------- src/encode.rs | 147 +++++++++++++++++++------------------ src/lib.rs | 18 ++--- 7 files changed, 198 insertions(+), 156 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a52323b..9fda538 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,5 @@ readme = "README.md" authors = ["Kurt Kühnert >(path: P) -> Result<(Self, Vec), DecodeError> { - let encoded = match fs::read(path) { - Ok(data) => data, - Err(_) => return Err(DecodeError::IoError), - }; - - DTM::decode_alloc(&encoded) - } - - /// Decodes a DTM image from a byte slice into a newly allocated `Vec`. - #[inline] - pub fn decode_alloc(bytes: &[u8]) -> Result<(Self, Vec), DecodeError> { - let header = if let Some(header) = bytes.get(..DTM_HEADER_SIZE) { + /// Reads header from encoded DTM image. + /// The returned header can be analyzed before proceeding parsing with [`DTM::decode_skip_header`]. + fn decode_header(encoded: &[u8]) -> Result { + let header = if let Some(header) = encoded.get(..DTM_HEADER_SIZE) { header } else { return Err(DecodeError::InsufficientInputData); @@ -74,9 +68,9 @@ impl DTM { return Err(DecodeError::InvalidMagic); } - let pixel_size = header[3] as usize; - let width = u32::from_be_bytes(header[4..8].try_into().unwrap()) as usize; - let height = u32::from_be_bytes(header[8..12].try_into().unwrap()) as usize; + let pixel_size = header[3] as u32; + let width = u32::from_be_bytes(header[4..8].try_into().unwrap()); + let height = u32::from_be_bytes(header[8..12].try_into().unwrap()); let mut channel_count = 0; let mut channel_sizes = [0; 4]; @@ -95,89 +89,130 @@ impl DTM { channel_count += 1; } - let descriptor = DTM { - pixel_size, - channel_count, - width, - height, + Ok(Header { + descriptor: DTM { + pixel_size, + channel_count, + width, + height, + }, + channel_sizes, + total_size, + }) + } + + /// Decodes a DTM image from a file into a newly allocated `Vec`. + #[inline] + pub fn decode_file>(path: P) -> Result<(Self, Vec), DecodeError> { + let encoded = match fs::read(path) { + Ok(encoded) => encoded, + Err(_) => return Err(DecodeError::IoError), }; - let mut bytes = match bytes.get(DTM_HEADER_SIZE..total_size) { - Some(bytes) => Bytes::new(bytes), + DTM::decode_alloc(&encoded) + } + + /// Decodes a DTM image from a byte slice into the `decoded` slice. + #[inline] + pub fn decode(encoded: &[u8], decoded: &mut [u8]) -> Result { + let Header { + descriptor, + channel_sizes, + total_size, + } = Self::decode_header(encoded)?; + + let mut encoded = match encoded.get(DTM_HEADER_SIZE..total_size) { + Some(encoded) => Encoded::new(encoded), None => return Err(DecodeError::InsufficientInputData), }; - let mut data = vec![0; descriptor.image_pixel_count()]; - let mut pixels = Pixels::new(width, height, channel_count, &mut data); + let mut decoded = Decoded::new( + descriptor.width as usize, + descriptor.height as usize, + descriptor.channel_count as usize, + decoded, + ); - for &channel_size in &channel_sizes[0..channel_count] { - bytes.next_channel(channel_size); + for &channel_size in &channel_sizes[0..descriptor.channel_count as usize] { + encoded.next_channel(channel_size); if channel_size < descriptor.channel_size() { - decode(&mut bytes, &mut pixels)?; + decode(&mut encoded, &mut decoded)?; } else if channel_size == descriptor.channel_size() { - &bytes.data[..channel_size] + encoded.data[..channel_size] .chunks_exact(2) - .for_each(|bytes| pixels.set(bytes[0] as u16 + ((bytes[1] as u16) << 8))); + .for_each(|encoded| { + decoded.set(encoded[0] as u16 + ((encoded[1] as u16) << 8)) + }); } else { return Err(DecodeError::InvalidChannels); } - pixels.next_channel(); + decoded.next_channel(); } - Ok((descriptor, data)) + Ok(descriptor) + } + + /// Decodes a DTM image from a byte slice into a newly allocated `Vec`. + #[inline] + pub fn decode_alloc(encoded: &[u8]) -> Result<(Self, Vec), DecodeError> { + let header = Self::decode_header(encoded)?; + let mut decoded = vec![0; header.descriptor.image_size()]; + let descriptor = Self::decode(encoded, &mut decoded)?; + + Ok((descriptor, decoded)) } } -fn decode(bytes: &mut Bytes, pixels: &mut Pixels) -> Result<(), DecodeError> { - while !bytes.is_empty() { - let byte = bytes.next(); +fn decode(encoded: &mut Encoded, decoded: &mut Decoded) -> Result<(), DecodeError> { + while !encoded.is_empty() { + let byte = encoded.next(); match byte { CACHE..=CACHE_END => { let index = MASK_6BIT & byte; - pixels.set(pixels.cache[index as usize]); + decoded.set(decoded.cache[index as usize]); } SINGLE_DIFF..=SINGLE_DIFF_END => { let diff = (MASK_6BIT & byte) as i32 - SINGLE_DIFF_RANGE; - let pixel = (pixels.paeth() as i32 + diff) as u16; - pixels.set(pixel); + let pixel = (decoded.paeth() as i32 + diff) as u16; + decoded.set(pixel); } DOUBLE_DIFF..=DOUBLE_DIFF_END => { let diff = (MASK_3BIT & (byte >> 3)) as i32 - DOUBLE_DIFF_RANGE; - let pixel = (pixels.paeth() as i32 + diff) as u16; - pixels.set(pixel); + let pixel = (decoded.paeth() as i32 + diff) as u16; + decoded.set(pixel); let diff = (MASK_3BIT & byte) as i32 - DOUBLE_DIFF_RANGE; - let pixel = (pixels.paeth() as i32 + diff) as u16; - pixels.set(pixel); + let pixel = (decoded.paeth() as i32 + diff) as u16; + decoded.set(pixel); } RUN_LENGTH..=RUN_LENGTH_END => { let run_length = (MASK_6BIT & byte + 1) as usize; - let pixel = pixels.previous(); - (0..run_length).for_each(|_| pixels.set(pixel)); + let pixel = decoded.previous(); + (0..run_length).for_each(|_| decoded.set(pixel)); } DEFAULT => { - pixels.set(bytes.next() as u16 + ((bytes.next() as u16) << 8)); + decoded.set(encoded.next() as u16 + ((encoded.next() as u16) << 8)); } } } - if pixels.is_empty() { + if decoded.is_empty() { Ok(()) } else { Err(DecodeError::InsufficientInputData) } } -struct Bytes<'a> { +struct Encoded<'a> { data: &'a [u8], index: usize, channel_size: usize, } -impl<'a> Bytes<'a> { +impl<'a> Encoded<'a> { #[inline] fn new(data: &'a [u8]) -> Self { Self { @@ -207,19 +242,19 @@ impl<'a> Bytes<'a> { } } -struct Pixels<'a> { +struct Decoded<'a> { width: usize, height: usize, channel_count: usize, - data: &'a mut [u16], + data: &'a mut [u8], cache: [u16; 64], channel: usize, index: usize, } -impl<'a> Pixels<'a> { +impl<'a> Decoded<'a> { #[inline] - pub fn new(width: usize, height: usize, channel_count: usize, data: &'a mut [u16]) -> Self { + pub fn new(width: usize, height: usize, channel_count: usize, data: &'a mut [u8]) -> Self { Self { width, height, @@ -233,7 +268,8 @@ impl<'a> Pixels<'a> { #[inline] fn get(&self, index: usize) -> u16 { - self.data[index * self.channel_count + self.channel] + let index = (index * self.channel_count + self.channel) << 1; + u16::from_le_bytes(self.data[index..index + 2].try_into().unwrap()) } #[inline] @@ -272,7 +308,8 @@ impl<'a> Pixels<'a> { #[inline] fn set(&mut self, pixel: u16) { - self.data[self.index * self.channel_count + self.channel] = pixel; + let index = (self.index * self.channel_count + self.channel) << 1; + self.data[index..index + 2].copy_from_slice(&pixel.to_le_bytes()); self.cache[pixel as usize % 64] = pixel; self.index += 1; } diff --git a/src/encode.rs b/src/encode.rs index d32dca6..cf97d02 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -46,10 +46,10 @@ impl Display for EncodeError { } impl DTM { - /// Encodes a DTM image from a pixel slice into file. + /// Encodes a DTM image from a pixel slice into a file. #[inline] - pub fn encode_file>(&self, path: P, pixels: &[u16]) -> Result<(), EncodeError> { - let encoded = self.encode_alloc(pixels)?; + pub fn encode_file>(&self, path: P, decoded: &[u8]) -> Result<(), EncodeError> { + let encoded = self.encode_alloc(decoded)?; match fs::write(path, encoded) { Ok(_) => Ok(()), @@ -59,40 +59,44 @@ impl DTM { /// Encodes a DTM image from a pixel slice into a newly allocated `Vec`. #[inline] - pub fn encode_alloc(&self, pixels: &[u16]) -> Result, EncodeError> { - let mut pixels = match pixels.get(..self.image_pixel_count()) { - Some(pixels) => Pixels::new(self.width, self.height, self.channel_count, pixels), + pub fn encode_alloc(&self, decoded: &[u8]) -> Result, EncodeError> { + let mut decoded = match decoded.get(..self.image_size()) { + Some(decoded) => Decoded::new( + self.width as usize, + self.height as usize, + self.channel_count as usize, + decoded, + ), None => return Err(EncodeError::InsufficientInputData), }; - let mut data = vec![0; DTM_HEADER_SIZE + 3 * self.image_pixel_count()]; - let mut bytes = Bytes::new(&mut data[DTM_HEADER_SIZE..]); + let mut data = vec![0; DTM_HEADER_SIZE + 3 * self.image_size() / 2]; + let mut encoded = Encoded::new(&mut data[DTM_HEADER_SIZE..]); let mut channel_sizes = [0; 4]; let mut total_size = DTM_HEADER_SIZE; - for channel_size in &mut channel_sizes[0..self.channel_count] { - encode(&mut bytes, &mut pixels); + for channel_size in &mut channel_sizes[0..self.channel_count as usize] { + encode(&mut encoded, &mut decoded); - if bytes.index >= self.channel_size() { - bytes.index = 0; - pixels + if encoded.index >= self.channel_size() { + encoded.index = 0; + decoded .data - .iter() - .skip(pixels.channel) - .step_by(pixels.channel_count) - .for_each(|&pixel| { - bytes.data[bytes.index] = pixel as u8; - bytes.data[bytes.index + 1] = (pixel >> 8) as u8; - bytes.index += 2; + .chunks(2) + .skip(decoded.channel) + .step_by(decoded.channel_count) + .for_each(|pixel| { + encoded.data[encoded.index..encoded.index + 2].copy_from_slice(pixel); + encoded.index += 2; }); }; - *channel_size = bytes.index; + *channel_size = encoded.index; total_size += *channel_size; - bytes = Bytes::new(&mut bytes.data[*channel_size..]); - pixels.next_channel(); + encoded = Encoded::new(&mut encoded.data[*channel_size..]); + decoded.next_channel(); } data[0..3].copy_from_slice(DTM_MAGIC); @@ -110,62 +114,62 @@ impl DTM { } } -fn encode(bytes: &mut Bytes, pixels: &mut Pixels) { - while !pixels.is_empty() { - let previous_pixel = pixels.previous(); - let pixel = pixels.current(); +fn encode(encoded: &mut Encoded, decoded: &mut Decoded) { + while !decoded.is_empty() { + let previous_pixel = decoded.previous(); + let pixel = decoded.current(); if pixel == previous_pixel { - bytes.run_length += 1; + encoded.run_length += 1; - if bytes.run_length == 63 { - finish_run(bytes, pixels); + if encoded.run_length == 63 { + finish_run(encoded, decoded); } } else { - if bytes.run_length > 0 { - finish_run(bytes, pixels); + if encoded.run_length > 0 { + finish_run(encoded, decoded); } - let diff = pixel as i32 - pixels.paeth() as i32; + let diff = pixel as i32 - decoded.paeth() as i32; if (-DOUBLE_DIFF_RANGE..DOUBLE_DIFF_RANGE).contains(&diff) { - if let Some(previous_diff) = bytes.outstanding_diff { - bytes.double_diff(previous_diff, diff); - bytes.outstanding_diff = None; + if let Some(previous_diff) = encoded.outstanding_diff { + encoded.double_diff(previous_diff, diff); + encoded.outstanding_diff = None; } else { - bytes.outstanding_diff = Some(diff); + encoded.outstanding_diff = Some(diff); } } else { - if let Some(previous_diff) = bytes.outstanding_diff { - bytes.single_diff(previous_diff); - bytes.outstanding_diff = None; + if let Some(previous_diff) = encoded.outstanding_diff { + encoded.single_diff(previous_diff); + encoded.outstanding_diff = None; } if (-SINGLE_DIFF_RANGE..SINGLE_DIFF_RANGE).contains(&diff) { - bytes.single_diff(diff); - } else if pixel == bytes.pixel_cache[(pixel % 64) as usize] { - bytes.cache(pixel); + encoded.single_diff(diff); + } else if pixel == encoded.pixel_cache[(pixel % 64) as usize] { + encoded.cache(pixel); } else { - bytes.default(pixel); + encoded.default(pixel); } } } - bytes.pixel_cache[(pixel % 64) as usize] = pixel; - pixels.index += 1; + encoded.pixel_cache[(pixel % 64) as usize] = pixel; + decoded.index += 1; } - if bytes.run_length > 0 { - finish_run(bytes, pixels); + if encoded.run_length > 0 { + finish_run(encoded, decoded); } - if let Some(previous_diff) = bytes.outstanding_diff { - bytes.single_diff(previous_diff); + if let Some(previous_diff) = encoded.outstanding_diff { + encoded.single_diff(previous_diff); } /* unsafe { - let size = pixels.width * pixels.height; + let size = decoded.width * decoded.height; println!( "Cache: {} ({}%)", C_CACHE, @@ -194,8 +198,8 @@ fn encode(bytes: &mut Bytes, pixels: &mut Pixels) { ); println!( "Total: {} ({}%)", - bytes.index, - bytes.index as f32 / (2 * size) as f32 * 100.0 + encoded.index, + encoded.index as f32 / (2 * size) as f32 * 100.0 ); println!(); @@ -215,38 +219,38 @@ fn encode(bytes: &mut Bytes, pixels: &mut Pixels) { } #[inline] -fn finish_run(bytes: &mut Bytes, pixels: &mut Pixels) { +fn finish_run(encoded: &mut Encoded, decoded: &mut Decoded) { let mut run = true; - if let Some(previous_diff) = bytes.outstanding_diff { - if bytes.run_length == 1 { - pixels.index -= 1; - let diff = pixels.current() as i32 - pixels.paeth() as i32; - pixels.index += 1; + if let Some(previous_diff) = encoded.outstanding_diff { + if encoded.run_length == 1 { + decoded.index -= 1; + let diff = decoded.current() as i32 - decoded.paeth() as i32; + decoded.index += 1; if (-DOUBLE_DIFF_RANGE..DOUBLE_DIFF_RANGE).contains(&previous_diff) && (-DOUBLE_DIFF_RANGE..DOUBLE_DIFF_RANGE).contains(&diff) { - bytes.double_diff(previous_diff, diff); + encoded.double_diff(previous_diff, diff); run = false; } } if run { - bytes.single_diff(previous_diff); + encoded.single_diff(previous_diff); } - bytes.outstanding_diff = None; + encoded.outstanding_diff = None; } if run { - bytes.run_length(); + encoded.run_length(); } - bytes.run_length = 0; + encoded.run_length = 0; } -struct Bytes<'a> { +struct Encoded<'a> { data: &'a mut [u8], pixel_cache: [u16; 64], outstanding_diff: Option, @@ -254,7 +258,7 @@ struct Bytes<'a> { index: usize, } -impl<'a> Bytes<'a> { +impl<'a> Encoded<'a> { #[inline] fn new(data: &'a mut [u8]) -> Self { Self { @@ -310,18 +314,18 @@ impl<'a> Bytes<'a> { } } -struct Pixels<'a> { +struct Decoded<'a> { width: usize, height: usize, channel_count: usize, - data: &'a [u16], + data: &'a [u8], channel: usize, index: usize, } -impl<'a> Pixels<'a> { +impl<'a> Decoded<'a> { #[inline] - pub fn new(width: usize, height: usize, channel_count: usize, data: &'a [u16]) -> Self { + pub fn new(width: usize, height: usize, channel_count: usize, data: &'a [u8]) -> Self { Self { width, height, @@ -334,7 +338,8 @@ impl<'a> Pixels<'a> { #[inline] fn get(&self, index: usize) -> u16 { - self.data[index * self.channel_count + self.channel] + let index = (index * self.channel_count + self.channel) << 1; + u16::from_le_bytes(self.data[index..index + 2].try_into().unwrap()) } #[inline] diff --git a/src/lib.rs b/src/lib.rs index 457fe07..91c1698 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,23 +13,23 @@ pub const DTM_MAGIC: &[u8] = "dtm".as_bytes(); /// This value is parsed from the image header during decoding or is specified for encoding. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DTM { - pub pixel_size: usize, - pub channel_count: usize, - pub width: usize, - pub height: usize, + pub pixel_size: u32, + pub channel_count: u32, + pub width: u32, + pub height: u32, } impl DTM { - /// Returns the number of pixels of the decoded image. + /// Returns the size of the decoded image in bytes . #[inline] - pub fn image_pixel_count(&self) -> usize { - self.channel_count * self.width * self.height + pub fn image_size(&self) -> usize { + (self.pixel_size * self.channel_count * self.width * self.height) as usize } - /// Returns the size of a channel of the decoded image. + /// Returns the size of a channel of the decoded image in bytes . #[inline] pub fn channel_size(&self) -> usize { - self.pixel_size * self.width * self.height + (self.pixel_size * self.width * self.height) as usize } }