image/codecs/jpeg/
encoder.rs

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