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