use rgb::RGB16;
use crate::{
colors::{BitDepth, ColorType},
headers::IhdrData,
png::PngImage,
};
#[must_use]
pub fn cleaned_alpha_channel(png: &PngImage) -> Option<PngImage> {
if !png.ihdr.color_type.has_alpha() {
return None;
}
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
let colored_bytes = bpp - byte_depth;
let mut reduced = Vec::with_capacity(png.data.len());
for pixel in png.data.chunks_exact(bpp) {
if pixel.iter().skip(colored_bytes).all(|b| *b == 0) {
reduced.resize(reduced.len() + bpp, 0);
} else {
reduced.extend_from_slice(pixel);
}
}
Some(PngImage {
data: reduced,
ihdr: png.ihdr.clone(),
})
}
#[must_use]
pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<PngImage> {
if !png.ihdr.color_type.has_alpha() {
return None;
}
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
let colored_bytes = bpp - byte_depth;
let mut has_transparency = false;
let mut used_colors = vec![false; 256];
for pixel in png.data.chunks_exact(bpp) {
if optimize_alpha && pixel.iter().skip(colored_bytes).all(|b| *b == 0) {
has_transparency = true;
} else if pixel.iter().skip(colored_bytes).any(|b| *b != 255) {
return None;
} else if optimize_alpha && pixel.iter().take(colored_bytes).all(|b| *b == pixel[0]) {
used_colors[pixel[0] as usize] = true;
}
}
let transparency_pixel = if has_transparency {
let unused = match png.ihdr.color_type {
ColorType::GrayscaleAlpha => [0x00, 0xFF, 0x55, 0xAA]
.into_iter()
.find(|&v| !used_colors[v as usize]),
_ => None,
}
.or_else(|| used_colors.iter().position(|&u| !u).map(|v| v as u8));
Some(unused?)
} else {
None
};
let mut raw_data = Vec::with_capacity(png.data.len());
for pixel in png.data.chunks_exact(bpp) {
match transparency_pixel {
Some(trns) if pixel.iter().skip(colored_bytes).all(|b| *b == 0) => {
raw_data.resize(raw_data.len() + colored_bytes, trns);
}
_ => raw_data.extend_from_slice(&pixel[0..colored_bytes]),
}
}
let transparent = transparency_pixel.map(|trns| match png.ihdr.bit_depth {
BitDepth::Sixteen => (u16::from(trns) << 8) | u16::from(trns),
_ => u16::from(trns),
});
let target_color_type = match png.ihdr.color_type {
ColorType::GrayscaleAlpha => ColorType::Grayscale {
transparent_shade: transparent,
},
_ => ColorType::RGB {
transparent_color: transparent.map(|t| RGB16::new(t, t, t)),
},
};
Some(PngImage {
data: raw_data,
ihdr: IhdrData {
color_type: target_color_type,
..png.ihdr
},
})
}