1use avif_decode::Decoder;
2use image::codecs::avif;
3use image::codecs::gif;
4use image::codecs::webp;
5use image::{AnimationDecoder, DynamicImage, ImageEncoder, ImageFormat, RgbaImage};
6use lodepng::Bitmap;
7use rgb::{ComponentBytes, RGB8, RGBA8};
8use snafu::{ResultExt, Snafu};
9use std::{
10 ffi::OsStr,
11 io::{BufRead, Seek},
12};
13
14#[derive(Debug, Snafu)]
15pub enum ImageError {
16 #[snafu(display("Handle image fail, category:{category}, message:{source}"))]
17 Image {
18 category: String,
19 source: image::ImageError,
20 },
21 #[snafu(display("Handle image fail, category:{category}, message:{source}"))]
22 ImageQuant {
23 category: String,
24 source: imagequant::Error,
25 },
26 #[snafu(display("Handle image fail, category:{category}, message:{source}"))]
27 AvifDecode {
28 category: String,
29 source: avif_decode::Error,
30 },
31 #[snafu(display("Handle image fail, category:{category}, message:{source}"))]
32 LodePNG {
33 category: String,
34 source: lodepng::Error,
35 },
36 #[snafu(display("Handle image fail, category:mozjpeg, message:unknown"))]
37 Mozjpeg {},
38 #[snafu(display("Io fail, {source}"))]
39 Io { source: std::io::Error },
40 #[snafu(display("Handle image fail"))]
41 Unknown,
42}
43
44type Result<T, E = ImageError> = std::result::Result<T, E>;
45
46pub struct ImageInfo {
47 pub buffer: Vec<RGBA8>,
49 pub width: usize,
51 pub height: usize,
53}
54
55impl From<Bitmap<RGBA8>> for ImageInfo {
56 fn from(info: Bitmap<RGBA8>) -> Self {
57 ImageInfo {
58 buffer: info.buffer,
59 width: info.width,
60 height: info.height,
61 }
62 }
63}
64
65impl From<RgbaImage> for ImageInfo {
66 fn from(img: RgbaImage) -> Self {
67 let width = img.width() as usize;
68 let height = img.height() as usize;
69 let mut buffer = Vec::with_capacity(width * height);
70
71 for ele in img.chunks(4) {
72 buffer.push(RGBA8 {
73 r: ele[0],
74 g: ele[1],
75 b: ele[2],
76 a: ele[3],
77 })
78 }
79
80 ImageInfo {
81 buffer,
82 width,
83 height,
84 }
85 }
86}
87
88pub fn avif_decode(data: &[u8]) -> Result<DynamicImage> {
91 let avif_result = Decoder::from_avif(data)
92 .context(AvifDecodeSnafu {
93 category: "decode".to_string(),
94 })?
95 .to_image()
96 .context(AvifDecodeSnafu {
97 category: "decode".to_string(),
98 })?;
99 match avif_result {
100 avif_decode::Image::Rgb8(img) => {
101 let width = img.width();
102 let height = img.height();
103 let mut buf = Vec::with_capacity(width * height * 3);
104 for item in img.buf() {
105 buf.push(item.r);
106 buf.push(item.g);
107 buf.push(item.b);
108 }
109 let rgb_image = image::RgbImage::from_raw(width as u32, height as u32, buf)
110 .ok_or(ImageError::Unknown)?;
111 Ok(DynamicImage::ImageRgb8(rgb_image))
112 }
113 avif_decode::Image::Rgba8(img) => {
114 let width = img.width();
115 let height = img.height();
116 let mut buf = Vec::with_capacity(width * height * 4);
117 for item in img.buf() {
118 buf.push(item.r);
119 buf.push(item.g);
120 buf.push(item.b);
121 buf.push(item.a);
122 }
123 let rgba_image = image::RgbaImage::from_raw(width as u32, height as u32, buf)
124 .ok_or(ImageError::Unknown)?;
125 Ok(DynamicImage::ImageRgba8(rgba_image))
126 }
127 avif_decode::Image::Rgba16(img) => {
128 let width = img.width();
129 let height = img.height();
130 let mut buf = Vec::with_capacity(width * height * 4);
131 for item in img.buf() {
132 buf.push((item.r / 257) as u8);
133 buf.push((item.g / 257) as u8);
134 buf.push((item.b / 257) as u8);
135 buf.push((item.a / 257) as u8);
136 }
137 let rgba_image = image::RgbaImage::from_raw(width as u32, height as u32, buf)
138 .ok_or(ImageError::Unknown)?;
139 Ok(DynamicImage::ImageRgba8(rgba_image))
140 }
141 avif_decode::Image::Rgb16(img) => {
142 let width = img.width();
143 let height = img.height();
144 let mut buf = Vec::with_capacity(width * height * 3);
145 for item in img.buf() {
146 buf.push((item.r / 257) as u8);
147 buf.push((item.g / 257) as u8);
148 buf.push((item.b / 257) as u8);
149 }
150 let rgb_image = image::RgbImage::from_raw(width as u32, height as u32, buf)
151 .ok_or(ImageError::Unknown)?;
152 Ok(DynamicImage::ImageRgb8(rgb_image))
153 }
154 _ => Err(ImageError::Unknown),
155 }
156}
157
158pub fn load<R: BufRead + Seek>(r: R, ext: &str) -> Result<ImageInfo> {
159 let format = ImageFormat::from_extension(OsStr::new(ext)).unwrap_or(ImageFormat::Jpeg);
160 let result = image::load(r, format).context(ImageSnafu { category: "load" })?;
161 let img = result.to_rgba8();
162 Ok(img.into())
163}
164
165pub fn to_gif<R>(r: R, speed: u8) -> Result<Vec<u8>>
166where
167 R: std::io::BufRead,
168 R: std::io::Seek,
169{
170 let decoder = gif::GifDecoder::new(r).context(ImageSnafu {
171 category: "gif_decode",
172 })?;
173 let frames = decoder.into_frames();
174
175 let mut w = Vec::new();
176
177 {
178 let mut encoder = gif::GifEncoder::new_with_speed(&mut w, speed as i32);
179 encoder
180 .set_repeat(gif::Repeat::Infinite)
181 .context(ImageSnafu {
182 category: "gif_set_repeat",
183 })?;
184 encoder
185 .try_encode_frames(frames.into_iter())
186 .context(ImageSnafu {
187 category: "git_encode",
188 })?;
189 }
190
191 Ok(w)
192}
193
194impl ImageInfo {
195 fn get_rgb8(&self) -> Vec<RGB8> {
197 let mut output_data: Vec<RGB8> = Vec::with_capacity(self.width * self.height);
198
199 for ele in &self.buffer {
200 output_data.push(ele.rgb())
201 }
202
203 output_data
204 }
205 pub fn to_png(&self, quality: u8) -> Result<Vec<u8>> {
208 let mut liq = imagequant::new();
209 liq.set_quality(0, quality).context(ImageQuantSnafu {
210 category: "png_set_quality",
211 })?;
212
213 let mut img = liq
214 .new_image(self.buffer.as_ref(), self.width, self.height, 0.0)
215 .context(ImageQuantSnafu {
216 category: "png_new_image",
217 })?;
218
219 let mut res = liq.quantize(&mut img).context(ImageQuantSnafu {
220 category: "png_quantize",
221 })?;
222
223 res.set_dithering_level(1.0).context(ImageQuantSnafu {
224 category: "png_set_level",
225 })?;
226
227 let (palette, pixels) = res.remapped(&mut img).context(ImageQuantSnafu {
228 category: "png_remapped",
229 })?;
230 let mut enc = lodepng::Encoder::new();
231 enc.set_palette(&palette).context(LodePNGSnafu {
232 category: "png_encoder",
233 })?;
234
235 let buf = enc
236 .encode(&pixels, self.width, self.height)
237 .context(LodePNGSnafu {
238 category: "png_encode",
239 })?;
240
241 Ok(buf)
242 }
243 pub fn to_webp(&self) -> Result<Vec<u8>> {
245 let mut w = Vec::new();
246
247 let img = webp::WebPEncoder::new_lossless(&mut w);
248
249 img.encode(
250 self.buffer.as_bytes(),
251 self.width as u32,
252 self.height as u32,
253 image::ColorType::Rgba8.into(),
254 )
255 .context(ImageSnafu {
256 category: "webp_encode",
257 })?;
258
259 Ok(w)
260 }
261 pub fn to_avif(&self, quality: u8, speed: u8) -> Result<Vec<u8>> {
265 let mut w = Vec::new();
266 let mut sp = speed;
267 if sp == 0 {
268 sp = 3;
269 }
270
271 let img = avif::AvifEncoder::new_with_speed_quality(&mut w, sp, quality);
272 img.write_image(
273 self.buffer.as_bytes(),
274 self.width as u32,
275 self.height as u32,
276 image::ColorType::Rgba8.into(),
277 )
278 .context(ImageSnafu {
279 category: "avif_encode",
280 })?;
281
282 Ok(w)
283 }
284 pub fn to_mozjpeg(&self, quality: u8) -> Result<Vec<u8>> {
286 let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB);
287 comp.set_size(self.width, self.height);
288 comp.set_quality(quality as f32);
289 let mut comp = comp.start_compress(Vec::new()).context(IoSnafu {})?;
290 comp.write_scanlines(self.get_rgb8().as_bytes())
291 .context(IoSnafu {})?;
292 let data = comp.finish().context(IoSnafu {})?;
293 Ok(data)
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::{load, ImageInfo};
300 use pretty_assertions::assert_eq;
301
302 use std::io::Cursor;
303 fn load_image() -> ImageInfo {
304 let data = include_bytes!("../assets/rust-logo.png");
305 load(Cursor::new(data), "png").unwrap()
306 }
307
308 #[test]
309 fn test_load_image() {
310 let img = load_image();
311 assert_eq!(img.height, 144);
312 assert_eq!(img.width, 144);
313 }
314 #[test]
315 fn test_to_png() {
316 let img = load_image();
317 let result = img.to_png(90).unwrap();
318 assert_eq!(result.len(), 1665);
320 }
321 #[test]
322 fn test_to_webp() {
323 let img = load_image();
324 let result = img.to_webp().unwrap();
325 assert_eq!(result.len(), 2764);
326 }
327 #[test]
328 fn test_to_jpeg() {
329 let img = load_image();
330 let result = img.to_mozjpeg(90).unwrap();
331 assert_eq!(result.len(), 392);
332 }
333 #[test]
334 fn test_to_avif() {
335 let img = load_image();
336 let result = img.to_avif(90, 3).unwrap();
337 assert_eq!(result.len(), 2345);
338 }
339}