mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
Added CZ4 read implementation
This commit is contained in:
parent
cf55210c4c
commit
a1fbb3368a
5 changed files with 229 additions and 18 deletions
|
@ -1,8 +1,8 @@
|
|||
use std::{collections::BTreeMap, io::{Cursor, Read, Seek, Write}};
|
||||
use std::{collections::BTreeMap, io::{Read, Seek, Write}};
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use bitstream_io::{read::BitReader, BitRead};
|
||||
use image::{buffer, ColorType, DynamicImage, GenericImage, GenericImageView, RgbImage, Rgba, RgbaImage};
|
||||
|
||||
use crate::common::CzError;
|
||||
use crate::common::{CzError, CzHeader};
|
||||
use crate::binio::BitIO;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -76,6 +76,8 @@ pub fn decompress<T: Seek + ReadBytesExt + Read>(
|
|||
}
|
||||
}
|
||||
|
||||
bitmap.truncate(chunk_info.total_size_raw);
|
||||
|
||||
Ok(bitmap)
|
||||
}
|
||||
|
||||
|
@ -189,3 +191,93 @@ fn copy_one(input: &[u8], src: usize) -> u8 {
|
|||
copy_one(input, get_offset(input, src))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_diff<T: CzHeader>(header_info: &T, data: &[u8]) -> Vec<u8> {
|
||||
let width = header_info.width() as u32;
|
||||
let height = header_info.height() as u32;
|
||||
let mut pic = image::RgbaImage::new(width, height);
|
||||
|
||||
let block_height = (f32::ceil(height as f32 / 4 as f32) as u16) as usize;
|
||||
let pixel_byte_count = header_info.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(line_byte_count);
|
||||
|
||||
let mut i = 0;
|
||||
for y in 0..height {
|
||||
curr_line = data[i..i+line_byte_count].to_vec();
|
||||
dbg!(curr_line.len());
|
||||
|
||||
if y % block_height as u32 != 0 {
|
||||
for x in 0..line_byte_count {
|
||||
curr_line[x] += prev_line[x]
|
||||
}
|
||||
}
|
||||
|
||||
prev_line = curr_line.clone();
|
||||
if header_info.version() == 4 {
|
||||
for x in 0..width {
|
||||
pic.get_pixel_mut(x as u32, y).0[3] = curr_line[x as usize];
|
||||
}
|
||||
} else if pixel_byte_count == 4 {
|
||||
let mut raw = pic.into_raw();
|
||||
raw[i..i+line_byte_count].copy_from_slice(&curr_line);
|
||||
|
||||
pic = RgbaImage::from_raw(width, height, raw).unwrap();
|
||||
} else if pixel_byte_count == 3 {
|
||||
for x in 0..line_byte_count {
|
||||
pic.get_pixel_mut((x/3) as u32, y).0 = [curr_line[x], curr_line[x + 1], curr_line[x + 2], 0xFF];
|
||||
}
|
||||
}
|
||||
|
||||
i += line_byte_count;
|
||||
}
|
||||
|
||||
pic.into_vec()
|
||||
}
|
||||
|
||||
pub fn line_diff_cz4(picture: &mut RgbaImage, color_block: u8, pixel_byte_count: usize, data: &[u8]) {
|
||||
let width = picture.width() as u32;
|
||||
let height = picture.height() as u32;
|
||||
let block_height = (f32::ceil(height as f32 / color_block as f32) as u16) as u32;
|
||||
|
||||
|
||||
let mut curr_line = vec![0u8; width as usize * pixel_byte_count];
|
||||
let mut prev_line = vec![0u8; width as usize * pixel_byte_count];
|
||||
|
||||
let mut i = 0;
|
||||
for y in 0..height {
|
||||
curr_line = data[i..i + width as usize * pixel_byte_count].to_vec();
|
||||
|
||||
if y % block_height != 0 {
|
||||
for x in 0..(width as usize * pixel_byte_count) {
|
||||
curr_line[x] += prev_line[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) = Rgba([
|
||||
curr_line[x * pixel_byte_count + 0],
|
||||
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) = Rgba([
|
||||
curr_line[x * pixel_byte_count + 0],
|
||||
curr_line[x * pixel_byte_count + 1],
|
||||
curr_line[x * pixel_byte_count + 2],
|
||||
0xFF
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
prev_line = curr_line.clone();
|
||||
i += width as usize * pixel_byte_count;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
||||
use crate::compression::{decompress, parse_chunk_info};
|
||||
use crate::compression::{decompress, line_diff, parse_chunk_info};
|
||||
use crate::common::{CommonHeader, CzError, CzHeader, CzImage};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -117,18 +117,9 @@ impl CzImage for Cz3Image {
|
|||
|
||||
let block_info = parse_chunk_info(bytes)?;
|
||||
|
||||
let mut bitmap = decompress(bytes, &block_info)?;
|
||||
let bitmap = decompress(bytes, &block_info)?;
|
||||
|
||||
let stride = (header.width() * (header.depth() / 8)) as usize;
|
||||
let third = ((header.height() + 2) / 3) as usize;
|
||||
for y in 0..header.height() as usize {
|
||||
let dst = y * stride;
|
||||
if y % third != 0 {
|
||||
for x in 0..stride {
|
||||
bitmap[dst + x] += bitmap[dst + x - stride];
|
||||
}
|
||||
}
|
||||
}
|
||||
let bitmap = line_diff(&header, &bitmap);
|
||||
|
||||
Ok(Self { header, bitmap })
|
||||
}
|
||||
|
|
123
cz/src/formats/cz4.rs
Normal file
123
cz/src/formats/cz4.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
use std::{
|
||||
io::{self, Read, Seek, SeekFrom},
|
||||
path::PathBuf
|
||||
};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use image::DynamicImage;
|
||||
|
||||
use crate::compression::{decompress, line_diff, line_diff_cz4, parse_chunk_info};
|
||||
use crate::common::{CommonHeader, CzError, CzHeader, CzImage};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Cz4Header {
|
||||
/// Common CZ# header
|
||||
common: CommonHeader,
|
||||
}
|
||||
|
||||
impl CzHeader for Cz4Header {
|
||||
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let common = CommonHeader::new(bytes)?;
|
||||
|
||||
if common.version() != 4 {
|
||||
return Err(CzError::VersionMismatch(common.version(), 3));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
common,
|
||||
})
|
||||
}
|
||||
|
||||
fn version(&self) -> u8 {
|
||||
self.common.version()
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
self.common.length()
|
||||
}
|
||||
|
||||
fn width(&self) -> u16 {
|
||||
self.common.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u16 {
|
||||
self.common.height()
|
||||
}
|
||||
|
||||
fn depth(&self) -> u16 {
|
||||
self.common.depth()
|
||||
}
|
||||
|
||||
fn color_block(&self) -> u8 {
|
||||
self.common.color_block()
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cz4Image {
|
||||
header: Cz4Header,
|
||||
bitmap: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CzImage for Cz4Image {
|
||||
type Header = Cz4Header;
|
||||
|
||||
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
|
||||
let header = Cz4Header::new(bytes)?;
|
||||
bytes.seek(SeekFrom::Start(header.length() as u64))?;
|
||||
|
||||
let block_info = parse_chunk_info(bytes)?;
|
||||
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
||||
|
||||
let bitmap = decompress(bytes, &block_info)?;
|
||||
|
||||
let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32);
|
||||
|
||||
let pixel_byte_count = 3;
|
||||
line_diff_cz4(&mut picture, 3, pixel_byte_count, &bitmap);
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
bitmap: picture.into_vec()
|
||||
})
|
||||
}
|
||||
|
||||
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError> {
|
||||
let img = image::RgbaImage::from_raw(
|
||||
self.header.width() as u32,
|
||||
self.header.height() as u32,
|
||||
self.bitmap.clone(),
|
||||
).unwrap();
|
||||
|
||||
img.save(name)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
|
||||
fn set_header(&mut self, header: Self::Header) {
|
||||
self.header = header
|
||||
}
|
||||
|
||||
fn into_bitmap(self) -> Vec<u8> {
|
||||
self.bitmap
|
||||
}
|
||||
|
||||
fn save_as_cz<T: Into<PathBuf>>(&self, path: T) -> Result<(), CzError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ pub mod formats {
|
|||
pub mod cz1;
|
||||
pub mod cz2;
|
||||
pub mod cz3;
|
||||
pub mod cz4;
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
|
@ -16,6 +17,8 @@ pub use formats::cz1::Cz1Image;
|
|||
pub use formats::cz2::Cz2Image;
|
||||
#[doc(inline)]
|
||||
pub use formats::cz3::Cz3Image;
|
||||
#[doc(inline)]
|
||||
pub use formats::cz4::Cz4Image;
|
||||
|
||||
/// Traits for CZ# images
|
||||
#[doc(inline)]
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use std::fs;
|
||||
use cz::{Cz2Image, CzImage};
|
||||
use cz::{Cz4Image, CzImage};
|
||||
|
||||
fn main() {
|
||||
let mut input = fs::File::open("../../test_files/font_files/24.cz2")
|
||||
let mut input = fs::File::open("../../test_files/BAD_BG_011_10.cz4")
|
||||
.expect("Failed to open file");
|
||||
|
||||
let img_file = Cz2Image::decode(&mut input)
|
||||
let timer = std::time::Instant::now();
|
||||
let img_file = Cz4Image::decode(&mut input)
|
||||
.expect("Failed to decode image");
|
||||
println!("{:?}", timer.elapsed());
|
||||
|
||||
img_file.save_as_png("test1.png").unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue