jpeg_encoder/
lib.rs

1//! # JPEG encoder
2//!
3//! ## Using the encoder
4//! ```no_run
5//! # use jpeg_encoder::EncodingError;
6//! # pub fn main() -> Result<(), EncodingError> {
7//! use jpeg_encoder::{Encoder, ColorType};
8//!
9//! // An array with 4 pixels in RGB format.
10//! let data = [
11//!     255,0,0,
12//!     0,255,0,
13//!     0,0,255,
14//!     255,255,255,
15//! ];
16//!
17//! // Create new encoder that writes to a file with maximum quality (100)
18//! let mut encoder = Encoder::new_file("some.jpeg", 100)?;
19//!
20//! // Encode the data with dimension 2x2
21//! encoder.encode(&data, 2, 2, ColorType::Rgb)?;
22//! # Ok(())
23//! # }
24
25#![no_std]
26#![cfg_attr(not(feature = "simd"), forbid(unsafe_code))]
27
28#[cfg(feature = "std")]
29extern crate std;
30
31extern crate alloc;
32extern crate core;
33
34#[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
35mod avx2;
36mod encoder;
37mod error;
38mod fdct;
39mod huffman;
40mod image_buffer;
41mod marker;
42mod quantization;
43mod writer;
44
45pub use encoder::{ColorType, Encoder, JpegColorType, SamplingFactor};
46pub use error::EncodingError;
47pub use image_buffer::{cmyk_to_ycck, rgb_to_ycbcr, ImageBuffer};
48pub use quantization::QuantizationTableType;
49pub use writer::{Density, JfifWrite};
50
51#[cfg(test)]
52mod tests {
53    use crate::image_buffer::rgb_to_ycbcr;
54    use crate::{ColorType, Encoder, QuantizationTableType, SamplingFactor};
55    use jpeg_decoder::{Decoder, ImageInfo, PixelFormat};
56
57    use alloc::boxed::Box;
58    use alloc::vec;
59    use alloc::vec::Vec;
60
61    fn create_test_img_rgb() -> (Vec<u8>, u16, u16) {
62        // Ensure size which which ensures an odd MCU count per row to test chroma subsampling
63        let width = 258;
64        let height = 128;
65
66        let mut data = Vec::with_capacity(width * height * 3);
67
68        for y in 0..height {
69            for x in 0..width {
70                let x = x.min(255);
71                data.push(x as u8);
72                data.push((y * 2) as u8);
73                data.push(((x + y * 2) / 2) as u8);
74            }
75        }
76
77        (data, width as u16, height as u16)
78    }
79
80    fn create_test_img_rgba() -> (Vec<u8>, u16, u16) {
81        // Ensure size which which ensures an odd MCU count per row to test chroma subsampling
82        let width = 258;
83        let height = 128;
84
85        let mut data = Vec::with_capacity(width * height * 3);
86
87        for y in 0..height {
88            for x in 0..width {
89                let x = x.min(255);
90                data.push(x as u8);
91                data.push((y * 2) as u8);
92                data.push(((x + y * 2) / 2) as u8);
93                data.push(x as u8);
94            }
95        }
96
97        (data, width as u16, height as u16)
98    }
99
100    fn create_test_img_gray() -> (Vec<u8>, u16, u16) {
101        let width = 258;
102        let height = 128;
103
104        let mut data = Vec::with_capacity(width * height);
105
106        for y in 0..height {
107            for x in 0..width {
108                let x = x.min(255);
109                let (y, _, _) = rgb_to_ycbcr(x as u8, (y * 2) as u8, ((x + y * 2) / 2) as u8);
110                data.push(y);
111            }
112        }
113
114        (data, width as u16, height as u16)
115    }
116
117    fn create_test_img_cmyk() -> (Vec<u8>, u16, u16) {
118        let width = 258;
119        let height = 192;
120
121        let mut data = Vec::with_capacity(width * height * 4);
122
123        for y in 0..height {
124            for x in 0..width {
125                let x = x.min(255);
126                data.push(x as u8);
127                data.push((y * 3 / 2) as u8);
128                data.push(((x + y * 3 / 2) / 2) as u8);
129                data.push((255 - (x + y) / 2) as u8);
130            }
131        }
132
133        (data, width as u16, height as u16)
134    }
135
136    fn decode(data: &[u8]) -> (Vec<u8>, ImageInfo) {
137        let mut decoder = Decoder::new(data);
138
139        (decoder.decode().unwrap(), decoder.info().unwrap())
140    }
141
142    fn check_result(
143        data: Vec<u8>,
144        width: u16,
145        height: u16,
146        result: &mut Vec<u8>,
147        pixel_format: PixelFormat,
148    ) {
149        let (img, info) = decode(&result);
150
151        assert_eq!(info.pixel_format, pixel_format);
152        assert_eq!(info.width, width);
153        assert_eq!(info.height, height);
154        assert_eq!(img.len(), data.len());
155
156        for (i, (&v1, &v2)) in data.iter().zip(img.iter()).enumerate() {
157            let diff = (v1 as i16 - v2 as i16).abs();
158            assert!(
159                diff < 20,
160                "Large color diff at index: {}: {} vs {}",
161                i,
162                v1,
163                v2
164            );
165        }
166    }
167
168    #[test]
169    fn test_gray_100() {
170        let (data, width, height) = create_test_img_gray();
171
172        let mut result = Vec::new();
173        let encoder = Encoder::new(&mut result, 100);
174        encoder
175            .encode(&data, width, height, ColorType::Luma)
176            .unwrap();
177
178        check_result(data, width, height, &mut result, PixelFormat::L8);
179    }
180
181    #[test]
182    fn test_rgb_100() {
183        let (data, width, height) = create_test_img_rgb();
184
185        let mut result = Vec::new();
186        let encoder = Encoder::new(&mut result, 100);
187        encoder
188            .encode(&data, width, height, ColorType::Rgb)
189            .unwrap();
190
191        check_result(data, width, height, &mut result, PixelFormat::RGB24);
192    }
193
194    #[test]
195    fn test_rgb_80() {
196        let (data, width, height) = create_test_img_rgb();
197
198        let mut result = Vec::new();
199        let encoder = Encoder::new(&mut result, 80);
200        encoder
201            .encode(&data, width, height, ColorType::Rgb)
202            .unwrap();
203
204        check_result(data, width, height, &mut result, PixelFormat::RGB24);
205    }
206
207    #[test]
208    fn test_rgba_80() {
209        let (data, width, height) = create_test_img_rgba();
210
211        let mut result = Vec::new();
212        let encoder = Encoder::new(&mut result, 80);
213        encoder
214            .encode(&data, width, height, ColorType::Rgba)
215            .unwrap();
216
217        let (data, width, height) = create_test_img_rgb();
218
219        check_result(data, width, height, &mut result, PixelFormat::RGB24);
220    }
221
222    #[test]
223    fn test_rgb_custom_q_table() {
224        let (data, width, height) = create_test_img_rgb();
225
226        let mut result = Vec::new();
227        let mut encoder = Encoder::new(&mut result, 100);
228
229        let table = QuantizationTableType::Custom(Box::new([
230            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
231            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
232            1, 1, 1, 1, 1, 1,
233        ]));
234
235        encoder.set_quantization_tables(table.clone(), table);
236
237        encoder
238            .encode(&data, width, height, ColorType::Rgb)
239            .unwrap();
240
241        check_result(data, width, height, &mut result, PixelFormat::RGB24);
242    }
243
244    #[test]
245    fn test_rgb_2_2() {
246        let (data, width, height) = create_test_img_rgb();
247
248        let mut result = Vec::new();
249        let mut encoder = Encoder::new(&mut result, 100);
250        encoder.set_sampling_factor(SamplingFactor::F_2_2);
251        encoder
252            .encode(&data, width, height, ColorType::Rgb)
253            .unwrap();
254
255        check_result(data, width, height, &mut result, PixelFormat::RGB24);
256    }
257
258    #[test]
259    fn test_rgb_2_1() {
260        let (data, width, height) = create_test_img_rgb();
261
262        let mut result = Vec::new();
263        let mut encoder = Encoder::new(&mut result, 100);
264        encoder.set_sampling_factor(SamplingFactor::F_2_1);
265        encoder
266            .encode(&data, width, height, ColorType::Rgb)
267            .unwrap();
268
269        check_result(data, width, height, &mut result, PixelFormat::RGB24);
270    }
271
272    #[test]
273    fn test_rgb_4_1() {
274        let (data, width, height) = create_test_img_rgb();
275
276        let mut result = Vec::new();
277        let mut encoder = Encoder::new(&mut result, 100);
278        encoder.set_sampling_factor(SamplingFactor::F_4_1);
279        encoder
280            .encode(&data, width, height, ColorType::Rgb)
281            .unwrap();
282
283        check_result(data, width, height, &mut result, PixelFormat::RGB24);
284    }
285
286    #[test]
287    fn test_rgb_1_1() {
288        let (data, width, height) = create_test_img_rgb();
289
290        let mut result = Vec::new();
291        let mut encoder = Encoder::new(&mut result, 100);
292        encoder.set_sampling_factor(SamplingFactor::F_1_1);
293        encoder
294            .encode(&data, width, height, ColorType::Rgb)
295            .unwrap();
296
297        check_result(data, width, height, &mut result, PixelFormat::RGB24);
298    }
299
300    #[test]
301    fn test_rgb_1_4() {
302        let (data, width, height) = create_test_img_rgb();
303
304        let mut result = Vec::new();
305        let mut encoder = Encoder::new(&mut result, 100);
306        encoder.set_sampling_factor(SamplingFactor::F_1_4);
307        encoder
308            .encode(&data, width, height, ColorType::Rgb)
309            .unwrap();
310
311        check_result(data, width, height, &mut result, PixelFormat::RGB24);
312    }
313
314    #[test]
315    fn test_rgb_progressive() {
316        let (data, width, height) = create_test_img_rgb();
317
318        let mut result = Vec::new();
319        let mut encoder = Encoder::new(&mut result, 100);
320        encoder.set_sampling_factor(SamplingFactor::F_2_1);
321        encoder.set_progressive(true);
322
323        encoder
324            .encode(&data, width, height, ColorType::Rgb)
325            .unwrap();
326
327        check_result(data, width, height, &mut result, PixelFormat::RGB24);
328    }
329
330    #[test]
331    fn test_rgb_optimized() {
332        let (data, width, height) = create_test_img_rgb();
333
334        let mut result = Vec::new();
335        let mut encoder = Encoder::new(&mut result, 100);
336        encoder.set_sampling_factor(SamplingFactor::F_2_2);
337        encoder.set_optimized_huffman_tables(true);
338
339        encoder
340            .encode(&data, width, height, ColorType::Rgb)
341            .unwrap();
342
343        check_result(data, width, height, &mut result, PixelFormat::RGB24);
344    }
345
346    #[test]
347    fn test_rgb_optimized_progressive() {
348        let (data, width, height) = create_test_img_rgb();
349
350        let mut result = Vec::new();
351        let mut encoder = Encoder::new(&mut result, 100);
352        encoder.set_sampling_factor(SamplingFactor::F_2_1);
353        encoder.set_progressive(true);
354        encoder.set_optimized_huffman_tables(true);
355
356        encoder
357            .encode(&data, width, height, ColorType::Rgb)
358            .unwrap();
359
360        check_result(data, width, height, &mut result, PixelFormat::RGB24);
361    }
362
363    #[test]
364    fn test_cmyk() {
365        let (data, width, height) = create_test_img_cmyk();
366
367        let mut result = Vec::new();
368        let encoder = Encoder::new(&mut result, 100);
369        encoder
370            .encode(&data, width, height, ColorType::Cmyk)
371            .unwrap();
372
373        check_result(data, width, height, &mut result, PixelFormat::CMYK32);
374    }
375
376    #[test]
377    fn test_ycck() {
378        let (data, width, height) = create_test_img_cmyk();
379
380        let mut result = Vec::new();
381        let encoder = Encoder::new(&mut result, 100);
382        encoder
383            .encode(&data, width, height, ColorType::CmykAsYcck)
384            .unwrap();
385
386        check_result(data, width, height, &mut result, PixelFormat::CMYK32);
387    }
388
389    #[test]
390    fn test_restart_interval() {
391        let (data, width, height) = create_test_img_rgb();
392
393        let mut result = Vec::new();
394        let mut encoder = Encoder::new(&mut result, 100);
395
396        encoder.set_restart_interval(32);
397        const DRI_DATA: &[u8; 6] = b"\xFF\xDD\0\x04\0\x20";
398
399        encoder
400            .encode(&data, width, height, ColorType::Rgb)
401            .unwrap();
402
403        assert!(result
404            .as_slice()
405            .windows(DRI_DATA.len())
406            .any(|w| w == DRI_DATA));
407
408        check_result(data, width, height, &mut result, PixelFormat::RGB24);
409    }
410
411    #[test]
412    fn test_restart_interval_4_1() {
413        let (data, width, height) = create_test_img_rgb();
414
415        let mut result = Vec::new();
416        let mut encoder = Encoder::new(&mut result, 100);
417        encoder.set_sampling_factor(SamplingFactor::F_4_1);
418
419        encoder.set_restart_interval(32);
420        const DRI_DATA: &[u8; 6] = b"\xFF\xDD\0\x04\0\x20";
421
422        encoder
423            .encode(&data, width, height, ColorType::Rgb)
424            .unwrap();
425
426        assert!(result
427            .as_slice()
428            .windows(DRI_DATA.len())
429            .any(|w| w == DRI_DATA));
430
431        check_result(data, width, height, &mut result, PixelFormat::RGB24);
432    }
433
434    #[test]
435    fn test_restart_interval_progressive() {
436        let (data, width, height) = create_test_img_rgb();
437
438        let mut result = Vec::new();
439        let mut encoder = Encoder::new(&mut result, 85);
440        encoder.set_progressive(true);
441
442        encoder.set_restart_interval(32);
443        const DRI_DATA: &[u8; 6] = b"\xFF\xDD\0\x04\0\x20";
444
445        encoder
446            .encode(&data, width, height, ColorType::Rgb)
447            .unwrap();
448
449        assert!(result
450            .as_slice()
451            .windows(DRI_DATA.len())
452            .any(|w| w == DRI_DATA));
453
454        check_result(data, width, height, &mut result, PixelFormat::RGB24);
455    }
456
457    #[test]
458    fn test_app_segment() {
459        let (data, width, height) = create_test_img_rgb();
460
461        let mut result = Vec::new();
462        let mut encoder = Encoder::new(&mut result, 100);
463
464        encoder.add_app_segment(15, b"HOHOHO\0").unwrap();
465
466        encoder
467            .encode(&data, width, height, ColorType::Rgb)
468            .unwrap();
469
470        let segment_data = b"\xEF\0\x09HOHOHO\0";
471
472        assert!(result
473            .as_slice()
474            .windows(segment_data.len())
475            .any(|w| w == segment_data));
476    }
477
478    #[test]
479    fn test_icc_profile() {
480        let (data, width, height) = create_test_img_rgb();
481
482        let mut result = Vec::new();
483        let mut encoder = Encoder::new(&mut result, 100);
484
485        let mut icc = Vec::with_capacity(128 * 1024);
486
487        for i in 0..128 * 1024 {
488            icc.push((i % 255) as u8);
489        }
490
491        encoder.add_icc_profile(&icc).unwrap();
492
493        encoder
494            .encode(&data, width, height, ColorType::Rgb)
495            .unwrap();
496
497        const MARKER: &[u8; 12] = b"ICC_PROFILE\0";
498
499        assert!(result.as_slice().windows(MARKER.len()).any(|w| w == MARKER));
500
501        let mut decoder = Decoder::new(result.as_slice());
502
503        decoder.decode().unwrap();
504
505        let icc_out = match decoder.icc_profile() {
506            Some(icc) => icc,
507            None => panic!("Missing icc profile"),
508        };
509
510        assert_eq!(icc, icc_out);
511    }
512
513    #[test]
514    fn test_rgb_optimized_missing_table_frequency() {
515        let data = vec![0xfb, 0x15, 0x15];
516
517        let mut result = Vec::new();
518        let mut encoder = Encoder::new(&mut result, 100);
519        encoder.set_sampling_factor(SamplingFactor::F_2_2);
520        encoder.set_optimized_huffman_tables(true);
521
522        encoder.encode(&data, 1, 1, ColorType::Rgb).unwrap();
523
524        check_result(data, 1, 1, &mut result, PixelFormat::RGB24);
525    }
526}