1#![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 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 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}