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