use image::{DynamicImage, GrayImage};
pub fn grayscale_on_white(img: DynamicImage) -> GrayImage {
if !img.has_alpha() {
return img.into_luma8();
}
flatten_alpha_on_white(img).into_luma8()
}
fn flatten_alpha_on_white(img: DynamicImage) -> DynamicImage {
match img {
DynamicImage::ImageLumaA8(mut pixels) => {
for pixel in pixels.pixels_mut() {
pixel[0] = composite_u8_on_white(pixel[0], pixel[1]);
pixel[1] = u8::MAX;
}
DynamicImage::ImageLumaA8(pixels)
}
DynamicImage::ImageRgba8(mut pixels) => {
for pixel in pixels.pixels_mut() {
let alpha = pixel[3];
for channel in &mut pixel.0[..3] {
*channel = composite_u8_on_white(*channel, alpha);
}
pixel[3] = u8::MAX;
}
DynamicImage::ImageRgba8(pixels)
}
DynamicImage::ImageLumaA16(mut pixels) => {
for pixel in pixels.pixels_mut() {
pixel[0] = composite_u16_on_white(pixel[0], pixel[1]);
pixel[1] = u16::MAX;
}
DynamicImage::ImageLumaA16(pixels)
}
DynamicImage::ImageRgba16(mut pixels) => {
for pixel in pixels.pixels_mut() {
let alpha = pixel[3];
for channel in &mut pixel.0[..3] {
*channel = composite_u16_on_white(*channel, alpha);
}
pixel[3] = u16::MAX;
}
DynamicImage::ImageRgba16(pixels)
}
DynamicImage::ImageRgba32F(mut pixels) => {
for pixel in pixels.pixels_mut() {
let alpha = pixel[3];
for channel in &mut pixel.0[..3] {
*channel = *channel * alpha + (1.0 - alpha);
}
pixel[3] = 1.0;
}
DynamicImage::ImageRgba32F(pixels)
}
image => image,
}
}
fn composite_u8_on_white(channel: u8, alpha: u8) -> u8 {
let alpha = u16::from(alpha);
((u16::from(channel) * alpha + u16::from(u8::MAX) * (u16::from(u8::MAX) - alpha) + 127)
/ u16::from(u8::MAX)) as u8
}
fn composite_u16_on_white(channel: u16, alpha: u16) -> u16 {
let alpha = u64::from(alpha);
((u64::from(channel) * alpha
+ u64::from(u16::MAX) * (u64::from(u16::MAX) - alpha)
+ u64::from(u16::MAX) / 2)
/ u64::from(u16::MAX)) as u16
}