Coverage Report

Created: 2026-11-12 08:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jpeg-encoder-0.7.1/src/encoder.rs
Line
Count
Source
1
use crate::fdct::fdct;
2
use crate::huffman::{CodingClass, HuffmanTable};
3
use crate::image_buffer::*;
4
use crate::marker::Marker;
5
use crate::quantization::{QuantizationTable, QuantizationTableType};
6
use crate::writer::{JfifWrite, JfifWriter, ZIGZAG};
7
use crate::{EncodingError, PixelDensity};
8
9
use alloc::vec;
10
use alloc::vec::Vec;
11
12
#[cfg(feature = "std")]
13
use std::io::BufWriter;
14
15
#[cfg(feature = "std")]
16
use std::fs::File;
17
18
#[cfg(feature = "std")]
19
use std::path::Path;
20
21
/// # Color types used in encoding
22
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23
pub 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))]
39
pub(crate) struct AlignedBlock {
40
    pub data: [i16; 64],
41
}
42
43
impl AlignedBlock {
44
0
    pub const fn new(data: [i16; 64]) -> Self {
45
0
        AlignedBlock { data }
46
0
    }
47
}
48
49
impl Default for AlignedBlock {
50
0
    fn default() -> Self {
51
0
        AlignedBlock { data: [0i16; 64] }
52
0
    }
53
}
54
55
impl JpegColorType {
56
0
    pub(crate) fn get_num_components(self) -> usize {
57
        use JpegColorType::*;
58
59
0
        match self {
60
0
            Luma => 1,
61
0
            Ycbcr => 3,
62
0
            Cmyk | Ycck => 4,
63
        }
64
0
    }
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)]
72
pub 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
101
impl ColorType {
102
0
    pub(crate) fn get_bytes_per_pixel(self) -> usize {
103
        use ColorType::*;
104
105
0
        match self {
106
0
            Luma => 1,
107
0
            Rgb | Bgr | Ycbcr => 3,
108
0
            Rgba | Bgra | Cmyk | CmykAsYcck | Ycck => 4,
109
        }
110
0
    }
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)]
120
pub 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
155
impl SamplingFactor {
156
    /// Get variant for supplied factors or None if not supported
157
0
    pub fn from_factors(horizontal: u8, vertical: u8) -> Option<SamplingFactor> {
158
        use SamplingFactor::*;
159
160
0
        match (horizontal, vertical) {
161
0
            (1, 1) => Some(F_1_1),
162
0
            (1, 2) => Some(F_1_2),
163
0
            (1, 4) => Some(F_1_4),
164
0
            (2, 1) => Some(F_2_1),
165
0
            (2, 2) => Some(F_2_2),
166
0
            (2, 4) => Some(F_2_4),
167
0
            (4, 1) => Some(F_4_1),
168
0
            (4, 2) => Some(F_4_2),
169
0
            _ => None,
170
        }
171
0
    }
172
173
0
    pub(crate) fn get_sampling_factors(self) -> (u8, u8) {
174
0
        let value = self as u8;
175
0
        ((value >> 4) & 0x07, value & 0xf)
176
0
    }
177
178
0
    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
0
        matches!(
184
0
            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
0
    }
188
}
189
190
/// Method for reducing each chroma block to a single sample when subsampling
191
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192
pub 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
199
pub(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
208
macro_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
222
pub 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
243
impl<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
0
    pub fn new(w: W, quality: u8) -> Encoder<W> {
250
0
        let huffman_tables = [
251
0
            (
252
0
                HuffmanTable::default_luma_dc(),
253
0
                HuffmanTable::default_luma_ac(),
254
0
            ),
255
0
            (
256
0
                HuffmanTable::default_chroma_dc(),
257
0
                HuffmanTable::default_chroma_ac(),
258
0
            ),
259
0
        ];
260
261
0
        let quantization_tables = [
262
0
            QuantizationTableType::Default,
263
0
            QuantizationTableType::Default,
264
0
        ];
265
266
0
        let sampling_factor = if quality < 90 {
267
0
            SamplingFactor::F_2_2
268
        } else {
269
0
            SamplingFactor::F_1_1
270
        };
271
272
0
        Encoder {
273
0
            writer: JfifWriter::new(w),
274
0
            density: PixelDensity::default(),
275
0
            quality,
276
0
            components: vec![],
277
0
            quantization_tables,
278
0
            huffman_tables,
279
0
            sampling_factor,
280
0
            chroma_subsampling_method: ChromaSubsamplingMethod::Nearest,
281
0
            progressive_scans: None,
282
0
            restart_interval: None,
283
0
            optimize_huffman_table: false,
284
0
            app_segments: Vec::new(),
285
0
        }
286
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::new
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new
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
0
    pub fn set_density(&mut self, density: PixelDensity) {
292
0
        self.density = density;
293
0
    }
294
295
    /// Return pixel density
296
0
    pub fn density(&self) -> PixelDensity {
297
0
        self.density
298
0
    }
299
300
    /// Set quality setting. Quality must be between 1 and 100 where 100 is the highest image quality.
301
0
    pub fn set_quality(&mut self, quality: u8) {
302
0
        self.quality = quality;
303
0
    }
304
305
    /// Get quality setting
306
0
    pub fn quality(&self) -> u8 {
307
0
        self.quality
308
0
    }
309
310
    /// Set chroma subsampling factor
311
0
    pub fn set_sampling_factor(&mut self, sampling: SamplingFactor) {
312
0
        self.sampling_factor = sampling;
313
0
    }
314
315
    /// Get chroma subsampling factor
316
0
    pub fn sampling_factor(&self) -> SamplingFactor {
317
0
        self.sampling_factor
318
0
    }
319
320
    /// Set the chroma subsampling method
321
0
    pub fn set_chroma_subsampling_method(&mut self, method: ChromaSubsamplingMethod) {
322
0
        self.chroma_subsampling_method = method;
323
0
    }
324
325
    /// Get the chroma subsampling method
326
0
    pub fn chroma_subsampling_method(&self) -> ChromaSubsamplingMethod {
327
0
        self.chroma_subsampling_method
328
0
    }
329
330
    /// Set quantization tables for luma and chroma components
331
0
    pub fn set_quantization_tables(
332
0
        &mut self,
333
0
        luma: QuantizationTableType,
334
0
        chroma: QuantizationTableType,
335
0
    ) {
336
0
        self.quantization_tables = [luma, chroma];
337
0
    }
338
339
    /// Get configured quantization tables
340
0
    pub fn quantization_tables(&self) -> &[QuantizationTableType; 2] {
341
0
        &self.quantization_tables
342
0
    }
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
0
    pub fn set_progressive(&mut self, progressive: bool) {
349
0
        self.progressive_scans = if progressive { Some(4) } else { None };
350
0
    }
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
0
    pub fn set_progressive_scans(&mut self, scans: u8) {
360
0
        assert!(
361
0
            (2..=64).contains(&scans),
362
0
            "Invalid number of scans: {}",
363
            scans
364
        );
365
0
        self.progressive_scans = Some(scans);
366
0
    }
367
368
    /// Return number of progressive scans if progressive encoding is enabled
369
0
    pub fn progressive_scans(&self) -> Option<u8> {
370
0
        self.progressive_scans
371
0
    }
372
373
    /// Set restart interval
374
    ///
375
    /// Set numbers of MCUs between restart markers.
376
0
    pub fn set_restart_interval(&mut self, interval: u16) {
377
0
        self.restart_interval = if interval == 0 { None } else { Some(interval) };
378
0
    }
379
380
    /// Return the restart interval
381
0
    pub fn restart_interval(&self) -> Option<u16> {
382
0
        self.restart_interval
383
0
    }
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
0
    pub fn set_optimized_huffman_tables(&mut self, optimize_huffman_table: bool) {
389
0
        self.optimize_huffman_table = optimize_huffman_table;
390
0
    }
391
392
    /// Returns if optimized huffman table should be generated
393
0
    pub fn optimized_huffman_tables(&self) -> bool {
394
0
        self.optimize_huffman_table
395
0
    }
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
0
    pub fn add_app_segment(&mut self, segment_nr: u8, data: Vec<u8>) -> Result<(), EncodingError> {
406
0
        if segment_nr == 0 || segment_nr > 15 {
407
0
            Err(EncodingError::InvalidAppSegment(segment_nr))
408
0
        } else if data.len() > 65533 {
409
0
            Err(EncodingError::AppSegmentTooLarge(data.len()))
410
        } else {
411
0
            self.app_segments.push((segment_nr, data));
412
0
            Ok(())
413
        }
414
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::add_app_segment
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::add_app_segment
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
0
    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
0
        let num_chunks = data.len().div_ceil(MAX_CHUNK_LENGTH);
431
432
        // Sequence number is stored as a byte and starts with 1
433
0
        if num_chunks >= 255 {
434
0
            return Err(EncodingError::IccTooLarge(data.len()));
435
0
        }
436
437
0
        for (i, data) in data.chunks(MAX_CHUNK_LENGTH).enumerate() {
438
0
            let mut chunk_data = Vec::with_capacity(MAX_CHUNK_LENGTH);
439
0
            chunk_data.extend_from_slice(MARKER);
440
0
            chunk_data.push(i as u8 + 1);
441
0
            chunk_data.push(num_chunks as u8);
442
0
            chunk_data.extend_from_slice(data);
443
444
0
            self.add_app_segment(2, chunk_data)?;
445
        }
446
447
0
        Ok(())
448
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::add_icc_profile
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::add_icc_profile
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
0
    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
0
        let mut formatted = EXIF_HEADER.to_vec();
463
0
        formatted.extend_from_slice(data);
464
465
0
        self.add_app_segment(1, formatted)
466
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::add_exif_metadata
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::add_exif_metadata
467
468
    /// Encode an image
469
    ///
470
    /// Data format and length must conform to specified width, height and color type.
471
0
    pub fn encode(
472
0
        self,
473
0
        data: &[u8],
474
0
        width: u16,
475
0
        height: u16,
476
0
        color_type: ColorType,
477
0
    ) -> Result<(), EncodingError> {
478
0
        let required_data_len = width as usize * height as usize * color_type.get_bytes_per_pixel();
479
480
0
        if data.len() < required_data_len {
481
0
            return Err(EncodingError::BadImageData {
482
0
                length: data.len(),
483
0
                required: required_data_len,
484
0
            });
485
0
        }
486
487
        #[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))]
488
        {
489
0
            if std::is_x86_feature_detected!("avx2") {
490
                use crate::avx2::*;
491
492
0
                return match color_type {
493
0
                    ColorType::Luma => self
494
0
                        .encode_image_internal::<_, AVX2Operations>(GrayImage(data, width, height)),
495
0
                    ColorType::Rgb => self.encode_image_internal::<_, AVX2Operations>(
496
0
                        RgbImageAVX2(data, width, height),
497
                    ),
498
0
                    ColorType::Rgba => self.encode_image_internal::<_, AVX2Operations>(
499
0
                        RgbaImageAVX2(data, width, height),
500
                    ),
501
0
                    ColorType::Bgr => self.encode_image_internal::<_, AVX2Operations>(
502
0
                        BgrImageAVX2(data, width, height),
503
                    ),
504
0
                    ColorType::Bgra => self.encode_image_internal::<_, AVX2Operations>(
505
0
                        BgraImageAVX2(data, width, height),
506
                    ),
507
0
                    ColorType::Ycbcr => self.encode_image_internal::<_, AVX2Operations>(
508
0
                        YCbCrImage(data, width, height),
509
                    ),
510
0
                    ColorType::Cmyk => self
511
0
                        .encode_image_internal::<_, AVX2Operations>(CmykImage(data, width, height)),
512
0
                    ColorType::CmykAsYcck => self.encode_image_internal::<_, AVX2Operations>(
513
0
                        CmykAsYcckImage(data, width, height),
514
                    ),
515
0
                    ColorType::Ycck => self
516
0
                        .encode_image_internal::<_, AVX2Operations>(YcckImage(data, width, height)),
517
                };
518
0
            }
519
        }
520
521
0
        match color_type {
522
0
            ColorType::Luma => self.encode_image(GrayImage(data, width, height))?,
523
0
            ColorType::Rgb => self.encode_image(RgbImage(data, width, height))?,
524
0
            ColorType::Rgba => self.encode_image(RgbaImage(data, width, height))?,
525
0
            ColorType::Bgr => self.encode_image(BgrImage(data, width, height))?,
526
0
            ColorType::Bgra => self.encode_image(BgraImage(data, width, height))?,
527
0
            ColorType::Ycbcr => self.encode_image(YCbCrImage(data, width, height))?,
528
0
            ColorType::Cmyk => self.encode_image(CmykImage(data, width, height))?,
529
0
            ColorType::CmykAsYcck => self.encode_image(CmykAsYcckImage(data, width, height))?,
530
0
            ColorType::Ycck => self.encode_image(YcckImage(data, width, height))?,
531
        }
532
533
0
        Ok(())
534
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode
535
536
    /// Encode an image
537
0
    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
0
            if std::is_x86_feature_detected!("avx2") {
541
                use crate::avx2::*;
542
0
                return self.encode_image_internal::<_, AVX2Operations>(image);
543
0
            }
544
        }
545
0
        self.encode_image_internal::<_, DefaultOperations>(image)
546
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image::<_>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::YCbCrImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::CmykAsYcckImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::BgrImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::RgbImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::BgraImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::CmykImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::GrayImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::RgbaImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::YcckImage>
547
548
0
    fn encode_image_internal<I: ImageBuffer, OP: Operations>(
549
0
        mut self,
550
0
        image: I,
551
0
    ) -> Result<(), EncodingError> {
552
0
        if image.width() == 0 || image.height() == 0 {
553
0
            return Err(EncodingError::ZeroImageDimensions {
554
0
                width: image.width(),
555
0
                height: image.height(),
556
0
            });
557
0
        }
558
559
0
        let q_tables = [
560
0
            QuantizationTable::new_with_quality(&self.quantization_tables[0], self.quality, true),
561
0
            QuantizationTable::new_with_quality(&self.quantization_tables[1], self.quality, false),
562
0
        ];
563
564
0
        let jpeg_color_type = image.get_jpeg_color_type();
565
0
        self.init_components(jpeg_color_type);
566
567
0
        self.writer.write_marker(Marker::SOI)?;
568
569
0
        self.writer.write_header(&self.density)?;
570
571
0
        if jpeg_color_type == JpegColorType::Cmyk {
572
            //Set ColorTransform info to "Unknown"
573
0
            let app_14 = b"Adobe\0\0\0\0\0\0\0";
574
0
            self.writer
575
0
                .write_segment(Marker::APP(14), app_14.as_ref())?;
576
0
        } else if jpeg_color_type == JpegColorType::Ycck {
577
            //Set ColorTransform info to YCCK
578
0
            let app_14 = b"Adobe\0\0\0\0\0\0\x02";
579
0
            self.writer
580
0
                .write_segment(Marker::APP(14), app_14.as_ref())?;
581
0
        }
582
583
0
        for (nr, data) in &self.app_segments {
584
0
            self.writer.write_segment(Marker::APP(*nr), data)?;
585
        }
586
587
0
        if let Some(scans) = self.progressive_scans {
588
0
            self.encode_image_progressive::<_, OP>(image, scans, &q_tables)?;
589
0
        } else if self.optimize_huffman_table || !self.sampling_factor.supports_interleaved() {
590
0
            self.encode_image_sequential::<_, OP>(image, &q_tables)?;
591
        } else {
592
0
            self.encode_image_interleaved::<_, OP>(image, &q_tables)?;
593
        }
594
595
0
        self.writer.write_marker(Marker::EOI)?;
596
597
0
        Ok(())
598
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_internal::<_, _>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations>
599
600
0
    fn init_components(&mut self, color: JpegColorType) {
601
0
        let (horizontal_sampling_factor, vertical_sampling_factor) =
602
0
            self.sampling_factor.get_sampling_factors();
603
604
0
        match color {
605
0
            JpegColorType::Luma => {
606
0
                add_component!(self.components, 0, 0, 1, 1);
607
0
            }
608
0
            JpegColorType::Ycbcr => {
609
0
                add_component!(
610
0
                    self.components,
611
0
                    0,
612
0
                    0,
613
0
                    horizontal_sampling_factor,
614
0
                    vertical_sampling_factor
615
0
                );
616
0
                add_component!(self.components, 1, 1, 1, 1);
617
0
                add_component!(self.components, 2, 1, 1, 1);
618
0
            }
619
0
            JpegColorType::Cmyk => {
620
0
                add_component!(self.components, 0, 1, 1, 1);
621
0
                add_component!(self.components, 1, 1, 1, 1);
622
0
                add_component!(self.components, 2, 1, 1, 1);
623
0
                add_component!(
624
0
                    self.components,
625
0
                    3,
626
0
                    0,
627
0
                    horizontal_sampling_factor,
628
0
                    vertical_sampling_factor
629
0
                );
630
0
            }
631
0
            JpegColorType::Ycck => {
632
0
                add_component!(
633
0
                    self.components,
634
0
                    0,
635
0
                    0,
636
0
                    horizontal_sampling_factor,
637
0
                    vertical_sampling_factor
638
0
                );
639
0
                add_component!(self.components, 1, 1, 1, 1);
640
0
                add_component!(self.components, 2, 1, 1, 1);
641
0
                add_component!(
642
0
                    self.components,
643
0
                    3,
644
0
                    0,
645
0
                    horizontal_sampling_factor,
646
0
                    vertical_sampling_factor
647
0
                );
648
0
            }
649
        }
650
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_components
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_components
651
652
0
    fn get_max_sampling_size(&self) -> (usize, usize) {
653
0
        let max_h_sampling = self.components.iter().fold(1, |value, component| {
654
0
            value.max(component.horizontal_sampling_factor)
655
0
        });
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size::{closure#0}
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size::{closure#0}
656
657
0
        let max_v_sampling = self.components.iter().fold(1, |value, component| {
658
0
            value.max(component.vertical_sampling_factor)
659
0
        });
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size::{closure#1}
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size::{closure#1}
660
661
0
        (usize::from(max_h_sampling), usize::from(max_v_sampling))
662
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size
663
664
0
    fn write_frame_header<I: ImageBuffer>(
665
0
        &mut self,
666
0
        image: &I,
667
0
        q_tables: &[QuantizationTable; 2],
668
0
    ) -> Result<(), EncodingError> {
669
0
        self.writer.write_frame_header(
670
0
            image.width(),
671
0
            image.height(),
672
0
            &self.components,
673
0
            self.progressive_scans.is_some(),
674
0
        )?;
675
676
0
        self.writer.write_quantization_segment(0, &q_tables[0])?;
677
0
        self.writer.write_quantization_segment(1, &q_tables[1])?;
678
679
0
        self.writer
680
0
            .write_huffman_segment(CodingClass::Dc, 0, &self.huffman_tables[0].0)?;
681
682
0
        self.writer
683
0
            .write_huffman_segment(CodingClass::Ac, 0, &self.huffman_tables[0].1)?;
684
685
0
        if image.get_jpeg_color_type().get_num_components() >= 3 {
686
0
            self.writer
687
0
                .write_huffman_segment(CodingClass::Dc, 1, &self.huffman_tables[1].0)?;
688
689
0
            self.writer
690
0
                .write_huffman_segment(CodingClass::Ac, 1, &self.huffman_tables[1].1)?;
691
0
        }
692
693
0
        if let Some(restart_interval) = self.restart_interval {
694
0
            self.writer.write_dri(restart_interval)?;
695
0
        }
696
697
0
        Ok(())
698
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::write_frame_header::<_>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::YCbCrImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::CmykAsYcckImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::BgrImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::RgbImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::BgraImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::CmykImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::GrayImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::RgbaImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::YcckImage>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2>
699
700
0
    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
0
        match self.components.len() {
705
0
            1 => [
706
0
                Vec::with_capacity(buffer_size),
707
0
                Vec::new(),
708
0
                Vec::new(),
709
0
                Vec::new(),
710
0
            ],
711
0
            3 => [
712
0
                Vec::with_capacity(buffer_size),
713
0
                Vec::with_capacity(buffer_size),
714
0
                Vec::with_capacity(buffer_size),
715
0
                Vec::new(),
716
0
            ],
717
0
            4 => [
718
0
                Vec::with_capacity(buffer_size),
719
0
                Vec::with_capacity(buffer_size),
720
0
                Vec::with_capacity(buffer_size),
721
0
                Vec::with_capacity(buffer_size),
722
0
            ],
723
0
            len => unreachable!("Unsupported component length: {}", len),
724
        }
725
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_rows
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_rows
726
727
    /// Encode all components with one scan
728
    ///
729
    /// This is only valid for sampling factors of 1 and 2
730
0
    fn encode_image_interleaved<I: ImageBuffer, OP: Operations>(
731
0
        &mut self,
732
0
        image: I,
733
0
        q_tables: &[QuantizationTable; 2],
734
0
    ) -> Result<(), EncodingError> {
735
0
        self.write_frame_header(&image, q_tables)?;
736
0
        self.writer
737
0
            .write_scan_header(&self.components.iter().collect::<Vec<_>>(), None)?;
738
739
0
        let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size();
740
741
0
        let width = image.width();
742
0
        let height = image.height();
743
744
0
        let num_cols = usize::from(width).div_ceil(8 * max_h_sampling);
745
0
        let num_rows = usize::from(height).div_ceil(8 * max_v_sampling);
746
747
0
        let buffer_width = num_cols * 8 * max_h_sampling;
748
0
        let buffer_size = buffer_width * 8 * max_v_sampling;
749
750
0
        let mut row: [Vec<_>; 4] = self.init_rows(buffer_size);
751
752
0
        let mut prev_dc = [0i16; 4];
753
754
0
        let restart_interval = self.restart_interval.unwrap_or(0);
755
0
        let mut restarts = 0;
756
0
        let mut restarts_to_go = restart_interval;
757
758
0
        for block_y in 0..num_rows {
759
0
            for r in &mut row {
760
0
                r.clear();
761
0
            }
762
763
0
            for y in 0..(8 * max_v_sampling) {
764
0
                let y = y + block_y * 8 * max_v_sampling;
765
0
                let y = (y.min(height as usize - 1)) as u16;
766
767
0
                image.fill_buffers(y, &mut row);
768
769
0
                for _ in usize::from(width)..buffer_width {
770
0
                    for channel in &mut row {
771
0
                        if !channel.is_empty() {
772
0
                            channel.push(channel[channel.len() - 1]);
773
0
                        }
774
                    }
775
                }
776
            }
777
778
0
            for block_x in 0..num_cols {
779
0
                if restart_interval > 0 && restarts_to_go == 0 {
780
0
                    self.writer.finalize_bit_buffer()?;
781
0
                    self.writer
782
0
                        .write_marker(Marker::RST((restarts % 8) as u8))?;
783
784
0
                    prev_dc[0] = 0;
785
0
                    prev_dc[1] = 0;
786
0
                    prev_dc[2] = 0;
787
0
                    prev_dc[3] = 0;
788
0
                }
789
790
0
                for (i, component) in self.components.iter().enumerate() {
791
0
                    let h_stride = max_h_sampling / component.horizontal_sampling_factor as usize;
792
0
                    let v_stride = max_v_sampling / component.vertical_sampling_factor as usize;
793
0
                    let average = self.chroma_subsampling_method
794
0
                        == ChromaSubsamplingMethod::Average
795
0
                        && (h_stride > 1 || v_stride > 1);
796
797
0
                    for v_offset in 0..component.vertical_sampling_factor as usize {
798
0
                        for h_offset in 0..component.horizontal_sampling_factor as usize {
799
0
                            let bx = block_x * 8 * max_h_sampling + (h_offset * 8);
800
0
                            let by = v_offset * 8;
801
0
                            let mut block = if average {
802
0
                                get_block_averaged(
803
0
                                    &row[i],
804
0
                                    bx,
805
0
                                    by,
806
0
                                    h_stride,
807
0
                                    v_stride,
808
0
                                    buffer_width,
809
                                )
810
                            } else {
811
0
                                get_block(&row[i], bx, by, h_stride, v_stride, buffer_width)
812
                            };
813
814
0
                            OP::fdct(&mut block);
815
816
0
                            let mut q_block = AlignedBlock::default();
817
818
0
                            OP::quantize_block(
819
0
                                &block,
820
0
                                &mut q_block,
821
0
                                &q_tables[component.quantization_table as usize],
822
                            );
823
824
0
                            self.writer.write_block(
825
0
                                &q_block,
826
0
                                prev_dc[i],
827
0
                                &self.huffman_tables[component.dc_huffman_table as usize].0,
828
0
                                &self.huffman_tables[component.ac_huffman_table as usize].1,
829
0
                            )?;
830
831
0
                            prev_dc[i] = q_block.data[0];
832
                        }
833
                    }
834
                }
835
836
0
                if restart_interval > 0 {
837
0
                    if restarts_to_go == 0 {
838
0
                        restarts_to_go = restart_interval;
839
0
                        restarts += 1;
840
0
                        restarts &= 7;
841
0
                    }
842
0
                    restarts_to_go -= 1;
843
0
                }
844
            }
845
        }
846
847
0
        self.writer.finalize_bit_buffer()?;
848
849
0
        Ok(())
850
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_interleaved::<_, _>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations>
851
852
    /// Encode components with one scan per component
853
0
    fn encode_image_sequential<I: ImageBuffer, OP: Operations>(
854
0
        &mut self,
855
0
        image: I,
856
0
        q_tables: &[QuantizationTable; 2],
857
0
    ) -> Result<(), EncodingError> {
858
0
        let blocks = self.encode_blocks::<_, OP>(&image, q_tables);
859
860
0
        if self.optimize_huffman_table {
861
0
            self.optimize_huffman_table(&blocks);
862
0
        }
863
864
0
        self.write_frame_header(&image, q_tables)?;
865
866
0
        for (i, component) in self.components.iter().enumerate() {
867
0
            let restart_interval = self.restart_interval.unwrap_or(0);
868
0
            let mut restarts = 0;
869
0
            let mut restarts_to_go = restart_interval;
870
871
0
            self.writer.write_scan_header(&[component], None)?;
872
873
0
            let mut prev_dc = 0;
874
875
0
            for block in &blocks[i] {
876
0
                if restart_interval > 0 && restarts_to_go == 0 {
877
0
                    self.writer.finalize_bit_buffer()?;
878
0
                    self.writer
879
0
                        .write_marker(Marker::RST((restarts % 8) as u8))?;
880
881
0
                    prev_dc = 0;
882
0
                }
883
884
0
                self.writer.write_block(
885
0
                    block,
886
0
                    prev_dc,
887
0
                    &self.huffman_tables[component.dc_huffman_table as usize].0,
888
0
                    &self.huffman_tables[component.ac_huffman_table as usize].1,
889
0
                )?;
890
891
0
                prev_dc = block.data[0];
892
893
0
                if restart_interval > 0 {
894
0
                    if restarts_to_go == 0 {
895
0
                        restarts_to_go = restart_interval;
896
0
                        restarts += 1;
897
0
                        restarts &= 7;
898
0
                    }
899
0
                    restarts_to_go -= 1;
900
0
                }
901
            }
902
903
0
            self.writer.finalize_bit_buffer()?;
904
        }
905
906
0
        Ok(())
907
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_sequential::<_, _>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations>
908
909
    /// Encode image in progressive mode
910
    ///
911
    /// This only support spectral selection for now
912
0
    fn encode_image_progressive<I: ImageBuffer, OP: Operations>(
913
0
        &mut self,
914
0
        image: I,
915
0
        scans: u8,
916
0
        q_tables: &[QuantizationTable; 2],
917
0
    ) -> Result<(), EncodingError> {
918
0
        let blocks = self.encode_blocks::<_, OP>(&image, q_tables);
919
920
0
        if self.optimize_huffman_table {
921
0
            self.optimize_huffman_table(&blocks);
922
0
        }
923
924
0
        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
0
        for (i, component) in self.components.iter().enumerate() {
929
0
            self.writer.write_scan_header(&[component], Some((0, 0)))?;
930
931
0
            let restart_interval = self.restart_interval.unwrap_or(0);
932
0
            let mut restarts = 0;
933
0
            let mut restarts_to_go = restart_interval;
934
935
0
            let mut prev_dc = 0;
936
937
0
            for block in &blocks[i] {
938
0
                if restart_interval > 0 && restarts_to_go == 0 {
939
0
                    self.writer.finalize_bit_buffer()?;
940
0
                    self.writer
941
0
                        .write_marker(Marker::RST((restarts % 8) as u8))?;
942
943
0
                    prev_dc = 0;
944
0
                }
945
946
0
                self.writer.write_dc(
947
0
                    block.data[0],
948
0
                    prev_dc,
949
0
                    &self.huffman_tables[component.dc_huffman_table as usize].0,
950
0
                )?;
951
952
0
                prev_dc = block.data[0];
953
954
0
                if restart_interval > 0 {
955
0
                    if restarts_to_go == 0 {
956
0
                        restarts_to_go = restart_interval;
957
0
                        restarts += 1;
958
0
                        restarts &= 7;
959
0
                    }
960
0
                    restarts_to_go -= 1;
961
0
                }
962
            }
963
964
0
            self.writer.finalize_bit_buffer()?;
965
        }
966
967
        // Phase 2: AC scans
968
0
        let scans = scans as usize - 1;
969
970
0
        let values_per_scan = 64 / scans;
971
972
0
        for scan in 0..scans {
973
0
            let start = (scan * values_per_scan).max(1);
974
0
            let end = if scan == scans - 1 {
975
                // ensure last scan is always transfers the remaining coefficients
976
0
                64
977
            } else {
978
0
                (scan + 1) * values_per_scan
979
            };
980
981
0
            for (i, component) in self.components.iter().enumerate() {
982
0
                let restart_interval = self.restart_interval.unwrap_or(0);
983
0
                let mut restarts = 0;
984
0
                let mut restarts_to_go = restart_interval;
985
986
0
                self.writer
987
0
                    .write_scan_header(&[component], Some((start as u8, end as u8 - 1)))?;
988
989
0
                for block in &blocks[i] {
990
0
                    if restart_interval > 0 && restarts_to_go == 0 {
991
0
                        self.writer.finalize_bit_buffer()?;
992
0
                        self.writer
993
0
                            .write_marker(Marker::RST((restarts % 8) as u8))?;
994
0
                    }
995
996
0
                    self.writer.write_ac_block(
997
0
                        block,
998
0
                        start,
999
0
                        end,
1000
0
                        &self.huffman_tables[component.ac_huffman_table as usize].1,
1001
0
                    )?;
1002
1003
0
                    if restart_interval > 0 {
1004
0
                        if restarts_to_go == 0 {
1005
0
                            restarts_to_go = restart_interval;
1006
0
                            restarts += 1;
1007
0
                            restarts &= 7;
1008
0
                        }
1009
0
                        restarts_to_go -= 1;
1010
0
                    }
1011
                }
1012
1013
0
                self.writer.finalize_bit_buffer()?;
1014
            }
1015
        }
1016
1017
0
        Ok(())
1018
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_progressive::<_, _>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations>
1019
1020
0
    fn encode_blocks<I: ImageBuffer, OP: Operations>(
1021
0
        &mut self,
1022
0
        image: &I,
1023
0
        q_tables: &[QuantizationTable; 2],
1024
0
    ) -> [Vec<AlignedBlock>; 4] {
1025
0
        let width = image.width();
1026
0
        let height = image.height();
1027
1028
0
        let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size();
1029
1030
0
        let num_cols = usize::from(width).div_ceil(8 * max_h_sampling) * max_h_sampling;
1031
0
        let num_rows = usize::from(height).div_ceil(8 * max_v_sampling) * max_v_sampling;
1032
1033
0
        debug_assert!(num_cols > 0);
1034
0
        debug_assert!(num_rows > 0);
1035
1036
0
        let buffer_width = num_cols * 8;
1037
0
        let buffer_size = num_cols * num_rows * 64;
1038
1039
0
        let mut row: [Vec<_>; 4] = self.init_rows(buffer_size);
1040
1041
0
        for y in 0..num_rows * 8 {
1042
0
            let y = (y.min(usize::from(height) - 1)) as u16;
1043
1044
0
            image.fill_buffers(y, &mut row);
1045
1046
0
            for _ in usize::from(width)..num_cols * 8 {
1047
0
                for channel in &mut row {
1048
0
                    if !channel.is_empty() {
1049
0
                        channel.push(channel[channel.len() - 1]);
1050
0
                    }
1051
                }
1052
            }
1053
        }
1054
1055
0
        let num_cols = usize::from(width).div_ceil(8);
1056
0
        let num_rows = usize::from(height).div_ceil(8);
1057
1058
0
        debug_assert!(num_cols > 0);
1059
0
        debug_assert!(num_rows > 0);
1060
1061
0
        let mut blocks: [Vec<_>; 4] = self.init_block_buffers(buffer_size / 64);
1062
1063
0
        for (i, component) in self.components.iter().enumerate() {
1064
0
            let h_scale = max_h_sampling / component.horizontal_sampling_factor as usize;
1065
0
            let v_scale = max_v_sampling / component.vertical_sampling_factor as usize;
1066
1067
0
            let cols = num_cols.div_ceil(h_scale);
1068
0
            let rows = num_rows.div_ceil(v_scale);
1069
1070
0
            debug_assert!(cols > 0);
1071
0
            debug_assert!(rows > 0);
1072
1073
0
            let average = self.chroma_subsampling_method == ChromaSubsamplingMethod::Average
1074
0
                && (h_scale > 1 || v_scale > 1);
1075
1076
0
            for block_y in 0..rows {
1077
0
                for block_x in 0..cols {
1078
0
                    let bx = block_x * 8 * h_scale;
1079
0
                    let by = block_y * 8 * v_scale;
1080
0
                    let mut block = if average {
1081
0
                        get_block_averaged(&row[i], bx, by, h_scale, v_scale, buffer_width)
1082
                    } else {
1083
0
                        get_block(&row[i], bx, by, h_scale, v_scale, buffer_width)
1084
                    };
1085
1086
0
                    OP::fdct(&mut block);
1087
1088
0
                    let mut q_block = AlignedBlock::default();
1089
1090
0
                    OP::quantize_block(
1091
0
                        &block,
1092
0
                        &mut q_block,
1093
0
                        &q_tables[component.quantization_table as usize],
1094
                    );
1095
1096
0
                    blocks[i].push(q_block);
1097
                }
1098
            }
1099
        }
1100
0
        blocks
1101
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_blocks::<_, _>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations>
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations>
1102
1103
0
    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
0
        match self.components.len() {
1108
0
            1 => [
1109
0
                Vec::with_capacity(buffer_size),
1110
0
                Vec::new(),
1111
0
                Vec::new(),
1112
0
                Vec::new(),
1113
0
            ],
1114
0
            3 => [
1115
0
                Vec::with_capacity(buffer_size),
1116
0
                Vec::with_capacity(buffer_size),
1117
0
                Vec::with_capacity(buffer_size),
1118
0
                Vec::new(),
1119
0
            ],
1120
0
            4 => [
1121
0
                Vec::with_capacity(buffer_size),
1122
0
                Vec::with_capacity(buffer_size),
1123
0
                Vec::with_capacity(buffer_size),
1124
0
                Vec::with_capacity(buffer_size),
1125
0
            ],
1126
0
            len => unreachable!("Unsupported component length: {}", len),
1127
        }
1128
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_block_buffers
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_block_buffers
1129
1130
    // Create new huffman tables optimized for this image
1131
0
    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
0
        let max_tables = self.components.len().min(2) as u8;
1135
1136
0
        for table in 0..max_tables {
1137
0
            let mut dc_freq = [0u32; 257];
1138
0
            dc_freq[256] = 1;
1139
0
            let mut ac_freq = [0u32; 257];
1140
0
            ac_freq[256] = 1;
1141
1142
0
            let mut had_ac = false;
1143
0
            let mut had_dc = false;
1144
1145
0
            for (i, component) in self.components.iter().enumerate() {
1146
0
                if component.dc_huffman_table == table {
1147
0
                    had_dc = true;
1148
1149
0
                    let mut prev_dc = 0;
1150
1151
0
                    debug_assert!(!blocks[i].is_empty());
1152
1153
0
                    for block in &blocks[i] {
1154
0
                        let value = block.data[0];
1155
0
                        let diff = value - prev_dc;
1156
0
                        let num_bits = get_num_bits(diff);
1157
0
1158
0
                        dc_freq[num_bits as usize] += 1;
1159
0
1160
0
                        prev_dc = value;
1161
0
                    }
1162
0
                }
1163
1164
0
                if component.ac_huffman_table == table {
1165
0
                    had_ac = true;
1166
1167
0
                    if let Some(scans) = self.progressive_scans {
1168
0
                        let scans = scans as usize - 1;
1169
1170
0
                        let values_per_scan = 64 / scans;
1171
1172
0
                        for scan in 0..scans {
1173
0
                            let start = (scan * values_per_scan).max(1);
1174
0
                            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
0
                                64
1177
                            } else {
1178
0
                                (scan + 1) * values_per_scan
1179
                            };
1180
1181
0
                            debug_assert!(!blocks[i].is_empty());
1182
1183
0
                            for block in &blocks[i] {
1184
0
                                let mut zero_run = 0;
1185
1186
0
                                for &value in &block.data[start..end] {
1187
0
                                    if value == 0 {
1188
0
                                        zero_run += 1;
1189
0
                                    } else {
1190
0
                                        while zero_run > 15 {
1191
0
                                            ac_freq[0xF0] += 1;
1192
0
                                            zero_run -= 16;
1193
0
                                        }
1194
0
                                        let num_bits = get_num_bits(value);
1195
0
                                        let symbol = (zero_run << 4) | num_bits;
1196
1197
0
                                        ac_freq[symbol as usize] += 1;
1198
1199
0
                                        zero_run = 0;
1200
                                    }
1201
                                }
1202
1203
0
                                if zero_run > 0 {
1204
0
                                    ac_freq[0] += 1;
1205
0
                                }
1206
                            }
1207
                        }
1208
                    } else {
1209
0
                        for block in &blocks[i] {
1210
0
                            let mut zero_run = 0;
1211
1212
0
                            for &value in &block.data[1..] {
1213
0
                                if value == 0 {
1214
0
                                    zero_run += 1;
1215
0
                                } else {
1216
0
                                    while zero_run > 15 {
1217
0
                                        ac_freq[0xF0] += 1;
1218
0
                                        zero_run -= 16;
1219
0
                                    }
1220
0
                                    let num_bits = get_num_bits(value);
1221
0
                                    let symbol = (zero_run << 4) | num_bits;
1222
1223
0
                                    ac_freq[symbol as usize] += 1;
1224
1225
0
                                    zero_run = 0;
1226
                                }
1227
                            }
1228
1229
0
                            if zero_run > 0 {
1230
0
                                ac_freq[0] += 1;
1231
0
                            }
1232
                        }
1233
                    }
1234
0
                }
1235
            }
1236
1237
0
            assert!(had_dc, "Missing DC data for table {}", table);
1238
0
            assert!(had_ac, "Missing AC data for table {}", table);
1239
1240
0
            self.huffman_tables[table as usize] = (
1241
0
                HuffmanTable::new_optimized(dc_freq),
1242
0
                HuffmanTable::new_optimized(ac_freq),
1243
            );
1244
        }
1245
0
    }
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::optimize_huffman_table
Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::optimize_huffman_table
1246
}
1247
1248
#[cfg(feature = "std")]
1249
impl 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
0
    pub fn new_file<P: AsRef<Path>>(
1258
0
        path: P,
1259
0
        quality: u8,
1260
0
    ) -> Result<Encoder<BufWriter<File>>, EncodingError> {
1261
0
        let file = File::create(path)?;
1262
0
        let buf = BufWriter::new(file);
1263
0
        Ok(Self::new(buf, quality))
1264
0
    }
1265
}
1266
1267
0
fn get_block(
1268
0
    data: &[u8],
1269
0
    start_x: usize,
1270
0
    start_y: usize,
1271
0
    col_stride: usize,
1272
0
    row_stride: usize,
1273
0
    width: usize,
1274
0
) -> AlignedBlock {
1275
0
    let mut block = [0i16; 64];
1276
1277
0
    for y in 0..8 {
1278
0
        for x in 0..8 {
1279
0
            let ix = start_x + (x * col_stride);
1280
0
            let iy = start_y + (y * row_stride);
1281
0
1282
0
            block[y * 8 + x] = (data[iy * width + ix] as i16) - 128;
1283
0
        }
1284
    }
1285
1286
0
    AlignedBlock::new(block)
1287
0
}
1288
1289
0
fn get_block_averaged(
1290
0
    data: &[u8],
1291
0
    start_x: usize,
1292
0
    start_y: usize,
1293
0
    col_stride: usize,
1294
0
    row_stride: usize,
1295
0
    width: usize,
1296
0
) -> AlignedBlock {
1297
0
    let mut block = [0i16; 64];
1298
0
    let n = col_stride * row_stride;
1299
    // Alternate the rounding bias per column as libjpeg does (see jcsample.c)
1300
0
    let bias_even = (n - 1) / 2;
1301
0
    let bias_odd = n / 2;
1302
1303
0
    for y in 0..8 {
1304
0
        for x in 0..8 {
1305
0
            let ix = start_x + (x * col_stride);
1306
0
            let iy = start_y + (y * row_stride);
1307
1308
0
            let mut sum = 0usize;
1309
0
            for dy in 0..row_stride {
1310
0
                for dx in 0..col_stride {
1311
0
                    sum += data[(iy + dy) * width + (ix + dx)] as usize;
1312
0
                }
1313
            }
1314
1315
0
            let bias = if x & 1 == 0 { bias_even } else { bias_odd };
1316
0
            block[y * 8 + x] = ((sum + bias) / n) as i16 - 128;
1317
        }
1318
    }
1319
1320
0
    AlignedBlock::new(block)
1321
0
}
1322
1323
0
fn get_num_bits(mut value: i16) -> u8 {
1324
0
    if value < 0 {
1325
0
        value = -value;
1326
0
    }
1327
1328
0
    let mut num_bits = 0;
1329
1330
0
    while value > 0 {
1331
0
        num_bits += 1;
1332
0
        value >>= 1;
1333
0
    }
1334
1335
0
    num_bits
1336
0
}
1337
1338
pub(crate) trait Operations {
1339
    #[inline(always)]
1340
0
    fn fdct(data: &mut AlignedBlock) {
1341
0
        fdct(data);
1342
0
    }
Unexecuted instantiation: <_ as jpeg_encoder::encoder::Operations>::fdct
Unexecuted instantiation: <jpeg_encoder::encoder::DefaultOperations as jpeg_encoder::encoder::Operations>::fdct
1343
1344
    #[inline(always)]
1345
0
    fn quantize_block(block: &AlignedBlock, q_block: &mut AlignedBlock, table: &QuantizationTable) {
1346
0
        for i in 0..64 {
1347
0
            let z = ZIGZAG[i] as usize & 0x3f;
1348
0
            q_block.data[i] = table.quantize(block.data[z], z);
1349
0
        }
1350
0
    }
Unexecuted instantiation: <_ as jpeg_encoder::encoder::Operations>::quantize_block
Unexecuted instantiation: <jpeg_encoder::avx2::AVX2Operations as jpeg_encoder::encoder::Operations>::quantize_block
Unexecuted instantiation: <jpeg_encoder::encoder::DefaultOperations as jpeg_encoder::encoder::Operations>::quantize_block
1351
}
1352
1353
pub(crate) struct DefaultOperations;
1354
1355
impl Operations for DefaultOperations {}
1356
1357
#[cfg(test)]
1358
mod 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
}