From 2c3c1d942b2da7192cca99e7cb1efdd2ccb4d748 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Thu, 25 Jul 2024 04:32:12 -0500 Subject: [PATCH] Significantly improved speed of decompression and paralellized it --- .gitignore | 1 + Cargo.toml | 1 + src/compression/lossless.rs | 39 +++++++++++++++++++++++++------------ src/main.rs | 4 +++- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 120ae3e..d373bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ Cargo.lock *.dpf *.raw *.rgba +*.jpg diff --git a/Cargo.toml b/Cargo.toml index a6cd009..380e456 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] byteorder = "1.5.0" image = "0.25.2" +rayon = "1.10.0" thiserror = "1.0.63" diff --git a/src/compression/lossless.rs b/src/compression/lossless.rs index 29da92f..e37953d 100644 --- a/src/compression/lossless.rs +++ b/src/compression/lossless.rs @@ -4,6 +4,8 @@ use std::{ }; use byteorder::{ReadBytesExt, WriteBytesExt, LE}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use thiserror::Error; use crate::binio::{BitReader, BitWriter}; @@ -43,6 +45,12 @@ impl CompressionInfo { } } +#[derive(Debug, Error)] +enum CompressionError { + #[error("bad compressed element \"{}\" at position {}", 0, 1)] + BadElement(u8, usize) +} + pub fn compress(data: &[u8]) -> (Vec, CompressionInfo) { let mut part_data; @@ -151,25 +159,31 @@ fn compress_lzw(data: &[u8], last: Vec) -> (usize, Vec, Vec) { pub fn decompress(input: &mut T, chunk_info: &CompressionInfo) -> Vec { let mut output_buf: Vec = vec![]; - for block in &chunk_info.chunks { - let mut buffer = vec![0u8; block.size_compressed]; + let mut compressed_chunks = Vec::new(); + for chunk_info in &chunk_info.chunks { + let mut buffer = vec![0u8; chunk_info.size_compressed]; input.read_exact(&mut buffer).unwrap(); - let raw_buf = decompress_lzw(&buffer, block.size_raw); - - output_buf.write_all(&raw_buf).unwrap(); + compressed_chunks.push((buffer, chunk_info.size_raw)); } + let decompressed_chunks: Vec> = compressed_chunks + .par_iter() + .map(|chunk| decompress_lzw(&chunk.0, chunk.1).unwrap()) + .collect(); + + decompressed_chunks.iter().for_each(|c| output_buf.write_all(&c).unwrap()); + output_buf } -fn decompress_lzw(input_data: &[u8], size: usize) -> Vec { +fn decompress_lzw(input_data: &[u8], size: usize) -> Result, CompressionError> { let mut data = Cursor::new(input_data); // Build the initial dictionary of 256 values - let mut dictionary = HashMap::new(); + let mut dictionary = Vec::new(); for i in 0..256 { - dictionary.insert(i as u64, vec![i as u8]); + dictionary.push(vec![i as u8]); } let mut dictionary_count = dictionary.len() as u64; @@ -177,7 +191,7 @@ fn decompress_lzw(input_data: &[u8], size: usize) -> Vec { let data_size = input_data.len(); let mut bit_io = BitReader::new(&mut data); - let mut w = dictionary.get(&0).unwrap().clone(); + let mut w = dictionary.get(0).unwrap().clone(); let mut element; loop { @@ -193,7 +207,7 @@ fn decompress_lzw(input_data: &[u8], size: usize) -> Vec { } let mut entry; - if let Some(x) = dictionary.get(&element) { + if let Some(x) = dictionary.get(element as usize) { // If the element was already in the dict, get it entry = x.clone() } else if element == dictionary_count { @@ -205,9 +219,10 @@ fn decompress_lzw(input_data: &[u8], size: usize) -> Vec { result.write_all(&entry).unwrap(); w.push(entry[0]); - dictionary.insert(dictionary_count, w.clone()); + dictionary.push(w.clone()); dictionary_count += 1; w.clone_from(&entry); } - result + + Ok(result) } diff --git a/src/main.rs b/src/main.rs index 322841e..327dd1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,13 +17,15 @@ use std::{ use image::RgbaImage; fn main() { - let image_data = image::open("littlespace.png").unwrap().to_rgba8(); + /* + let image_data = image::open("kirara_motorbike.jpg").unwrap().to_rgba8(); let encoded_dpf = DangoPicture::from_raw(image_data.width(), image_data.height(), &image_data); let timer = Instant::now(); let mut outfile = BufWriter::new(File::create("test.dpf").unwrap()); encoded_dpf.encode(&mut outfile); println!("Encoding took {}ms", timer.elapsed().as_millis()); + */ let timer = Instant::now(); let mut infile = BufReader::new(File::open("test.dpf").unwrap());