Generic header encoding interface

This commit is contained in:
Jaby 2024-12-22 21:10:38 +01:00
parent 0e48009f93
commit 9409ae11e0
4 changed files with 42 additions and 25 deletions

View File

@ -8,11 +8,10 @@ pub mod types;
use args::{ColorType, ClutAlignment};
use color_clut::{IndexedImage, OutputType};
use color_full16::{RgbaImage, RgbImage};
use types::{Color as PSXColor, PSXImageConverter};
use types::{Color as PSXColor, HeaderEncoder, PSXImageConverter};
use image::{DynamicImage, io::Reader as ImageReader};
use std::io::{Cursor, Write};
use tool_helper::{Error, Input};
use reduced_tim::types::Header;
fn modify_palette(mut image: IndexedImage, clut_align: ClutAlignment, semi_transparent: bool, transparent_palette: bool) -> IndexedImage {
if semi_transparent {
@ -37,7 +36,7 @@ fn modify_palette(mut image: IndexedImage, clut_align: ClutAlignment, semi_trans
image
}
fn encode<T: PSXImageConverter>(image: T, color_depth: ColorType, clut_align: ClutAlignment, output: &mut dyn Write) -> Result<(), Error> {
fn encode<T: PSXImageConverter>(header_conv: &dyn HeaderEncoder, image: T, color_depth: ColorType, clut_align: ClutAlignment, output: &mut dyn Write) -> Result<(), Error> {
let (width, height) = {
fn return_error(clut_type: u32, div: u32, width: u16, height: u16) -> Result<(u16, u16), Error> {
return Err(Error::from_callback(|| {format!("CLUT {} images require a width divideable by {} (found width: {}/{}={}, height: {})", clut_type, div, width, div, (width as f32/div as f32), height)}));
@ -94,9 +93,9 @@ fn encode<T: PSXImageConverter>(image: T, color_depth: ColorType, clut_align: Cl
}
};
let header = Header::encode(width, height, pal_width, pal_height).ok_or(Error::from_callback(|| {format!("Image size (width: {}, height: {}) needs to be even", width, height)}))?;
let header_raw = header_conv.encode(width, height, pal_width, pal_height)?;
tool_helper::raw::write_raw(output, &header)?;
output.write(&header_raw)?;
if let Some(palette) = palette {
let mut color_count = pal_width*pal_height;
for color in palette {
@ -117,12 +116,12 @@ fn encode<T: PSXImageConverter>(image: T, color_depth: ColorType, clut_align: Cl
Ok(())
}
fn convert_full16(input: Input, output: &mut dyn Write) -> Result<(), Error> {
fn convert_full16(header_conv: &dyn HeaderEncoder, input: Input, output: &mut dyn Write) -> Result<(), Error> {
match ImageReader::new(Cursor::new(tool_helper::input_to_vec(input)?)).with_guessed_format()?.decode() {
Ok(image) => {
match image {
DynamicImage::ImageRgb8(image) => encode(RgbImage::new(image), ColorType::Full16, ClutAlignment::None, output),
DynamicImage::ImageRgba8(image) => encode(RgbaImage::new(image), ColorType::Full16, ClutAlignment::None, output),
DynamicImage::ImageRgb8(image) => encode(header_conv, RgbImage::new(image), ColorType::Full16, ClutAlignment::None, output),
DynamicImage::ImageRgba8(image) => encode(header_conv, RgbaImage::new(image), ColorType::Full16, ClutAlignment::None, output),
_ => Err(Error::from_str("Only RGB and RGBA images are supported for 16bit encoding"))
}
@ -131,7 +130,7 @@ fn convert_full16(input: Input, output: &mut dyn Write) -> Result<(), Error> {
}
}
fn convert_palette_based(input: Input, output: &mut dyn Write, color_type: ColorType, clut_align: ClutAlignment, semi_transparent: bool, transparent_palette: bool) -> Result<(), Error> {
fn convert_palette_based(header_conv: &dyn HeaderEncoder, input: Input, output: &mut dyn Write, color_type: ColorType, clut_align: ClutAlignment, semi_transparent: bool, transparent_palette: bool) -> Result<(), Error> {
match png::Decoder::new(input).read_info() {
Ok(reader) => {
let output_type = {
@ -142,7 +141,7 @@ fn convert_palette_based(input: Input, output: &mut dyn Write, color_type: Color
}
};
encode(modify_palette(IndexedImage::new(reader, output_type)?, clut_align, semi_transparent, transparent_palette), color_type, clut_align, output)
encode(header_conv, modify_palette(IndexedImage::new(reader, output_type)?, clut_align, semi_transparent, transparent_palette), color_type, clut_align, output)
},
Err(error) => Err(Error::from_error(error))
}

View File

@ -2,13 +2,15 @@ pub mod types;
use super::args::ColorType;
use std::io::Write;
use types::Header;
use tool_helper::{Error, Input};
pub type Arguments = super::args::Arguments;
pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> {
let header_conv = Header::default();
match args.color_depth {
ColorType::Full16 => super::convert_full16(input, output),
_ => super::convert_palette_based(input, output, args.color_depth, args.clut_align, args.semi_transparent, args.transparent_palette),
ColorType::Full16 => super::convert_full16(&header_conv, input, output),
_ => super::convert_palette_based(&header_conv, input, output, args.color_depth, args.clut_align, args.semi_transparent, args.transparent_palette),
}
}

View File

@ -1,5 +1,5 @@
use super::super::types::set_member_value;
use tool_helper::{bits::BitRange, raw::RawConversion};
use super::super::types::{HeaderEncoder, set_member_value};
use tool_helper::{bits::BitRange, raw::RawConversion, Error};
#[repr(packed(1))]
#[allow(dead_code)]
@ -14,9 +14,9 @@ impl Header {
const CLUT_WIDTH_BIT_RANGE: BitRange = BitRange::from_to(17, 22);
const CLUT_HEIGHT_BIT_RANGE: BitRange = BitRange::from_to(23, 31);
pub fn encode(tex_width: u16, tex_height: u16, clut_width: u16, clut_height: u16) -> Option<Header> {
pub fn from(tex_width: u16, tex_height: u16, clut_width: u16, clut_height: u16) -> Result<Header, Error> {
if tex_width & 1 == 1 || tex_height & 1 == 1 {
None
Err(Error::from_text(format!("Image size (width: {}, height: {}) needs to be even", tex_width, tex_height)))
}
else {
@ -26,11 +26,23 @@ impl Header {
clut_width, 4, u32),
clut_height, 0, u32);
Some(Header{value})
Ok(Header{value})
}
}
}
impl Default for Header {
fn default() -> Self {
Header{value: 0}
}
}
impl HeaderEncoder for Header {
fn encode(&self, tex_width: u16, tex_height: u16, clut_width: u16, clut_height: u16) -> Result<std::vec::Vec<u8>, Error> {
Ok(Header::from(tex_width, tex_height, clut_width, clut_height)?.convert_to_raw().to_vec())
}
}
impl RawConversion<4> for Header {
fn convert_to_raw(&self) -> [u8; 4] {
self.value.to_le_bytes()

View File

@ -1,11 +1,5 @@
use tool_helper::{*, bits::BitRange, raw::RawConversion};
#[repr(packed(1))]
#[allow(dead_code)]
pub struct Color {
value: u16
}
macro_rules! set_member_value {
($dst:expr, $color:ident, $shift:expr, $bits:ident) => {
paste::item! {
@ -14,8 +8,6 @@ macro_rules! set_member_value {
};
}
pub(crate) use set_member_value;
macro_rules! make_member_getter_setter {
($color:ident, $shift:expr, $bits:ident) => {
paste::item! {
@ -30,6 +22,14 @@ macro_rules! make_member_getter_setter {
};
}
pub(crate) use set_member_value;
#[repr(packed(1))]
#[allow(dead_code)]
pub struct Color {
value: u16
}
#[allow(dead_code)]
impl Color {
const COLOR_SHIFT: u16 = 3;
@ -120,6 +120,10 @@ impl RawConversion<2> for Color {
}
}
pub trait HeaderEncoder {
fn encode(&self, tex_width: u16, tex_height: u16, clut_width: u16, clut_height: u16) -> Result<std::vec::Vec<u8>, Error>;
}
pub trait PSXImageConverter: std::iter::Iterator<Item = Color> {
fn width(&self) -> u16;
fn height(&self) -> u16;