diff --git a/src/compression.rs b/src/compression.rs index 6d1ffeb..4abd32b 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -11,6 +11,7 @@ struct CoefTile { coefs: [i16; TILE_SZ2] } +#[derive(Clone, Copy)] struct QuantTile { quants: [i16; TILE_SZ2] } @@ -53,8 +54,10 @@ pub fn compress( for t in tiles.iter() { t.write_zero(writer)?; } - for t in tiles.iter() { - t.write_rest(writer)?; + for ti in 0..tiles.len() { + let prev = if ti > 0 { Some(tiles[ti - 1]) } else { None }; + let t = tiles[ti]; + t.write_rest(prev, writer)?; } Ok(()) @@ -116,9 +119,28 @@ impl QuantTile { Ok(()) } - fn write_rest(&self, writer: &mut ProtocolWriter) -> ProtocolWriterResult<()> { - for i in 1..self.quants.len() { + fn write_rest(&self, prev: Option, writer: &mut ProtocolWriter) -> ProtocolWriterResult<()> { + if let Some(p) = prev { + if self.quants[1..] == p.quants[1..] { + // 255 zeroes can also encode "copy the rest from the previous tile" + writer.write_u8_wide(0xff)?; + return Ok(()); + } + } + + let mut i = 0; + loop { + let mut zeros: u8 = 0; + while i < TILE_SZ2 && self.quants[i] == 0 { + zeros += 1; + i += 1; + } + writer.write_u8_wide(zeros)?; + + if i >= TILE_SZ2 { break; } + writer.write_i16_packed(self.quants[i])?; + i += 1; } Ok(()) } diff --git a/src/decompression.rs b/src/decompression.rs index 2537fb5..d74e894 100644 --- a/src/decompression.rs +++ b/src/decompression.rs @@ -12,7 +12,7 @@ struct CoefTile { coefs: [i16; TILE_SZ2] } -#[derive(Clone)] +#[derive(Clone, Copy)] struct QuantTile { quants: [i16; TILE_SZ2] } @@ -42,7 +42,11 @@ pub fn decompress( tiles[i].load_zero(reader)?; } for i in 0..n_tiles { - tiles[i].load_rest(reader)?; + let prev = if i > 0 { Some(tiles[i - 1]) } else { None }; + tiles[i].load_rest( + prev, + reader + )?; } let mut tile_i = 0; @@ -120,10 +124,28 @@ impl QuantTile { Ok(()) } - fn load_rest(&mut self, reader: &mut ProtocolReader) -> ProtocolReaderResult<()> { - for i in 1..self.quants.len() { + fn load_rest(& + mut self, + prev: Option, + reader: &mut ProtocolReader + ) -> ProtocolReaderResult<()> { + let mut i: usize = 0; + loop { + let cmd = reader.read_u8_wide()? as usize; + if let Some(p) = prev { + if cmd == 0xff { + self.quants[1..].copy_from_slice(&p.quants[1..]); + return Ok(()); + } + } + + i += cmd; + if i >= TILE_SZ2 { break; } + self.quants[i] = reader.read_i16_packed()?; + i += 1; } + Ok(()) } diff --git a/src/protocol.rs b/src/protocol.rs index 5b1efdb..d4bcf9b 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -65,6 +65,10 @@ impl ProtocolWriter { self.writer.write_all(&value.to_le_bytes())?; Ok(()) } + pub fn write_u8_wide(&mut self, value: u8) -> ProtocolWriterResult<()> { + self.writer.write_all(&value.to_le_bytes())?; + Ok(()) + } pub fn write_i16_packed(&mut self, value: i16) -> ProtocolWriterResult<()> { // We reserve i8::MAX and i16::MAX as signal values that `value` is too big // for each respective type @@ -110,6 +114,13 @@ impl ProtocolReader { Ok(i16::from_le_bytes(i16_buf)) } + pub fn read_u8_wide(&mut self) -> ProtocolReaderResult { + let mut u8_buf = [0; 1]; + self.read_exact(&mut u8_buf)?; + + Ok(u8::from_le_bytes(u8_buf)) + } + pub fn read_i16_packed(&mut self) -> ProtocolReaderResult { let mut i8_buf = [0; 1]; self.read_exact(&mut i8_buf)?;