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::{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 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 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}