image/codecs/jpeg/
encoder.rs

1#![allow(clippy::too_many_arguments)]
2use std::borrow::Cow;
3use std::io::{self, Write};
4use std::{error, fmt};
5
6use crate::error::{
7    EncodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
8};
9use crate::traits::PixelWithColorType;
10use crate::utils::clamp;
11use crate::{
12    ColorType, DynamicImage, ExtendedColorType, GenericImageView, ImageBuffer, ImageEncoder,
13    ImageFormat, Luma, Pixel, Rgb,
14};
15
16use num_traits::ToPrimitive;
17
18use super::entropy::build_huff_lut_const;
19use super::transform;
20
21// Markers
22// Baseline DCT
23static SOF0: u8 = 0xC0;
24// Huffman Tables
25static DHT: u8 = 0xC4;
26// Start of Image (standalone)
27static SOI: u8 = 0xD8;
28// End of image (standalone)
29static EOI: u8 = 0xD9;
30// Start of Scan
31static SOS: u8 = 0xDA;
32// Quantization Tables
33static DQT: u8 = 0xDB;
34// Application segments start and end
35static APP0: u8 = 0xE0;
36static APP1: u8 = 0xE1;
37static APP2: u8 = 0xE2;
38
39// section K.1
40// table K.1
41#[rustfmt::skip]
42static STD_LUMA_QTABLE: [u8; 64] = [
43    16, 11, 10, 16,  24,  40,  51,  61,
44    12, 12, 14, 19,  26,  58,  60,  55,
45    14, 13, 16, 24,  40,  57,  69,  56,
46    14, 17, 22, 29,  51,  87,  80,  62,
47    18, 22, 37, 56,  68, 109, 103,  77,
48    24, 35, 55, 64,  81, 104, 113,  92,
49    49, 64, 78, 87, 103, 121, 120, 101,
50    72, 92, 95, 98, 112, 100, 103,  99,
51];
52
53// table K.2
54#[rustfmt::skip]
55static STD_CHROMA_QTABLE: [u8; 64] = [
56    17, 18, 24, 47, 99, 99, 99, 99,
57    18, 21, 26, 66, 99, 99, 99, 99,
58    24, 26, 56, 99, 99, 99, 99, 99,
59    47, 66, 99, 99, 99, 99, 99, 99,
60    99, 99, 99, 99, 99, 99, 99, 99,
61    99, 99, 99, 99, 99, 99, 99, 99,
62    99, 99, 99, 99, 99, 99, 99, 99,
63    99, 99, 99, 99, 99, 99, 99, 99,
64];
65
66// section K.3
67// Code lengths and values for table K.3
68static STD_LUMA_DC_CODE_LENGTHS: [u8; 16] = [
69    0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70];
71
72static STD_LUMA_DC_VALUES: [u8; 12] = [
73    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
74];
75
76static STD_LUMA_DC_HUFF_LUT: [(u8, u16); 256] =
77    build_huff_lut_const(&STD_LUMA_DC_CODE_LENGTHS, &STD_LUMA_DC_VALUES);
78
79// Code lengths and values for table K.4
80static STD_CHROMA_DC_CODE_LENGTHS: [u8; 16] = [
81    0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
82];
83
84static STD_CHROMA_DC_VALUES: [u8; 12] = [
85    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
86];
87
88static STD_CHROMA_DC_HUFF_LUT: [(u8, u16); 256] =
89    build_huff_lut_const(&STD_CHROMA_DC_CODE_LENGTHS, &STD_CHROMA_DC_VALUES);
90
91// Code lengths and values for table k.5
92static STD_LUMA_AC_CODE_LENGTHS: [u8; 16] = [
93    0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D,
94];
95
96static STD_LUMA_AC_VALUES: [u8; 162] = [
97    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
98    0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
99    0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
100    0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
101    0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
102    0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
103    0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
104    0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
105    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
106    0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
107    0xF9, 0xFA,
108];
109
110static STD_LUMA_AC_HUFF_LUT: [(u8, u16); 256] =
111    build_huff_lut_const(&STD_LUMA_AC_CODE_LENGTHS, &STD_LUMA_AC_VALUES);
112
113// Code lengths and values for table k.6
114static STD_CHROMA_AC_CODE_LENGTHS: [u8; 16] = [
115    0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
116];
117static STD_CHROMA_AC_VALUES: [u8; 162] = [
118    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
119    0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
120    0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
121    0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
122    0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
123    0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
124    0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
125    0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
126    0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
127    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
128    0xF9, 0xFA,
129];
130
131static STD_CHROMA_AC_HUFF_LUT: [(u8, u16); 256] =
132    build_huff_lut_const(&STD_CHROMA_AC_CODE_LENGTHS, &STD_CHROMA_AC_VALUES);
133
134static DCCLASS: u8 = 0;
135static ACCLASS: u8 = 1;
136
137static LUMADESTINATION: u8 = 0;
138static CHROMADESTINATION: u8 = 1;
139
140static LUMAID: u8 = 1;
141static CHROMABLUEID: u8 = 2;
142static CHROMAREDID: u8 = 3;
143
144/// The permutation of dct coefficients.
145#[rustfmt::skip]
146static UNZIGZAG: [u8; 64] = [
147     0,  1,  8, 16,  9,  2,  3, 10,
148    17, 24, 32, 25, 18, 11,  4,  5,
149    12, 19, 26, 33, 40, 48, 41, 34,
150    27, 20, 13,  6,  7, 14, 21, 28,
151    35, 42, 49, 56, 57, 50, 43, 36,
152    29, 22, 15, 23, 30, 37, 44, 51,
153    58, 59, 52, 45, 38, 31, 39, 46,
154    53, 60, 61, 54, 47, 55, 62, 63,
155];
156
157// E x i f \0 \0
158/// The header for an EXIF APP1 segment
159static EXIF_HEADER: [u8; 6] = [0x45, 0x78, 0x69, 0x66, 0x00, 0x00];
160
161/// A representation of a JPEG component
162#[derive(Copy, Clone)]
163struct Component {
164    /// The Component's identifier
165    id: u8,
166
167    /// Horizontal sampling factor
168    h: u8,
169
170    /// Vertical sampling factor
171    v: u8,
172
173    /// The quantization table selector
174    tq: u8,
175
176    /// Index to the Huffman DC Table
177    dc_table: u8,
178
179    /// Index to the AC Huffman Table
180    ac_table: u8,
181
182    /// The dc prediction of the component
183    _dc_pred: i32,
184}
185
186pub(crate) struct BitWriter<W> {
187    w: W,
188    accumulator: u32,
189    nbits: u8,
190}
191
192impl<W: Write> BitWriter<W> {
193    fn new(w: W) -> Self {
194        BitWriter {
195            w,
196            accumulator: 0,
197            nbits: 0,
198        }
199    }
200
201    fn write_bits(&mut self, bits: u16, size: u8) -> io::Result<()> {
202        if size == 0 {
203            return Ok(());
204        }
205
206        self.nbits += size;
207        self.accumulator |= u32::from(bits) << (32 - self.nbits) as usize;
208
209        while self.nbits >= 8 {
210            let byte = self.accumulator >> 24;
211            self.w.write_all(&[byte as u8])?;
212
213            if byte == 0xFF {
214                self.w.write_all(&[0x00])?;
215            }
216
217            self.nbits -= 8;
218            self.accumulator <<= 8;
219        }
220
221        Ok(())
222    }
223
224    fn pad_byte(&mut self) -> io::Result<()> {
225        self.write_bits(0x7F, 7)
226    }
227
228    fn huffman_encode(&mut self, val: u8, table: &[(u8, u16); 256]) -> io::Result<()> {
229        let (size, code) = table[val as usize];
230
231        assert!(size <= 16, "bad huffman value");
232
233        self.write_bits(code, size)
234    }
235
236    fn write_block(
237        &mut self,
238        block: &[i32; 64],
239        prevdc: i32,
240        dctable: &[(u8, u16); 256],
241        actable: &[(u8, u16); 256],
242    ) -> io::Result<i32> {
243        // Differential DC encoding
244        let dcval = block[0];
245        let diff = dcval - prevdc;
246        let (size, value) = encode_coefficient(diff);
247
248        self.huffman_encode(size, dctable)?;
249        self.write_bits(value, size)?;
250
251        // Figure F.2
252        let mut zero_run = 0;
253
254        for &k in &UNZIGZAG[1..] {
255            if block[k as usize] == 0 {
256                zero_run += 1;
257            } else {
258                while zero_run > 15 {
259                    self.huffman_encode(0xF0, actable)?;
260                    zero_run -= 16;
261                }
262
263                let (size, value) = encode_coefficient(block[k as usize]);
264                let symbol = (zero_run << 4) | size;
265
266                self.huffman_encode(symbol, actable)?;
267                self.write_bits(value, size)?;
268
269                zero_run = 0;
270            }
271        }
272
273        if block[UNZIGZAG[63] as usize] == 0 {
274            self.huffman_encode(0x00, actable)?;
275        }
276
277        Ok(dcval)
278    }
279
280    fn write_marker(&mut self, marker: u8) -> io::Result<()> {
281        self.w.write_all(&[0xFF, marker])
282    }
283
284    fn write_segment(&mut self, marker: u8, data: &[u8]) -> io::Result<()> {
285        self.w.write_all(&[0xFF, marker])?;
286        self.w.write_all(&(data.len() as u16 + 2).to_be_bytes())?;
287        self.w.write_all(data)
288    }
289}
290
291/// Represents a unit in which the density of an image is measured
292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
293pub enum PixelDensityUnit {
294    /// Represents the absence of a unit, the values indicate only a
295    /// [pixel aspect ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio)
296    PixelAspectRatio,
297
298    /// Pixels per inch (2.54 cm)
299    Inches,
300
301    /// Pixels per centimeter
302    Centimeters,
303}
304
305/// Represents the pixel density of an image
306///
307/// For example, a 300 DPI image is represented by:
308///
309/// ```rust
310/// use image::codecs::jpeg::*;
311/// let hdpi = PixelDensity::dpi(300);
312/// assert_eq!(hdpi, PixelDensity {density: (300,300), unit: PixelDensityUnit::Inches})
313/// ```
314#[derive(Clone, Copy, Debug, Eq, PartialEq)]
315pub struct PixelDensity {
316    /// A couple of values for (Xdensity, Ydensity)
317    pub density: (u16, u16),
318    /// The unit in which the density is measured
319    pub unit: PixelDensityUnit,
320}
321
322impl PixelDensity {
323    /// Creates the most common pixel density type:
324    /// the horizontal and the vertical density are equal,
325    /// and measured in pixels per inch.
326    #[must_use]
327    pub fn dpi(density: u16) -> Self {
328        PixelDensity {
329            density: (density, density),
330            unit: PixelDensityUnit::Inches,
331        }
332    }
333}
334
335impl Default for PixelDensity {
336    /// Returns a pixel density with a pixel aspect ratio of 1
337    fn default() -> Self {
338        PixelDensity {
339            density: (1, 1),
340            unit: PixelDensityUnit::PixelAspectRatio,
341        }
342    }
343}
344
345/// Errors that can occur when encoding a JPEG image
346#[derive(Debug, Copy, Clone)]
347enum EncoderError {
348    /// JPEG does not support this size
349    InvalidSize(u32, u32),
350}
351
352impl fmt::Display for EncoderError {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        match self {
355            EncoderError::InvalidSize(w, h) => f.write_fmt(format_args!(
356                "Invalid image size ({w} x {h}) to encode as JPEG: \
357                 width and height must be >= 1 and <= 65535"
358            )),
359        }
360    }
361}
362
363impl From<EncoderError> for ImageError {
364    fn from(e: EncoderError) -> ImageError {
365        ImageError::Encoding(EncodingError::new(ImageFormat::Jpeg.into(), e))
366    }
367}
368
369impl error::Error for EncoderError {}
370
371/// The representation of a JPEG encoder
372pub struct JpegEncoder<W> {
373    writer: BitWriter<W>,
374
375    components: Vec<Component>,
376    tables: Vec<[u8; 64]>,
377
378    luma_dctable: Cow<'static, [(u8, u16); 256]>,
379    luma_actable: Cow<'static, [(u8, u16); 256]>,
380    chroma_dctable: Cow<'static, [(u8, u16); 256]>,
381    chroma_actable: Cow<'static, [(u8, u16); 256]>,
382
383    pixel_density: PixelDensity,
384
385    icc_profile: Vec<u8>,
386    exif: Vec<u8>,
387}
388
389impl<W: Write> JpegEncoder<W> {
390    /// Create a new encoder that writes its output to ```w```
391    pub fn new(w: W) -> JpegEncoder<W> {
392        JpegEncoder::new_with_quality(w, 75)
393    }
394
395    /// Create a new encoder that writes its output to ```w```, and has
396    /// the quality parameter ```quality``` with a value in the range 1-100
397    /// where 1 is the worst and 100 is the best.
398    pub fn new_with_quality(w: W, quality: u8) -> JpegEncoder<W> {
399        let components = vec![
400            Component {
401                id: LUMAID,
402                h: 1,
403                v: 1,
404                tq: LUMADESTINATION,
405                dc_table: LUMADESTINATION,
406                ac_table: LUMADESTINATION,
407                _dc_pred: 0,
408            },
409            Component {
410                id: CHROMABLUEID,
411                h: 1,
412                v: 1,
413                tq: CHROMADESTINATION,
414                dc_table: CHROMADESTINATION,
415                ac_table: CHROMADESTINATION,
416                _dc_pred: 0,
417            },
418            Component {
419                id: CHROMAREDID,
420                h: 1,
421                v: 1,
422                tq: CHROMADESTINATION,
423                dc_table: CHROMADESTINATION,
424                ac_table: CHROMADESTINATION,
425                _dc_pred: 0,
426            },
427        ];
428
429        // Derive our quantization table scaling value using the libjpeg algorithm
430        let scale = u32::from(clamp(quality, 1, 100));
431        let scale = if scale < 50 {
432            5000 / scale
433        } else {
434            200 - scale * 2
435        };
436
437        let mut tables = vec![STD_LUMA_QTABLE, STD_CHROMA_QTABLE];
438        for t in tables.iter_mut() {
439            for v in t.iter_mut() {
440                *v = clamp((u32::from(*v) * scale + 50) / 100, 1, u32::from(u8::MAX)) as u8;
441            }
442        }
443
444        JpegEncoder {
445            writer: BitWriter::new(w),
446
447            components,
448            tables,
449
450            luma_dctable: Cow::Borrowed(&STD_LUMA_DC_HUFF_LUT),
451            luma_actable: Cow::Borrowed(&STD_LUMA_AC_HUFF_LUT),
452            chroma_dctable: Cow::Borrowed(&STD_CHROMA_DC_HUFF_LUT),
453            chroma_actable: Cow::Borrowed(&STD_CHROMA_AC_HUFF_LUT),
454
455            pixel_density: PixelDensity::default(),
456
457            icc_profile: Vec::new(),
458            exif: Vec::new(),
459        }
460    }
461
462    /// Set the pixel density of the images the encoder will encode.
463    /// If this method is not called, then a default pixel aspect ratio of 1x1 will be applied,
464    /// and no DPI information will be stored in the image.
465    pub fn set_pixel_density(&mut self, pixel_density: PixelDensity) {
466        self.pixel_density = pixel_density;
467    }
468
469    /// Encodes the image stored in the raw byte buffer ```image```
470    /// that has dimensions ```width``` and ```height```
471    /// and ```ColorType``` ```c```
472    ///
473    /// The Image in encoded with subsampling ratio 4:2:2
474    ///
475    /// # Panics
476    ///
477    /// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`.
478    #[track_caller]
479    pub fn encode(
480        &mut self,
481        image: &[u8],
482        width: u32,
483        height: u32,
484        color_type: ExtendedColorType,
485    ) -> ImageResult<()> {
486        let expected_buffer_len = color_type.buffer_size(width, height);
487        assert_eq!(
488            expected_buffer_len,
489            image.len() as u64,
490            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
491            image.len(),
492        );
493
494        match color_type {
495            ExtendedColorType::L8 => {
496                let image: ImageBuffer<Luma<_>, _> =
497                    ImageBuffer::from_raw(width, height, image).unwrap();
498                self.encode_image(&image)
499            }
500            ExtendedColorType::Rgb8 => {
501                let image: ImageBuffer<Rgb<_>, _> =
502                    ImageBuffer::from_raw(width, height, image).unwrap();
503                self.encode_image(&image)
504            }
505            _ => Err(ImageError::Unsupported(
506                UnsupportedError::from_format_and_kind(
507                    ImageFormat::Jpeg.into(),
508                    UnsupportedErrorKind::Color(color_type),
509                ),
510            )),
511        }
512    }
513
514    fn write_exif(&mut self) -> ImageResult<()> {
515        if !self.exif.is_empty() {
516            let mut formatted = EXIF_HEADER.to_vec();
517            formatted.extend_from_slice(&self.exif);
518            self.writer.write_segment(APP1, &formatted)?;
519        }
520
521        Ok(())
522    }
523
524    /// Encodes the given image.
525    ///
526    /// As a special feature this does not require the whole image to be present in memory at the
527    /// same time such that it may be computed on the fly, which is why this method exists on this
528    /// encoder but not on others. Instead the encoder will iterate over 8-by-8 blocks of pixels at
529    /// a time, inspecting each pixel exactly once. You can rely on this behaviour when calling
530    /// this method.
531    ///
532    /// The Image in encoded with subsampling ratio 4:2:2
533    pub fn encode_image<I: GenericImageView>(&mut self, image: &I) -> ImageResult<()>
534    where
535        I::Pixel: PixelWithColorType,
536    {
537        let n = I::Pixel::CHANNEL_COUNT;
538        let color_type = I::Pixel::COLOR_TYPE;
539        let num_components = if n == 1 || n == 2 { 1 } else { 3 };
540
541        let (width, height) = match (u16::try_from(image.width()), u16::try_from(image.height())) {
542            (Ok(w @ 1..), Ok(h @ 1..)) => (w, h),
543            _ => return Err(EncoderError::InvalidSize(image.width(), image.height()).into()),
544        };
545
546        self.writer.write_marker(SOI)?;
547
548        let mut buf = Vec::new();
549
550        build_jfif_header(&mut buf, self.pixel_density);
551        self.writer.write_segment(APP0, &buf)?;
552        self.write_exif()?;
553
554        // Write ICC profile chunks if present
555        self.write_icc_profile_chunks()?;
556
557        build_frame_header(
558            &mut buf,
559            8,
560            width,
561            height,
562            &self.components[..num_components],
563        );
564        self.writer.write_segment(SOF0, &buf)?;
565
566        assert_eq!(self.tables.len(), 2);
567        let numtables = if num_components == 1 { 1 } else { 2 };
568
569        for (i, table) in self.tables[..numtables].iter().enumerate() {
570            build_quantization_segment(&mut buf, 8, i as u8, table);
571            self.writer.write_segment(DQT, &buf)?;
572        }
573
574        build_huffman_segment(
575            &mut buf,
576            DCCLASS,
577            LUMADESTINATION,
578            &STD_LUMA_DC_CODE_LENGTHS,
579            &STD_LUMA_DC_VALUES,
580        );
581        self.writer.write_segment(DHT, &buf)?;
582
583        build_huffman_segment(
584            &mut buf,
585            ACCLASS,
586            LUMADESTINATION,
587            &STD_LUMA_AC_CODE_LENGTHS,
588            &STD_LUMA_AC_VALUES,
589        );
590        self.writer.write_segment(DHT, &buf)?;
591
592        if num_components == 3 {
593            build_huffman_segment(
594                &mut buf,
595                DCCLASS,
596                CHROMADESTINATION,
597                &STD_CHROMA_DC_CODE_LENGTHS,
598                &STD_CHROMA_DC_VALUES,
599            );
600            self.writer.write_segment(DHT, &buf)?;
601
602            build_huffman_segment(
603                &mut buf,
604                ACCLASS,
605                CHROMADESTINATION,
606                &STD_CHROMA_AC_CODE_LENGTHS,
607                &STD_CHROMA_AC_VALUES,
608            );
609            self.writer.write_segment(DHT, &buf)?;
610        }
611
612        build_scan_header(&mut buf, &self.components[..num_components]);
613        self.writer.write_segment(SOS, &buf)?;
614
615        if ExtendedColorType::Rgb8 == color_type || ExtendedColorType::Rgba8 == color_type {
616            self.encode_rgb(image)
617        } else {
618            self.encode_gray(image)
619        }?;
620
621        self.writer.pad_byte()?;
622        self.writer.write_marker(EOI)?;
623        Ok(())
624    }
625
626    fn encode_gray<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
627        let mut yblock = [0u8; 64];
628        let mut y_dcprev = 0;
629        let mut dct_yblock = [0i32; 64];
630
631        for y in (0..image.height()).step_by(8) {
632            for x in (0..image.width()).step_by(8) {
633                copy_blocks_gray(image, x, y, &mut yblock);
634
635                // Level shift and fdct
636                // Coeffs are scaled by 8
637                transform::fdct(&yblock, &mut dct_yblock);
638
639                // Quantization
640                for (i, dct) in dct_yblock.iter_mut().enumerate() {
641                    *dct = ((*dct / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
642                }
643
644                let la = &*self.luma_actable;
645                let ld = &*self.luma_dctable;
646
647                y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
648            }
649        }
650
651        Ok(())
652    }
653
654    fn encode_rgb<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
655        let mut y_dcprev = 0;
656        let mut cb_dcprev = 0;
657        let mut cr_dcprev = 0;
658
659        let mut dct_yblock = [0i32; 64];
660        let mut dct_cb_block = [0i32; 64];
661        let mut dct_cr_block = [0i32; 64];
662
663        let mut yblock = [0u8; 64];
664        let mut cb_block = [0u8; 64];
665        let mut cr_block = [0u8; 64];
666
667        for y in (0..image.height()).step_by(8) {
668            for x in (0..image.width()).step_by(8) {
669                // RGB -> YCbCr
670                copy_blocks_ycbcr(image, x, y, &mut yblock, &mut cb_block, &mut cr_block);
671
672                // Level shift and fdct
673                // Coeffs are scaled by 8
674                transform::fdct(&yblock, &mut dct_yblock);
675                transform::fdct(&cb_block, &mut dct_cb_block);
676                transform::fdct(&cr_block, &mut dct_cr_block);
677
678                // Quantization
679                for i in 0usize..64 {
680                    dct_yblock[i] =
681                        ((dct_yblock[i] / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
682                    dct_cb_block[i] = ((dct_cb_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
683                        .round() as i32;
684                    dct_cr_block[i] = ((dct_cr_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
685                        .round() as i32;
686                }
687
688                let la = &*self.luma_actable;
689                let ld = &*self.luma_dctable;
690                let cd = &*self.chroma_dctable;
691                let ca = &*self.chroma_actable;
692
693                y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
694                cb_dcprev = self.writer.write_block(&dct_cb_block, cb_dcprev, cd, ca)?;
695                cr_dcprev = self.writer.write_block(&dct_cr_block, cr_dcprev, cd, ca)?;
696            }
697        }
698
699        Ok(())
700    }
701
702    fn write_icc_profile_chunks(&mut self) -> io::Result<()> {
703        if self.icc_profile.is_empty() {
704            return Ok(());
705        }
706
707        const MAX_CHUNK_SIZE: usize = 65533 - 14;
708        const MAX_CHUNK_COUNT: usize = 255;
709        const MAX_ICC_PROFILE_SIZE: usize = MAX_CHUNK_SIZE * MAX_CHUNK_COUNT;
710
711        if self.icc_profile.len() > MAX_ICC_PROFILE_SIZE {
712            return Err(io::Error::new(
713                io::ErrorKind::InvalidInput,
714                "ICC profile too large",
715            ));
716        }
717
718        let chunk_iter = self.icc_profile.chunks(MAX_CHUNK_SIZE);
719        let num_chunks = chunk_iter.len() as u8;
720        let mut segment = Vec::new();
721
722        for (i, chunk) in chunk_iter.enumerate() {
723            let chunk_number = (i + 1) as u8;
724            let length = 14 + chunk.len();
725
726            segment.clear();
727            segment.reserve(length);
728            segment.extend_from_slice(b"ICC_PROFILE\0");
729            segment.push(chunk_number);
730            segment.push(num_chunks);
731            segment.extend_from_slice(chunk);
732
733            self.writer.write_segment(APP2, &segment)?;
734        }
735
736        Ok(())
737    }
738}
739
740impl<W: Write> ImageEncoder for JpegEncoder<W> {
741    #[track_caller]
742    fn write_image(
743        mut self,
744        buf: &[u8],
745        width: u32,
746        height: u32,
747        color_type: ExtendedColorType,
748    ) -> ImageResult<()> {
749        self.encode(buf, width, height, color_type)
750    }
751
752    fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
753        self.icc_profile = icc_profile;
754        Ok(())
755    }
756
757    fn set_exif_metadata(&mut self, exif: Vec<u8>) -> Result<(), UnsupportedError> {
758        self.exif = exif;
759        Ok(())
760    }
761
762    fn make_compatible_img(
763        &self,
764        _: crate::io::encoder::MethodSealedToImage,
765        img: &DynamicImage,
766    ) -> Option<DynamicImage> {
767        use ColorType::*;
768        match img.color() {
769            L8 | Rgb8 => None,
770            La8 | L16 | La16 => Some(img.to_luma8().into()),
771            Rgba8 | Rgb16 | Rgb32F | Rgba16 | Rgba32F => Some(img.to_rgb8().into()),
772        }
773    }
774}
775
776fn build_jfif_header(m: &mut Vec<u8>, density: PixelDensity) {
777    m.clear();
778    m.extend_from_slice(b"JFIF");
779    m.extend_from_slice(&[
780        0,
781        0x01,
782        0x02,
783        match density.unit {
784            PixelDensityUnit::PixelAspectRatio => 0x00,
785            PixelDensityUnit::Inches => 0x01,
786            PixelDensityUnit::Centimeters => 0x02,
787        },
788    ]);
789    m.extend_from_slice(&density.density.0.to_be_bytes());
790    m.extend_from_slice(&density.density.1.to_be_bytes());
791    m.extend_from_slice(&[0, 0]);
792}
793
794fn build_frame_header(
795    m: &mut Vec<u8>,
796    precision: u8,
797    width: u16,
798    height: u16,
799    components: &[Component],
800) {
801    m.clear();
802
803    m.push(precision);
804    m.extend_from_slice(&height.to_be_bytes());
805    m.extend_from_slice(&width.to_be_bytes());
806    m.push(components.len() as u8);
807
808    for &comp in components {
809        let hv = (comp.h << 4) | comp.v;
810        m.extend_from_slice(&[comp.id, hv, comp.tq]);
811    }
812}
813
814fn build_scan_header(m: &mut Vec<u8>, components: &[Component]) {
815    m.clear();
816
817    m.push(components.len() as u8);
818
819    for &comp in components {
820        let tables = (comp.dc_table << 4) | comp.ac_table;
821        m.extend_from_slice(&[comp.id, tables]);
822    }
823
824    // spectral start and end, approx. high and low
825    m.extend_from_slice(&[0, 63, 0]);
826}
827
828fn build_huffman_segment(
829    m: &mut Vec<u8>,
830    class: u8,
831    destination: u8,
832    numcodes: &[u8; 16],
833    values: &[u8],
834) {
835    m.clear();
836
837    let tcth = (class << 4) | destination;
838    m.push(tcth);
839
840    m.extend_from_slice(numcodes);
841
842    let sum: usize = numcodes.iter().map(|&x| x as usize).sum();
843
844    assert_eq!(sum, values.len());
845
846    m.extend_from_slice(values);
847}
848
849fn build_quantization_segment(m: &mut Vec<u8>, precision: u8, identifier: u8, qtable: &[u8; 64]) {
850    m.clear();
851
852    let p = if precision == 8 { 0 } else { 1 };
853
854    let pqtq = (p << 4) | identifier;
855    m.push(pqtq);
856
857    for &i in &UNZIGZAG[..] {
858        m.push(qtable[i as usize]);
859    }
860}
861
862fn encode_coefficient(coefficient: i32) -> (u8, u16) {
863    let mut magnitude = coefficient.unsigned_abs() as u16;
864    let mut num_bits = 0u8;
865
866    while magnitude > 0 {
867        magnitude >>= 1;
868        num_bits += 1;
869    }
870
871    let mask = (1 << num_bits as usize) - 1;
872
873    let val = if coefficient < 0 {
874        (coefficient - 1) as u16 & mask
875    } else {
876        coefficient as u16 & mask
877    };
878
879    (num_bits, val)
880}
881
882#[inline]
883fn rgb_to_ycbcr<P: Pixel>(pixel: P) -> (u8, u8, u8) {
884    let [r, g, b] = pixel.to_rgb().0;
885    let r: i32 = i32::from(r.to_u8().unwrap());
886    let g: i32 = i32::from(g.to_u8().unwrap());
887    let b: i32 = i32::from(b.to_u8().unwrap());
888
889    /*
890       JPEG RGB -> YCbCr is defined as following equations using Bt.601 Full Range matrix:
891       Y  =  0.29900 * R + 0.58700 * G + 0.11400 * B
892       Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B  + 128
893       Cr =  0.50000 * R - 0.41869 * G - 0.08131 * B  + 128
894
895       To avoid using slow floating point conversion is done in fixed point,
896       using following coefficients with rounding to nearest integer mode:
897    */
898
899    const C_YR: i32 = 19595; // 0.29900 = 19595 * 2^-16
900    const C_YG: i32 = 38469; // 0.58700 = 38469 * 2^-16
901    const C_YB: i32 = 7471; // 0.11400 = 7471 * 2^-16
902    const Y_ROUNDING: i32 = (1 << 15) - 1; // + 0.5 to perform rounding shift right in-place
903    const C_UR: i32 = 11059; // 0.16874 = 11059 * 2^-16
904    const C_UG: i32 = 21709; // 0.33126 = 21709 * 2^-16
905    const C_UB: i32 = 32768; // 0.5 = 32768 * 2^-16
906    const UV_BIAS_ROUNDING: i32 = (128 * (1 << 16)) + ((1 << 15) - 1); // 128 + 0.5 = ((128 * (1 << 16)) + ((1 << 15) - 1)) * 2^-16 ; + 0.5 to perform rounding shift right in-place
907    const C_VR: i32 = C_UB; // 0.5 = 32768 * 2^-16
908    const C_VG: i32 = 27439; // 0.41869 = 27439 * 2^-16
909    const C_VB: i32 = 5329; // 0.08131409 = 5329 * 2^-16
910
911    let y = (C_YR * r + C_YG * g + C_YB * b + Y_ROUNDING) >> 16;
912    let cb = (-C_UR * r - C_UG * g + C_UB * b + UV_BIAS_ROUNDING) >> 16;
913    let cr = (C_VR * r - C_VG * g - C_VB * b + UV_BIAS_ROUNDING) >> 16;
914
915    (y as u8, cb as u8, cr as u8)
916}
917
918/// Returns the pixel at (x,y) if (x,y) is in the image,
919/// otherwise the closest pixel in the image
920#[inline]
921fn pixel_at_or_near<I: GenericImageView>(source: &I, x: u32, y: u32) -> I::Pixel {
922    if source.in_bounds(x, y) {
923        source.get_pixel(x, y)
924    } else {
925        source.get_pixel(x.min(source.width() - 1), y.min(source.height() - 1))
926    }
927}
928
929fn copy_blocks_ycbcr<I: GenericImageView>(
930    source: &I,
931    x0: u32,
932    y0: u32,
933    yb: &mut [u8; 64],
934    cbb: &mut [u8; 64],
935    crb: &mut [u8; 64],
936) {
937    for y in 0..8 {
938        for x in 0..8 {
939            let pixel = pixel_at_or_near(source, x + x0, y + y0);
940            let (yc, cb, cr) = rgb_to_ycbcr(pixel);
941
942            yb[(y * 8 + x) as usize] = yc;
943            cbb[(y * 8 + x) as usize] = cb;
944            crb[(y * 8 + x) as usize] = cr;
945        }
946    }
947}
948
949fn copy_blocks_gray<I: GenericImageView>(source: &I, x0: u32, y0: u32, gb: &mut [u8; 64]) {
950    use num_traits::cast::ToPrimitive;
951    for y in 0..8 {
952        for x in 0..8 {
953            let pixel = pixel_at_or_near(source, x0 + x, y0 + y);
954            let [luma] = pixel.to_luma().0;
955            gb[(y * 8 + x) as usize] = luma.to_u8().unwrap();
956        }
957    }
958}
959
960#[cfg(test)]
961mod tests {
962    use std::io::Cursor;
963
964    #[cfg(feature = "benchmarks")]
965    extern crate test;
966    #[cfg(feature = "benchmarks")]
967    use test::Bencher;
968
969    use crate::{ColorType, DynamicImage, ExtendedColorType, ImageEncoder, ImageError};
970    use crate::{ImageDecoder as _, ImageFormat};
971
972    use super::super::JpegDecoder;
973    use super::{
974        build_frame_header, build_huffman_segment, build_jfif_header, build_quantization_segment,
975        build_scan_header, Component, JpegEncoder, PixelDensity, DCCLASS, LUMADESTINATION,
976        STD_LUMA_DC_CODE_LENGTHS, STD_LUMA_DC_VALUES,
977    };
978
979    fn decode(encoded: &[u8]) -> Vec<u8> {
980        let decoder = JpegDecoder::new(Cursor::new(encoded)).expect("Could not decode image");
981
982        let mut decoded = vec![0; decoder.total_bytes() as usize];
983        decoder
984            .read_image(&mut decoded)
985            .expect("Could not decode image");
986        decoded
987    }
988
989    #[test]
990    fn roundtrip_sanity_check() {
991        // create a 1x1 8-bit image buffer containing a single red pixel
992        let img = [255u8, 0, 0];
993
994        // encode it into a memory buffer
995        let mut encoded_img = Vec::new();
996        {
997            let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
998            encoder
999                .write_image(&img, 1, 1, ExtendedColorType::Rgb8)
1000                .expect("Could not encode image");
1001        }
1002
1003        // decode it from the memory buffer
1004        {
1005            let decoded = decode(&encoded_img);
1006            // note that, even with the encode quality set to 100, we do not get the same image
1007            // back. Therefore, we're going to assert that it's at least red-ish:
1008            assert_eq!(3, decoded.len());
1009            assert!(decoded[0] > 0x80);
1010            assert!(decoded[1] < 0x80);
1011            assert!(decoded[2] < 0x80);
1012        }
1013    }
1014
1015    #[test]
1016    fn grayscale_roundtrip_sanity_check() {
1017        // create a 2x2 8-bit image buffer containing a white diagonal
1018        let img = [255u8, 0, 0, 255];
1019
1020        // encode it into a memory buffer
1021        let mut encoded_img = Vec::new();
1022        {
1023            let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
1024            encoder
1025                .write_image(&img[..], 2, 2, ExtendedColorType::L8)
1026                .expect("Could not encode image");
1027        }
1028
1029        // decode it from the memory buffer
1030        {
1031            let decoded = decode(&encoded_img);
1032            // note that, even with the encode quality set to 100, we do not get the same image
1033            // back. Therefore, we're going to assert that the diagonal is at least white-ish:
1034            assert_eq!(4, decoded.len());
1035            assert!(decoded[0] > 0x80);
1036            assert!(decoded[1] < 0x80);
1037            assert!(decoded[2] < 0x80);
1038            assert!(decoded[3] > 0x80);
1039        }
1040    }
1041
1042    #[test]
1043    fn jfif_header_density_check() {
1044        let mut buffer = Vec::new();
1045        build_jfif_header(&mut buffer, PixelDensity::dpi(300));
1046        assert_eq!(
1047            buffer,
1048            vec![
1049                b'J',
1050                b'F',
1051                b'I',
1052                b'F',
1053                0,
1054                1,
1055                2, // JFIF version 1.2
1056                1, // density is in dpi
1057                300u16.to_be_bytes()[0],
1058                300u16.to_be_bytes()[1],
1059                300u16.to_be_bytes()[0],
1060                300u16.to_be_bytes()[1],
1061                0,
1062                0, // No thumbnail
1063            ]
1064        );
1065    }
1066
1067    #[test]
1068    fn test_image_too_large() {
1069        // JPEG cannot encode images larger than 65,535×65,535
1070        // create a 65,536×1 8-bit black image buffer
1071        let img = [0; 65_536];
1072        // Try to encode an image that is too large
1073        let mut encoded = Vec::new();
1074        let encoder = JpegEncoder::new_with_quality(&mut encoded, 100);
1075        let result = encoder.write_image(&img, 65_536, 1, ExtendedColorType::L8);
1076        if !matches!(result, Err(ImageError::Encoding(_))) {
1077            panic!(
1078                "Encoding an image that is too large should return an \
1079                EncodingError; it returned {result:?} instead"
1080            )
1081        }
1082    }
1083
1084    #[test]
1085    fn test_build_jfif_header() {
1086        let mut buf = vec![];
1087        let density = PixelDensity::dpi(100);
1088        build_jfif_header(&mut buf, density);
1089        assert_eq!(
1090            buf,
1091            [0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0, 100, 0, 100, 0, 0]
1092        );
1093    }
1094
1095    #[test]
1096    fn test_build_frame_header() {
1097        let mut buf = vec![];
1098        let components = vec![
1099            Component {
1100                id: 1,
1101                h: 1,
1102                v: 1,
1103                tq: 5,
1104                dc_table: 5,
1105                ac_table: 5,
1106                _dc_pred: 0,
1107            },
1108            Component {
1109                id: 2,
1110                h: 1,
1111                v: 1,
1112                tq: 4,
1113                dc_table: 4,
1114                ac_table: 4,
1115                _dc_pred: 0,
1116            },
1117        ];
1118        build_frame_header(&mut buf, 5, 100, 150, &components);
1119        assert_eq!(
1120            buf,
1121            [5, 0, 150, 0, 100, 2, 1, (1 << 4) | 1, 5, 2, (1 << 4) | 1, 4]
1122        );
1123    }
1124
1125    #[test]
1126    fn test_build_scan_header() {
1127        let mut buf = vec![];
1128        let components = vec![
1129            Component {
1130                id: 1,
1131                h: 1,
1132                v: 1,
1133                tq: 5,
1134                dc_table: 5,
1135                ac_table: 5,
1136                _dc_pred: 0,
1137            },
1138            Component {
1139                id: 2,
1140                h: 1,
1141                v: 1,
1142                tq: 4,
1143                dc_table: 4,
1144                ac_table: 4,
1145                _dc_pred: 0,
1146            },
1147        ];
1148        build_scan_header(&mut buf, &components);
1149        assert_eq!(buf, [2, 1, (5 << 4) | 5, 2, (4 << 4) | 4, 0, 63, 0]);
1150    }
1151
1152    #[test]
1153    fn test_build_huffman_segment() {
1154        let mut buf = vec![];
1155        build_huffman_segment(
1156            &mut buf,
1157            DCCLASS,
1158            LUMADESTINATION,
1159            &STD_LUMA_DC_CODE_LENGTHS,
1160            &STD_LUMA_DC_VALUES,
1161        );
1162        assert_eq!(
1163            buf,
1164            vec![
1165                0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1166                10, 11
1167            ]
1168        );
1169    }
1170
1171    #[test]
1172    fn test_build_quantization_segment() {
1173        let mut buf = vec![];
1174        let qtable = [0u8; 64];
1175        build_quantization_segment(&mut buf, 8, 1, &qtable);
1176        let mut expected = vec![];
1177        expected.push(1);
1178        expected.extend_from_slice(&[0; 64]);
1179        assert_eq!(buf, expected);
1180    }
1181
1182    #[test]
1183    fn check_color_types() {
1184        const ALL: &[ColorType] = &[
1185            ColorType::L8,
1186            ColorType::L16,
1187            ColorType::La8,
1188            ColorType::Rgb8,
1189            ColorType::Rgba8,
1190            ColorType::La16,
1191            ColorType::Rgb16,
1192            ColorType::Rgba16,
1193            ColorType::Rgb32F,
1194            ColorType::Rgba32F,
1195        ];
1196
1197        for color in ALL {
1198            let image = DynamicImage::new(1, 1, *color);
1199
1200            image
1201                .write_to(&mut Cursor::new(vec![]), ImageFormat::Jpeg)
1202                .expect("supported or converted");
1203        }
1204    }
1205
1206    #[cfg(feature = "benchmarks")]
1207    #[bench]
1208    fn bench_jpeg_encoder_new(b: &mut Bencher) {
1209        b.iter(|| {
1210            let mut y = vec![];
1211            let _x = JpegEncoder::new(&mut y);
1212        });
1213    }
1214}
1215
1216// Tests regressions of `encode_image` against #1412, confusion about the subimage's position vs.
1217// dimensions. (We no longer have a position, four `u32` returns was confusing).
1218#[test]
1219fn sub_image_encoder_regression_1412() {
1220    let image = DynamicImage::new_rgb8(1280, 720);
1221    let subimg = crate::imageops::crop_imm(&image, 0, 358, 425, 361);
1222
1223    let mut encoded_crop = vec![];
1224    let mut encoder = JpegEncoder::new(&mut encoded_crop);
1225
1226    let result = encoder.encode_image(&*subimg);
1227    assert!(result.is_ok(), "Failed to encode subimage: {result:?}");
1228}