Skip to main content

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