1use crate::fdct::fdct;
2use crate::huffman::{CodingClass, HuffmanTable};
3use crate::image_buffer::*;
4use crate::marker::Marker;
5use crate::quantization::{QuantizationTable, QuantizationTableType};
6use crate::writer::{JfifWrite, JfifWriter, ZIGZAG};
7use crate::{EncodingError, PixelDensity};
8
9use alloc::vec;
10use alloc::vec::Vec;
11
12#[cfg(feature = "std")]
13use std::io::BufWriter;
14
15#[cfg(feature = "std")]
16use std::fs::File;
17
18#[cfg(feature = "std")]
19use std::path::Path;
20
21#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23pub enum JpegColorType {
24 Luma,
26
27 Ycbcr,
29
30 Cmyk,
32
33 Ycck,
35}
36
37#[derive(Copy, Clone)]
38#[repr(C, align(32))]
39pub(crate) struct AlignedBlock {
40 pub data: [i16; 64],
41}
42
43impl AlignedBlock {
44 pub const fn new(data: [i16; 64]) -> Self {
45 AlignedBlock { data }
46 }
47}
48
49impl Default for AlignedBlock {
50 fn default() -> Self {
51 AlignedBlock { data: [0i16; 64] }
52 }
53}
54
55impl JpegColorType {
56 pub(crate) fn get_num_components(self) -> usize {
57 use JpegColorType::*;
58
59 match self {
60 Luma => 1,
61 Ycbcr => 3,
62 Cmyk | Ycck => 4,
63 }
64 }
65}
66
67#[derive(Copy, Clone, Debug, Eq, PartialEq)]
72pub enum ColorType {
73 Luma,
75
76 Rgb,
78
79 Rgba,
81
82 Bgr,
84
85 Bgra,
87
88 Ycbcr,
90
91 Cmyk,
93
94 CmykAsYcck,
96
97 Ycck,
99}
100
101impl ColorType {
102 pub(crate) fn get_bytes_per_pixel(self) -> usize {
103 use ColorType::*;
104
105 match self {
106 Luma => 1,
107 Rgb | Bgr | Ycbcr => 3,
108 Rgba | Bgra | Cmyk | CmykAsYcck | Ycck => 4,
109 }
110 }
111}
112
113#[repr(u8)]
114#[derive(Copy, Clone, Debug, Eq, PartialEq)]
115#[allow(non_camel_case_types)]
120pub enum SamplingFactor {
121 F_1_1 = 1 << 4 | 1,
122 F_2_1 = 2 << 4 | 1,
123 F_1_2 = 1 << 4 | 2,
124 F_2_2 = 2 << 4 | 2,
125 F_4_1 = 4 << 4 | 1,
126 F_4_2 = 4 << 4 | 2,
127 F_1_4 = 1 << 4 | 4,
128 F_2_4 = 2 << 4 | 4,
129
130 R_4_4_4 = 0x80 | 1 << 4 | 1,
132
133 R_4_4_0 = 0x80 | 1 << 4 | 2,
135
136 R_4_4_1 = 0x80 | 1 << 4 | 4,
138
139 R_4_2_2 = 0x80 | 2 << 4 | 1,
141
142 R_4_2_0 = 0x80 | 2 << 4 | 2,
144
145 R_4_2_1 = 0x80 | 2 << 4 | 4,
147
148 R_4_1_1 = 0x80 | 4 << 4 | 1,
150
151 R_4_1_0 = 0x80 | 4 << 4 | 2,
153}
154
155impl SamplingFactor {
156 pub fn from_factors(horizontal: u8, vertical: u8) -> Option<SamplingFactor> {
158 use SamplingFactor::*;
159
160 match (horizontal, vertical) {
161 (1, 1) => Some(F_1_1),
162 (1, 2) => Some(F_1_2),
163 (1, 4) => Some(F_1_4),
164 (2, 1) => Some(F_2_1),
165 (2, 2) => Some(F_2_2),
166 (2, 4) => Some(F_2_4),
167 (4, 1) => Some(F_4_1),
168 (4, 2) => Some(F_4_2),
169 _ => None,
170 }
171 }
172
173 pub(crate) fn get_sampling_factors(self) -> (u8, u8) {
174 let value = self as u8;
175 ((value >> 4) & 0x07, value & 0xf)
176 }
177
178 pub(crate) fn supports_interleaved(self) -> bool {
179 use SamplingFactor::*;
180
181 matches!(
184 self,
185 F_1_1 | F_2_1 | F_1_2 | F_2_2 | R_4_4_4 | R_4_4_0 | R_4_2_2 | R_4_2_0
186 )
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum ChromaSubsamplingMethod {
193 Nearest,
195 Average,
197}
198
199pub(crate) struct Component {
200 pub id: u8,
201 pub quantization_table: u8,
202 pub dc_huffman_table: u8,
203 pub ac_huffman_table: u8,
204 pub horizontal_sampling_factor: u8,
205 pub vertical_sampling_factor: u8,
206}
207
208macro_rules! add_component {
209 ($components:expr, $id:expr, $dest:expr, $h_sample:expr, $v_sample:expr) => {
210 $components.push(Component {
211 id: $id,
212 quantization_table: $dest,
213 dc_huffman_table: $dest,
214 ac_huffman_table: $dest,
215 horizontal_sampling_factor: $h_sample,
216 vertical_sampling_factor: $v_sample,
217 });
218 };
219}
220
221pub struct Encoder<W: JfifWrite> {
223 writer: JfifWriter<W>,
224 density: PixelDensity,
225 quality: u8,
226
227 components: Vec<Component>,
228 quantization_tables: [QuantizationTableType; 2],
229 huffman_tables: [(HuffmanTable, HuffmanTable); 2],
230
231 sampling_factor: SamplingFactor,
232 chroma_subsampling_method: ChromaSubsamplingMethod,
233
234 progressive_scans: Option<u8>,
235
236 restart_interval: Option<u16>,
237
238 optimize_huffman_table: bool,
239
240 app_segments: Vec<(u8, Vec<u8>)>,
241}
242
243impl<W: JfifWrite> Encoder<W> {
244 pub fn new(w: W, quality: u8) -> Encoder<W> {
250 let huffman_tables = [
251 (
252 HuffmanTable::default_luma_dc(),
253 HuffmanTable::default_luma_ac(),
254 ),
255 (
256 HuffmanTable::default_chroma_dc(),
257 HuffmanTable::default_chroma_ac(),
258 ),
259 ];
260
261 let quantization_tables = [
262 QuantizationTableType::Default,
263 QuantizationTableType::Default,
264 ];
265
266 let sampling_factor = if quality < 90 {
267 SamplingFactor::F_2_2
268 } else {
269 SamplingFactor::F_1_1
270 };
271
272 Encoder {
273 writer: JfifWriter::new(w),
274 density: PixelDensity::default(),
275 quality,
276 components: vec![],
277 quantization_tables,
278 huffman_tables,
279 sampling_factor,
280 chroma_subsampling_method: ChromaSubsamplingMethod::Nearest,
281 progressive_scans: None,
282 restart_interval: None,
283 optimize_huffman_table: false,
284 app_segments: Vec::new(),
285 }
286 }
287
288 pub fn set_density(&mut self, density: PixelDensity) {
292 self.density = density;
293 }
294
295 pub fn density(&self) -> PixelDensity {
297 self.density
298 }
299
300 pub fn set_quality(&mut self, quality: u8) {
302 self.quality = quality;
303 }
304
305 pub fn quality(&self) -> u8 {
307 self.quality
308 }
309
310 pub fn set_sampling_factor(&mut self, sampling: SamplingFactor) {
312 self.sampling_factor = sampling;
313 }
314
315 pub fn sampling_factor(&self) -> SamplingFactor {
317 self.sampling_factor
318 }
319
320 pub fn set_chroma_subsampling_method(&mut self, method: ChromaSubsamplingMethod) {
322 self.chroma_subsampling_method = method;
323 }
324
325 pub fn chroma_subsampling_method(&self) -> ChromaSubsamplingMethod {
327 self.chroma_subsampling_method
328 }
329
330 pub fn set_quantization_tables(
332 &mut self,
333 luma: QuantizationTableType,
334 chroma: QuantizationTableType,
335 ) {
336 self.quantization_tables = [luma, chroma];
337 }
338
339 pub fn quantization_tables(&self) -> &[QuantizationTableType; 2] {
341 &self.quantization_tables
342 }
343
344 pub fn set_progressive(&mut self, progressive: bool) {
349 self.progressive_scans = if progressive { Some(4) } else { None };
350 }
351
352 pub fn set_progressive_scans(&mut self, scans: u8) {
360 assert!(
361 (2..=64).contains(&scans),
362 "Invalid number of scans: {}",
363 scans
364 );
365 self.progressive_scans = Some(scans);
366 }
367
368 pub fn progressive_scans(&self) -> Option<u8> {
370 self.progressive_scans
371 }
372
373 pub fn set_restart_interval(&mut self, interval: u16) {
377 self.restart_interval = if interval == 0 { None } else { Some(interval) };
378 }
379
380 pub fn restart_interval(&self) -> Option<u16> {
382 self.restart_interval
383 }
384
385 pub fn set_optimized_huffman_tables(&mut self, optimize_huffman_table: bool) {
389 self.optimize_huffman_table = optimize_huffman_table;
390 }
391
392 pub fn optimized_huffman_tables(&self) -> bool {
394 self.optimize_huffman_table
395 }
396
397 pub fn add_app_segment(&mut self, segment_nr: u8, data: Vec<u8>) -> Result<(), EncodingError> {
406 if segment_nr == 0 || segment_nr > 15 {
407 Err(EncodingError::InvalidAppSegment(segment_nr))
408 } else if data.len() > 65533 {
409 Err(EncodingError::AppSegmentTooLarge(data.len()))
410 } else {
411 self.app_segments.push((segment_nr, data));
412 Ok(())
413 }
414 }
415
416 pub fn add_icc_profile(&mut self, data: &[u8]) -> Result<(), EncodingError> {
424 const MARKER: &[u8; 12] = b"ICC_PROFILE\0";
428 const MAX_CHUNK_LENGTH: usize = 65535 - 2 - 12 - 2;
429
430 let num_chunks = data.len().div_ceil(MAX_CHUNK_LENGTH);
431
432 if num_chunks >= 255 {
434 return Err(EncodingError::IccTooLarge(data.len()));
435 }
436
437 for (i, data) in data.chunks(MAX_CHUNK_LENGTH).enumerate() {
438 let mut chunk_data = Vec::with_capacity(MAX_CHUNK_LENGTH);
439 chunk_data.extend_from_slice(MARKER);
440 chunk_data.push(i as u8 + 1);
441 chunk_data.push(num_chunks as u8);
442 chunk_data.extend_from_slice(data);
443
444 self.add_app_segment(2, chunk_data)?;
445 }
446
447 Ok(())
448 }
449
450 pub fn add_exif_metadata(&mut self, data: &[u8]) -> Result<(), EncodingError> {
458 const EXIF_HEADER: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00];
461
462 let mut formatted = EXIF_HEADER.to_vec();
463 formatted.extend_from_slice(data);
464
465 self.add_app_segment(1, formatted)
466 }
467
468 pub fn encode(
472 self,
473 data: &[u8],
474 width: u16,
475 height: u16,
476 color_type: ColorType,
477 ) -> Result<(), EncodingError> {
478 let required_data_len = width as usize * height as usize * color_type.get_bytes_per_pixel();
479
480 if data.len() < required_data_len {
481 return Err(EncodingError::BadImageData {
482 length: data.len(),
483 required: required_data_len,
484 });
485 }
486
487 #[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
488 {
489 if std::is_x86_feature_detected!("avx2") {
490 use crate::avx2::*;
491
492 return match color_type {
493 ColorType::Luma => self
494 .encode_image_internal::<_, AVX2Operations>(GrayImage(data, width, height)),
495 ColorType::Rgb => self.encode_image_internal::<_, AVX2Operations>(
496 RgbImageAVX2(data, width, height),
497 ),
498 ColorType::Rgba => self.encode_image_internal::<_, AVX2Operations>(
499 RgbaImageAVX2(data, width, height),
500 ),
501 ColorType::Bgr => self.encode_image_internal::<_, AVX2Operations>(
502 BgrImageAVX2(data, width, height),
503 ),
504 ColorType::Bgra => self.encode_image_internal::<_, AVX2Operations>(
505 BgraImageAVX2(data, width, height),
506 ),
507 ColorType::Ycbcr => self.encode_image_internal::<_, AVX2Operations>(
508 YCbCrImage(data, width, height),
509 ),
510 ColorType::Cmyk => self
511 .encode_image_internal::<_, AVX2Operations>(CmykImage(data, width, height)),
512 ColorType::CmykAsYcck => self.encode_image_internal::<_, AVX2Operations>(
513 CmykAsYcckImage(data, width, height),
514 ),
515 ColorType::Ycck => self
516 .encode_image_internal::<_, AVX2Operations>(YcckImage(data, width, height)),
517 };
518 }
519 }
520
521 match color_type {
522 ColorType::Luma => self.encode_image(GrayImage(data, width, height))?,
523 ColorType::Rgb => self.encode_image(RgbImage(data, width, height))?,
524 ColorType::Rgba => self.encode_image(RgbaImage(data, width, height))?,
525 ColorType::Bgr => self.encode_image(BgrImage(data, width, height))?,
526 ColorType::Bgra => self.encode_image(BgraImage(data, width, height))?,
527 ColorType::Ycbcr => self.encode_image(YCbCrImage(data, width, height))?,
528 ColorType::Cmyk => self.encode_image(CmykImage(data, width, height))?,
529 ColorType::CmykAsYcck => self.encode_image(CmykAsYcckImage(data, width, height))?,
530 ColorType::Ycck => self.encode_image(YcckImage(data, width, height))?,
531 }
532
533 Ok(())
534 }
535
536 pub fn encode_image<I: ImageBuffer>(self, image: I) -> Result<(), EncodingError> {
538 #[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
539 {
540 if std::is_x86_feature_detected!("avx2") {
541 use crate::avx2::*;
542 return self.encode_image_internal::<_, AVX2Operations>(image);
543 }
544 }
545 self.encode_image_internal::<_, DefaultOperations>(image)
546 }
547
548 fn encode_image_internal<I: ImageBuffer, OP: Operations>(
549 mut self,
550 image: I,
551 ) -> Result<(), EncodingError> {
552 if image.width() == 0 || image.height() == 0 {
553 return Err(EncodingError::ZeroImageDimensions {
554 width: image.width(),
555 height: image.height(),
556 });
557 }
558
559 let q_tables = [
560 QuantizationTable::new_with_quality(&self.quantization_tables[0], self.quality, true),
561 QuantizationTable::new_with_quality(&self.quantization_tables[1], self.quality, false),
562 ];
563
564 let jpeg_color_type = image.get_jpeg_color_type();
565 self.init_components(jpeg_color_type);
566
567 self.writer.write_marker(Marker::SOI)?;
568
569 self.writer.write_header(&self.density)?;
570
571 if jpeg_color_type == JpegColorType::Cmyk {
572 let app_14 = b"Adobe\0\0\0\0\0\0\0";
574 self.writer
575 .write_segment(Marker::APP(14), app_14.as_ref())?;
576 } else if jpeg_color_type == JpegColorType::Ycck {
577 let app_14 = b"Adobe\0\0\0\0\0\0\x02";
579 self.writer
580 .write_segment(Marker::APP(14), app_14.as_ref())?;
581 }
582
583 for (nr, data) in &self.app_segments {
584 self.writer.write_segment(Marker::APP(*nr), data)?;
585 }
586
587 if let Some(scans) = self.progressive_scans {
588 self.encode_image_progressive::<_, OP>(image, scans, &q_tables)?;
589 } else if self.optimize_huffman_table || !self.sampling_factor.supports_interleaved() {
590 self.encode_image_sequential::<_, OP>(image, &q_tables)?;
591 } else {
592 self.encode_image_interleaved::<_, OP>(image, &q_tables)?;
593 }
594
595 self.writer.write_marker(Marker::EOI)?;
596
597 Ok(())
598 }
599
600 fn init_components(&mut self, color: JpegColorType) {
601 let (horizontal_sampling_factor, vertical_sampling_factor) =
602 self.sampling_factor.get_sampling_factors();
603
604 match color {
605 JpegColorType::Luma => {
606 add_component!(self.components, 0, 0, 1, 1);
607 }
608 JpegColorType::Ycbcr => {
609 add_component!(
610 self.components,
611 0,
612 0,
613 horizontal_sampling_factor,
614 vertical_sampling_factor
615 );
616 add_component!(self.components, 1, 1, 1, 1);
617 add_component!(self.components, 2, 1, 1, 1);
618 }
619 JpegColorType::Cmyk => {
620 add_component!(self.components, 0, 1, 1, 1);
621 add_component!(self.components, 1, 1, 1, 1);
622 add_component!(self.components, 2, 1, 1, 1);
623 add_component!(
624 self.components,
625 3,
626 0,
627 horizontal_sampling_factor,
628 vertical_sampling_factor
629 );
630 }
631 JpegColorType::Ycck => {
632 add_component!(
633 self.components,
634 0,
635 0,
636 horizontal_sampling_factor,
637 vertical_sampling_factor
638 );
639 add_component!(self.components, 1, 1, 1, 1);
640 add_component!(self.components, 2, 1, 1, 1);
641 add_component!(
642 self.components,
643 3,
644 0,
645 horizontal_sampling_factor,
646 vertical_sampling_factor
647 );
648 }
649 }
650 }
651
652 fn get_max_sampling_size(&self) -> (usize, usize) {
653 let max_h_sampling = self.components.iter().fold(1, |value, component| {
654 value.max(component.horizontal_sampling_factor)
655 });
656
657 let max_v_sampling = self.components.iter().fold(1, |value, component| {
658 value.max(component.vertical_sampling_factor)
659 });
660
661 (usize::from(max_h_sampling), usize::from(max_v_sampling))
662 }
663
664 fn write_frame_header<I: ImageBuffer>(
665 &mut self,
666 image: &I,
667 q_tables: &[QuantizationTable; 2],
668 ) -> Result<(), EncodingError> {
669 self.writer.write_frame_header(
670 image.width(),
671 image.height(),
672 &self.components,
673 self.progressive_scans.is_some(),
674 )?;
675
676 self.writer.write_quantization_segment(0, &q_tables[0])?;
677 self.writer.write_quantization_segment(1, &q_tables[1])?;
678
679 self.writer
680 .write_huffman_segment(CodingClass::Dc, 0, &self.huffman_tables[0].0)?;
681
682 self.writer
683 .write_huffman_segment(CodingClass::Ac, 0, &self.huffman_tables[0].1)?;
684
685 if image.get_jpeg_color_type().get_num_components() >= 3 {
686 self.writer
687 .write_huffman_segment(CodingClass::Dc, 1, &self.huffman_tables[1].0)?;
688
689 self.writer
690 .write_huffman_segment(CodingClass::Ac, 1, &self.huffman_tables[1].1)?;
691 }
692
693 if let Some(restart_interval) = self.restart_interval {
694 self.writer.write_dri(restart_interval)?;
695 }
696
697 Ok(())
698 }
699
700 fn init_rows(&mut self, buffer_size: usize) -> [Vec<u8>; 4] {
701 match self.components.len() {
705 1 => [
706 Vec::with_capacity(buffer_size),
707 Vec::new(),
708 Vec::new(),
709 Vec::new(),
710 ],
711 3 => [
712 Vec::with_capacity(buffer_size),
713 Vec::with_capacity(buffer_size),
714 Vec::with_capacity(buffer_size),
715 Vec::new(),
716 ],
717 4 => [
718 Vec::with_capacity(buffer_size),
719 Vec::with_capacity(buffer_size),
720 Vec::with_capacity(buffer_size),
721 Vec::with_capacity(buffer_size),
722 ],
723 len => unreachable!("Unsupported component length: {}", len),
724 }
725 }
726
727 fn encode_image_interleaved<I: ImageBuffer, OP: Operations>(
731 &mut self,
732 image: I,
733 q_tables: &[QuantizationTable; 2],
734 ) -> Result<(), EncodingError> {
735 self.write_frame_header(&image, q_tables)?;
736 self.writer
737 .write_scan_header(&self.components.iter().collect::<Vec<_>>(), None)?;
738
739 let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size();
740
741 let width = image.width();
742 let height = image.height();
743
744 let num_cols = usize::from(width).div_ceil(8 * max_h_sampling);
745 let num_rows = usize::from(height).div_ceil(8 * max_v_sampling);
746
747 let buffer_width = num_cols * 8 * max_h_sampling;
748 let buffer_size = buffer_width * 8 * max_v_sampling;
749
750 let mut row: [Vec<_>; 4] = self.init_rows(buffer_size);
751
752 let mut prev_dc = [0i16; 4];
753
754 let restart_interval = self.restart_interval.unwrap_or(0);
755 let mut restarts = 0;
756 let mut restarts_to_go = restart_interval;
757
758 for block_y in 0..num_rows {
759 for r in &mut row {
760 r.clear();
761 }
762
763 for y in 0..(8 * max_v_sampling) {
764 let y = y + block_y * 8 * max_v_sampling;
765 let y = (y.min(height as usize - 1)) as u16;
766
767 image.fill_buffers(y, &mut row);
768
769 for _ in usize::from(width)..buffer_width {
770 for channel in &mut row {
771 if !channel.is_empty() {
772 channel.push(channel[channel.len() - 1]);
773 }
774 }
775 }
776 }
777
778 for block_x in 0..num_cols {
779 if restart_interval > 0 && restarts_to_go == 0 {
780 self.writer.finalize_bit_buffer()?;
781 self.writer
782 .write_marker(Marker::RST((restarts % 8) as u8))?;
783
784 prev_dc[0] = 0;
785 prev_dc[1] = 0;
786 prev_dc[2] = 0;
787 prev_dc[3] = 0;
788 }
789
790 for (i, component) in self.components.iter().enumerate() {
791 let h_stride = max_h_sampling / component.horizontal_sampling_factor as usize;
792 let v_stride = max_v_sampling / component.vertical_sampling_factor as usize;
793 let average = self.chroma_subsampling_method
794 == ChromaSubsamplingMethod::Average
795 && (h_stride > 1 || v_stride > 1);
796
797 for v_offset in 0..component.vertical_sampling_factor as usize {
798 for h_offset in 0..component.horizontal_sampling_factor as usize {
799 let bx = block_x * 8 * max_h_sampling + (h_offset * 8);
800 let by = v_offset * 8;
801 let mut block = if average {
802 get_block_averaged(
803 &row[i],
804 bx,
805 by,
806 h_stride,
807 v_stride,
808 buffer_width,
809 )
810 } else {
811 get_block(&row[i], bx, by, h_stride, v_stride, buffer_width)
812 };
813
814 OP::fdct(&mut block);
815
816 let mut q_block = AlignedBlock::default();
817
818 OP::quantize_block(
819 &block,
820 &mut q_block,
821 &q_tables[component.quantization_table as usize],
822 );
823
824 self.writer.write_block(
825 &q_block,
826 prev_dc[i],
827 &self.huffman_tables[component.dc_huffman_table as usize].0,
828 &self.huffman_tables[component.ac_huffman_table as usize].1,
829 )?;
830
831 prev_dc[i] = q_block.data[0];
832 }
833 }
834 }
835
836 if restart_interval > 0 {
837 if restarts_to_go == 0 {
838 restarts_to_go = restart_interval;
839 restarts += 1;
840 restarts &= 7;
841 }
842 restarts_to_go -= 1;
843 }
844 }
845 }
846
847 self.writer.finalize_bit_buffer()?;
848
849 Ok(())
850 }
851
852 fn encode_image_sequential<I: ImageBuffer, OP: Operations>(
854 &mut self,
855 image: I,
856 q_tables: &[QuantizationTable; 2],
857 ) -> Result<(), EncodingError> {
858 let blocks = self.encode_blocks::<_, OP>(&image, q_tables);
859
860 if self.optimize_huffman_table {
861 self.optimize_huffman_table(&blocks);
862 }
863
864 self.write_frame_header(&image, q_tables)?;
865
866 for (i, component) in self.components.iter().enumerate() {
867 let restart_interval = self.restart_interval.unwrap_or(0);
868 let mut restarts = 0;
869 let mut restarts_to_go = restart_interval;
870
871 self.writer.write_scan_header(&[component], None)?;
872
873 let mut prev_dc = 0;
874
875 for block in &blocks[i] {
876 if restart_interval > 0 && restarts_to_go == 0 {
877 self.writer.finalize_bit_buffer()?;
878 self.writer
879 .write_marker(Marker::RST((restarts % 8) as u8))?;
880
881 prev_dc = 0;
882 }
883
884 self.writer.write_block(
885 block,
886 prev_dc,
887 &self.huffman_tables[component.dc_huffman_table as usize].0,
888 &self.huffman_tables[component.ac_huffman_table as usize].1,
889 )?;
890
891 prev_dc = block.data[0];
892
893 if restart_interval > 0 {
894 if restarts_to_go == 0 {
895 restarts_to_go = restart_interval;
896 restarts += 1;
897 restarts &= 7;
898 }
899 restarts_to_go -= 1;
900 }
901 }
902
903 self.writer.finalize_bit_buffer()?;
904 }
905
906 Ok(())
907 }
908
909 fn encode_image_progressive<I: ImageBuffer, OP: Operations>(
913 &mut self,
914 image: I,
915 scans: u8,
916 q_tables: &[QuantizationTable; 2],
917 ) -> Result<(), EncodingError> {
918 let blocks = self.encode_blocks::<_, OP>(&image, q_tables);
919
920 if self.optimize_huffman_table {
921 self.optimize_huffman_table(&blocks);
922 }
923
924 self.write_frame_header(&image, q_tables)?;
925
926 for (i, component) in self.components.iter().enumerate() {
929 self.writer.write_scan_header(&[component], Some((0, 0)))?;
930
931 let restart_interval = self.restart_interval.unwrap_or(0);
932 let mut restarts = 0;
933 let mut restarts_to_go = restart_interval;
934
935 let mut prev_dc = 0;
936
937 for block in &blocks[i] {
938 if restart_interval > 0 && restarts_to_go == 0 {
939 self.writer.finalize_bit_buffer()?;
940 self.writer
941 .write_marker(Marker::RST((restarts % 8) as u8))?;
942
943 prev_dc = 0;
944 }
945
946 self.writer.write_dc(
947 block.data[0],
948 prev_dc,
949 &self.huffman_tables[component.dc_huffman_table as usize].0,
950 )?;
951
952 prev_dc = block.data[0];
953
954 if restart_interval > 0 {
955 if restarts_to_go == 0 {
956 restarts_to_go = restart_interval;
957 restarts += 1;
958 restarts &= 7;
959 }
960 restarts_to_go -= 1;
961 }
962 }
963
964 self.writer.finalize_bit_buffer()?;
965 }
966
967 let scans = scans as usize - 1;
969
970 let values_per_scan = 64 / scans;
971
972 for scan in 0..scans {
973 let start = (scan * values_per_scan).max(1);
974 let end = if scan == scans - 1 {
975 64
977 } else {
978 (scan + 1) * values_per_scan
979 };
980
981 for (i, component) in self.components.iter().enumerate() {
982 let restart_interval = self.restart_interval.unwrap_or(0);
983 let mut restarts = 0;
984 let mut restarts_to_go = restart_interval;
985
986 self.writer
987 .write_scan_header(&[component], Some((start as u8, end as u8 - 1)))?;
988
989 for block in &blocks[i] {
990 if restart_interval > 0 && restarts_to_go == 0 {
991 self.writer.finalize_bit_buffer()?;
992 self.writer
993 .write_marker(Marker::RST((restarts % 8) as u8))?;
994 }
995
996 self.writer.write_ac_block(
997 block,
998 start,
999 end,
1000 &self.huffman_tables[component.ac_huffman_table as usize].1,
1001 )?;
1002
1003 if restart_interval > 0 {
1004 if restarts_to_go == 0 {
1005 restarts_to_go = restart_interval;
1006 restarts += 1;
1007 restarts &= 7;
1008 }
1009 restarts_to_go -= 1;
1010 }
1011 }
1012
1013 self.writer.finalize_bit_buffer()?;
1014 }
1015 }
1016
1017 Ok(())
1018 }
1019
1020 fn encode_blocks<I: ImageBuffer, OP: Operations>(
1021 &mut self,
1022 image: &I,
1023 q_tables: &[QuantizationTable; 2],
1024 ) -> [Vec<AlignedBlock>; 4] {
1025 let width = image.width();
1026 let height = image.height();
1027
1028 let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size();
1029
1030 let num_cols = usize::from(width).div_ceil(8 * max_h_sampling) * max_h_sampling;
1031 let num_rows = usize::from(height).div_ceil(8 * max_v_sampling) * max_v_sampling;
1032
1033 debug_assert!(num_cols > 0);
1034 debug_assert!(num_rows > 0);
1035
1036 let buffer_width = num_cols * 8;
1037 let buffer_size = num_cols * num_rows * 64;
1038
1039 let mut row: [Vec<_>; 4] = self.init_rows(buffer_size);
1040
1041 for y in 0..num_rows * 8 {
1042 let y = (y.min(usize::from(height) - 1)) as u16;
1043
1044 image.fill_buffers(y, &mut row);
1045
1046 for _ in usize::from(width)..num_cols * 8 {
1047 for channel in &mut row {
1048 if !channel.is_empty() {
1049 channel.push(channel[channel.len() - 1]);
1050 }
1051 }
1052 }
1053 }
1054
1055 let num_cols = usize::from(width).div_ceil(8);
1056 let num_rows = usize::from(height).div_ceil(8);
1057
1058 debug_assert!(num_cols > 0);
1059 debug_assert!(num_rows > 0);
1060
1061 let mut blocks: [Vec<_>; 4] = self.init_block_buffers(buffer_size / 64);
1062
1063 for (i, component) in self.components.iter().enumerate() {
1064 let h_scale = max_h_sampling / component.horizontal_sampling_factor as usize;
1065 let v_scale = max_v_sampling / component.vertical_sampling_factor as usize;
1066
1067 let cols = num_cols.div_ceil(h_scale);
1068 let rows = num_rows.div_ceil(v_scale);
1069
1070 debug_assert!(cols > 0);
1071 debug_assert!(rows > 0);
1072
1073 let average = self.chroma_subsampling_method == ChromaSubsamplingMethod::Average
1074 && (h_scale > 1 || v_scale > 1);
1075
1076 for block_y in 0..rows {
1077 for block_x in 0..cols {
1078 let bx = block_x * 8 * h_scale;
1079 let by = block_y * 8 * v_scale;
1080 let mut block = if average {
1081 get_block_averaged(&row[i], bx, by, h_scale, v_scale, buffer_width)
1082 } else {
1083 get_block(&row[i], bx, by, h_scale, v_scale, buffer_width)
1084 };
1085
1086 OP::fdct(&mut block);
1087
1088 let mut q_block = AlignedBlock::default();
1089
1090 OP::quantize_block(
1091 &block,
1092 &mut q_block,
1093 &q_tables[component.quantization_table as usize],
1094 );
1095
1096 blocks[i].push(q_block);
1097 }
1098 }
1099 }
1100 blocks
1101 }
1102
1103 fn init_block_buffers(&mut self, buffer_size: usize) -> [Vec<AlignedBlock>; 4] {
1104 match self.components.len() {
1108 1 => [
1109 Vec::with_capacity(buffer_size),
1110 Vec::new(),
1111 Vec::new(),
1112 Vec::new(),
1113 ],
1114 3 => [
1115 Vec::with_capacity(buffer_size),
1116 Vec::with_capacity(buffer_size),
1117 Vec::with_capacity(buffer_size),
1118 Vec::new(),
1119 ],
1120 4 => [
1121 Vec::with_capacity(buffer_size),
1122 Vec::with_capacity(buffer_size),
1123 Vec::with_capacity(buffer_size),
1124 Vec::with_capacity(buffer_size),
1125 ],
1126 len => unreachable!("Unsupported component length: {}", len),
1127 }
1128 }
1129
1130 fn optimize_huffman_table(&mut self, blocks: &[Vec<AlignedBlock>; 4]) {
1132 let max_tables = self.components.len().min(2) as u8;
1135
1136 for table in 0..max_tables {
1137 let mut dc_freq = [0u32; 257];
1138 dc_freq[256] = 1;
1139 let mut ac_freq = [0u32; 257];
1140 ac_freq[256] = 1;
1141
1142 let mut had_ac = false;
1143 let mut had_dc = false;
1144
1145 for (i, component) in self.components.iter().enumerate() {
1146 if component.dc_huffman_table == table {
1147 had_dc = true;
1148
1149 let mut prev_dc = 0;
1150
1151 debug_assert!(!blocks[i].is_empty());
1152
1153 for block in &blocks[i] {
1154 let value = block.data[0];
1155 let diff = value - prev_dc;
1156 let num_bits = get_num_bits(diff);
1157
1158 dc_freq[num_bits as usize] += 1;
1159
1160 prev_dc = value;
1161 }
1162 }
1163
1164 if component.ac_huffman_table == table {
1165 had_ac = true;
1166
1167 if let Some(scans) = self.progressive_scans {
1168 let scans = scans as usize - 1;
1169
1170 let values_per_scan = 64 / scans;
1171
1172 for scan in 0..scans {
1173 let start = (scan * values_per_scan).max(1);
1174 let end = if scan == scans - 1 {
1175 64
1177 } else {
1178 (scan + 1) * values_per_scan
1179 };
1180
1181 debug_assert!(!blocks[i].is_empty());
1182
1183 for block in &blocks[i] {
1184 let mut zero_run = 0;
1185
1186 for &value in &block.data[start..end] {
1187 if value == 0 {
1188 zero_run += 1;
1189 } else {
1190 while zero_run > 15 {
1191 ac_freq[0xF0] += 1;
1192 zero_run -= 16;
1193 }
1194 let num_bits = get_num_bits(value);
1195 let symbol = (zero_run << 4) | num_bits;
1196
1197 ac_freq[symbol as usize] += 1;
1198
1199 zero_run = 0;
1200 }
1201 }
1202
1203 if zero_run > 0 {
1204 ac_freq[0] += 1;
1205 }
1206 }
1207 }
1208 } else {
1209 for block in &blocks[i] {
1210 let mut zero_run = 0;
1211
1212 for &value in &block.data[1..] {
1213 if value == 0 {
1214 zero_run += 1;
1215 } else {
1216 while zero_run > 15 {
1217 ac_freq[0xF0] += 1;
1218 zero_run -= 16;
1219 }
1220 let num_bits = get_num_bits(value);
1221 let symbol = (zero_run << 4) | num_bits;
1222
1223 ac_freq[symbol as usize] += 1;
1224
1225 zero_run = 0;
1226 }
1227 }
1228
1229 if zero_run > 0 {
1230 ac_freq[0] += 1;
1231 }
1232 }
1233 }
1234 }
1235 }
1236
1237 assert!(had_dc, "Missing DC data for table {}", table);
1238 assert!(had_ac, "Missing AC data for table {}", table);
1239
1240 self.huffman_tables[table as usize] = (
1241 HuffmanTable::new_optimized(dc_freq),
1242 HuffmanTable::new_optimized(ac_freq),
1243 );
1244 }
1245 }
1246}
1247
1248#[cfg(feature = "std")]
1249impl Encoder<BufWriter<File>> {
1250 pub fn new_file<P: AsRef<Path>>(
1258 path: P,
1259 quality: u8,
1260 ) -> Result<Encoder<BufWriter<File>>, EncodingError> {
1261 let file = File::create(path)?;
1262 let buf = BufWriter::new(file);
1263 Ok(Self::new(buf, quality))
1264 }
1265}
1266
1267fn get_block(
1268 data: &[u8],
1269 start_x: usize,
1270 start_y: usize,
1271 col_stride: usize,
1272 row_stride: usize,
1273 width: usize,
1274) -> AlignedBlock {
1275 let mut block = [0i16; 64];
1276
1277 for y in 0..8 {
1278 for x in 0..8 {
1279 let ix = start_x + (x * col_stride);
1280 let iy = start_y + (y * row_stride);
1281
1282 block[y * 8 + x] = (data[iy * width + ix] as i16) - 128;
1283 }
1284 }
1285
1286 AlignedBlock::new(block)
1287}
1288
1289fn get_block_averaged(
1290 data: &[u8],
1291 start_x: usize,
1292 start_y: usize,
1293 col_stride: usize,
1294 row_stride: usize,
1295 width: usize,
1296) -> AlignedBlock {
1297 let mut block = [0i16; 64];
1298 let n = col_stride * row_stride;
1299 let bias_even = (n - 1) / 2;
1301 let bias_odd = n / 2;
1302
1303 for y in 0..8 {
1304 for x in 0..8 {
1305 let ix = start_x + (x * col_stride);
1306 let iy = start_y + (y * row_stride);
1307
1308 let mut sum = 0usize;
1309 for dy in 0..row_stride {
1310 for dx in 0..col_stride {
1311 sum += data[(iy + dy) * width + (ix + dx)] as usize;
1312 }
1313 }
1314
1315 let bias = if x & 1 == 0 { bias_even } else { bias_odd };
1316 block[y * 8 + x] = ((sum + bias) / n) as i16 - 128;
1317 }
1318 }
1319
1320 AlignedBlock::new(block)
1321}
1322
1323fn get_num_bits(mut value: i16) -> u8 {
1324 if value < 0 {
1325 value = -value;
1326 }
1327
1328 let mut num_bits = 0;
1329
1330 while value > 0 {
1331 num_bits += 1;
1332 value >>= 1;
1333 }
1334
1335 num_bits
1336}
1337
1338pub(crate) trait Operations {
1339 #[inline(always)]
1340 fn fdct(data: &mut AlignedBlock) {
1341 fdct(data);
1342 }
1343
1344 #[inline(always)]
1345 fn quantize_block(block: &AlignedBlock, q_block: &mut AlignedBlock, table: &QuantizationTable) {
1346 for i in 0..64 {
1347 let z = ZIGZAG[i] as usize & 0x3f;
1348 q_block.data[i] = table.quantize(block.data[z], z);
1349 }
1350 }
1351}
1352
1353pub(crate) struct DefaultOperations;
1354
1355impl Operations for DefaultOperations {}
1356
1357#[cfg(test)]
1358mod tests {
1359 use alloc::vec;
1360
1361 use crate::encoder::{get_block, get_block_averaged, get_num_bits};
1362 use crate::writer::get_code;
1363 use crate::{Encoder, SamplingFactor};
1364
1365 #[test]
1366 fn test_get_block_averaged_2x2() {
1367 let width = 16;
1369 let mut data = vec![0u8; width * 16];
1370 for (i, v) in data.iter_mut().enumerate() {
1371 *v = if (i % width) % 2 == 0 { 0 } else { 252 };
1372 }
1373
1374 let nearest = get_block(&data, 0, 0, 2, 2, width);
1375 let averaged = get_block_averaged(&data, 0, 0, 2, 2, width);
1376
1377 assert!(nearest.data.iter().all(|&v| v == -128));
1378 assert!(averaged.data.iter().all(|&v| v == 126 - 128));
1379 }
1380
1381 #[test]
1382 fn test_get_block_averaged_dithers_bias() {
1383 let width = 16;
1385 let mut data = vec![0u8; width * 16];
1386 for (i, v) in data.iter_mut().enumerate() {
1387 *v = if (i % width) % 2 == 0 { 1 } else { 2 };
1388 }
1389
1390 let averaged = get_block_averaged(&data, 0, 0, 2, 2, width);
1391 assert_eq!(averaged.data[0], 1 - 128);
1392 assert_eq!(averaged.data[1], 2 - 128);
1393 assert_eq!(averaged.data[8], 1 - 128);
1394 }
1395
1396 #[test]
1397 fn test_get_num_bits() {
1398 let min_max = 2i16.pow(13);
1399
1400 for value in -min_max..=min_max {
1401 let num_bits1 = get_num_bits(value);
1402 let (num_bits2, _) = get_code(value);
1403
1404 assert_eq!(
1405 num_bits1, num_bits2,
1406 "Difference in num bits for value {}: {} vs {}",
1407 value, num_bits1, num_bits2
1408 );
1409 }
1410 }
1411
1412 #[test]
1413 fn sampling_factors() {
1414 assert_eq!(SamplingFactor::F_1_1.get_sampling_factors(), (1, 1));
1415 assert_eq!(SamplingFactor::F_2_1.get_sampling_factors(), (2, 1));
1416 assert_eq!(SamplingFactor::F_1_2.get_sampling_factors(), (1, 2));
1417 assert_eq!(SamplingFactor::F_2_2.get_sampling_factors(), (2, 2));
1418 assert_eq!(SamplingFactor::F_4_1.get_sampling_factors(), (4, 1));
1419 assert_eq!(SamplingFactor::F_4_2.get_sampling_factors(), (4, 2));
1420 assert_eq!(SamplingFactor::F_1_4.get_sampling_factors(), (1, 4));
1421 assert_eq!(SamplingFactor::F_2_4.get_sampling_factors(), (2, 4));
1422
1423 assert_eq!(SamplingFactor::R_4_4_4.get_sampling_factors(), (1, 1));
1424 assert_eq!(SamplingFactor::R_4_4_0.get_sampling_factors(), (1, 2));
1425 assert_eq!(SamplingFactor::R_4_4_1.get_sampling_factors(), (1, 4));
1426 assert_eq!(SamplingFactor::R_4_2_2.get_sampling_factors(), (2, 1));
1427 assert_eq!(SamplingFactor::R_4_2_0.get_sampling_factors(), (2, 2));
1428 assert_eq!(SamplingFactor::R_4_2_1.get_sampling_factors(), (2, 4));
1429 assert_eq!(SamplingFactor::R_4_1_1.get_sampling_factors(), (4, 1));
1430 assert_eq!(SamplingFactor::R_4_1_0.get_sampling_factors(), (4, 2));
1431 }
1432
1433 #[test]
1434 fn test_set_progressive() {
1435 let mut encoder = Encoder::new(vec![], 100);
1436 encoder.set_progressive(true);
1437 assert_eq!(encoder.progressive_scans(), Some(4));
1438
1439 encoder.set_progressive(false);
1440 assert_eq!(encoder.progressive_scans(), None);
1441 }
1442}