mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
Merge branch 'main' of https://github.com/G2-Games/lbee-utils
This commit is contained in:
commit
ae94c46aa8
9 changed files with 91 additions and 55 deletions
|
@ -11,4 +11,4 @@ A encoder/decoder for CZ# image files used in
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
thiserror = "1.0.59"
|
thiserror = "1.0.59"
|
||||||
png = "0.17.13"
|
png = "0.17.13"
|
||||||
image = "0.25.1"
|
image = { version = "0.25.1", default-features = false }
|
||||||
|
|
|
@ -18,10 +18,10 @@ impl BitIO {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bit_offset(&self) -> usize {
|
pub fn bit_offset(&self) -> usize {
|
||||||
self.byte_offset
|
self.bit_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_vec(self) -> Vec<u8> {
|
pub fn into_vec(self) -> Vec<u8> {
|
||||||
self.data
|
self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl BitIO {
|
||||||
let mut result = 0;
|
let mut result = 0;
|
||||||
for i in 0..bit_len {
|
for i in 0..bit_len {
|
||||||
let bit_value =
|
let bit_value =
|
||||||
((self.data[self.byte_offset] as usize >> self.bit_offset as usize) & 1) as u64;
|
((self.data[self.byte_offset] as usize >> self.bit_offset) & 1) as u64;
|
||||||
self.bit_offset += 1;
|
self.bit_offset += 1;
|
||||||
|
|
||||||
if self.bit_offset == 8 {
|
if self.bit_offset == 8 {
|
||||||
|
@ -49,7 +49,7 @@ impl BitIO {
|
||||||
result |= bit_value << i;
|
result |= bit_value << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, byte_len: usize) -> u64 {
|
pub fn read(&mut self, byte_len: usize) -> u64 {
|
||||||
|
|
|
@ -101,7 +101,7 @@ impl CzHeader for CommonHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common(&self) -> &CommonHeader {
|
fn common(&self) -> &CommonHeader {
|
||||||
&self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u8 {
|
fn version(&self) -> u8 {
|
||||||
|
@ -184,7 +184,7 @@ pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
|
||||||
Ok(colormap)
|
Ok(colormap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_palette(input: &mut Vec<u8>, palette: &[[u8; 4]]) -> Vec<u8> {
|
pub fn apply_palette(input: &mut &[u8], palette: &[[u8; 4]]) -> Vec<u8> {
|
||||||
let mut output_map = Vec::new();
|
let mut output_map = Vec::new();
|
||||||
|
|
||||||
for byte in input.iter() {
|
for byte in input.iter() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use image::RgbaImage;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
io::{Read, Seek, Write},
|
io::{Read, Seek, Write},
|
||||||
|
@ -160,11 +161,11 @@ pub fn decompress_lzw2(input_data: &[u8], size: usize) -> Vec<u8> {
|
||||||
|
|
||||||
//println!("{}", element);
|
//println!("{}", element);
|
||||||
|
|
||||||
result.write(&entry).unwrap();
|
result.write_all(&entry).unwrap();
|
||||||
w.push(entry[0]);
|
w.push(entry[0]);
|
||||||
dictionary.insert(dictionary_count, w.clone());
|
dictionary.insert(dictionary_count, w.clone());
|
||||||
dictionary_count += 1;
|
dictionary_count += 1;
|
||||||
w = entry.clone();
|
w.clone_from(&entry);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -234,7 +235,7 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_line = curr_line.clone();
|
prev_line.clone_from(&curr_line);
|
||||||
if pixel_byte_count == 4 {
|
if pixel_byte_count == 4 {
|
||||||
output_buf[i..i + line_byte_count].copy_from_slice(&curr_line);
|
output_buf[i..i + line_byte_count].copy_from_slice(&curr_line);
|
||||||
} else if pixel_byte_count == 3 {
|
} else if pixel_byte_count == 3 {
|
||||||
|
@ -242,7 +243,7 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
||||||
let loc = ((y * width) as usize + x) * 4;
|
let loc = ((y * width) as usize + x) * 4;
|
||||||
|
|
||||||
output_buf[loc..loc + 4].copy_from_slice(&[
|
output_buf[loc..loc + 4].copy_from_slice(&[
|
||||||
curr_line[x + 0],
|
curr_line[x],
|
||||||
curr_line[x + 1],
|
curr_line[x + 1],
|
||||||
curr_line[x + 2],
|
curr_line[x + 2],
|
||||||
0xFF
|
0xFF
|
||||||
|
@ -256,16 +257,11 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
||||||
output_buf
|
output_buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_diff_cz4<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
pub fn line_diff_cz4(picture: &mut RgbaImage, pixel_byte_count: usize, data: &[u8]) {
|
||||||
let width = header.width() as u32;
|
let width = picture.width();
|
||||||
let height = header.height() as u32;
|
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 pixel_byte_count = (header.depth() >> 3) as usize;
|
|
||||||
let pixel_byte_count = 3;
|
|
||||||
|
|
||||||
let mut output_buf = data.to_vec();
|
|
||||||
|
|
||||||
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 * pixel_byte_count];
|
||||||
|
|
||||||
|
@ -280,30 +276,26 @@ pub fn line_diff_cz4<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in 0..width as usize {
|
for x in 0..width as usize {
|
||||||
let loc = ((y * width) as usize + x) * 4;
|
|
||||||
|
|
||||||
if pixel_byte_count == 1 {
|
if pixel_byte_count == 1 {
|
||||||
output_buf[loc + 4] = curr_line[x];
|
picture.get_pixel_mut(x as u32, y).0[3] = curr_line[x];
|
||||||
} else if pixel_byte_count == 4 {
|
} else if pixel_byte_count == 4 {
|
||||||
output_buf[loc..loc + 4].copy_from_slice(&[
|
picture.get_pixel_mut(x as u32, y).0 = [
|
||||||
curr_line[x * pixel_byte_count + 0],
|
curr_line[x * pixel_byte_count],
|
||||||
curr_line[x * pixel_byte_count + 1],
|
curr_line[x * pixel_byte_count + 1],
|
||||||
curr_line[x * pixel_byte_count + 2],
|
curr_line[x * pixel_byte_count + 2],
|
||||||
curr_line[x * pixel_byte_count + 3],
|
curr_line[x * pixel_byte_count + 3],
|
||||||
]);
|
];
|
||||||
} else if pixel_byte_count == 3 {
|
} else if pixel_byte_count == 3 {
|
||||||
output_buf[loc..loc + 4].copy_from_slice(&[
|
picture.get_pixel_mut(x as u32, y).0 = [
|
||||||
curr_line[x * pixel_byte_count + 0],
|
curr_line[x * pixel_byte_count],
|
||||||
curr_line[x * pixel_byte_count + 1],
|
curr_line[x * pixel_byte_count + 1],
|
||||||
curr_line[x * pixel_byte_count + 2],
|
curr_line[x * pixel_byte_count + 2],
|
||||||
0xFF,
|
0xFF,
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_line = curr_line.clone();
|
prev_line.clone_from(&curr_line);
|
||||||
i += width as usize * pixel_byte_count;
|
i += width as usize * pixel_byte_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
output_buf
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::io::{Read, Seek};
|
use std::{io::{Read, Seek}, path::{Path, PathBuf}};
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -20,12 +20,18 @@ pub enum DynamicCz {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicCz {
|
impl DynamicCz {
|
||||||
pub fn save_as_png(&self, name: &str) -> Result<(), png::EncodingError> {
|
pub fn open<P: ?Sized + AsRef<Path>>(path: &P) -> Result<Self, CzError> {
|
||||||
let file = std::fs::File::create(name).unwrap();
|
let mut img_file = std::fs::File::open(path)?;
|
||||||
let ref mut w = std::io::BufWriter::new(file);
|
|
||||||
|
Self::decode(&mut img_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_as_png<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), png::EncodingError> {
|
||||||
|
let file = std::fs::File::create(path).unwrap();
|
||||||
|
let writer = std::io::BufWriter::new(file);
|
||||||
|
|
||||||
let mut encoder = png::Encoder::new(
|
let mut encoder = png::Encoder::new(
|
||||||
w,
|
writer,
|
||||||
self.header().width() as u32,
|
self.header().width() as u32,
|
||||||
self.header().height() as u32,
|
self.header().height() as u32,
|
||||||
);
|
);
|
||||||
|
@ -33,7 +39,7 @@ impl DynamicCz {
|
||||||
encoder.set_depth(png::BitDepth::Eight);
|
encoder.set_depth(png::BitDepth::Eight);
|
||||||
let mut writer = encoder.write_header()?;
|
let mut writer = encoder.write_header()?;
|
||||||
|
|
||||||
writer.write_image_data(&self.bitmap())?;
|
writer.write_image_data(self.bitmap())?; // Save
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -70,11 +76,11 @@ impl CzImage for DynamicCz {
|
||||||
|
|
||||||
fn header(&self) -> &Self::Header {
|
fn header(&self) -> &Self::Header {
|
||||||
match self {
|
match self {
|
||||||
DynamicCz::CZ0(img) => &img.header().common(),
|
DynamicCz::CZ0(img) => img.header().common(),
|
||||||
DynamicCz::CZ1(img) => &img.header().common(),
|
DynamicCz::CZ1(img) => img.header().common(),
|
||||||
DynamicCz::CZ2(img) => &img.header().common(),
|
DynamicCz::CZ2(img) => img.header().common(),
|
||||||
DynamicCz::CZ3(img) => &img.header().common(),
|
DynamicCz::CZ3(img) => img.header().common(),
|
||||||
DynamicCz::CZ4(img) => &img.header().common(),
|
DynamicCz::CZ4(img) => img.header().common(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ impl CzImage for Cz1Image {
|
||||||
bitmap.clone_into(raw);
|
bitmap.clone_into(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = apply_palette(&mut bitmap, pal);
|
bitmap = apply_palette(&mut bitmap.as_slice(), pal);
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = Self {
|
let image = Self {
|
||||||
|
|
|
@ -102,7 +102,7 @@ impl CzImage for Cz2Image {
|
||||||
|
|
||||||
// Apply the palette if it exists
|
// Apply the palette if it exists
|
||||||
if let Some(pal) = &palette {
|
if let Some(pal) = &palette {
|
||||||
bitmap = apply_palette(&mut bitmap, pal);
|
bitmap = apply_palette(&mut bitmap.as_slice(), pal);
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = Self {
|
let image = Self {
|
||||||
|
|
|
@ -76,15 +76,22 @@ impl CzImage for Cz4Image {
|
||||||
let block_info = parse_chunk_info(bytes)?;
|
let block_info = parse_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;
|
let pcount = (header.width() as usize * header.height() as usize) * 3;
|
||||||
let bitmap = decompress(bytes, &block_info)?;
|
let data = decompress(bytes, &block_info)?;
|
||||||
let data2 = bitmap[pcount * 3..].to_vec();
|
let data2 = data[pcount..].to_vec();
|
||||||
|
|
||||||
let bitmap = line_diff_cz4(&header, &bitmap);
|
let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32);
|
||||||
|
|
||||||
let bitmap = line_diff_cz4(&header, &bitmap);
|
let pixel_byte_count = 3;
|
||||||
|
line_diff_cz4(&mut picture, pixel_byte_count, &data);
|
||||||
|
|
||||||
Ok(Self { header, bitmap })
|
let pixel_byte_count = 1;
|
||||||
|
line_diff_cz4(&mut picture, pixel_byte_count, &data2);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
header,
|
||||||
|
bitmap: picture.into_raw()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header(&self) -> &Self::Header {
|
fn header(&self) -> &Self::Header {
|
||||||
|
@ -107,7 +114,12 @@ impl CzImage for Cz4Image {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) {
|
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) {
|
||||||
todo!()
|
todo!()
|
||||||
|
=======
|
||||||
|
fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
|
||||||
|
self.bitmap = bitmap.to_vec();
|
||||||
|
>>>>>>> 613e38c6b560990c77e994f2359fc4c30ff112a4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,35 @@
|
||||||
use cz::{dynamic::DynamicCz, Cz0Image, CzImage};
|
use std::{fs::DirBuilder, time::{Duration, Instant}};
|
||||||
use std::fs;
|
|
||||||
|
use cz::dynamic::DynamicCz;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut file = fs::File::open("TEXTBOX.CZ3").unwrap();
|
let _ = DirBuilder::new().create("test");
|
||||||
let img = DynamicCz::decode(&mut file).unwrap();
|
|
||||||
|
|
||||||
img.save_as_png("test.png").unwrap();
|
let mut total_time = Duration::default();
|
||||||
|
let mut num_images = 0;
|
||||||
|
for entry in WalkDir::new("../../test_files/loopers/") {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
if entry.path().is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = Instant::now();
|
||||||
|
let img = match DynamicCz::open(entry.path()) {
|
||||||
|
Ok(img) => img,
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}: {}", entry.path().file_name().unwrap().to_string_lossy(), err);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let elapsed = timer.elapsed();
|
||||||
|
total_time += elapsed;
|
||||||
|
num_images += 1;
|
||||||
|
|
||||||
|
img.save_as_png(
|
||||||
|
&format!("test/{}.png", entry.path().file_name().unwrap().to_string_lossy())
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(total_time / num_images);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue