mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
Added CZ4 recompression
This commit is contained in:
parent
67700df6ef
commit
8aa150ee46
10 changed files with 286 additions and 261 deletions
|
@ -67,12 +67,12 @@ impl BitIO {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_bit(&mut self, data: u64, bit_len: usize) {
|
pub fn write_bit(&mut self, data: u64, bit_len: usize) {
|
||||||
if bit_len > 8*8 {
|
if bit_len > 8 * 8 {
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
|
|
||||||
if bit_len % 8 == 0 && self.bit_offset == 0 {
|
if bit_len % 8 == 0 && self.bit_offset == 0 {
|
||||||
self.write(data, bit_len/8);
|
self.write(data, bit_len / 8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,8 @@ impl BitIO {
|
||||||
let mut padded_slice = [0u8; 8];
|
let mut padded_slice = [0u8; 8];
|
||||||
padded_slice.copy_from_slice(&data.to_le_bytes());
|
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_offset += byte_len;
|
||||||
|
|
||||||
self.byte_size = self.byte_offset + (self.bit_offset + 7) / 8;
|
self.byte_size = self.byte_offset + (self.bit_offset + 7) / 8;
|
||||||
|
|
104
cz/src/color.rs
Normal file
104
cz/src/color.rs
Normal file
|
@ -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<T: Seek + ReadBytesExt + Read>(
|
||||||
|
input: &mut T,
|
||||||
|
num_colors: usize,
|
||||||
|
) -> Result<Vec<Rgba<u8>>, 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<u8>]) -> Result<Vec<u8>, 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<u8>]) -> Result<Vec<u8>, 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<u8>, Vec<image::Rgba<u8>>), 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<Rgba<u8>> {
|
||||||
|
let mut colormap = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..=0xFF {
|
||||||
|
colormap.push(Rgba([0xFF, 0xFF, 0xFF, i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
colormap
|
||||||
|
}
|
124
cz/src/common.rs
124
cz/src/common.rs
|
@ -1,13 +1,8 @@
|
||||||
//! Shared types and traits between CZ# files
|
//! Shared types and traits between CZ# files
|
||||||
|
|
||||||
use std::{
|
use std::io::{self, Read, Seek, Write};
|
||||||
collections::HashMap,
|
|
||||||
io::{self, Read, Seek, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use image::Rgba;
|
|
||||||
use quantizr::Image;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -21,8 +16,8 @@ pub enum CzError {
|
||||||
#[error("Bitmap size does not match image size")]
|
#[error("Bitmap size does not match image size")]
|
||||||
BitmapFormat,
|
BitmapFormat,
|
||||||
|
|
||||||
#[error("File data is incorrect, it might be corrupt")]
|
#[error("File data is incorrect, it might be corrupt: {0}")]
|
||||||
Corrupt,
|
Corrupt(String),
|
||||||
|
|
||||||
#[error("File is not a CZ image")]
|
#[error("File is not a CZ image")]
|
||||||
NotCzFile,
|
NotCzFile,
|
||||||
|
@ -117,9 +112,7 @@ impl CommonHeader {
|
||||||
pub fn set_length(&mut self, length: u32) {
|
pub fn set_length(&mut self, length: u32) {
|
||||||
self.length = length
|
self.length = length
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CommonHeader {
|
|
||||||
pub fn from_bytes<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
pub fn from_bytes<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
@ -243,6 +236,12 @@ pub struct ExtendedHeader {
|
||||||
unknown_2: Option<u32>,
|
unknown_2: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ExtendedHeader {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExtendedHeader {
|
impl ExtendedHeader {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ExtendedHeader {
|
ExtendedHeader {
|
||||||
|
@ -339,108 +338,3 @@ impl ExtendedHeader {
|
||||||
Ok((output.stream_position()? - pos) as usize)
|
Ok((output.stream_position()? - pos) as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExtendedHeader {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_palette<T: Seek + ReadBytesExt + Read>(
|
|
||||||
input: &mut T,
|
|
||||||
num_colors: usize,
|
|
||||||
) -> Result<Vec<Rgba<u8>>, 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<u8>]
|
|
||||||
) -> Result<Vec<u8>, 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<u8>]
|
|
||||||
) -> Result<Vec<u8>, 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<u8>, Vec<image::Rgba<u8>>), 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<Rgba<u8>> {
|
|
||||||
let mut colormap = Vec::new();
|
|
||||||
|
|
||||||
for i in 0..=0xFF {
|
|
||||||
colormap.push(Rgba([0xFF, 0xFF, 0xFF, i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
colormap
|
|
||||||
}
|
|
||||||
|
|
|
@ -116,19 +116,11 @@ pub fn decompress<T: Seek + ReadBytesExt + Read>(
|
||||||
Ok(bitmap)
|
Ok(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_offset(
|
fn get_offset(input: &[u8], src: usize) -> usize {
|
||||||
input: &[u8],
|
|
||||||
src: usize
|
|
||||||
) -> usize {
|
|
||||||
(((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2
|
(((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_range(
|
fn copy_range(bitmap: &mut Vec<u8>, input: &[u8], src: usize, dst: usize) -> usize {
|
||||||
bitmap: &mut Vec<u8>,
|
|
||||||
input: &[u8],
|
|
||||||
src: usize,
|
|
||||||
dst: usize
|
|
||||||
) -> usize {
|
|
||||||
let mut dst = dst;
|
let mut dst = dst;
|
||||||
let start_pos = dst;
|
let start_pos = dst;
|
||||||
|
|
||||||
|
@ -156,10 +148,7 @@ fn copy_range(
|
||||||
dst - start_pos
|
dst - start_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_one(
|
fn copy_one(input: &[u8], src: usize) -> u8 {
|
||||||
input: &[u8],
|
|
||||||
src: usize
|
|
||||||
) -> u8 {
|
|
||||||
if input[src + 1] == 0 {
|
if input[src + 1] == 0 {
|
||||||
input[src]
|
input[src]
|
||||||
} else if get_offset(input, src) == src {
|
} else if get_offset(input, src) == src {
|
||||||
|
@ -188,10 +177,7 @@ pub fn decompress_2<T: Seek + ReadBytesExt + Read>(
|
||||||
Ok(output_buf)
|
Ok(output_buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decompress_lzw2(
|
fn decompress_lzw2(input_data: &[u8], size: usize) -> Vec<u8> {
|
||||||
input_data: &[u8],
|
|
||||||
size: usize
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let mut data = input_data.to_vec();
|
let mut data = input_data.to_vec();
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
let mut dictionary = HashMap::new();
|
let mut dictionary = HashMap::new();
|
||||||
|
@ -256,7 +242,7 @@ pub fn compress(
|
||||||
let mut count;
|
let mut count;
|
||||||
let mut last = Vec::new();
|
let mut last = Vec::new();
|
||||||
|
|
||||||
let mut output_buf = Vec::new();
|
let mut output_buf: Vec<u8> = vec![];
|
||||||
let mut output_info = CompressionInfo {
|
let mut output_info = CompressionInfo {
|
||||||
total_size_raw: data.len(),
|
total_size_raw: data.len(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -270,7 +256,7 @@ pub fn compress(
|
||||||
offset += count;
|
offset += count;
|
||||||
|
|
||||||
for d in &part_data {
|
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 {
|
output_info.chunks.push(ChunkInfo {
|
||||||
|
@ -283,9 +269,6 @@ pub fn compress(
|
||||||
|
|
||||||
if output_info.chunk_count == 0 {
|
if output_info.chunk_count == 0 {
|
||||||
panic!("No chunks compressed!")
|
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;
|
output_info.total_size_compressed = output_buf.len() / 2;
|
||||||
|
@ -293,11 +276,7 @@ pub fn compress(
|
||||||
(output_buf, output_info)
|
(output_buf, output_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress_lzw(
|
fn compress_lzw(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u16>, Vec<u8>) {
|
||||||
data: &[u8],
|
|
||||||
size: usize,
|
|
||||||
last: Vec<u8>
|
|
||||||
) -> (usize, Vec<u16>, Vec<u8>) {
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut dictionary = HashMap::new();
|
let mut dictionary = HashMap::new();
|
||||||
for i in 0..=255 {
|
for i in 0..=255 {
|
||||||
|
@ -306,7 +285,7 @@ fn compress_lzw(
|
||||||
let mut dictionary_count = (dictionary.len() + 1) as u16;
|
let mut dictionary_count = (dictionary.len() + 1) as u16;
|
||||||
|
|
||||||
let mut element = Vec::new();
|
let mut element = Vec::new();
|
||||||
if last.is_empty() {
|
if last.len() != 0 {
|
||||||
element = last
|
element = last
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +294,7 @@ fn compress_lzw(
|
||||||
let mut entry = element.clone();
|
let mut entry = element.clone();
|
||||||
entry.push(*c);
|
entry.push(*c);
|
||||||
|
|
||||||
if dictionary.contains_key(&entry){
|
if dictionary.get(&entry).is_some() {
|
||||||
element = entry
|
element = entry
|
||||||
} else {
|
} else {
|
||||||
compressed.push(*dictionary.get(&element).unwrap());
|
compressed.push(*dictionary.get(&element).unwrap());
|
||||||
|
@ -332,15 +311,15 @@ fn compress_lzw(
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_element = element;
|
let last_element = element;
|
||||||
if compressed.is_empty() {
|
if compressed.len() == 0 {
|
||||||
if last_element.is_empty() {
|
if last_element.len() != 0 {
|
||||||
for c in last_element {
|
for c in last_element {
|
||||||
compressed.push(*dictionary.get(&vec![c]).unwrap());
|
compressed.push(*dictionary.get(&vec![c]).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (count, compressed, Vec::new())
|
return (count, compressed, Vec::new())
|
||||||
} else if compressed.len() < size {
|
} else if compressed.len() < size {
|
||||||
if last_element.is_empty() {
|
if last_element.len() != 0 {
|
||||||
compressed.push(*dictionary.get(&last_element).unwrap());
|
compressed.push(*dictionary.get(&last_element).unwrap());
|
||||||
}
|
}
|
||||||
return (count, compressed, Vec::new())
|
return (count, compressed, Vec::new())
|
||||||
|
@ -349,15 +328,8 @@ fn compress_lzw(
|
||||||
(count, compressed, last_element)
|
(count, compressed, last_element)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compress2(
|
pub fn compress2(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
data: &[u8],
|
let size = if size == 0 { 0x87BDF } else { size };
|
||||||
size: usize
|
|
||||||
) -> (Vec<u8>, CompressionInfo) {
|
|
||||||
let size = if size == 0 {
|
|
||||||
0x87BDF
|
|
||||||
} else {
|
|
||||||
size
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut part_data;
|
let mut part_data;
|
||||||
|
|
||||||
|
@ -371,27 +343,23 @@ pub fn compress2(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
loop {
|
loop {
|
||||||
(count, part_data, last) = compress_lzw2(&data[offset..], size, last);
|
(count, part_data, last) = compress_lzw2(&data[offset..], size, last);
|
||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += count;
|
offset += count;
|
||||||
|
|
||||||
output_buf.write_all(&part_data).unwrap();
|
output_buf.write_all(&part_data).unwrap();
|
||||||
|
|
||||||
output_info.chunks.push(
|
output_info.chunks.push(ChunkInfo {
|
||||||
ChunkInfo {
|
|
||||||
size_compressed: part_data.len(),
|
size_compressed: part_data.len(),
|
||||||
size_raw: count
|
size_raw: count,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
output_info.chunk_count += 1;
|
output_info.chunk_count += 1;
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if output_info.chunk_count == 0 {
|
if output_info.chunk_count == 0 {
|
||||||
|
@ -405,13 +373,9 @@ pub fn compress2(
|
||||||
(output_buf, output_info)
|
(output_buf, output_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress_lzw2(
|
fn compress_lzw2(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u8>, Vec<u8>) {
|
||||||
data: &[u8],
|
|
||||||
size: usize,
|
|
||||||
last: Vec<u8>
|
|
||||||
) -> (usize, Vec<u8>, Vec<u8>) {
|
|
||||||
let mut data = data.to_vec();
|
let mut data = data.to_vec();
|
||||||
if data.len() > 0 {
|
if data.is_empty() {
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
}
|
}
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
@ -454,7 +418,7 @@ fn compress_lzw2(
|
||||||
|
|
||||||
if size > 0 && bit_io.byte_size() >= size {
|
if size > 0 && bit_io.byte_size() >= size {
|
||||||
count -= 1;
|
count -= 1;
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,12 +429,12 @@ fn compress_lzw2(
|
||||||
write_bit(&mut bit_io, *dictionary.get(&vec![c]).unwrap());
|
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 {
|
} else if bit_io.byte_size() < size {
|
||||||
if !last_element.is_empty() {
|
if !last_element.is_empty() {
|
||||||
write_bit(&mut bit_io, *dictionary.get(&last_element).unwrap());
|
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)
|
(count, bit_io.bytes(), last_element)
|
||||||
|
|
|
@ -7,11 +7,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
color::{apply_palette, get_palette, indexed_gen_palette, rgba_to_indexed},
|
||||||
apply_palette, get_palette, indexed_gen_palette,
|
common::{CommonHeader, CzError, CzVersion, ExtendedHeader},
|
||||||
rgba_to_indexed, CommonHeader, CzError,
|
|
||||||
CzVersion, ExtendedHeader
|
|
||||||
},
|
|
||||||
formats::{cz0, cz1, cz2, cz3, cz4},
|
formats::{cz0, cz1, cz2, cz3, cz4},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,9 +35,7 @@ impl DynamicCz {
|
||||||
/// The input must begin with the
|
/// The input must begin with the
|
||||||
/// [magic bytes](https://en.wikipedia.org/wiki/File_format#Magic_number)
|
/// [magic bytes](https://en.wikipedia.org/wiki/File_format#Magic_number)
|
||||||
/// of the file
|
/// of the file
|
||||||
fn decode<T: Seek + ReadBytesExt + Read>(
|
fn decode<T: Seek + ReadBytesExt + Read>(input: &mut T) -> Result<Self, CzError> {
|
||||||
input: &mut T
|
|
||||||
) -> Result<Self, CzError> {
|
|
||||||
// Get the header common to all CZ images
|
// Get the header common to all CZ images
|
||||||
let header_common = CommonHeader::from_bytes(input)?;
|
let header_common = CommonHeader::from_bytes(input)?;
|
||||||
let mut header_extended = None;
|
let mut header_extended = None;
|
||||||
|
@ -70,21 +65,26 @@ impl DynamicCz {
|
||||||
let image_size = header_common.width() as usize * header_common.height() as usize;
|
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 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
|
// 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);
|
eprintln!(
|
||||||
return Err(CzError::Corrupt);
|
"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() {
|
match header_common.depth() {
|
||||||
4 => {
|
4 => {
|
||||||
|
eprintln!("Files with a bit depth of 4 are not yet supported");
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
8 => {
|
8 => {
|
||||||
if let Some(palette) = &palette {
|
if let Some(palette) = &palette {
|
||||||
bitmap = apply_palette(&bitmap, palette)?;
|
bitmap = apply_palette(&bitmap, palette)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(CzError::PaletteError)
|
return Err(CzError::PaletteError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
24 => {
|
24 => {
|
||||||
bitmap = bitmap
|
bitmap = bitmap
|
||||||
.windows(3)
|
.windows(3)
|
||||||
|
@ -93,7 +93,12 @@ impl DynamicCz {
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
32 => (),
|
32 => (),
|
||||||
_ => panic!()
|
_ => {
|
||||||
|
return Err(CzError::Corrupt(format!(
|
||||||
|
"Invalid bit depth: {}",
|
||||||
|
header_common.depth()
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -107,10 +112,7 @@ impl DynamicCz {
|
||||||
/// Save the `DynamicCz` as a CZ# file. The format saved in is determined
|
/// Save the `DynamicCz` as a CZ# file. The format saved in is determined
|
||||||
/// from the format in the header. Check [`CommonHeader::set_version()`]
|
/// from the format in the header. Check [`CommonHeader::set_version()`]
|
||||||
/// to change the CZ# version.
|
/// to change the CZ# version.
|
||||||
pub fn save_as_cz<T: Into<std::path::PathBuf>>(
|
pub fn save_as_cz<T: Into<std::path::PathBuf>>(&self, path: T) -> Result<(), CzError> {
|
||||||
&self,
|
|
||||||
path: T
|
|
||||||
) -> Result<(), CzError> {
|
|
||||||
let mut out_file = BufWriter::new(File::create(path.into())?);
|
let mut out_file = BufWriter::new(File::create(path.into())?);
|
||||||
|
|
||||||
self.header_common.write_into(&mut out_file)?;
|
self.header_common.write_into(&mut out_file)?;
|
||||||
|
@ -129,6 +131,7 @@ impl DynamicCz {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
8 => {
|
8 => {
|
||||||
|
// Do things with palettes
|
||||||
if let Some(pal) = &self.palette {
|
if let Some(pal) = &self.palette {
|
||||||
// Use the existing palette to palette the image
|
// Use the existing palette to palette the image
|
||||||
output_bitmap = rgba_to_indexed(self.bitmap(), pal)?;
|
output_bitmap = rgba_to_indexed(self.bitmap(), pal)?;
|
||||||
|
@ -138,10 +141,7 @@ impl DynamicCz {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Generate a palette and corresponding indexed bitmap if there is none
|
// Generate a palette and corresponding indexed bitmap if there is none
|
||||||
let result = indexed_gen_palette(
|
let result = indexed_gen_palette(self.bitmap(), self.header())?;
|
||||||
self.bitmap(),
|
|
||||||
self.header()
|
|
||||||
)?;
|
|
||||||
|
|
||||||
output_bitmap = result.0;
|
output_bitmap = result.0;
|
||||||
let palette = result.1;
|
let palette = result.1;
|
||||||
|
@ -149,22 +149,30 @@ impl DynamicCz {
|
||||||
for rgba in palette {
|
for rgba in palette {
|
||||||
let mut rgba_clone = rgba.0;
|
let mut rgba_clone = rgba.0;
|
||||||
if false {
|
if false {
|
||||||
|
// TODO: Make a toggle for this
|
||||||
rgba_clone[0..3].reverse();
|
rgba_clone[0..3].reverse();
|
||||||
}
|
}
|
||||||
out_file.write_all(&rgba_clone)?;
|
out_file.write_all(&rgba_clone)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
24 => {
|
24 => {
|
||||||
output_bitmap = self.bitmap
|
// Convert from RGBA to RGB
|
||||||
|
output_bitmap = self
|
||||||
|
.bitmap
|
||||||
.windows(4)
|
.windows(4)
|
||||||
.step_by(4)
|
.step_by(4)
|
||||||
.flat_map(|p| &p[0..3])
|
.flat_map(|p| &p[0..3])
|
||||||
.copied()
|
.copied()
|
||||||
.collect();
|
.collect();
|
||||||
},
|
}
|
||||||
32 => output_bitmap = self.bitmap.clone(),
|
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() {
|
match self.header_common.version() {
|
||||||
|
@ -172,7 +180,7 @@ impl DynamicCz {
|
||||||
CzVersion::CZ1 => cz1::encode(&mut out_file, &output_bitmap)?,
|
CzVersion::CZ1 => cz1::encode(&mut out_file, &output_bitmap)?,
|
||||||
CzVersion::CZ2 => cz2::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::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!(),
|
CzVersion::CZ5 => todo!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,13 +222,9 @@ impl DynamicCz {
|
||||||
depth: u16,
|
depth: u16,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
bitmap: Vec<u8>
|
bitmap: Vec<u8>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut header_common = CommonHeader::new(
|
let mut header_common = CommonHeader::new(version, width, height);
|
||||||
version,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
|
||||||
header_common.set_depth(depth);
|
header_common.set_depth(depth);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -13,10 +13,7 @@ pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, C
|
||||||
Ok(bitmap)
|
Ok(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode<T: WriteBytesExt + Write>(
|
pub fn encode<T: WriteBytesExt + Write>(output: &mut T, bitmap: &[u8]) -> Result<(), CzError> {
|
||||||
output: &mut T,
|
|
||||||
bitmap: &[u8],
|
|
||||||
) -> Result<(), CzError> {
|
|
||||||
let (compressed_data, compressed_info) = compress2(bitmap, 0x87BDF);
|
let (compressed_data, compressed_info) = compress2(bitmap, 0x87BDF);
|
||||||
|
|
||||||
dbg!(&compressed_info);
|
dbg!(&compressed_info);
|
||||||
|
|
|
@ -42,8 +42,7 @@ fn line_diff(header: &CommonHeader, data: &[u8]) -> Vec<u8> {
|
||||||
let height = header.height() as u32;
|
let height = header.height() as u32;
|
||||||
let mut output_buf = data.to_vec();
|
let mut output_buf = data.to_vec();
|
||||||
|
|
||||||
let block_height =
|
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize;
|
||||||
(f32::ceil(height as f32 / 3.0) as u16) as usize;
|
|
||||||
let pixel_byte_count = header.depth() >> 3;
|
let pixel_byte_count = header.depth() >> 3;
|
||||||
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use image::RgbaImage;
|
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::common::{CommonHeader, CzError};
|
||||||
use crate::compression::{decompress, get_chunk_info};
|
use crate::compression::{compress, decompress, get_chunk_info};
|
||||||
|
|
||||||
pub fn decode<T: Seek + ReadBytesExt + Read>(
|
pub fn decode<T: Seek + ReadBytesExt + Read>(
|
||||||
bytes: &mut T,
|
bytes: &mut T,
|
||||||
|
@ -12,60 +13,118 @@ pub fn decode<T: Seek + ReadBytesExt + Read>(
|
||||||
let block_info = get_chunk_info(bytes)?;
|
let block_info = get_chunk_info(bytes)?;
|
||||||
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
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 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 mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32);
|
||||||
|
|
||||||
let pixel_byte_count = 3;
|
line_diff(&mut picture, &data);
|
||||||
line_diff_cz4(&mut picture, pixel_byte_count, &data);
|
|
||||||
|
|
||||||
let pixel_byte_count = 1;
|
|
||||||
line_diff_cz4(&mut picture, pixel_byte_count, &data2);
|
|
||||||
|
|
||||||
Ok(picture.into_raw())
|
Ok(picture.into_raw())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_diff_cz4(picture: &mut RgbaImage, pixel_byte_count: usize, data: &[u8]) {
|
pub fn encode<T: WriteBytesExt + Write>(
|
||||||
|
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 width = picture.width();
|
||||||
let height = picture.height();
|
let height = picture.height();
|
||||||
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as u32;
|
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as u32;
|
||||||
|
|
||||||
let mut curr_line;
|
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 i = 0;
|
||||||
|
let mut z = 0;
|
||||||
for y in 0..height {
|
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 {
|
if y % block_height != 0 {
|
||||||
for x in 0..(width as usize * pixel_byte_count) {
|
for x in 0..(width as usize * 3) {
|
||||||
curr_line[x] = u8::wrapping_add(curr_line[x], prev_line[x])
|
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 {
|
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 = [
|
picture.get_pixel_mut(x as u32, y).0 = [
|
||||||
curr_line[x * pixel_byte_count],
|
curr_line[x * 3],
|
||||||
curr_line[x * pixel_byte_count + 1],
|
curr_line[x * 3 + 1],
|
||||||
curr_line[x * pixel_byte_count + 2],
|
curr_line[x * 3 + 2],
|
||||||
curr_line[x * pixel_byte_count + 3],
|
curr_alpha[x],
|
||||||
];
|
];
|
||||||
} 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,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_line.clone_from(&curr_line);
|
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<u8> {
|
||||||
|
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<u8>;
|
||||||
|
let mut prev_line: Vec<u8> = Vec::with_capacity(width as usize * 3);
|
||||||
|
|
||||||
|
let mut curr_alpha: Vec<u8>;
|
||||||
|
let mut prev_alpha: Vec<u8> = 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
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod binio;
|
mod binio;
|
||||||
|
mod color;
|
||||||
mod compression;
|
mod compression;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
use std::path::PathBuf;
|
use cz::{common::CzVersion, dynamic::DynamicCz};
|
||||||
|
|
||||||
use cz::{
|
|
||||||
common::CzVersion,
|
|
||||||
dynamic::DynamicCz
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Open the desired PNG
|
// Open the desired PNG
|
||||||
let new_bitmap = image::open("mio.png")
|
//let new_bitmap = image::open("mio.png").unwrap().to_rgba8();
|
||||||
.unwrap()
|
|
||||||
.to_rgba8();
|
|
||||||
|
|
||||||
let mut gallery_cz = DynamicCz::open("24.cz2").unwrap();
|
let gallery_cz = DynamicCz::open("test.cz4").unwrap();
|
||||||
gallery_cz.save_as_png("24.png").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.set_bitmap(new_bitmap.into_vec());
|
||||||
gallery_cz.header_mut().set_depth(8);
|
gallery_cz.header_mut().set_depth(8);
|
||||||
gallery_cz.remove_palette();
|
gallery_cz.remove_palette();
|
||||||
|
@ -25,4 +26,5 @@ fn main() {
|
||||||
|
|
||||||
// Save the newly decoded CZ3 as another PNG as a test
|
// Save the newly decoded CZ3 as another PNG as a test
|
||||||
cz_image_test.save_as_png("24-modified.png").unwrap();
|
cz_image_test.save_as_png("24-modified.png").unwrap();
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue