1use crate::error::WebpError;
2#[cfg(feature = "image-codecs")]
3use crate::image_api::encode_dynamic_image;
4use crate::options::WebpOptions;
5
6pub(crate) const PNG_SIG: &[u8; 8] = b"\x89PNG\r\n\x1a\n";
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub enum InputFormat {
10 Jpeg,
11 Png,
12}
13
14pub fn detect_format(input: &[u8]) -> Option<InputFormat> {
15 if input.starts_with(PNG_SIG) {
16 Some(InputFormat::Png)
17 } else if input.len() >= 3 && input[0] == 0xff && input[1] == 0xd8 && input[2] == 0xff {
18 Some(InputFormat::Jpeg)
19 } else {
20 None
21 }
22}
23
24pub fn convert_bytes_to_webp(input: &[u8], options: WebpOptions) -> Result<Vec<u8>, WebpError> {
25 convert_bytes_to_webp_impl(input, options)
26}
27
28#[cfg(feature = "image-codecs")]
29fn convert_bytes_to_webp_impl(input: &[u8], options: WebpOptions) -> Result<Vec<u8>, WebpError> {
30 let image_format = match detect_format(input).ok_or(WebpError::UnsupportedFormat)? {
31 InputFormat::Jpeg => image::ImageFormat::Jpeg,
32 InputFormat::Png => image::ImageFormat::Png,
33 };
34 let image =
35 image::load_from_memory_with_format(input, image_format).map_err(
36 |err| match image_format {
37 image::ImageFormat::Jpeg => WebpError::DecodeJpeg(err.to_string()),
38 image::ImageFormat::Png => WebpError::DecodePng(err.to_string()),
39 _ => WebpError::UnsupportedFormat,
40 },
41 )?;
42
43 encode_dynamic_image(&image, options)
44}
45
46#[cfg(not(feature = "image-codecs"))]
47fn convert_bytes_to_webp_impl(_input: &[u8], _options: WebpOptions) -> Result<Vec<u8>, WebpError> {
48 Err(WebpError::UnsupportedFormat)
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn detects_formats() {
57 assert_eq!(detect_format(PNG_SIG), Some(InputFormat::Png));
58 assert_eq!(
59 detect_format(&[0xff, 0xd8, 0xff, 0xdb]),
60 Some(InputFormat::Jpeg)
61 );
62 assert_eq!(detect_format(b"not an image"), None);
63 }
64}