image/codecs/webp/
encoder.rs1use std::io::Write;
4
5use crate::error::{EncodingError, UnsupportedError, UnsupportedErrorKind};
6use crate::{DynamicImage, ExtendedColorType, ImageEncoder, ImageError, ImageFormat, ImageResult};
7
8pub struct WebPEncoder<W> {
27 inner: image_webp::WebPEncoder<W>,
28}
29
30impl<W: Write> WebPEncoder<W> {
31 pub fn new_lossless(w: W) -> Self {
35 Self {
36 inner: image_webp::WebPEncoder::new(w),
37 }
38 }
39
40 #[track_caller]
48 pub fn encode(
49 self,
50 buf: &[u8],
51 width: u32,
52 height: u32,
53 color_type: ExtendedColorType,
54 ) -> ImageResult<()> {
55 let expected_buffer_len = color_type.buffer_size(width, height);
56 assert_eq!(
57 expected_buffer_len,
58 buf.len() as u64,
59 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
60 buf.len(),
61 );
62
63 let color_type = match color_type {
64 ExtendedColorType::L8 => image_webp::ColorType::L8,
65 ExtendedColorType::La8 => image_webp::ColorType::La8,
66 ExtendedColorType::Rgb8 => image_webp::ColorType::Rgb8,
67 ExtendedColorType::Rgba8 => image_webp::ColorType::Rgba8,
68 _ => {
69 return Err(ImageError::Unsupported(
70 UnsupportedError::from_format_and_kind(
71 ImageFormat::WebP.into(),
72 UnsupportedErrorKind::Color(color_type),
73 ),
74 ))
75 }
76 };
77
78 self.inner
79 .encode(buf, width, height, color_type)
80 .map_err(ImageError::from_webp_encode)
81 }
82}
83
84impl<W: Write> ImageEncoder for WebPEncoder<W> {
85 #[track_caller]
86 fn write_image(
87 self,
88 buf: &[u8],
89 width: u32,
90 height: u32,
91 color_type: ExtendedColorType,
92 ) -> ImageResult<()> {
93 self.encode(buf, width, height, color_type)
94 }
95
96 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
97 self.inner.set_icc_profile(icc_profile);
98 Ok(())
99 }
100
101 fn set_exif_metadata(&mut self, exif: Vec<u8>) -> Result<(), UnsupportedError> {
102 self.inner.set_exif_metadata(exif);
103 Ok(())
104 }
105
106 fn make_compatible_img(
107 &self,
108 _: crate::io::encoder::MethodSealedToImage,
109 img: &DynamicImage,
110 ) -> Option<DynamicImage> {
111 crate::io::encoder::dynimage_conversion_8bit(img)
112 }
113}
114
115impl ImageError {
116 fn from_webp_encode(e: image_webp::EncodingError) -> Self {
117 match e {
118 image_webp::EncodingError::IoError(e) => ImageError::IoError(e),
119 _ => ImageError::Encoding(EncodingError::new(ImageFormat::WebP.into(), e)),
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use crate::{ImageEncoder, RgbaImage};
127
128 #[test]
129 fn write_webp() {
130 let img = RgbaImage::from_raw(10, 6, (0..240).collect()).unwrap();
131
132 let mut output = Vec::new();
133 super::WebPEncoder::new_lossless(&mut output)
134 .write_image(
135 img.inner_pixels(),
136 img.width(),
137 img.height(),
138 crate::ExtendedColorType::Rgba8,
139 )
140 .unwrap();
141
142 let img2 = crate::load_from_memory_with_format(&output, crate::ImageFormat::WebP)
143 .unwrap()
144 .to_rgba8();
145
146 assert_eq!(img, img2);
147 }
148}