1use crate::blending::demultiply_image;
5use crate::errors::PConvertError;
6use image::codecs::png::{CompressionType, FilterType, PngDecoder, PngEncoder};
7use image::ImageDecoder;
8use image::ImageEncoder;
9use image::{ColorType, ImageBuffer, Rgba};
10use std::fs::File;
11use std::io::{BufWriter, Read, Write};
12
13pub fn decode_png(
20 readable_stream: impl Read,
21 demultiply: bool,
22) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, PConvertError> {
23 let decoder = PngDecoder::new(readable_stream)?;
24 let (width, height) = decoder.dimensions();
25
26 let mut reader = decoder.into_reader()?;
27
28 let mut bytes = Vec::<u8>::new();
29 reader.read_to_end(&mut bytes)?;
30
31 let mut img = ImageBuffer::from_vec(width, height, bytes).unwrap();
32
33 if demultiply {
34 demultiply_image(&mut img)
35 }
36
37 Ok(img)
38}
39
40pub fn read_png_from_file(
47 file_in: String,
48 demultiply: bool,
49) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, PConvertError> {
50 let file = File::open(file_in)?;
51 decode_png(file, demultiply)
52}
53
54pub fn encode_png(
63 writable_buff: impl Write,
64 png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
65 compression: CompressionType,
66 filter: FilterType,
67) -> Result<(), PConvertError> {
68 let buff = BufWriter::new(writable_buff);
69 let encoder = PngEncoder::new_with_quality(buff, compression, filter);
70 Ok(encoder.write_image(png, png.width(), png.height(), ColorType::Rgba8)?)
71}
72
73pub fn write_png_to_file(
83 file_out: String,
84 png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
85 compression: CompressionType,
86 filter: FilterType,
87) -> Result<(), PConvertError> {
88 let file = File::create(file_out)?;
89 encode_png(file, png, compression, filter)
90}
91
92pub fn write_png_to_file_d(
102 file_out: String,
103 png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
104) -> Result<(), PConvertError> {
105 let file = File::create(file_out)?;
106 encode_png(file, png, CompressionType::Fast, FilterType::NoFilter)
107}
108
109#[cfg(not(feature = "wasm-extension"))]
119pub fn write_png_parallel(
120 file_out: String,
121 png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
122 compression: CompressionType,
123 filter: FilterType,
124) -> Result<(), PConvertError> {
125 let writer = File::create(file_out)?;
126
127 let mut header = mtpng::Header::new();
128 header.set_size(png.width(), png.height())?;
129 header.set_color(mtpng::ColorType::TruecolorAlpha, 8)?;
130
131 let mut options = mtpng::encoder::Options::new();
132 options.set_compression_level(mtpng_compression_from(compression))?;
133 options.set_filter_mode(mtpng::Mode::Fixed(mtpng_filter_from(filter)))?;
134
135 let mut encoder = mtpng::encoder::Encoder::new(writer, &options);
136 encoder.write_header(&header)?;
137 encoder.write_image_rows(png)?;
138 encoder.finish()?;
139
140 Ok(())
141}
142
143#[cfg(feature = "wasm-extension")]
153pub fn write_png_parallel(
154 file_out: String,
155 png: &ImageBuffer<Rgba<u8>, Vec<u8>>,
156 compression: CompressionType,
157 filter: FilterType,
158) -> Result<(), PConvertError> {
159 write_png_to_file(file_out, png, compression, filter)
160}
161
162pub fn image_compression_from(compression: String) -> CompressionType {
166 match compression.trim().to_lowercase().as_str() {
167 "best" => CompressionType::Best,
168 "default" => CompressionType::Default,
169 "fast" => CompressionType::Fast,
170 _ => CompressionType::Fast,
171 }
172}
173
174pub fn image_filter_from(filter: String) -> FilterType {
178 match filter.trim().to_lowercase().as_str() {
179 "avg" => FilterType::Avg,
180 "nofilter" => FilterType::NoFilter,
181 "paeth" => FilterType::Paeth,
182 "sub" => FilterType::Sub,
183 "up" => FilterType::Up,
184 _ => FilterType::NoFilter,
185 }
186}
187
188#[cfg(not(feature = "wasm-extension"))]
189fn mtpng_compression_from(compression: CompressionType) -> mtpng::CompressionLevel {
190 match compression {
191 CompressionType::Default => mtpng::CompressionLevel::Default,
192 CompressionType::Best => mtpng::CompressionLevel::High,
193 CompressionType::Fast => mtpng::CompressionLevel::Fast,
194 _ => mtpng::CompressionLevel::Fast,
195 }
196}
197
198#[cfg(not(feature = "wasm-extension"))]
199fn mtpng_filter_from(filter: FilterType) -> mtpng::Filter {
200 match filter {
201 FilterType::Avg => mtpng::Filter::Average,
202 FilterType::Paeth => mtpng::Filter::Paeth,
203 FilterType::Sub => mtpng::Filter::Sub,
204 FilterType::Up => mtpng::Filter::Up,
205 FilterType::NoFilter => mtpng::Filter::None,
206 _ => mtpng::Filter::None,
207 }
208}
209
210pub fn max<T: PartialOrd>(x: T, y: T) -> T {
212 if x > y {
213 x
214 } else {
215 y
216 }
217}
218
219pub fn min<T: PartialOrd>(x: T, y: T) -> T {
221 if x < y {
222 x
223 } else {
224 y
225 }
226}