zune_image/codecs/
jpeg.rs1#![cfg_attr(feature = "docs", doc(cfg(feature = "jpeg")))]
9#![cfg(feature = "jpeg")]
10use jpeg_encoder::{ColorType, EncodingError, JfifWrite};
18use zune_core::bit_depth::BitDepth;
19use zune_core::bytestream::{ZByteIoError, ZByteReaderTrait, ZByteWriterTrait, ZWriter};
20use zune_core::colorspace::ColorSpace;
21use zune_core::log::warn;
22use zune_core::options::EncoderOptions;
23use zune_jpeg::errors::DecodeErrors;
24pub use zune_jpeg::{ImageInfo, JpegDecoder};
25
26use crate::codecs::{create_options_for_encoder, ImageFormat};
27use crate::errors::{ImageErrors, ImgEncodeErrors};
28use crate::image::Image;
29use crate::metadata::ImageMetadata;
30use crate::traits::{DecodeInto, DecoderTrait, EncoderTrait};
31
32struct TempVt<'a, T: ZByteWriterTrait> {
33 inner: &'a mut ZWriter<T>
34}
35impl<'a, T: ZByteWriterTrait> JfifWrite for TempVt<'a, T> {
36 fn write_all(&mut self, buf: &[u8]) -> Result<(), EncodingError> {
37 self.inner.write_all(buf).map_err(|r| match r {
38 ZByteIoError::StdIoError(e) => EncodingError::IoError(e),
39 r => EncodingError::Write(format!("{:?}", r))
40 })
41 }
42}
43impl<T: ZByteReaderTrait> DecoderTrait for zune_jpeg::JpegDecoder<T> {
44 fn decode(&mut self) -> Result<Image, crate::errors::ImageErrors> {
45 let metadata = self.read_headers()?.unwrap();
46
47 let pixels = self
48 .decode()
49 .map_err(<DecodeErrors as Into<ImageErrors>>::into)?;
50
51 let colorspace = self.output_colorspace().unwrap();
52 let (width, height) = self.dimensions().unwrap();
53
54 let mut image = Image::from_u8(&pixels, width, height, colorspace);
55 image.metadata = metadata;
56 image.metadata.colorspace = self.output_colorspace().unwrap();
57 Ok(image)
58 }
59
60 fn dimensions(&self) -> Option<(usize, usize)> {
61 self.dimensions().map(|dims| (dims.0, dims.1))
62 }
63
64 fn out_colorspace(&self) -> ColorSpace {
65 self.output_colorspace().unwrap()
66 }
67
68 fn name(&self) -> &'static str {
69 "JPEG decoder"
70 }
71
72 fn read_headers(&mut self) -> Result<Option<ImageMetadata>, crate::errors::ImageErrors> {
73 self.decode_headers()
74 .map_err(<DecodeErrors as Into<ImageErrors>>::into)?;
75
76 let (width, height) = self.dimensions().unwrap();
77
78 let mut metadata = ImageMetadata {
79 format: Some(ImageFormat::JPEG),
80 colorspace: self.input_colorspace().unwrap(),
81 depth: BitDepth::Eight,
82 width: width,
83 height: height,
84 ..Default::default()
85 };
86 #[cfg(feature = "metadata")]
87 {
88 if let Some(exif) = self.exif() {
90 metadata.parse_raw_exif(exif)
91 }
92 }
93 if let Some(icc) = self.icc_profile() {
94 metadata.set_icc_chunk(icc);
95 }
96
97 Ok(Some(metadata))
98 }
99}
100
101impl From<zune_jpeg::errors::DecodeErrors> for ImageErrors {
102 fn from(from: zune_jpeg::errors::DecodeErrors) -> Self {
103 let err = format!("jpg: {from:?}");
104
105 ImageErrors::ImageDecodeErrors(err)
106 }
107}
108#[derive(Copy, Clone, Default)]
114pub struct JpegEncoder {
115 options: Option<EncoderOptions>
116}
117
118impl JpegEncoder {
119 pub fn new() -> JpegEncoder {
121 JpegEncoder::default()
122 }
123 pub fn new_with_options(options: EncoderOptions) -> JpegEncoder {
125 JpegEncoder {
126 options: Some(options)
127 }
128 }
129}
130
131impl EncoderTrait for JpegEncoder {
132 fn name(&self) -> &'static str {
133 "jpeg-encoder(vstroebel)"
134 }
135
136 fn encode_inner<T: ZByteWriterTrait>(
137 &mut self, image: &Image, sink: T
138 ) -> Result<usize, ImageErrors> {
139 assert_eq!(
140 image.depth(),
141 BitDepth::Eight,
142 "Unsupported bit depth{:?}",
143 image.depth()
144 );
145 let pixels = &image.flatten_frames::<u8>()[0];
146
147 if let Some(colorspace) = match_colorspace_to_colortype(image.colorspace()) {
148 let max_dims = usize::from(u16::MAX);
149
150 let (width, height) = image.dimensions();
151
152 if (width > max_dims) || (height > max_dims) {
154 let msg = format!(
155 "Too large image dimensions {} x {}, maximum is {} x {}",
156 width, height, max_dims, max_dims
157 );
158 return Err(ImgEncodeErrors::ImageEncodeErrors(msg).into());
159 }
160 let mut writer = ZWriter::new(sink);
161 let temp_c = TempVt { inner: &mut writer };
162 let options = create_options_for_encoder(self.options, image);
165
166 let mut encoder = jpeg_encoder::Encoder::new(temp_c, options.quality());
169
170 encoder.set_progressive(options.jpeg_encode_progressive());
172 encoder.set_optimized_huffman_tables(options.jpeg_optimized_huffman_tables());
173
174 #[cfg(feature = "metadata")]
175 {
176 use exif::experimental::Writer;
177
178 if options.strip_metadata() {
179 } else if let Some(metadata) = &image.metadata.exif {
181 let mut writer = Writer::new();
182 let mut buf = std::io::Cursor::new(b"Exif\x00\x00".to_vec());
184 buf.set_position(6);
186
187 for metadatum in metadata {
188 writer.push_field(metadatum);
189 }
190 let result = writer.write(&mut buf, false);
191 if result.is_ok() {
192 encoder.add_app_segment(1, buf.into_inner())?;
194 } else {
195 warn!("Writing exif failed {:?}", result);
196 }
197 }
198 }
199
200 encoder.encode(pixels, width as u16, height as u16, colorspace)?;
201
202 Ok(writer.bytes_written())
203 } else {
204 Err(ImgEncodeErrors::UnsupportedColorspace(
205 image.colorspace(),
206 self.supported_colorspaces()
207 )
208 .into())
209 }
210 }
211
212 fn supported_colorspaces(&self) -> &'static [ColorSpace] {
213 &[
216 ColorSpace::Luma,
217 ColorSpace::RGB,
218 ColorSpace::RGBA,
219 ColorSpace::YCbCr,
220 ColorSpace::YCCK,
221 ColorSpace::CMYK
222 ]
223 }
224
225 fn format(&self) -> ImageFormat {
226 ImageFormat::JPEG
227 }
228
229 fn supported_bit_depth(&self) -> &'static [BitDepth] {
230 &[BitDepth::Eight]
231 }
232
233 fn default_depth(&self, _: BitDepth) -> BitDepth {
234 BitDepth::Eight
235 }
236
237 fn set_options(&mut self, options: EncoderOptions) {
238 self.options = Some(options)
239 }
240}
241
242const fn match_colorspace_to_colortype(colorspace: ColorSpace) -> Option<ColorType> {
244 match colorspace {
245 ColorSpace::RGBA => Some(ColorType::Rgba),
246 ColorSpace::RGB => Some(ColorType::Rgb),
247 ColorSpace::YCbCr => Some(ColorType::Ycbcr),
248 ColorSpace::Luma => Some(ColorType::Luma),
249 ColorSpace::YCCK => Some(ColorType::Ycck),
250 ColorSpace::CMYK => Some(ColorType::Cmyk),
251 _ => None
252 }
253}
254
255impl From<EncodingError> for ImageErrors {
256 fn from(value: EncodingError) -> Self {
257 ImageErrors::EncodeErrors(ImgEncodeErrors::Generic(value.to_string()))
258 }
259}
260
261impl<T> DecodeInto for JpegDecoder<T>
262where
263 T: ZByteReaderTrait
264{
265 type BufferType = u8;
266
267 fn decode_into(&mut self, buffer: &mut [u8]) -> Result<(), ImageErrors> {
268 self.decode_into(buffer)
269 .map_err(<DecodeErrors as Into<ImageErrors>>::into)?;
270
271 Ok(())
272 }
273
274 fn decode_output_buffer_size(&mut self) -> Result<usize, ImageErrors> {
275 self.decode_headers()
276 .map_err(<DecodeErrors as Into<ImageErrors>>::into)?;
277
278 Ok(self.output_buffer_size().unwrap())
280 }
281}