use crate::blending::demultiply_image;
use crate::errors::PConvertError;
use image::codecs::png::{CompressionType, FilterType, PngDecoder, PngEncoder};
use image::ImageDecoder;
use image::ImageEncoder;
use image::{ColorType, ImageBuffer, Rgba};
use std::fs::File;
use std::io::{BufWriter, Read, Write};
pub fn decode_png(
readable_stream: impl Read,
demultiply: bool,
) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, PConvertError> {
let decoder = PngDecoder::new(readable_stream)?;
let (width, height) = decoder.dimensions();
let mut reader = decoder.into_reader()?;
let mut bytes = Vec::<u8>::new();
reader.read_to_end(&mut bytes)?;
let mut img = ImageBuffer::from_vec(width, height, bytes).unwrap();
if demultiply {
demultiply_image(&mut img)
}
Ok(img)
}
pub fn read_png_from_file(
file_in: String,
demultiply: bool,
) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, PConvertError> {
let file = File::open(file_in)?;
decode_png(file, demultiply)
}
pub fn encode_png(
writable_buff: impl Write,
png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
compression: CompressionType,
filter: FilterType,
) -> Result<(), PConvertError> {
let buff = BufWriter::new(writable_buff);
let encoder = PngEncoder::new_with_quality(buff, compression, filter);
Ok(encoder.write_image(png, png.width(), png.height(), ColorType::Rgba8)?)
}
pub fn write_png_to_file(
file_out: String,
png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
compression: CompressionType,
filter: FilterType,
) -> Result<(), PConvertError> {
let file = File::create(file_out)?;
encode_png(file, png, compression, filter)
}
pub fn write_png_to_file_d(
file_out: String,
png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
) -> Result<(), PConvertError> {
let file = File::create(file_out)?;
encode_png(file, png, CompressionType::Fast, FilterType::NoFilter)
}
#[cfg(not(feature = "wasm-extension"))]
pub fn write_png_parallel(
file_out: String,
png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
compression: CompressionType,
filter: FilterType,
) -> Result<(), PConvertError> {
let writer = File::create(file_out)?;
let mut header = mtpng::Header::new();
header.set_size(png.width(), png.height())?;
header.set_color(mtpng::ColorType::TruecolorAlpha, 8)?;
let mut options = mtpng::encoder::Options::new();
options.set_compression_level(mtpng_compression_from(compression))?;
options.set_filter_mode(mtpng::Mode::Fixed(mtpng_filter_from(filter)))?;
let mut encoder = mtpng::encoder::Encoder::new(writer, &options);
encoder.write_header(&header)?;
encoder.write_image_rows(png)?;
encoder.finish()?;
Ok(())
}
#[cfg(feature = "wasm-extension")]
pub fn write_png_parallel(
file_out: String,
png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
compression: CompressionType,
filter: FilterType,
) -> Result<(), PConvertError> {
write_png_to_file(file_out, png, compression, filter)
}
pub fn image_compression_from(compression: String) -> CompressionType {
match compression.trim().to_lowercase().as_str() {
"best" => CompressionType::Best,
"default" => CompressionType::Default,
"fast" => CompressionType::Fast,
_ => CompressionType::Fast,
}
}
pub fn image_filter_from(filter: String) -> FilterType {
match filter.trim().to_lowercase().as_str() {
"avg" => FilterType::Avg,
"nofilter" => FilterType::NoFilter,
"paeth" => FilterType::Paeth,
"sub" => FilterType::Sub,
"up" => FilterType::Up,
_ => FilterType::NoFilter,
}
}
#[cfg(not(feature = "wasm-extension"))]
fn mtpng_compression_from(compression: CompressionType) -> mtpng::CompressionLevel {
match compression {
CompressionType::Default => mtpng::CompressionLevel::Default,
CompressionType::Best => mtpng::CompressionLevel::High,
CompressionType::Fast => mtpng::CompressionLevel::Fast,
_ => mtpng::CompressionLevel::Fast,
}
}
#[cfg(not(feature = "wasm-extension"))]
fn mtpng_filter_from(filter: FilterType) -> mtpng::Filter {
match filter {
FilterType::Avg => mtpng::Filter::Average,
FilterType::Paeth => mtpng::Filter::Paeth,
FilterType::Sub => mtpng::Filter::Sub,
FilterType::Up => mtpng::Filter::Up,
FilterType::NoFilter => mtpng::Filter::None,
_ => mtpng::Filter::None,
}
}
pub fn max<T: PartialOrd>(x: T, y: T) -> T {
if x > y {
x
} else {
y
}
}
pub fn min<T: PartialOrd>(x: T, y: T) -> T {
if x < y {
x
} else {
y
}
}