libwebp_image/
lib.rs

1// Copyright 2020-2022 Masaki Hara
2// Licensed under MIT or Apache-2.0.
3// See LICENSE and LICENSE-Apache-2.0 in the repository.
4
5use std::io::{self, Read, Write};
6use std::ops::Deref;
7
8use image::error::{DecodingError, EncodingError, ImageFormatHint};
9use image::{
10    ColorType, DynamicImage, ImageBuffer, ImageDecoder, ImageError, ImageResult, Rgb, RgbImage,
11    Rgba, RgbaImage,
12};
13use libwebp::boxed::WebpBox;
14
15#[derive(Debug)]
16pub struct WebpReader<R: Read> {
17    reader: Reader<R>,
18    index: usize,
19}
20
21impl<R: Read> WebpReader<R> {
22    fn new(reader: Reader<R>) -> Self {
23        Self { reader, index: 0 }
24    }
25}
26
27impl<R: Read> Read for WebpReader<R> {
28    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
29        let (_, _, _, image_buf) = self.reader.data().unwrap();
30        let new_index = (self.index + buf.len()).min(image_buf.len());
31        let num_written = new_index - self.index;
32        buf[..num_written].copy_from_slice(&image_buf[self.index..new_index]);
33        self.index = new_index;
34        Ok(num_written)
35    }
36}
37
38#[derive(Debug)]
39pub struct WebpDecoder<R: Read> {
40    reader: Reader<R>,
41}
42
43#[allow(clippy::upper_case_acronyms)]
44#[derive(Debug)]
45enum WebpColor {
46    RGB,
47    RGBA,
48}
49
50impl<R: Read> WebpDecoder<R> {
51    pub fn new(reader: R) -> ImageResult<Self> {
52        Self::new_inner(reader, WebpColor::RGBA)
53    }
54
55    pub fn new_rgba(reader: R) -> ImageResult<Self> {
56        Self::new_inner(reader, WebpColor::RGBA)
57    }
58
59    pub fn new_rgb(reader: R) -> ImageResult<Self> {
60        Self::new_inner(reader, WebpColor::RGB)
61    }
62
63    fn new_inner(reader: R, colortype: WebpColor) -> ImageResult<Self> {
64        let mut reader = Reader::new(reader, colortype);
65        reader.read_info()?;
66        Ok(Self { reader })
67    }
68}
69
70impl<'a, R: Read + 'a> ImageDecoder<'a> for WebpDecoder<R> {
71    type Reader = WebpReader<R>;
72
73    fn dimensions(&self) -> (u32, u32) {
74        self.reader.info().unwrap()
75    }
76    fn color_type(&self) -> ColorType {
77        match self.reader.colortype {
78            WebpColor::RGB => ColorType::Rgb8,
79            WebpColor::RGBA => ColorType::Rgba8,
80        }
81    }
82    fn into_reader(mut self) -> ImageResult<Self::Reader> {
83        self.reader.read_data()?;
84        Ok(WebpReader::new(self.reader))
85    }
86}
87
88const READER_READ_UNIT: usize = 1024;
89
90#[derive(Debug)]
91struct Reader<R: Read> {
92    reader: R,
93    colortype: WebpColor,
94    buf: Vec<u8>,
95    info: Option<(u32, u32)>,
96    data: Option<(u32, u32, u32, WebpBox<[u8]>)>,
97}
98
99impl<R: Read> Reader<R> {
100    fn new(reader: R, colortype: WebpColor) -> Self {
101        Self {
102            reader,
103            colortype,
104            buf: Vec::new(),
105            info: None,
106            data: None,
107        }
108    }
109
110    fn info(&self) -> Option<(u32, u32)> {
111        self.info
112    }
113
114    fn read_info(&mut self) -> io::Result<()> {
115        if self.info.is_some() {
116            return Ok(());
117        }
118        loop {
119            let read_len = self.read_into_buf(READER_READ_UNIT)?;
120            if let Ok(info) = libwebp::WebPGetInfo(&self.buf) {
121                self.info = Some(info);
122                return Ok(());
123            }
124            if read_len == 0 {
125                return Err(io::Error::new(
126                    io::ErrorKind::InvalidInput,
127                    "Invalid webp header",
128                ));
129            }
130        }
131    }
132
133    fn data(&self) -> Option<(u32, u32, u32, &[u8])> {
134        let (w, h, s, ref buf) = *self.data.as_ref()?;
135        Some((w, h, s, buf))
136    }
137
138    fn read_data(&mut self) -> io::Result<()> {
139        self.read_info()?;
140        if self.data.is_some() {
141            return Ok(());
142        }
143
144        self.reader.read_to_end(&mut self.buf)?;
145        let data = match self.colortype {
146            WebpColor::RGB => libwebp::WebPDecodeRGB(&self.buf).map(|(w, h, b)| (w, h, w * 3, b)),
147            WebpColor::RGBA => libwebp::WebPDecodeRGBA(&self.buf).map(|(w, h, b)| (w, h, w * 4, b)),
148        }
149        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Invalid webp data"))?;
150        self.data = Some(data);
151        Ok(())
152    }
153
154    fn read_into_buf(&mut self, by: usize) -> io::Result<usize> {
155        let old_len = self.buf.len();
156        self.buf.resize(old_len + by, 0);
157        let result = self.reader.read(&mut self.buf[old_len..]);
158        self.buf.resize(old_len + result.as_ref().unwrap_or(&0), 0);
159        result
160    }
161}
162
163pub fn webp_load<R: Read>(r: R) -> ImageResult<DynamicImage> {
164    Ok(DynamicImage::ImageRgba8(webp_load_rgba(r)?))
165}
166
167pub fn webp_load_rgba<R: Read>(mut r: R) -> ImageResult<RgbaImage> {
168    let mut buf = Vec::new();
169    r.read_to_end(&mut buf)?;
170    webp_load_rgba_from_memory(&buf)
171}
172
173pub fn webp_load_rgb<R: Read>(mut r: R) -> ImageResult<RgbImage> {
174    let mut buf = Vec::new();
175    r.read_to_end(&mut buf)?;
176    webp_load_rgb_from_memory(&buf)
177}
178
179pub fn webp_load_from_memory(buf: &[u8]) -> ImageResult<DynamicImage> {
180    Ok(DynamicImage::ImageRgba8(webp_load_rgba_from_memory(buf)?))
181}
182
183pub fn webp_load_rgba_from_memory(buf: &[u8]) -> ImageResult<RgbaImage> {
184    let (width, height, buf) = libwebp::WebPDecodeRGBA(buf)
185        .map_err(|_| DecodingError::new(ImageFormatHint::Unknown, "Webp Format Error".to_string()))
186        .map_err(ImageError::Decoding)?;
187    Ok(ImageBuffer::from_raw(width, height, buf.to_vec()).unwrap())
188}
189
190pub fn webp_load_rgb_from_memory(buf: &[u8]) -> ImageResult<RgbImage> {
191    let (width, height, buf) = libwebp::WebPDecodeRGB(buf)
192        .map_err(|_| DecodingError::new(ImageFormatHint::Unknown, "Webp Format Error".to_string()))
193        .map_err(ImageError::Decoding)?;
194    Ok(ImageBuffer::from_raw(width, height, buf.to_vec()).unwrap())
195}
196
197pub fn webp_write<W: Write>(img: &DynamicImage, w: &mut W) -> ImageResult<()> {
198    match img {
199        DynamicImage::ImageRgb8(img) => webp_write_rgb(img, w),
200        DynamicImage::ImageRgba8(img) => webp_write_rgba(img, w),
201        DynamicImage::ImageLuma8(_) => webp_write_rgb(&img.to_rgb8(), w),
202        DynamicImage::ImageLumaA8(_) => webp_write_rgba(&img.to_rgba8(), w),
203        DynamicImage::ImageRgb16(_) => webp_write_rgb(&img.to_rgb8(), w),
204        DynamicImage::ImageRgba16(_) => webp_write_rgba(&img.to_rgba8(), w),
205        DynamicImage::ImageLuma16(_) => webp_write_rgb(&img.to_rgb8(), w),
206        DynamicImage::ImageLumaA16(_) => webp_write_rgba(&img.to_rgba8(), w),
207        DynamicImage::ImageRgb32F(_) => webp_write_rgb(&img.to_rgb8(), w),
208        DynamicImage::ImageRgba32F(_) => webp_write_rgba(&img.to_rgba8(), w),
209        _ => webp_write_rgba(&img.to_rgba8(), w),
210    }
211}
212
213pub fn webp_write_rgba<W: Write, C>(img: &ImageBuffer<Rgba<u8>, C>, w: &mut W) -> ImageResult<()>
214where
215    C: Deref<Target = [u8]>,
216{
217    let buf = libwebp::WebPEncodeRGBA(&img, img.width(), img.height(), img.width() * 4, 75.0)
218        .map_err(|_| EncodingError::new(ImageFormatHint::Unknown, "Webp Format Error".to_string()))
219        .map_err(ImageError::Encoding)?;
220    w.write_all(&buf)?;
221    Ok(())
222}
223
224pub fn webp_write_rgb<W: Write, C>(img: &ImageBuffer<Rgb<u8>, C>, w: &mut W) -> ImageResult<()>
225where
226    C: Deref<Target = [u8]>,
227{
228    let buf = libwebp::WebPEncodeRGB(&img, img.width(), img.height(), img.width() * 3, 75.0)
229        .map_err(|_| EncodingError::new(ImageFormatHint::Unknown, "Webp Format Error".to_string()))
230        .map_err(ImageError::Encoding)?;
231    w.write_all(&buf)?;
232    Ok(())
233}