mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 23:32:55 -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 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;
|
use crate::binio::BitIO;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -76,6 +76,8 @@ pub fn decompress<T: Seek + ReadBytesExt + Read>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitmap.truncate(chunk_info.total_size_raw);
|
||||||
|
|
||||||
Ok(bitmap)
|
Ok(bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,3 +191,93 @@ fn copy_one(input: &[u8], src: usize) -> u8 {
|
||||||
copy_one(input, get_offset(input, src))
|
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 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};
|
use crate::common::{CommonHeader, CzError, CzHeader, CzImage};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -117,18 +117,9 @@ impl CzImage for Cz3Image {
|
||||||
|
|
||||||
let block_info = parse_chunk_info(bytes)?;
|
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 bitmap = line_diff(&header, &bitmap);
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { 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 cz1;
|
||||||
pub mod cz2;
|
pub mod cz2;
|
||||||
pub mod cz3;
|
pub mod cz3;
|
||||||
|
pub mod cz4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
@ -16,6 +17,8 @@ pub use formats::cz1::Cz1Image;
|
||||||
pub use formats::cz2::Cz2Image;
|
pub use formats::cz2::Cz2Image;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use formats::cz3::Cz3Image;
|
pub use formats::cz3::Cz3Image;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use formats::cz4::Cz4Image;
|
||||||
|
|
||||||
/// Traits for CZ# images
|
/// Traits for CZ# images
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use cz::{Cz2Image, CzImage};
|
use cz::{Cz4Image, CzImage};
|
||||||
|
|
||||||
fn main() {
|
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");
|
.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");
|
.expect("Failed to decode image");
|
||||||
|
println!("{:?}", timer.elapsed());
|
||||||
|
|
||||||
img_file.save_as_png("test1.png").unwrap();
|
img_file.save_as_png("test1.png").unwrap();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue