Convert IndexedImages

This commit is contained in:
Jaby 2022-09-27 20:52:44 +02:00 committed by Jaby
parent 0f0fb1f612
commit 243bea90fa
5 changed files with 138 additions and 24 deletions

View File

@ -1,16 +1,21 @@
use tool_helper::Error; use tool_helper::Error;
use super::types::Color; use super::types::{Color, PSXImageConverter};
pub enum OutputType { pub enum OutputType {
TwoIndex, FourBit,
EightBit,
}
enum IndexPerByte {
OneIndex, OneIndex,
TwoIndices,
} }
pub struct IndexedImage { pub struct IndexedImage {
palette: Vec<Color>, palette: Vec<Color>,
data: Vec<u8>, raw_data: std::vec::IntoIter<u8>,
bit_depth: png::BitDepth, index_byte: IndexPerByte,
output_type: OutputType, next_func: fn(&mut Self) -> Option<Color>,
width: u16, width: u16,
height: u16, height: u16,
} }
@ -24,19 +29,27 @@ impl IndexedImage {
Ok(info) => { Ok(info) => {
let width = info.width as u16; let width = info.width as u16;
let height = info.height as u16; let height = info.height as u16;
let bit_depth = info.bit_depth; let index_byte = {
match info.bit_depth {
png::BitDepth::Four => IndexPerByte::TwoIndices,
png::BitDepth::Eight => IndexPerByte::OneIndex,
_ => {
return Err(Error::from_str("Only 4 and 8bit color depth are supported").with_action(action_name));
}
}
};
if info.color_type != png::ColorType::Indexed { if info.color_type != png::ColorType::Indexed {
return Err(Error::from_str("PNG file must be indexed").with_action(action_name)); return Err(Error::from_str("PNG file must be indexed").with_action(action_name));
} }
if bit_depth != png::BitDepth::Four && bit_depth != png::BitDepth::Eight {
Err(Error::from_str("Only 4 and 8bit color depth are supported").with_action(action_name))
}
else { else {
Ok(IndexedImage{palette: Self::make_palette(reader.info()), raw_data: buffer.into_iter(), index_byte, next_func: {
Ok(IndexedImage{palette: Self::make_palette(), data: buffer, bit_depth, output_type, width, height}) match output_type {
OutputType::FourBit => Self::next_four_bit,
OutputType::EightBit => Self::next_eight_bit,
}
}, width, height})
} }
} }
@ -46,7 +59,83 @@ impl IndexedImage {
} }
} }
fn make_palette() -> Vec<Color> { fn make_palette(png_info: &png::Info) -> Vec<Color> {
Vec::new() fn make_color(iter: &mut dyn std::iter::Iterator<Item = &u8>) -> Option<Color> {
let r = iter.next()?;
let g = iter.next()?;
let b = iter.next()?;
Some(Color::non_transparent(*r, *g, *b))
}
let mut new_palette = Vec::new();
if let Some(palette) = &png_info.palette {
let mut iter = palette.iter();
while let Some(color) = make_color(&mut iter) {
new_palette.push(color);
}
}
new_palette
}
fn split_into_two_indices(index: u8) -> (u8, u8) {
(index >> 4, index & 0xF)
}
fn next_four_bit(&mut self) -> Option<Color> {
let (pixel0, pixel1, pixel2, pixel3) = {
match self.index_byte {
IndexPerByte::OneIndex => {
(self.raw_data.next()?, self.raw_data.next()?, self.raw_data.next()?, self.raw_data.next()?)
}
IndexPerByte::TwoIndices => {
let (pixel0, pixel1) = Self::split_into_two_indices(self.raw_data.next()?);
let (pixel2, pixel3) = Self::split_into_two_indices(self.raw_data.next()?);
(pixel0, pixel1, pixel2, pixel3)
},
}
};
Some(Color::new_4bit_entry(pixel0, pixel1, pixel2, pixel3))
}
fn next_eight_bit(&mut self) -> Option<Color> {
let (pixel0, pixel1) = {
match self.index_byte {
IndexPerByte::OneIndex => {
(self.raw_data.next()?, self.raw_data.next()?)
},
IndexPerByte::TwoIndices => {
Self::split_into_two_indices(self.raw_data.next()?)
}
}
};
Some(Color::new_8bit_entry(pixel0, pixel1))
}
}
impl PSXImageConverter for IndexedImage {
fn width(&self) -> u16 {
self.width
}
fn height(&self) -> u16 {
self.height
}
fn get_palette(&self) -> Option<&Vec<Color>> {
Some(&self.palette)
}
}
impl std::iter::Iterator for IndexedImage {
type Item = Color;
fn next(&mut self) -> Option<Self::Item> {
(self.next_func)(self)
} }
} }

View File

@ -25,7 +25,7 @@ impl PSXImageConverter for RgbImage {
self.height self.height
} }
fn get_palette(&self) -> Option<Vec<Color>> { fn get_palette(&self) -> Option<&Vec<Color>> {
None None
} }
} }
@ -67,7 +67,7 @@ impl PSXImageConverter for RgbaImage {
self.height self.height
} }
fn get_palette(&self) -> Option<Vec<Color>> { fn get_palette(&self) -> Option<&Vec<Color>> {
None None
} }
} }

View File

@ -77,19 +77,17 @@ fn convert_full16(input: Input, output: Output) -> Result<(), Error> {
} }
} }
fn convert_palette_based(input: Input, _: Output, color_type: ColorType) -> Result<(), Error> { fn convert_palette_based(input: Input, output: Output, color_type: ColorType) -> Result<(), Error> {
match png::Decoder::new(input).read_info() { match png::Decoder::new(input).read_info() {
Ok(reader) => { Ok(reader) => {
let output_type = { let output_type = {
match color_type { match color_type {
ColorType::Clut4 => color_clut::OutputType::TwoIndex, ColorType::Clut4 => color_clut::OutputType::FourBit,
ColorType::Clut8 => color_clut::OutputType::OneIndex, ColorType::Clut8 => color_clut::OutputType::EightBit,
_ => return Err(Error::from_str("ColorType not supported")) _ => return Err(Error::from_str("ColorType not supported"))
} }
}; };
let _image = IndexedImage::new(reader, output_type)?; encode(IndexedImage::new(reader, output_type)?, ClutAlignment::None, output)
Err(Error::from_str("Under construction"))
}, },
Err(error) => Err(Error::from_error(error)) Err(error) => Err(Error::from_error(error))
} }

View File

@ -40,7 +40,15 @@ impl Color {
const STP_BIT_RANGE: BitRange = BitRange::from_to(15, 15); const STP_BIT_RANGE: BitRange = BitRange::from_to(15, 15);
const RED_BIT_RANGE: BitRange = BitRange::from_to(0, 4); const RED_BIT_RANGE: BitRange = BitRange::from_to(0, 4);
const GREEN_BIT_RANGE: BitRange = BitRange::from_to(5, 9); const GREEN_BIT_RANGE: BitRange = BitRange::from_to(5, 9);
const BLUE_BIT_RANGE: BitRange = BitRange::from_to(10, 14); const BLUE_BIT_RANGE: BitRange = BitRange::from_to(10, 14);
const PIXEL3_4_BIT_RANGE: BitRange = BitRange::from_to(12, 15);
const PIXEL2_4_BIT_RANGE: BitRange = BitRange::from_to(8, 11);
const PIXEL1_4_BIT_RANGE: BitRange = BitRange::from_to(4, 7);
const PIXEL0_4_BIT_RANGE: BitRange = BitRange::from_to(0, 3);
const PIXEL1_8_BIT_RANGE: BitRange = BitRange::from_to(8, 15);
const PIXEL0_8_BIT_RANGE: BitRange = BitRange::from_to(0, 7);
pub const fn transparent() -> Color { pub const fn transparent() -> Color {
Color::new(0, 0, 0, 0) Color::new(0, 0, 0, 0)
@ -76,6 +84,24 @@ impl Color {
Color::new(1, 0, 0, 0) Color::new(1, 0, 0, 0)
} }
pub const fn new_4bit_entry(pixel0_4: u8, pixel1_4: u8, pixel2_4: u8, pixel3_4: u8) -> Color {
let value = set_member_value!(set_member_value!(set_member_value!(set_member_value!(0,
pixel0_4, 0, u16),
pixel1_4, 0, u16),
pixel2_4, 0, u16),
pixel3_4, 0, u16);
Color{value}
}
pub const fn new_8bit_entry(pixel0_8: u8, pixel1_8: u8) -> Color {
let value = set_member_value!(set_member_value!(0,
pixel0_8, 0, u16),
pixel1_8, 0, u16);
Color{value}
}
const fn new(stp: u8, red: u8, green: u8, blue: u8) -> Color { const fn new(stp: u8, red: u8, green: u8, blue: u8) -> Color {
let value = set_member_value!(set_member_value!(set_member_value!(set_member_value!(0, let value = set_member_value!(set_member_value!(set_member_value!(set_member_value!(0,
stp, 0, u16), stp, 0, u16),
@ -120,5 +146,5 @@ pub trait PSXImageConverter: std::iter::Iterator<Item = Color> {
fn width(&self) -> u16; fn width(&self) -> u16;
fn height(&self) -> u16; fn height(&self) -> u16;
fn get_palette(&self) -> Option<Vec<Color>>; fn get_palette(&self) -> Option<&Vec<Color>>;
} }

View File

@ -31,5 +31,6 @@ macro_rules! create_bit_functions {
}; };
} }
create_bit_functions!(u8);
create_bit_functions!(u16); create_bit_functions!(u16);
create_bit_functions!(u32); create_bit_functions!(u32);