diff --git a/cz/src/binio.rs b/cz/src/binio.rs index 69805e4..68be0e0 100644 --- a/cz/src/binio.rs +++ b/cz/src/binio.rs @@ -67,12 +67,12 @@ impl BitIO { } pub fn write_bit(&mut self, data: u64, bit_len: usize) { - if bit_len > 8*8 { + if bit_len > 8 * 8 { panic!(); } if bit_len % 8 == 0 && self.bit_offset == 0 { - self.write(data, bit_len/8); + self.write(data, bit_len / 8); return; } @@ -101,7 +101,8 @@ impl BitIO { let mut padded_slice = [0u8; 8]; padded_slice.copy_from_slice(&data.to_le_bytes()); - self.data[self.byte_offset..self.byte_offset + byte_len].copy_from_slice(&padded_slice[..byte_len]); + self.data[self.byte_offset..self.byte_offset + byte_len] + .copy_from_slice(&padded_slice[..byte_len]); self.byte_offset += byte_len; self.byte_size = self.byte_offset + (self.bit_offset + 7) / 8; diff --git a/cz/src/color.rs b/cz/src/color.rs new file mode 100644 index 0000000..9633513 --- /dev/null +++ b/cz/src/color.rs @@ -0,0 +1,104 @@ +use std::{ + collections::HashMap, + io::{Read, Seek}, +}; + +use byteorder::ReadBytesExt; +use image::Rgba; +use quantizr::Image; + +use crate::common::{CommonHeader, CzError}; + +pub fn get_palette( + input: &mut T, + num_colors: usize, +) -> Result>, CzError> { + let mut colormap = Vec::with_capacity(num_colors); + let mut rgba_buf = [0u8; 4]; + + for _ in 0..num_colors { + input.read_exact(&mut rgba_buf)?; + colormap.push(rgba_buf.into()); + } + + Ok(colormap) +} + +/// Take a bitmap of indicies, and map a given palette to it, returning a new +/// RGBA bitmap +pub fn apply_palette(input: &[u8], palette: &[Rgba]) -> Result, CzError> { + let mut output_map = Vec::new(); + + for byte in input.iter() { + let color = palette.get(*byte as usize); + if let Some(color) = color { + output_map.extend_from_slice(&color.0); + } else { + return Err(CzError::PaletteError); + } + } + + Ok(output_map) +} + +pub fn rgba_to_indexed(input: &[u8], palette: &[Rgba]) -> Result, CzError> { + let mut output_map = Vec::new(); + let mut cache = HashMap::new(); + + for rgba in input.windows(4).step_by(4) { + let value = match cache.get(rgba) { + Some(val) => *val, + None => { + let value = palette.iter().position(|e| e.0 == rgba).unwrap_or_default() as u8; + cache.insert(rgba, value); + value + } + }; + + output_map.push(value) + } + + Ok(output_map) +} + +pub fn indexed_gen_palette( + input: &[u8], + header: &CommonHeader, +) -> Result<(Vec, Vec>), CzError> { + let size = (header.width() as u32 * header.height() as u32) * 4; + + let mut buf = vec![0; size as usize]; + buf[..input.len()].copy_from_slice(input); + + let image = Image::new(&buf, header.width() as usize, header.height() as usize).unwrap(); + + let mut opts = quantizr::Options::default(); + opts.set_max_colors(1 << header.depth()).unwrap(); + + let mut result = quantizr::QuantizeResult::quantize(&image, &opts); + result.set_dithering_level(0.5).unwrap(); + + let mut indicies = vec![0u8; header.width() as usize * header.height() as usize]; + result.remap_image(&image, indicies.as_mut_slice()).unwrap(); + + let palette = result.get_palette(); + + let gen_palette = palette + .entries + .as_slice() + .iter() + .map(|c| Rgba([c.r, c.g, c.b, c.a])) + .collect(); + + Ok((indicies, gen_palette)) +} + +pub fn default_palette() -> Vec> { + let mut colormap = Vec::new(); + + for i in 0..=0xFF { + colormap.push(Rgba([0xFF, 0xFF, 0xFF, i])) + } + + colormap +} diff --git a/cz/src/common.rs b/cz/src/common.rs index 7f32e9c..3cae834 100644 --- a/cz/src/common.rs +++ b/cz/src/common.rs @@ -1,13 +1,8 @@ //! Shared types and traits between CZ# files -use std::{ - collections::HashMap, - io::{self, Read, Seek, Write}, -}; +use std::io::{self, Read, Seek, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use image::Rgba; -use quantizr::Image; use thiserror::Error; #[derive(Error, Debug)] @@ -21,8 +16,8 @@ pub enum CzError { #[error("Bitmap size does not match image size")] BitmapFormat, - #[error("File data is incorrect, it might be corrupt")] - Corrupt, + #[error("File data is incorrect, it might be corrupt: {0}")] + Corrupt(String), #[error("File is not a CZ image")] NotCzFile, @@ -117,9 +112,7 @@ impl CommonHeader { pub fn set_length(&mut self, length: u32) { self.length = length } -} -impl CommonHeader { pub fn from_bytes(bytes: &mut T) -> Result where Self: Sized, @@ -243,6 +236,12 @@ pub struct ExtendedHeader { unknown_2: Option, } +impl Default for ExtendedHeader { + fn default() -> Self { + Self::new() + } +} + impl ExtendedHeader { pub fn new() -> Self { ExtendedHeader { @@ -339,108 +338,3 @@ impl ExtendedHeader { Ok((output.stream_position()? - pos) as usize) } } - -impl Default for ExtendedHeader { - fn default() -> Self { - Self::new() - } -} - -pub fn get_palette( - input: &mut T, - num_colors: usize, -) -> Result>, CzError> { - let mut colormap = Vec::with_capacity(num_colors); - let mut rgba_buf = [0u8; 4]; - - for _ in 0..num_colors { - input.read_exact(&mut rgba_buf)?; - colormap.push(rgba_buf.into()); - } - - Ok(colormap) -} - -/// Take a bitmap of indicies, and map a given palette to it, returning a new -/// RGBA bitmap -pub fn apply_palette( - input: &[u8], - palette: &[Rgba] -) -> Result, CzError> { - let mut output_map = Vec::new(); - - for byte in input.iter() { - let color = palette.get(*byte as usize); - if let Some(color) = color { - output_map.extend_from_slice(&color.0); - } else { - return Err(CzError::PaletteError); - } - } - - Ok(output_map) -} - -pub fn rgba_to_indexed( - input: &[u8], - palette: &[Rgba] -) -> Result, CzError> { - let mut output_map = Vec::new(); - let mut cache = HashMap::new(); - - for rgba in input.windows(4).step_by(4) { - let value = match cache.get(rgba) { - Some(val) => *val, - None => { - let value = palette.iter().position(|e| e.0 == rgba).unwrap_or_default() as u8; - cache.insert(rgba, value); - value - } - }; - - output_map.push(value) - } - - Ok(output_map) -} - -pub fn indexed_gen_palette( - input: &[u8], - header: &CommonHeader, -) -> Result<(Vec, Vec>), CzError> { - let size = (header.width() as u32 * header.height() as u32) * 4; - - let mut buf = vec![0; size as usize]; - buf[..input.len()].copy_from_slice(&input); - - let image = Image::new( - &buf, - header.width() as usize, - header.height() as usize - ).unwrap(); - - let mut opts = quantizr::Options::default(); - opts.set_max_colors(1 << header.depth()).unwrap(); - - let mut result = quantizr::QuantizeResult::quantize(&image, &opts); - result.set_dithering_level(0.5).unwrap(); - - let mut indicies = vec![0u8; header.width() as usize * header.height() as usize]; - result.remap_image(&image, indicies.as_mut_slice()).unwrap(); - - let palette = result.get_palette(); - - let gen_palette = palette.entries.as_slice().iter().map(|c| Rgba([c.r, c.g, c.b, c.a])).collect(); - - Ok((indicies, gen_palette)) -} - -pub fn default_palette() -> Vec> { - let mut colormap = Vec::new(); - - for i in 0..=0xFF { - colormap.push(Rgba([0xFF, 0xFF, 0xFF, i])) - } - - colormap -} diff --git a/cz/src/compression.rs b/cz/src/compression.rs index ccfe6d9..8f78d2c 100644 --- a/cz/src/compression.rs +++ b/cz/src/compression.rs @@ -116,19 +116,11 @@ pub fn decompress( Ok(bitmap) } -fn get_offset( - input: &[u8], - src: usize -) -> usize { +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; @@ -156,10 +148,7 @@ fn copy_range( dst - start_pos } -fn copy_one( - input: &[u8], - src: usize -) -> u8 { +fn copy_one(input: &[u8], src: usize) -> u8 { if input[src + 1] == 0 { input[src] } else if get_offset(input, src) == src { @@ -188,10 +177,7 @@ pub fn decompress_2( Ok(output_buf) } -pub fn decompress_lzw2( - input_data: &[u8], - size: usize -) -> Vec { +fn decompress_lzw2(input_data: &[u8], size: usize) -> Vec { let mut data = input_data.to_vec(); data[0] = 0; let mut dictionary = HashMap::new(); @@ -256,7 +242,7 @@ pub fn compress( let mut count; let mut last = Vec::new(); - let mut output_buf = Vec::new(); + let mut output_buf: Vec = vec![]; let mut output_info = CompressionInfo { total_size_raw: data.len(), ..Default::default() @@ -270,7 +256,7 @@ pub fn compress( offset += count; for d in &part_data { - output_buf.write_all(&d.to_le_bytes()).unwrap(); + output_buf.write(&d.to_le_bytes()).unwrap(); } output_info.chunks.push(ChunkInfo { @@ -283,9 +269,6 @@ pub fn compress( if output_info.chunk_count == 0 { panic!("No chunks compressed!") - } else if output_info.chunk_count != 1 { - output_info.chunks[0].size_raw -= 1; - output_info.chunks[output_info.chunk_count - 1].size_raw += 1; } output_info.total_size_compressed = output_buf.len() / 2; @@ -293,11 +276,7 @@ pub fn compress( (output_buf, output_info) } -fn compress_lzw( - data: &[u8], - size: usize, - last: Vec -) -> (usize, Vec, Vec) { +fn compress_lzw(data: &[u8], size: usize, last: Vec) -> (usize, Vec, Vec) { let mut count = 0; let mut dictionary = HashMap::new(); for i in 0..=255 { @@ -306,7 +285,7 @@ fn compress_lzw( let mut dictionary_count = (dictionary.len() + 1) as u16; let mut element = Vec::new(); - if last.is_empty() { + if last.len() != 0 { element = last } @@ -315,7 +294,7 @@ fn compress_lzw( let mut entry = element.clone(); entry.push(*c); - if dictionary.contains_key(&entry){ + if dictionary.get(&entry).is_some() { element = entry } else { compressed.push(*dictionary.get(&element).unwrap()); @@ -332,15 +311,15 @@ fn compress_lzw( } let last_element = element; - if compressed.is_empty() { - if last_element.is_empty() { + if compressed.len() == 0 { + if last_element.len() != 0 { for c in last_element { compressed.push(*dictionary.get(&vec![c]).unwrap()); } } return (count, compressed, Vec::new()) } else if compressed.len() < size { - if last_element.is_empty() { + if last_element.len() != 0 { compressed.push(*dictionary.get(&last_element).unwrap()); } return (count, compressed, Vec::new()) @@ -349,15 +328,8 @@ fn compress_lzw( (count, compressed, last_element) } -pub fn compress2( - data: &[u8], - size: usize -) -> (Vec, CompressionInfo) { - let size = if size == 0 { - 0x87BDF - } else { - size - }; +pub fn compress2(data: &[u8], size: usize) -> (Vec, CompressionInfo) { + let size = if size == 0 { 0x87BDF } else { size }; let mut part_data; @@ -371,27 +343,23 @@ pub fn compress2( ..Default::default() }; - let mut i = 0; loop { (count, part_data, last) = compress_lzw2(&data[offset..], size, last); if count == 0 { - break + break; } offset += count; output_buf.write_all(&part_data).unwrap(); - output_info.chunks.push( - ChunkInfo { - size_compressed: part_data.len(), - size_raw: count - } - ); + output_info.chunks.push(ChunkInfo { + size_compressed: part_data.len(), + size_raw: count, + }); output_info.chunk_count += 1; - i += 1; } if output_info.chunk_count == 0 { @@ -405,13 +373,9 @@ pub fn compress2( (output_buf, output_info) } -fn compress_lzw2( - data: &[u8], - size: usize, - last: Vec -) -> (usize, Vec, Vec) { +fn compress_lzw2(data: &[u8], size: usize, last: Vec) -> (usize, Vec, Vec) { let mut data = data.to_vec(); - if data.len() > 0 { + if data.is_empty() { data[0] = 0; } let mut count = 0; @@ -454,7 +418,7 @@ fn compress_lzw2( if size > 0 && bit_io.byte_size() >= size { count -= 1; - break + break; } } @@ -465,12 +429,12 @@ fn compress_lzw2( write_bit(&mut bit_io, *dictionary.get(&vec![c]).unwrap()); } } - return (count, bit_io.bytes(), Vec::new()) + return (count, bit_io.bytes(), Vec::new()); } else if bit_io.byte_size() < size { if !last_element.is_empty() { write_bit(&mut bit_io, *dictionary.get(&last_element).unwrap()); } - return (count, bit_io.bytes(), Vec::new()) + return (count, bit_io.bytes(), Vec::new()); } (count, bit_io.bytes(), last_element) diff --git a/cz/src/dynamic.rs b/cz/src/dynamic.rs index 918f983..8ee564f 100644 --- a/cz/src/dynamic.rs +++ b/cz/src/dynamic.rs @@ -7,11 +7,8 @@ use std::{ }; use crate::{ - common::{ - apply_palette, get_palette, indexed_gen_palette, - rgba_to_indexed, CommonHeader, CzError, - CzVersion, ExtendedHeader - }, + color::{apply_palette, get_palette, indexed_gen_palette, rgba_to_indexed}, + common::{CommonHeader, CzError, CzVersion, ExtendedHeader}, formats::{cz0, cz1, cz2, cz3, cz4}, }; @@ -38,9 +35,7 @@ impl DynamicCz { /// The input must begin with the /// [magic bytes](https://en.wikipedia.org/wiki/File_format#Magic_number) /// of the file - fn decode( - input: &mut T - ) -> Result { + fn decode(input: &mut T) -> Result { // Get the header common to all CZ images let header_common = CommonHeader::from_bytes(input)?; let mut header_extended = None; @@ -70,22 +65,27 @@ 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 - eprintln!("Image is wrong, length is {}, expected {}", bitmap.len(), image_size * (header_common.depth() >> 3) as usize); - return Err(CzError::Corrupt); + eprintln!( + "Image is wrong, length is {}, expected {}", + bitmap.len(), + image_size * (header_common.depth() >> 3) as usize + ); + return Err(CzError::Corrupt(String::from("Bitmap size incorrect"))); } match header_common.depth() { 4 => { + eprintln!("Files with a bit depth of 4 are not yet supported"); todo!() } 8 => { if let Some(palette) = &palette { bitmap = apply_palette(&bitmap, palette)?; } else { - return Err(CzError::PaletteError) + return Err(CzError::PaletteError); } - }, - 24 => { + } + 24 => { bitmap = bitmap .windows(3) .step_by(3) @@ -93,7 +93,12 @@ impl DynamicCz { .collect(); } 32 => (), - _ => panic!() + _ => { + return Err(CzError::Corrupt(format!( + "Invalid bit depth: {}", + header_common.depth() + ))) + } } Ok(Self { @@ -107,10 +112,7 @@ impl DynamicCz { /// Save the `DynamicCz` as a CZ# file. The format saved in is determined /// from the format in the header. Check [`CommonHeader::set_version()`] /// to change the CZ# version. - pub fn save_as_cz>( - &self, - path: T - ) -> Result<(), CzError> { + 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)?; @@ -129,6 +131,7 @@ impl DynamicCz { todo!() } 8 => { + // Do things with palettes if let Some(pal) = &self.palette { // Use the existing palette to palette the image output_bitmap = rgba_to_indexed(self.bitmap(), pal)?; @@ -138,10 +141,7 @@ impl DynamicCz { } } else { // Generate a palette and corresponding indexed bitmap if there is none - let result = indexed_gen_palette( - self.bitmap(), - self.header() - )?; + let result = indexed_gen_palette(self.bitmap(), self.header())?; output_bitmap = result.0; let palette = result.1; @@ -149,22 +149,30 @@ impl DynamicCz { for rgba in palette { let mut rgba_clone = rgba.0; if false { + // TODO: Make a toggle for this rgba_clone[0..3].reverse(); } out_file.write_all(&rgba_clone)?; } } - }, + } 24 => { - output_bitmap = self.bitmap + // Convert from RGBA to RGB + output_bitmap = self + .bitmap .windows(4) .step_by(4) .flat_map(|p| &p[0..3]) .copied() .collect(); - }, + } 32 => output_bitmap = self.bitmap.clone(), - _ => return Err(CzError::Corrupt) + _ => { + return Err(CzError::Corrupt(format!( + "Invalid bit depth: {}", + self.header_common.depth() + ))) + } } match self.header_common.version() { @@ -172,7 +180,7 @@ impl DynamicCz { CzVersion::CZ1 => cz1::encode(&mut out_file, &output_bitmap)?, CzVersion::CZ2 => cz2::encode(&mut out_file, &output_bitmap)?, CzVersion::CZ3 => cz3::encode(&mut out_file, &output_bitmap, &self.header_common)?, - CzVersion::CZ4 => todo!(), + CzVersion::CZ4 => cz4::encode(&mut out_file, &output_bitmap, &self.header_common)?, CzVersion::CZ5 => todo!(), } @@ -214,13 +222,9 @@ impl DynamicCz { depth: u16, width: u16, height: u16, - bitmap: Vec + bitmap: Vec, ) -> Self { - let mut header_common = CommonHeader::new( - version, - width, - height - ); + let mut header_common = CommonHeader::new(version, width, height); header_common.set_depth(depth); Self { diff --git a/cz/src/formats/cz2.rs b/cz/src/formats/cz2.rs index feeeae3..31a6d72 100644 --- a/cz/src/formats/cz2.rs +++ b/cz/src/formats/cz2.rs @@ -13,10 +13,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) = compress2(bitmap, 0x87BDF); dbg!(&compressed_info); diff --git a/cz/src/formats/cz3.rs b/cz/src/formats/cz3.rs index 232823a..a598f97 100644 --- a/cz/src/formats/cz3.rs +++ b/cz/src/formats/cz3.rs @@ -42,8 +42,7 @@ fn line_diff(header: &CommonHeader, 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; diff --git a/cz/src/formats/cz4.rs b/cz/src/formats/cz4.rs index 2716879..aa47e6d 100644 --- a/cz/src/formats/cz4.rs +++ b/cz/src/formats/cz4.rs @@ -1,9 +1,10 @@ -use byteorder::ReadBytesExt; +use byteorder::{ReadBytesExt, WriteBytesExt}; use image::RgbaImage; -use std::io::{Read, Seek, SeekFrom}; +use std::{fs, time}; +use std::io::{Read, Seek, SeekFrom, Write}; use crate::common::{CommonHeader, CzError}; -use crate::compression::{decompress, get_chunk_info}; +use crate::compression::{compress, decompress, get_chunk_info}; pub fn decode( bytes: &mut T, @@ -12,60 +13,118 @@ pub fn decode( let block_info = get_chunk_info(bytes)?; bytes.seek(SeekFrom::Start(block_info.length as u64))?; - let pcount = (header.width() as usize * header.height() as usize) * 3; let data = decompress(bytes, &block_info)?; - let data2 = data[pcount..].to_vec(); + let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32); - let pixel_byte_count = 3; - line_diff_cz4(&mut picture, pixel_byte_count, &data); - - let pixel_byte_count = 1; - line_diff_cz4(&mut picture, pixel_byte_count, &data2); + line_diff(&mut picture, &data); Ok(picture.into_raw()) } -pub fn line_diff_cz4(picture: &mut RgbaImage, pixel_byte_count: usize, data: &[u8]) { +pub fn encode( + output: &mut T, + bitmap: &[u8], + header: &CommonHeader, +) -> Result<(), CzError> { + let bitmap = diff_line(header, bitmap); + + let (compressed_data, compressed_info) = compress(&bitmap, 0xFEFD); + + compressed_info.write_into(output)?; + + output.write_all(&compressed_data)?; + + Ok(()) +} + +fn line_diff(picture: &mut RgbaImage, data: &[u8]) { let width = picture.width(); let height = picture.height(); let block_height = (f32::ceil(height as f32 / 3.0) as u16) as u32; let mut curr_line; - let mut prev_line = vec![0u8; width as usize * pixel_byte_count]; + let mut prev_line = vec![0u8; width as usize * 3]; + + let mut curr_alpha; + let mut prev_alpha = vec![0u8; width as usize]; + + let pcount = (width * height * 3) as usize; let mut i = 0; + let mut z = 0; for y in 0..height { - curr_line = data[i..i + width as usize * pixel_byte_count].to_vec(); + curr_line = data[i..i + width as usize * 3].to_vec(); + curr_alpha = data[pcount + z..pcount + z + width as usize].to_vec(); if y % block_height != 0 { - for x in 0..(width as usize * pixel_byte_count) { - curr_line[x] = u8::wrapping_add(curr_line[x], prev_line[x]) + for x in 0..(width as usize * 3) { + curr_line[x] = curr_line[x].wrapping_add(prev_line[x]) + } + for x in 0..width as usize { + curr_alpha[x] = curr_alpha[x].wrapping_add(prev_alpha[x]) } } for x in 0..width as usize { - if pixel_byte_count == 1 { - picture.get_pixel_mut(x as u32, y).0[3] = curr_line[x]; - } else if pixel_byte_count == 4 { - picture.get_pixel_mut(x as u32, y).0 = [ - curr_line[x * pixel_byte_count], - curr_line[x * pixel_byte_count + 1], - curr_line[x * pixel_byte_count + 2], - curr_line[x * pixel_byte_count + 3], - ]; - } else if pixel_byte_count == 3 { - picture.get_pixel_mut(x as u32, y).0 = [ - curr_line[x * pixel_byte_count], - curr_line[x * pixel_byte_count + 1], - curr_line[x * pixel_byte_count + 2], - 0xFF, - ]; - } + picture.get_pixel_mut(x as u32, y).0 = [ + curr_line[x * 3], + curr_line[x * 3 + 1], + curr_line[x * 3 + 2], + curr_alpha[x], + ]; } prev_line.clone_from(&curr_line); - i += width as usize * pixel_byte_count; + prev_alpha.clone_from(&curr_alpha); + i += width as usize * 3; + z += width as usize; } } + +fn diff_line(header: &CommonHeader, input: &[u8]) -> Vec { + let width = header.width() as u32; + let height = header.height() as u32; + + let mut data = Vec::with_capacity(input.len()); + let mut alpha_data = Vec::with_capacity(width 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; + + let mut curr_line: Vec; + let mut prev_line: Vec = Vec::with_capacity(width as usize * 3); + + let mut curr_alpha: Vec; + let mut prev_alpha: Vec = Vec::with_capacity(width as usize); + + let mut i = 0; + for y in 0..height { + curr_line = input[i..i + line_byte_count].windows(4).step_by(4).flat_map(|r| &r[0..3]).copied().collect(); + curr_alpha = input[i..i + line_byte_count].iter().skip(3).step_by(4).copied().collect(); + + if y % block_height as u32 != 0 { + for x in 0..width as usize * 3 { + curr_line[x] = curr_line[x].wrapping_sub(prev_line[x]); + prev_line[x] = prev_line[x].wrapping_add(curr_line[x]); + } + for x in 0..width as usize { + curr_alpha[x] = curr_alpha[x].wrapping_sub(prev_alpha[x]); + prev_alpha[x] = prev_alpha[x].wrapping_add(curr_alpha[x]); + } + } else { + prev_line.clone_from(&curr_line); + prev_alpha.clone_from(&curr_alpha); + } + + data.extend_from_slice(&curr_line); + alpha_data.extend_from_slice(&curr_alpha); + i += line_byte_count; + } + + data.extend_from_slice(&alpha_data); + + data +} diff --git a/cz/src/lib.rs b/cz/src/lib.rs index b9277d6..122581c 100644 --- a/cz/src/lib.rs +++ b/cz/src/lib.rs @@ -1,4 +1,5 @@ mod binio; +mod color; mod compression; pub mod common; diff --git a/utils/src/main.rs b/utils/src/main.rs index b08144b..6cfa823 100644 --- a/utils/src/main.rs +++ b/utils/src/main.rs @@ -1,19 +1,20 @@ -use std::path::PathBuf; - -use cz::{ - common::CzVersion, - dynamic::DynamicCz -}; +use cz::{common::CzVersion, dynamic::DynamicCz}; fn main() { // Open the desired PNG - let new_bitmap = image::open("mio.png") - .unwrap() - .to_rgba8(); + //let new_bitmap = image::open("mio.png").unwrap().to_rgba8(); - let mut gallery_cz = DynamicCz::open("24.cz2").unwrap(); - gallery_cz.save_as_png("24.png").unwrap(); + let gallery_cz = DynamicCz::open("test.cz4").unwrap(); + gallery_cz.save_as_png("test.png").unwrap(); + gallery_cz.save_as_cz("test-reencode.cz4").unwrap(); + + let cz_image_test = DynamicCz::open("test-reencode.cz4").unwrap(); + + // Save the newly decoded CZ3 as another PNG as a test + cz_image_test.save_as_png("test-reencode.png").unwrap(); + + /* gallery_cz.set_bitmap(new_bitmap.into_vec()); gallery_cz.header_mut().set_depth(8); gallery_cz.remove_palette(); @@ -25,4 +26,5 @@ fn main() { // Save the newly decoded CZ3 as another PNG as a test cz_image_test.save_as_png("24-modified.png").unwrap(); + */ }