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