diff --git a/cz/src/binio.rs b/cz/src/binio.rs index 9a21db9..9e9053b 100644 --- a/cz/src/binio.rs +++ b/cz/src/binio.rs @@ -17,14 +17,6 @@ impl BitIO { self.byte_offset } - pub fn bit_offset(&self) -> usize { - self.bit_offset - } - - pub fn into_vec(self) -> Vec { - self.data - } - pub fn read_bit(&mut self, bit_len: usize) -> u64 { //print!("{}: ", bit_len); if bit_len > 8 * 8 { @@ -37,8 +29,7 @@ impl BitIO { let mut result = 0; for i in 0..bit_len { - let bit_value = - ((self.data[self.byte_offset] as usize >> self.bit_offset) & 1) as u64; + let bit_value = ((self.data[self.byte_offset] as usize >> self.bit_offset) & 1) as u64; self.bit_offset += 1; if self.bit_offset == 8 { diff --git a/cz/src/common.rs b/cz/src/common.rs index 7cbe9be..35a0793 100644 --- a/cz/src/common.rs +++ b/cz/src/common.rs @@ -1,6 +1,9 @@ //! Shared types and traits between CZ# files -use std::{collections::HashMap, io::{self, Read, Seek, Write}}; +use std::{ + collections::HashMap, + io::{self, Read, Seek, Write}, +}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use thiserror::Error; @@ -66,7 +69,10 @@ pub trait CzHeader { fn common(&self) -> &CommonHeader; /// Turn the header into bytes equivalent to the original header from the file - fn write_into(&self, output: &mut T) -> Result; + fn write_into( + &self, + output: &mut T, + ) -> Result; /// The version of the image fn version(&self) -> CzVersion; @@ -123,18 +129,14 @@ pub struct CommonHeader { } impl CommonHeader { - pub fn new( - version: CzVersion, - width: u16, - height: u16, - ) -> Self { + pub fn new(version: CzVersion, width: u16, height: u16) -> Self { Self { version, length: 15, width, height, depth: 32, - unknown: 0 + unknown: 0, } } @@ -264,29 +266,34 @@ pub struct ExtendedHeader { } impl ExtendedHeader { - pub fn new( - crop_width: u16, - crop_height: u16, - bounds_width: u16, - bounds_height: u16, - ) -> Self { + pub fn new() -> Self { ExtendedHeader { unknown_1: [0u8; 5], - crop_width, - crop_height, - bounds_width, - bounds_height, + crop_width: 0, + crop_height: 0, + bounds_width: 0, + bounds_height: 0, offset_width: None, offset_height: None, - unknown_2: None + unknown_2: None, } } - pub fn with_offset( - mut self, - offset_width: u16, - offset_height: u16 - ) -> Self { + pub fn with_crop(mut self, crop_width: u16, crop_height: u16) -> Self { + self.crop_width = crop_width; + self.crop_height = crop_height; + + self + } + + pub fn with_bounds(mut self, bounds_height: u16, bounds_width: u16) -> Self { + self.bounds_width = bounds_width; + self.bounds_height = bounds_height; + + self + } + + pub fn with_offset(mut self, offset_width: u16, offset_height: u16) -> Self { self.offset_width = Some(offset_width); self.offset_height = Some(offset_height); self.unknown_2 = Some(0); @@ -296,7 +303,7 @@ impl ExtendedHeader { pub fn from_bytes( input: &mut T, - common_header: &CommonHeader + common_header: &CommonHeader, ) -> Result { let mut unknown_1 = [0u8; 5]; input.read_exact(&mut unknown_1)?; @@ -335,7 +342,7 @@ impl ExtendedHeader { pub fn write_into( &self, - output: &mut T + output: &mut T, ) -> Result { let pos = output.stream_position()?; @@ -355,6 +362,12 @@ impl ExtendedHeader { } } +impl Default for ExtendedHeader { + fn default() -> Self { + Self::new() + } +} + pub fn get_palette( input: &mut T, num_colors: usize, @@ -370,10 +383,7 @@ pub fn get_palette( Ok(colormap) } -pub fn apply_palette( - input: &[u8], - palette: &[[u8; 4]] -) -> Result, CzError> { +pub fn apply_palette(input: &[u8], palette: &[[u8; 4]]) -> Result, CzError> { let mut output_map = Vec::new(); for byte in input.iter() { @@ -381,7 +391,7 @@ pub fn apply_palette( if let Some(color) = color { output_map.extend_from_slice(color); } else { - return Err(CzError::PaletteError) + return Err(CzError::PaletteError); } } diff --git a/cz/src/compression.rs b/cz/src/compression.rs index 01b4e3d..0b02242 100644 --- a/cz/src/compression.rs +++ b/cz/src/compression.rs @@ -1,6 +1,6 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, io::{Read, Seek, Write}, }; @@ -18,7 +18,7 @@ pub struct ChunkInfo { } /// A CZ# file's information about compression chunks -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] pub struct CompressionInfo { /// Number of compression chunks pub chunk_count: usize, @@ -36,18 +36,6 @@ pub struct CompressionInfo { pub length: usize, } -impl Default for CompressionInfo { - fn default() -> Self { - Self { - chunk_count: 0, - total_size_compressed: 0, - total_size_raw: 0, - chunks: Vec::new(), - length: 0 - } - } -} - impl CompressionInfo { pub fn write_into( &self, @@ -201,12 +189,7 @@ fn get_offset(input: &[u8], src: usize) -> usize { (((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2 } -fn copy_range( - bitmap: &mut Vec, - input: &[u8], - src: usize, - dst: usize -) -> usize { +fn copy_range(bitmap: &mut Vec, input: &[u8], src: usize, dst: usize) -> usize { let mut dst = dst; let start_pos = dst; @@ -249,8 +232,7 @@ pub fn line_diff(header: &T, data: &[u8]) -> Vec { let height = header.height() as u32; let mut output_buf = data.to_vec(); - let block_height = - (f32::ceil(height as f32 / 3.0) as u16) as usize; + let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize; let pixel_byte_count = header.depth() >> 3; let line_byte_count = (width * pixel_byte_count as u32) as usize; @@ -278,7 +260,7 @@ pub fn line_diff(header: &T, data: &[u8]) -> Vec { curr_line[x], curr_line[x + 1], curr_line[x + 2], - 0xFF + 0xFF, ]) } } @@ -295,8 +277,7 @@ pub fn diff_line(header: &T, input: &[u8]) -> Vec { let mut data = Vec::with_capacity(input.len()); - let block_height = - (f32::ceil(height as f32 / 3.0) as u16) as usize; + let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize; let pixel_byte_count = header.depth() >> 3; let line_byte_count = (width * pixel_byte_count as u32) as usize; @@ -308,8 +289,8 @@ pub fn diff_line(header: &T, input: &[u8]) -> Vec { curr_line = input[i..i + line_byte_count].to_vec(); if y % block_height as u32 != 0 { for x in 0..line_byte_count { - curr_line[x] -= prev_line[x]; - prev_line[x] += curr_line[x]; + curr_line[x] = curr_line[x].wrapping_sub(prev_line[x]); + prev_line[x] = prev_line[x].wrapping_add(curr_line[x]); } } else { prev_line.clone_from(&curr_line); @@ -322,10 +303,7 @@ pub fn diff_line(header: &T, input: &[u8]) -> Vec { data } -pub fn compress( - data: &[u8], - size: usize, -) -> (Vec, CompressionInfo) { +pub fn compress(data: &[u8], size: usize) -> (Vec, CompressionInfo) { let mut size = size; if size == 0 { size = 0xFEFD @@ -346,17 +324,17 @@ pub fn compress( loop { (count, part_data, last) = compress_lzw(&data[offset..], size, last); if count == 0 { - break + break; } offset += count; for d in &part_data { - output_buf.write(&d.to_le_bytes()).unwrap(); + output_buf.write_all(&d.to_le_bytes()).unwrap(); } output_info.chunks.push(ChunkInfo { size_compressed: part_data.len(), - size_raw: count + size_raw: count, }); output_info.chunk_count += 1; @@ -383,7 +361,7 @@ fn compress_lzw(data: &[u8], size: usize, last: Vec) -> (usize, Vec, Ve let mut dictionary_count = (dictionary.len() + 1) as u16; let mut element = Vec::new(); - if last.len() != 0 { + if !last.is_empty() { element = last } @@ -392,7 +370,7 @@ fn compress_lzw(data: &[u8], size: usize, last: Vec) -> (usize, Vec, Ve let mut entry = element.clone(); entry.push(*c); - if dictionary.get(&entry).is_some() { + if dictionary.contains_key(&entry) { element = entry } else { compressed.push(*dictionary.get(&element).unwrap()); @@ -404,23 +382,23 @@ fn compress_lzw(data: &[u8], size: usize, last: Vec) -> (usize, Vec, Ve count += 1; if size > 0 && compressed.len() == size { - break + break; } } let last_element = element; - if compressed.len() == 0 { - if last_element.len() != 0 { + if !compressed.is_empty() { + if !last_element.is_empty() { for c in last_element { compressed.push(*dictionary.get(&vec![c]).unwrap()); } } - return (count, compressed, Vec::new()) + return (count, compressed, Vec::new()); } else if compressed.len() < size { - if last_element.len() != 0 { + if !last_element.is_empty() { compressed.push(*dictionary.get(&last_element).unwrap()); } - return (count, compressed, Vec::new()) + return (count, compressed, Vec::new()); } (count, compressed, last_element) diff --git a/cz/src/dynamic.rs b/cz/src/dynamic.rs index 6424aec..a0eaec9 100644 --- a/cz/src/dynamic.rs +++ b/cz/src/dynamic.rs @@ -1,10 +1,15 @@ -use std::{ - fs::{self, File}, io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}, path::Path -}; use byteorder::ReadBytesExt; +use std::{ + fs::File, + io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}, + path::Path, +}; use crate::{ - common::{apply_palette, get_palette, rgba_to_indexed, CommonHeader, CzError, CzHeader, CzVersion, ExtendedHeader}, + common::{ + apply_palette, get_palette, rgba_to_indexed, CommonHeader, CzError, CzHeader, CzVersion, + ExtendedHeader, + }, formats::{cz0, cz1, cz2, cz3, cz4}, }; @@ -22,14 +27,20 @@ impl DynamicCz { Self::decode(&mut img_file) } - pub fn save_as_png>(&self, path: &P) -> Result<(), image::error::EncodingError> { + pub fn save_as_png>( + &self, + path: &P, + ) -> Result<(), image::error::EncodingError> { let image = image::RgbaImage::from_raw( self.header_common.width() as u32, self.header_common.height() as u32, - self.bitmap.clone() - ).unwrap(); + self.bitmap.clone(), + ) + .unwrap(); - image.save_with_format(path, image::ImageFormat::Png).unwrap(); + image + .save_with_format(path, image::ImageFormat::Png) + .unwrap(); Ok(()) } @@ -38,13 +49,9 @@ impl DynamicCz { version: CzVersion, width: u16, height: u16, - bitmap: Vec, + bitmap: Vec ) -> Self { - let header_common = CommonHeader::new( - version, - width, - height - ); + let header_common = CommonHeader::new(version, width, height); Self { header_common, @@ -61,10 +68,10 @@ impl DynamicCz { } pub fn with_extended_header(mut self, ext_header: ExtendedHeader) -> Self { - if ext_header.offset_width.is_none() { - self.header_common.set_length(28) - } else { + if ext_header.offset_width.is_some() { self.header_common.set_length(36) + } else { + self.header_common.set_length(28) } self.header_extended = Some(ext_header); @@ -104,7 +111,7 @@ impl DynamicCz { let image_size = header_common.width() as usize * header_common.height() as usize; if bitmap.len() != image_size * (header_common.depth() >> 3) as usize { // If the bitmap is smaller or larger than the image size, it is likely wrong - return Err(CzError::Corrupt) + return Err(CzError::Corrupt); } if let Some(palette) = &palette { @@ -119,8 +126,11 @@ impl DynamicCz { }) } - pub fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - let mut out_file = File::create(path.into())?; + pub fn save_as_cz>( + &self, + path: T + ) -> Result<(), CzError> { + let mut out_file = BufWriter::new(File::create(path.into())?); self.header_common.write_into(&mut out_file)?; @@ -131,13 +141,13 @@ impl DynamicCz { let output_bitmap; match &self.palette { Some(pal) if self.header_common.depth() <= 8 => { - output_bitmap = rgba_to_indexed(&self.bitmap(), &pal)?; + output_bitmap = rgba_to_indexed(self.bitmap(), pal)?; for rgba in pal { out_file.write_all(rgba)?; } - }, - _ => output_bitmap = self.bitmap().clone() + } + _ => output_bitmap = self.bitmap().clone(), } match self.header_common.version() { @@ -161,7 +171,7 @@ impl DynamicCz { } pub fn set_header(&mut self, header: &CommonHeader) { - self.header_common = header.to_owned() + header.clone_into(&mut self.header_common) } pub fn bitmap(&self) -> &Vec { @@ -174,7 +184,7 @@ impl DynamicCz { pub fn set_bitmap(&mut self, bitmap: Vec, width: u16, height: u16) -> Result<(), CzError> { if bitmap.len() != width as usize * height as usize { - return Err(CzError::BitmapFormat) + return Err(CzError::BitmapFormat); } self.bitmap = bitmap; diff --git a/cz/src/formats/cz0.rs b/cz/src/formats/cz0.rs index 779f103..a836f61 100644 --- a/cz/src/formats/cz0.rs +++ b/cz/src/formats/cz0.rs @@ -1,11 +1,9 @@ -use std::io::{Read, Write, Seek}; use byteorder::{ReadBytesExt, WriteBytesExt}; +use std::io::{Read, Seek, Write}; use crate::common::CzError; -pub fn decode( - input: &mut T -) -> Result, CzError> { +pub fn decode(input: &mut T) -> Result, CzError> { // Get the rest of the file, which is the bitmap let mut bitmap = vec![]; input.read_to_end(&mut bitmap)?; @@ -13,10 +11,7 @@ pub fn decode( Ok(bitmap) } -pub fn encode( - output: &mut T, - bitmap: &[u8] -) -> Result<(), CzError> { +pub fn encode(output: &mut T, bitmap: &[u8]) -> Result<(), CzError> { output.write_all(bitmap)?; Ok(()) diff --git a/cz/src/formats/cz1.rs b/cz/src/formats/cz1.rs index 52a79f8..2711feb 100644 --- a/cz/src/formats/cz1.rs +++ b/cz/src/formats/cz1.rs @@ -17,10 +17,7 @@ pub fn decode(bytes: &mut T) -> Result, C Ok(bitmap) } -pub fn encode( - output: &mut T, - bitmap: &[u8] -) -> Result<(), CzError> { +pub fn encode(output: &mut T, bitmap: &[u8]) -> Result<(), CzError> { let (compressed_data, compressed_info) = compress(bitmap, 0xFEFD); dbg!(&compressed_info); diff --git a/cz/src/formats/cz2.rs b/cz/src/formats/cz2.rs index 42a4a3c..2d0a6da 100644 --- a/cz/src/formats/cz2.rs +++ b/cz/src/formats/cz2.rs @@ -4,9 +4,7 @@ use std::io::{Read, Seek, SeekFrom}; use crate::common::CzError; use crate::compression::{decompress_2, get_chunk_info}; -pub fn decode(bytes: &mut T) - -> Result, CzError> -{ +pub fn decode(bytes: &mut T) -> Result, CzError> { let block_info = get_chunk_info(bytes)?; bytes.seek(SeekFrom::Start(block_info.length as u64))?; diff --git a/cz/src/formats/cz3.rs b/cz/src/formats/cz3.rs index 5e0c0ab..31a35e3 100644 --- a/cz/src/formats/cz3.rs +++ b/cz/src/formats/cz3.rs @@ -1,12 +1,12 @@ -use std::io::{Read, Write, Seek, SeekFrom}; use byteorder::{ReadBytesExt, WriteBytesExt}; +use std::io::{Read, Seek, SeekFrom, Write}; use crate::common::{CommonHeader, CzError, CzHeader}; use crate::compression::{compress, decompress, diff_line, get_chunk_info, line_diff}; pub fn decode( bytes: &mut T, - header: &CommonHeader + header: &CommonHeader, ) -> Result, CzError> { let block_info = get_chunk_info(bytes)?; bytes.seek(SeekFrom::Start(block_info.length as u64))?; diff --git a/cz/src/formats/cz4.rs b/cz/src/formats/cz4.rs index ab02f55..fd29a25 100644 --- a/cz/src/formats/cz4.rs +++ b/cz/src/formats/cz4.rs @@ -1,11 +1,14 @@ -use std::io::{Read, Seek, SeekFrom}; use byteorder::ReadBytesExt; use image::RgbaImage; +use std::io::{Read, Seek, SeekFrom}; use crate::common::{CommonHeader, CzError, CzHeader}; use crate::compression::{decompress, get_chunk_info}; -pub fn decode(bytes: &mut T, header: &CommonHeader) -> Result, CzError> { +pub fn decode( + bytes: &mut T, + header: &CommonHeader, +) -> Result, CzError> { let block_info = get_chunk_info(bytes)?; bytes.seek(SeekFrom::Start(block_info.length as u64))?; @@ -24,10 +27,7 @@ pub fn decode(bytes: &mut T, header: &CommonHeade Ok(picture.into_raw()) } -pub fn line_diff_cz4( - picture: &mut RgbaImage, - pixel_byte_count: usize, data: &[u8] -) { +pub fn line_diff_cz4(picture: &mut RgbaImage, pixel_byte_count: usize, data: &[u8]) { let width = picture.width(); let height = picture.height(); let block_height = (f32::ceil(height as f32 / 3.0) as u16) as u32; diff --git a/cz/src/lib.rs b/cz/src/lib.rs index 07a6f42..ed7d095 100644 --- a/cz/src/lib.rs +++ b/cz/src/lib.rs @@ -1,8 +1,8 @@ mod binio; mod compression; -pub mod dynamic; pub mod common; +pub mod dynamic; pub mod formats { pub mod cz0; diff --git a/utils/src/main.rs b/utils/src/main.rs index 04deefc..dd06e88 100644 --- a/utils/src/main.rs +++ b/utils/src/main.rs @@ -1,23 +1,38 @@ -use cz::{common::{CzVersion, ExtendedHeader}, dynamic::DynamicCz}; +use std::time::Instant; + +use cz::{ + common::{CzVersion, ExtendedHeader}, + dynamic::DynamicCz, +}; fn main() { - let mio = image::open("mio_inverted.png").unwrap(); - let mio = mio.to_rgba8(); + let timer = Instant::now(); + let mio = image::open("mio_inverted.png").unwrap().to_rgba8(); + println!("Opening PNG took {:?}", timer.elapsed()); - let cz_mio = - DynamicCz::from_raw( - CzVersion::CZ3, - mio.width() as u16, - mio.height() as u16, - mio.into_raw() - ) - .with_extended_header( - ExtendedHeader::new(1280, 960, 1280, 960) - ); + let timer = Instant::now(); + let cz_mio = DynamicCz::from_raw( + CzVersion::CZ3, + mio.width() as u16, + mio.height() as u16, + mio.into_raw(), + ) + .with_extended_header( + ExtendedHeader::new() + .with_crop(1280, 960) + .with_bounds(1280, 960), + ); + println!("Constructing CZ3 took {:?}", timer.elapsed()); + let timer = Instant::now(); + cz_mio.save_as_png("test_save.png").unwrap(); + println!("Saving CZ3 as PNG took {:?}", timer.elapsed()); + + let timer = Instant::now(); cz_mio.save_as_cz("test1.cz3").unwrap(); + println!("Saving CZ3 as CZ3 took {:?}", timer.elapsed()); + let timer = Instant::now(); let img = DynamicCz::open("test1.cz3").unwrap(); - - img.save_as_png("test.png").unwrap(); + println!("Opening saved CZ3 took {:?}", timer.elapsed()); }