Skip to main content

jpeg_encoder/
encoder.rs

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/// # Color types used in encoding
22#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23pub enum JpegColorType {
24    /// One component grayscale colorspace
25    Luma,
26
27    /// Three component YCbCr colorspace
28    Ycbcr,
29
30    /// 4 Component CMYK colorspace
31    Cmyk,
32
33    /// 4 Component YCbCrK colorspace
34    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/// # Color types for input images
68///
69/// Available color input formats for [Encoder::encode]. Other types can be used
70/// by implementing an [ImageBuffer](crate::ImageBuffer).
71#[derive(Copy, Clone, Debug, Eq, PartialEq)]
72pub enum ColorType {
73    /// Grayscale with 1 byte per pixel
74    Luma,
75
76    /// RGB with 3 bytes per pixel
77    Rgb,
78
79    /// Red, Green, Blue with 4 bytes per pixel. The alpha channel will be ignored during encoding.
80    Rgba,
81
82    /// RGB with 3 bytes per pixel
83    Bgr,
84
85    /// RGBA with 4 bytes per pixel. The alpha channel will be ignored during encoding.
86    Bgra,
87
88    /// YCbCr with 3 bytes per pixel.
89    Ycbcr,
90
91    /// CMYK with 4 bytes per pixel.
92    Cmyk,
93
94    /// CMYK with 4 bytes per pixel. Encoded as YCCK (YCbCrK)
95    CmykAsYcck,
96
97    /// YCCK (YCbCrK) with 4 bytes per pixel.
98    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/// # Sampling factors for chroma subsampling
116///
117/// ## Warning
118/// Sampling factor of 4 are not supported by all decoders or applications
119#[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    /// Alias for F_1_1
131    R_4_4_4 = 0x80 | 1 << 4 | 1,
132
133    /// Alias for F_1_2
134    R_4_4_0 = 0x80 | 1 << 4 | 2,
135
136    /// Alias for F_1_4
137    R_4_4_1 = 0x80 | 1 << 4 | 4,
138
139    /// Alias for F_2_1
140    R_4_2_2 = 0x80 | 2 << 4 | 1,
141
142    /// Alias for F_2_2
143    R_4_2_0 = 0x80 | 2 << 4 | 2,
144
145    /// Alias for F_2_4
146    R_4_2_1 = 0x80 | 2 << 4 | 4,
147
148    /// Alias for F_4_1
149    R_4_1_1 = 0x80 | 4 << 4 | 1,
150
151    /// Alias for F_4_2
152    R_4_1_0 = 0x80 | 4 << 4 | 2,
153}
154
155impl SamplingFactor {
156    /// Get variant for supplied factors or None if not supported
157    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        // Interleaved mode is only supported with h/v sampling factors of 1 or 2.
182        // Sampling factors of 4 needs sequential encoding
183        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/// Method for reducing each chroma block to a single sample when subsampling
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum ChromaSubsamplingMethod {
193    /// Use the top-left pixel of each block (fastest, default)
194    Nearest,
195    /// Box-average each block, matching libjpeg's `h2v2_downsample`
196    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
221/// # The JPEG encoder
222pub 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    /// Create a new encoder with the given quality
245    ///
246    /// The quality must be between 1 and 100 where 100 is the highest image quality.<br>
247    /// By default, quality settings below 90 use a chroma subsampling (2x2 / 4:2:0) which can
248    /// be changed with [set_sampling_factor](Encoder::set_sampling_factor)
249    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    /// Set pixel density for the image
289    ///
290    /// By default, this value is None which is equal to "1 pixel per pixel".
291    pub fn set_density(&mut self, density: PixelDensity) {
292        self.density = density;
293    }
294
295    /// Return pixel density
296    pub fn density(&self) -> PixelDensity {
297        self.density
298    }
299
300    /// Set quality setting. Quality must be between 1 and 100 where 100 is the highest image quality.
301    pub fn set_quality(&mut self, quality: u8) {
302        self.quality = quality;
303    }
304
305    /// Get quality setting
306    pub fn quality(&self) -> u8 {
307        self.quality
308    }
309
310    /// Set chroma subsampling factor
311    pub fn set_sampling_factor(&mut self, sampling: SamplingFactor) {
312        self.sampling_factor = sampling;
313    }
314
315    /// Get chroma subsampling factor
316    pub fn sampling_factor(&self) -> SamplingFactor {
317        self.sampling_factor
318    }
319
320    /// Set the chroma subsampling method
321    pub fn set_chroma_subsampling_method(&mut self, method: ChromaSubsamplingMethod) {
322        self.chroma_subsampling_method = method;
323    }
324
325    /// Get the chroma subsampling method
326    pub fn chroma_subsampling_method(&self) -> ChromaSubsamplingMethod {
327        self.chroma_subsampling_method
328    }
329
330    /// Set quantization tables for luma and chroma components
331    pub fn set_quantization_tables(
332        &mut self,
333        luma: QuantizationTableType,
334        chroma: QuantizationTableType,
335    ) {
336        self.quantization_tables = [luma, chroma];
337    }
338
339    /// Get configured quantization tables
340    pub fn quantization_tables(&self) -> &[QuantizationTableType; 2] {
341        &self.quantization_tables
342    }
343
344    /// Controls if progressive encoding is used.
345    ///
346    /// By default, progressive encoding uses 4 scans.<br>
347    /// Use [set_progressive_scans](Encoder::set_progressive_scans) to use a different number of scans
348    pub fn set_progressive(&mut self, progressive: bool) {
349        self.progressive_scans = if progressive { Some(4) } else { None };
350    }
351
352    /// Set number of scans per component for progressive encoding
353    ///
354    /// Number of scans must be between 2 and 64.
355    /// There is at least one scan for the DC coefficients and one for the remaining 63 AC coefficients.
356    ///
357    /// # Panics
358    /// If number of scans is not within valid range
359    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    /// Return number of progressive scans if progressive encoding is enabled
369    pub fn progressive_scans(&self) -> Option<u8> {
370        self.progressive_scans
371    }
372
373    /// Set restart interval
374    ///
375    /// Set numbers of MCUs between restart markers.
376    pub fn set_restart_interval(&mut self, interval: u16) {
377        self.restart_interval = if interval == 0 { None } else { Some(interval) };
378    }
379
380    /// Return the restart interval
381    pub fn restart_interval(&self) -> Option<u16> {
382        self.restart_interval
383    }
384
385    /// Set if optimized huffman table should be created
386    ///
387    /// Optimized tables result in slightly smaller file sizes but decrease encoding performance.
388    pub fn set_optimized_huffman_tables(&mut self, optimize_huffman_table: bool) {
389        self.optimize_huffman_table = optimize_huffman_table;
390    }
391
392    /// Returns if optimized huffman table should be generated
393    pub fn optimized_huffman_tables(&self) -> bool {
394        self.optimize_huffman_table
395    }
396
397    /// Appends a custom app segment to the JFIF file
398    ///
399    /// Segment numbers need to be in the range between 1 and 15<br>
400    /// The maximum allowed data length is 2^16 - 2 bytes.
401    ///
402    /// # Errors
403    ///
404    /// Returns an error if the segment number is invalid or data exceeds the allowed size
405    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    /// Add an ICC profile
417    ///
418    /// The maximum allowed data length is 16,707,345 bytes.
419    ///
420    /// # Errors
421    ///
422    /// Returns an Error if the data exceeds the maximum size for the ICC profile
423    pub fn add_icc_profile(&mut self, data: &[u8]) -> Result<(), EncodingError> {
424        // Based on https://www.color.org/ICC_Minor_Revision_for_Web.pdf
425        // B.4  Embedding ICC profiles in JFIF files
426
427        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        // Sequence number is stored as a byte and starts with 1
433        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    /// Embeds Exif metadata into the image
451    ///
452    /// The maximum allowed data length is 65,528 bytes.
453    ///
454    /// # Errors
455    ///
456    /// Returns an Error if the data exceeds the maximum size for the Exif metadata
457    pub fn add_exif_metadata(&mut self, data: &[u8]) -> Result<(), EncodingError> {
458        // E x i f \0 \0
459        /// The header for an EXIF APP1 segment
460        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    /// Encode an image
469    ///
470    /// Data format and length must conform to specified width, height and color type.
471    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    /// Encode an image
537    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            //Set ColorTransform info to "Unknown"
573            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            //Set ColorTransform info to YCCK
578            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        // To simplify the code and to give the compiler more infos to optimize stuff we always initialize 4 components
702        // Resource overhead should be minimal because an empty Vec doesn't allocate
703
704        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    /// Encode all components with one scan
728    ///
729    /// This is only valid for sampling factors of 1 and 2
730    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    /// Encode components with one scan per component
853    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    /// Encode image in progressive mode
910    ///
911    /// This only support spectral selection for now
912    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        // Phase 1: DC Scan
927        //          Only the DC coefficients can be transfer in the first component scans
928        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        // Phase 2: AC scans
968        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                // ensure last scan is always transfers the remaining coefficients
976                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        // To simplify the code and to give the compiler more infos to optimize stuff we always initialize 4 components
1105        // Resource overhead should be minimal because an empty Vec doesn't allocate
1106
1107        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    // Create new huffman tables optimized for this image
1131    fn optimize_huffman_table(&mut self, blocks: &[Vec<AlignedBlock>; 4]) {
1132        // TODO: Find out if it's possible to reuse some code from the writer
1133
1134        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                                // Due to rounding we might need to transfer more than values_per_scan values in the last scan
1176                                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    /// Create a new decoder that writes into a file
1251    ///
1252    /// See [new](Encoder::new) for further information.
1253    ///
1254    /// # Errors
1255    ///
1256    /// Returns an `IoError(std::io::Error)` if the file can't be created
1257    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    // Alternate the rounding bias per column as libjpeg does (see jcsample.c)
1300    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        // Every 2x2 block is {0, 252, 0, 252}; averages to 126.
1368        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        // Every 2x2 block averages to 1.5; bias dither rounds to 1 on even cols, 2 on odd.
1384        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}