use std::io::Cursor;
use cfg_if::cfg_if;
use crate::structs::*;
pub fn encode_auto_select<'a>(im_info: &ImageInfo, config: &CompressionConfig) -> &'a str {
#[allow(unused_mut)] let mut support_webp = true;
#[allow(unused_mut)] let mut support_png = true;
#[allow(unused_mut)] let mut support_jpeg_xl = true;
cfg_if! {
if #[cfg(not(feature="webp"))] {
support_webp = false;
}
}
cfg_if! {
if #[cfg(not(feature="png"))] {
support_png = false;
}
}
cfg_if! {
if #[cfg(not(feature="jpegxl"))] {
support_jpeg_xl = false;
}
}
match im_info.pixels {
ImagePixels::U8(_) => {
if support_webp && is_webp_supported_image(&im_info, &config) {
"webp"
}
else if support_png && is_png_supported_image(&im_info, &config) {
"png"
}
else
{
""
}
},
ImagePixels::U16(_) => {
if support_png && is_png_supported_image(&im_info, &config) {
"png"
}
else
{
""
}
},
_ => {
""
}
}
}
pub fn encode_auto(im_info: ImageInfo, config: CompressionConfig) -> (String, Vec<u8>) {
let decide = encode_auto_select(&im_info, &config);
match decide {
#[cfg(feature="webp")]
"webp" => {
let webp_memory = encode_lossless_webp(im_info);
("webp".to_owned(), webp_memory)
},
#[cfg(feature="png")]
"png" => {
let png_memory = encode_png(im_info, config);
("png".to_owned(), png_memory)
},
_ => {
panic!("Unsupported image format");
}
}
}
fn is_webp_supported_image(im_info: &ImageInfo, config: &CompressionConfig) -> bool {
config.allow_webp &&
(im_info.bit_depth == 8) &&
(im_info.color_type == 2 || im_info.color_type == 6)
}
fn is_png_supported_image(im_info: &ImageInfo, config: &CompressionConfig) -> bool {
config.allow_png &&
(im_info.bit_depth == 1 || im_info.bit_depth == 2 || im_info.bit_depth == 4 || im_info.bit_depth == 8 || im_info.bit_depth == 16) &&
(im_info.color_type == 0 || im_info.color_type == 2 || im_info.color_type == 4 || im_info.color_type == 6)
}
#[cfg(feature="webp")]
pub fn encode_lossless_webp(im_info: ImageInfo) -> Vec<u8> {
let bit_per_pixel = match im_info.color_type {
2 => 3,
6 => 4,
_ => panic!("Unsupported color type"),
};
let pixel_layout = match im_info.color_type {
2 => webp::PixelLayout::Rgb,
6 => webp::PixelLayout::Rgba,
_ => panic!("Unsupported color type"),
};
let mut image_pixels = vec![0; im_info.width as usize * im_info.height as usize * bit_per_pixel];
match im_info.pixels {
ImagePixels::U8(pixels) => {
for i in 0..pixels.len() {
image_pixels[i] = pixels[i];
}
},
_ => panic!("Unsupported pixel type"),
}
let encoder = webp::Encoder::new(&*image_pixels, pixel_layout, im_info.width, im_info.height);
let webp_memory = encoder.encode_lossless();
webp_memory.to_vec()
}
#[cfg(feature="png")]
pub fn encode_png(im_info: ImageInfo, compression_config: CompressionConfig) -> Vec<u8> {
cfg_if! {
if #[cfg(feature="oxipng")] {
return encode_png_oxipng(im_info, compression_config);
} else {
return encode_png_image_rs(im_info);
}
}
}
#[cfg(feature="png")]
pub fn encode_png_image_rs(im_info: ImageInfo) -> Vec<u8> {
let mut output_vec: Vec<u8> = Vec::new();
{
let output_cursor = Cursor::new(&mut output_vec);
let mut encoder = png::Encoder::new(output_cursor, im_info.width, im_info.height);
let color_type = match im_info.color_type {
0 => png::ColorType::Grayscale,
2 => png::ColorType::Rgb,
3 => png::ColorType::Indexed,
4 => png::ColorType::GrayscaleAlpha,
6 => png::ColorType::Rgba,
_ => panic!("Unsupported color type"),
};
encoder.set_color(color_type);
let color_depth = match im_info.bit_depth {
1 => png::BitDepth::One,
2 => png::BitDepth::Two,
4 => png::BitDepth::Four,
8 => png::BitDepth::Eight,
16 => png::BitDepth::Sixteen,
_ => panic!("Unsupported bit_depth"),
};
encoder.set_depth(color_depth);
let mut pixels;
match im_info.pixels {
ImagePixels::U8(pxl) => {
pixels = pxl;
},
ImagePixels::U16(pxl) => {
pixels = Vec::new();
for i in 0..pxl.len() {
pixels.push((pxl[i] >> 8) as u8);
pixels.push((pxl[i] & 0xFF) as u8);
}
},
_ => panic!("Unsupported pixel type"),
}
let mut writer = encoder.write_header().unwrap();
writer.write_image_data(&*pixels).unwrap();
}
let mut output: Vec<u8> = vec![0; output_vec.len()];
for i in 0..output.len() {
output[i] = (&*output_vec)[i];
}
output
}
#[cfg(feature="oxipng")]
pub fn encode_png_oxipng(im_info: ImageInfo, compression_config: CompressionConfig) -> Vec<u8> {
let color_type = match im_info.color_type {
0 => oxipng::ColorType::Grayscale {
transparent_shade: None,
},
2 => oxipng::ColorType::RGB {
transparent_color: None,
},
4 => oxipng::ColorType::GrayscaleAlpha,
6 => oxipng::ColorType::RGBA,
_ => panic!("Unsupported color type"),
};
let bit_depth = match im_info.bit_depth {
1 => oxipng::BitDepth::One,
2 => oxipng::BitDepth::Two,
4 => oxipng::BitDepth::Four,
8 => oxipng::BitDepth::Eight,
16 => oxipng::BitDepth::Sixteen,
_ => panic!("Unsupported bit depth"),
};
let pixels;
match im_info.pixels {
ImagePixels::U8(pixels8) => {
pixels = pixels8;
},
ImagePixels::U16(pixels16) => {
let mut pixels8 = vec![0; pixels16.len() * 2];
for i in 0..pixels16.len() {
pixels8[i * 2] = (pixels16[i] >> 8) as u8;
pixels8[i * 2 + 1] = pixels16[i] as u8;
}
pixels = pixels8;
},
_ => panic!("Unsupported pixel type"),
}
let oxipng_img = oxipng::RawImage::new(
im_info.width,
im_info.height,
color_type,
bit_depth,
pixels,
).unwrap();
let mut options = oxipng::Options::max_compression();
if compression_config.allow_oxipng_zopfli {
options.deflate = oxipng::Deflaters::Zopfli {
iterations: std::num::NonZeroU8::new(15).unwrap(), };
}
let optimized = oxipng_img.create_optimized_png(&options).unwrap();
optimized
}