1use std::io::Cursor;
2use cfg_if::cfg_if;
3use crate::structs::*;
4
5pub fn encode_auto_select<'a>(im_info: &ImageInfo, config: &CompressionConfig) -> &'a str {
6 #[allow(unused_mut)] let mut support_webp = true;
7 #[allow(unused_mut)] let mut support_png = true;
8 #[allow(unused_mut)] let mut support_jpeg_xl = true;
9
10 cfg_if! {
11 if #[cfg(not(feature="webp"))] {
12 support_webp = false;
13 }
14 }
15 cfg_if! {
16 if #[cfg(not(feature="png"))] {
17 support_png = false;
18 }
19 }
20 cfg_if! {
21 if #[cfg(not(feature="jpegxl"))] {
22 support_jpeg_xl = false;
23 }
24 }
25
26 match im_info.pixels {
27 ImagePixels::U8(_) => {
28 if support_webp && is_webp_supported_image(&im_info, &config) {
29 "webp"
31 }
32 else if support_png && is_png_supported_image(&im_info, &config) {
33 "png"
34 }
35 else
36 {
37 ""
38 }
39 },
40 ImagePixels::U16(_) => {
41 if support_png && is_png_supported_image(&im_info, &config) {
42 "png"
43 }
44 else
45 {
46 ""
47 }
48 },
49 _ => {
50 ""
51 }
52 }
53}
54
55pub fn encode_auto(im_info: ImageInfo, config: CompressionConfig) -> (String, Vec<u8>) {
57 let decide = encode_auto_select(&im_info, &config);
58
59 match decide {
60 #[cfg(feature="webp")]
61 "webp" => {
62 let webp_memory = encode_lossless_webp(im_info);
63 ("webp".to_owned(), webp_memory)
64 },
65 #[cfg(feature="png")]
66 "png" => {
67 let png_memory = encode_png(im_info, config);
68 ("png".to_owned(), png_memory)
69 },
70 _ => {
71 panic!("Unsupported image format");
72 }
73 }
74}
75
76fn is_webp_supported_image(im_info: &ImageInfo, config: &CompressionConfig) -> bool {
77 config.allow_webp &&
78 (im_info.bit_depth == 8) &&
79 (im_info.color_type == 2 || im_info.color_type == 6)
80}
81
82fn is_png_supported_image(im_info: &ImageInfo, config: &CompressionConfig) -> bool {
83 config.allow_png &&
84 (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) &&
85 (im_info.color_type == 0 || im_info.color_type == 2 || im_info.color_type == 4 || im_info.color_type == 6)
86}
87
88#[cfg(feature="webp")]
89pub fn encode_lossless_webp(im_info: ImageInfo) -> Vec<u8> {
90 let bit_per_pixel = match im_info.color_type {
91 2 => 3,
92 6 => 4,
93 _ => panic!("Unsupported color type"),
94 };
95
96 let pixel_layout = match im_info.color_type {
97 2 => webp::PixelLayout::Rgb,
98 6 => webp::PixelLayout::Rgba,
99 _ => panic!("Unsupported color type"),
100 };
101
102 let mut image_pixels = vec![0; im_info.width as usize * im_info.height as usize * bit_per_pixel];
103
104 match im_info.pixels {
105 ImagePixels::U8(pixels) => {
106 for i in 0..pixels.len() {
107 image_pixels[i] = pixels[i];
108 }
109 },
110 _ => panic!("Unsupported pixel type"),
111 }
112
113 let encoder = webp::Encoder::new(&*image_pixels, pixel_layout, im_info.width, im_info.height);
114
115 let webp_memory = encoder.encode_lossless();
117
118 webp_memory.to_vec()
119}
120
121#[cfg(feature="png")]
122pub fn encode_png(im_info: ImageInfo, compression_config: CompressionConfig) -> Vec<u8> {
123 cfg_if! {
124 if #[cfg(feature="oxipng")] {
125 return encode_png_oxipng(im_info, compression_config);
126 } else {
127 return encode_png_image_rs(im_info);
128 }
129 }
130}
131
132#[cfg(feature="png")]
133pub fn encode_png_image_rs(im_info: ImageInfo) -> Vec<u8> {
134 let mut output_vec: Vec<u8> = Vec::new();
135 {
136 let output_cursor = Cursor::new(&mut output_vec);
137
138 let mut encoder = png::Encoder::new(output_cursor, im_info.width, im_info.height);
139
140 let color_type = match im_info.color_type {
141 0 => png::ColorType::Grayscale,
142 2 => png::ColorType::Rgb,
143 3 => png::ColorType::Indexed,
144 4 => png::ColorType::GrayscaleAlpha,
145 6 => png::ColorType::Rgba,
146 _ => panic!("Unsupported color type"),
147 };
148
149 encoder.set_color(color_type);
150
151 let color_depth = match im_info.bit_depth {
152 1 => png::BitDepth::One,
153 2 => png::BitDepth::Two,
154 4 => png::BitDepth::Four,
155 8 => png::BitDepth::Eight,
156 16 => png::BitDepth::Sixteen,
157 _ => panic!("Unsupported bit_depth"),
158 };
159
160 encoder.set_depth(color_depth);
161
162 let mut pixels;
163
164 match im_info.pixels {
165 ImagePixels::U8(pxl) => {
166 pixels = pxl;
167 },
168 ImagePixels::U16(pxl) => {
169 pixels = Vec::new();
170 for i in 0..pxl.len() {
171 pixels.push((pxl[i] >> 8) as u8);
172 pixels.push((pxl[i] & 0xFF) as u8);
173 }
174 },
175 _ => panic!("Unsupported pixel type"),
176 }
177
178 let mut writer = encoder.write_header().unwrap();
179
180 writer.write_image_data(&*pixels).unwrap();
182 }
183
184 let mut output: Vec<u8> = vec![0; output_vec.len()];
185 for i in 0..output.len() {
186 output[i] = (&*output_vec)[i];
187 }
188 output
189}
190
191#[cfg(feature="oxipng")]
192pub fn encode_png_oxipng(im_info: ImageInfo, compression_config: CompressionConfig) -> Vec<u8> {
193 let color_type = match im_info.color_type {
194 0 => oxipng::ColorType::Grayscale {
195 transparent_shade: None,
196 },
197 2 => oxipng::ColorType::RGB {
198 transparent_color: None,
199 },
200 4 => oxipng::ColorType::GrayscaleAlpha,
201 6 => oxipng::ColorType::RGBA,
202 _ => panic!("Unsupported color type"),
203 };
204
205 let bit_depth = match im_info.bit_depth {
206 1 => oxipng::BitDepth::One,
207 2 => oxipng::BitDepth::Two,
208 4 => oxipng::BitDepth::Four,
209 8 => oxipng::BitDepth::Eight,
210 16 => oxipng::BitDepth::Sixteen,
211 _ => panic!("Unsupported bit depth"),
212 };
213
214 let pixels;
215 match im_info.pixels {
216 ImagePixels::U8(pixels8) => {
217 pixels = pixels8;
218 },
219 ImagePixels::U16(pixels16) => {
220 let mut pixels8 = vec![0; pixels16.len() * 2];
221 for i in 0..pixels16.len() {
222 pixels8[i * 2] = (pixels16[i] >> 8) as u8;
223 pixels8[i * 2 + 1] = pixels16[i] as u8;
224 }
225 pixels = pixels8;
226 },
227 _ => panic!("Unsupported pixel type"),
228 }
229
230 let oxipng_img = oxipng::RawImage::new(
231 im_info.width,
232 im_info.height,
233 color_type,
234 bit_depth,
235 pixels,
236 ).unwrap();
237
238 let mut options = oxipng::Options::max_compression();
239 if compression_config.allow_oxipng_zopfli {
240 options.deflate = oxipng::Deflaters::Zopfli {
241 iterations: std::num::NonZeroU8::new(15).unwrap(), };
243 }
244
245 let optimized = oxipng_img.create_optimized_png(&options).unwrap();
246 optimized
247}