image/codecs/jpeg/
encoder.rs

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