Fixed issues with changing color format

This commit is contained in:
G2-Games 2024-07-29 02:46:37 -05:00
parent eaf2d4f0eb
commit 5ae122a0a9
3 changed files with 54 additions and 28 deletions

View file

@ -93,6 +93,12 @@ pub enum ColorFormat {
/// RGB, 8 bits per channel /// RGB, 8 bits per channel
Rgb8 = 1, Rgb8 = 1,
/// Grayscale with alpha, 8 bits per channel
GrayA8 = 2,
/// Grayscale, 8 bits per channel
Gray8 = 3,
} }
impl ColorFormat { impl ColorFormat {
@ -101,8 +107,10 @@ impl ColorFormat {
/// Ex. `Rgba8` has `8bpc` /// Ex. `Rgba8` has `8bpc`
pub fn bpc(&self) -> u8 { pub fn bpc(&self) -> u8 {
match self { match self {
ColorFormat::Rgba8 => 8, Self::Rgba8 => 8,
ColorFormat::Rgb8 => 8, Self::Rgb8 => 8,
Self::GrayA8 => 8,
Self::Gray8 => 8,
} }
} }
@ -111,8 +119,10 @@ impl ColorFormat {
/// Ex. `Rgba8` has `32bpp` /// Ex. `Rgba8` has `32bpp`
pub fn bpp(&self) -> u16 { pub fn bpp(&self) -> u16 {
match self { match self {
ColorFormat::Rgba8 => 32, Self::Rgba8 => 32,
ColorFormat::Rgb8 => 24, Self::Rgb8 => 24,
Self::GrayA8 => 16,
Self::Gray8 => 8,
} }
} }
@ -121,23 +131,30 @@ impl ColorFormat {
/// Ex. `Rgba8` has `4` channels /// Ex. `Rgba8` has `4` channels
pub fn channels(&self) -> u16 { pub fn channels(&self) -> u16 {
match self { match self {
ColorFormat::Rgba8 => 4, Self::Rgba8 => 4,
ColorFormat::Rgb8 => 3, Self::Rgb8 => 3,
Self::GrayA8 => 2,
Self::Gray8 => 1,
} }
} }
/// The channel in which alpha is contained, or [`None`] if there is none. /// The channel in which alpha is contained, or [`None`] if there is none.
/// ///
/// Ex. `Rgba8`'s 3rd channel is alpha /// Ex. `Rgba8`'s 3rd channel is alpha
pub fn alpha_channel(&self) -> Option<u8> { pub fn alpha_channel(&self) -> Option<usize> {
match self { match self {
ColorFormat::Rgba8 => Some(4), Self::Rgba8 => Some(3),
ColorFormat::Rgb8 => None, Self::Rgb8 => None,
Self::GrayA8 => Some(1),
Self::Gray8 => None,
} }
} }
pub fn pixel_byte_count(&self) -> u16 { /// Pixel Byte Count, The number of bytes per pixel.
self.bpp() / 8 ///
/// Convenience method over [`Self::bpp`]
pub fn pbc(&self) -> usize {
(self.bpp() / 8).into()
} }
} }
@ -148,6 +165,8 @@ impl TryFrom<u8> for ColorFormat {
Ok(match value { Ok(match value {
0 => Self::Rgba8, 0 => Self::Rgba8,
1 => Self::Rgb8, 1 => Self::Rgb8,
2 => Self::GrayA8,
3 => Self::Gray8,
v => return Err(format!("invalid color format {v}")), v => return Err(format!("invalid color format {v}")),
}) })
} }

View file

@ -2,10 +2,10 @@ use crate::ColorFormat;
use rayon::prelude::*; use rayon::prelude::*;
pub fn sub_rows(width: u32, height: u32, color_format: ColorFormat, input: &[u8]) -> Vec<u8> { pub fn sub_rows(width: u32, height: u32, color_format: ColorFormat, input: &[u8]) -> Vec<u8> {
let mut data = Vec::with_capacity(width as usize * (color_format.bpp() / 8) as usize); let mut data = Vec::with_capacity(width as usize * color_format.pbc());
let block_height = f32::ceil(height as f32 / 3.0) as u32; let block_height = f32::ceil(height as f32 / 3.0) as u32;
let line_byte_count = (width * color_format.pixel_byte_count() as u32) as usize; let line_byte_count = (width * color_format.pbc() as u32) as usize;
let mut curr_line: Vec<u8>; let mut curr_line: Vec<u8>;
let mut prev_line: Vec<u8> = Vec::new(); let mut prev_line: Vec<u8> = Vec::new();
@ -30,22 +30,22 @@ pub fn sub_rows(width: u32, height: u32, color_format: ColorFormat, input: &[u8]
} }
if color_format.alpha_channel().is_some() { if color_format.alpha_channel().is_some() {
let (pixels, alpha): (Vec<[u8; 3]>, Vec<u8>) = let (pixels, alpha): (Vec<&[u8]>, Vec<u8>) =
data.chunks(4) data.chunks(color_format.pbc())
.map(|i| ( .map(|i| (
[i[0], i[1], i[2]], &i[..color_format.pbc() - 1],
i[3] i[color_format.alpha_channel().unwrap()]
)) ))
.unzip(); .unzip();
pixels.into_iter().flatten().chain(alpha).collect() pixels.into_iter().flatten().copied().chain(alpha).collect()
} else { } else {
data data
} }
} }
pub fn add_rows(width: u32, height: u32, color_format: ColorFormat, data: &[u8]) -> Vec<u8> { pub fn add_rows(width: u32, height: u32, color_format: ColorFormat, data: &[u8]) -> Vec<u8> {
let mut output_buf = Vec::with_capacity((width * height * color_format.channels() as u32) as usize); let mut output_buf = Vec::with_capacity((width * height * color_format.pbc() as u32) as usize);
let block_height = f32::ceil(height as f32 / 3.0) as u32; let block_height = f32::ceil(height as f32 / 3.0) as u32;
@ -53,11 +53,12 @@ pub fn add_rows(width: u32, height: u32, color_format: ColorFormat, data: &[u8])
let mut prev_line = Vec::new(); let mut prev_line = Vec::new();
let mut rgb_index = 0; let mut rgb_index = 0;
let mut alpha_index = (width * height * (color_format.channels() as u32 - 1)) as usize; let mut alpha_index = (width * height * (color_format.pbc() - 1) as u32) as usize;
for y in 0..height { for y in 0..height {
curr_line = if color_format.alpha_channel().is_some() { curr_line = if color_format.alpha_channel().is_some() {
data[rgb_index..rgb_index + width as usize * 3] // Interleave the offset alpha into the RGB bytes
.chunks(3) data[rgb_index..rgb_index + width as usize * (color_format.pbc() - 1)]
.chunks(color_format.pbc() - 1)
.zip(data[alpha_index..alpha_index + width as usize].into_iter()) .zip(data[alpha_index..alpha_index + width as usize].into_iter())
.flat_map(|(a, b)| { .flat_map(|(a, b)| {
a.into_iter().chain(vec![b]) a.into_iter().chain(vec![b])
@ -65,7 +66,7 @@ pub fn add_rows(width: u32, height: u32, color_format: ColorFormat, data: &[u8])
.copied() .copied()
.collect() .collect()
} else { } else {
data[rgb_index..rgb_index + width as usize * 3].to_vec() data[rgb_index..rgb_index + width as usize * color_format.pbc()].to_vec()
}; };
if y % block_height != 0 { if y % block_height != 0 {
@ -81,7 +82,11 @@ pub fn add_rows(width: u32, height: u32, color_format: ColorFormat, data: &[u8])
output_buf.extend_from_slice(&curr_line); output_buf.extend_from_slice(&curr_line);
prev_line.clone_from(&curr_line); prev_line.clone_from(&curr_line);
rgb_index += width as usize * 3; rgb_index += if color_format.alpha_channel().is_some() {
width as usize * (color_format.pbc() - 1)
} else {
width as usize * color_format.pbc()
};
alpha_index += width as usize; alpha_index += width as usize;
} }

View file

@ -156,9 +156,6 @@ impl SquishyPicture {
}, },
}; };
let mut inspection_file = File::create("raw_data").unwrap();
inspection_file.write_all(&modified_data).unwrap();
// Compress the final image data using the basic LZW scheme // Compress the final image data using the basic LZW scheme
let (compressed_data, compression_info) = compress(modified_data)?; let (compressed_data, compression_info) = compress(modified_data)?;
@ -192,7 +189,12 @@ impl SquishyPicture {
let bitmap = match header.compression_type { let bitmap = match header.compression_type {
CompressionType::None => pre_bitmap, CompressionType::None => pre_bitmap,
CompressionType::Lossless => { CompressionType::Lossless => {
add_rows(header.width, header.height, header.color_format, &pre_bitmap) add_rows(
header.width,
header.height,
header.color_format,
&pre_bitmap
)
}, },
CompressionType::LossyDct => { CompressionType::LossyDct => {
dct_decompress( dct_decompress(