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::{ExtendedColorType, GenericImageView, ImageBuffer, Luma, Pixel, Rgb};
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        assert!(size <= 16, "bad huffman value");
223
224        self.write_bits(code, size)
225    }
226
227    fn write_block(
228        &mut self,
229        block: &[i32; 64],
230        prevdc: i32,
231        dctable: &[(u8, u16); 256],
232        actable: &[(u8, u16); 256],
233    ) -> io::Result<i32> {
234        // Differential DC encoding
235        let dcval = block[0];
236        let diff = dcval - prevdc;
237        let (size, value) = encode_coefficient(diff);
238
239        self.huffman_encode(size, dctable)?;
240        self.write_bits(value, size)?;
241
242        // Figure F.2
243        let mut zero_run = 0;
244
245        for &k in &UNZIGZAG[1..] {
246            if block[k as usize] == 0 {
247                zero_run += 1;
248            } else {
249                while zero_run > 15 {
250                    self.huffman_encode(0xF0, actable)?;
251                    zero_run -= 16;
252                }
253
254                let (size, value) = encode_coefficient(block[k as usize]);
255                let symbol = (zero_run << 4) | size;
256
257                self.huffman_encode(symbol, actable)?;
258                self.write_bits(value, size)?;
259
260                zero_run = 0;
261            }
262        }
263
264        if block[UNZIGZAG[63] as usize] == 0 {
265            self.huffman_encode(0x00, actable)?;
266        }
267
268        Ok(dcval)
269    }
270
271    fn write_marker(&mut self, marker: u8) -> io::Result<()> {
272        self.w.write_all(&[0xFF, marker])
273    }
274
275    fn write_segment(&mut self, marker: u8, data: &[u8]) -> io::Result<()> {
276        self.w.write_all(&[0xFF, marker])?;
277        self.w.write_all(&(data.len() as u16 + 2).to_be_bytes())?;
278        self.w.write_all(data)
279    }
280}
281
282/// Represents a unit in which the density of an image is measured
283#[derive(Clone, Copy, Debug, Eq, PartialEq)]
284pub enum PixelDensityUnit {
285    /// Represents the absence of a unit, the values indicate only a
286    /// [pixel aspect ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio)
287    PixelAspectRatio,
288
289    /// Pixels per inch (2.54 cm)
290    Inches,
291
292    /// Pixels per centimeter
293    Centimeters,
294}
295
296/// Represents the pixel density of an image
297///
298/// For example, a 300 DPI image is represented by:
299///
300/// ```rust
301/// use image::codecs::jpeg::*;
302/// let hdpi = PixelDensity::dpi(300);
303/// assert_eq!(hdpi, PixelDensity {density: (300,300), unit: PixelDensityUnit::Inches})
304/// ```
305#[derive(Clone, Copy, Debug, Eq, PartialEq)]
306pub struct PixelDensity {
307    /// A couple of values for (Xdensity, Ydensity)
308    pub density: (u16, u16),
309    /// The unit in which the density is measured
310    pub unit: PixelDensityUnit,
311}
312
313impl PixelDensity {
314    /// Creates the most common pixel density type:
315    /// the horizontal and the vertical density are equal,
316    /// and measured in pixels per inch.
317    #[must_use]
318    pub fn dpi(density: u16) -> Self {
319        PixelDensity {
320            density: (density, density),
321            unit: PixelDensityUnit::Inches,
322        }
323    }
324}
325
326impl Default for PixelDensity {
327    /// Returns a pixel density with a pixel aspect ratio of 1
328    fn default() -> Self {
329        PixelDensity {
330            density: (1, 1),
331            unit: PixelDensityUnit::PixelAspectRatio,
332        }
333    }
334}
335
336/// The representation of a JPEG encoder
337pub struct JpegEncoder<W> {
338    writer: BitWriter<W>,
339
340    components: Vec<Component>,
341    tables: Vec<[u8; 64]>,
342
343    luma_dctable: Cow<'static, [(u8, u16); 256]>,
344    luma_actable: Cow<'static, [(u8, u16); 256]>,
345    chroma_dctable: Cow<'static, [(u8, u16); 256]>,
346    chroma_actable: Cow<'static, [(u8, u16); 256]>,
347
348    pixel_density: PixelDensity,
349}
350
351impl<W: Write> JpegEncoder<W> {
352    /// Create a new encoder that writes its output to ```w```
353    pub fn new(w: W) -> JpegEncoder<W> {
354        JpegEncoder::new_with_quality(w, 75)
355    }
356
357    /// Create a new encoder that writes its output to ```w```, and has
358    /// the quality parameter ```quality``` with a value in the range 1-100
359    /// where 1 is the worst and 100 is the best.
360    pub fn new_with_quality(w: W, quality: u8) -> JpegEncoder<W> {
361        let components = vec![
362            Component {
363                id: LUMAID,
364                h: 1,
365                v: 1,
366                tq: LUMADESTINATION,
367                dc_table: LUMADESTINATION,
368                ac_table: LUMADESTINATION,
369                _dc_pred: 0,
370            },
371            Component {
372                id: CHROMABLUEID,
373                h: 1,
374                v: 1,
375                tq: CHROMADESTINATION,
376                dc_table: CHROMADESTINATION,
377                ac_table: CHROMADESTINATION,
378                _dc_pred: 0,
379            },
380            Component {
381                id: CHROMAREDID,
382                h: 1,
383                v: 1,
384                tq: CHROMADESTINATION,
385                dc_table: CHROMADESTINATION,
386                ac_table: CHROMADESTINATION,
387                _dc_pred: 0,
388            },
389        ];
390
391        // Derive our quantization table scaling value using the libjpeg algorithm
392        let scale = u32::from(clamp(quality, 1, 100));
393        let scale = if scale < 50 {
394            5000 / scale
395        } else {
396            200 - scale * 2
397        };
398
399        let mut tables = vec![STD_LUMA_QTABLE, STD_CHROMA_QTABLE];
400        tables.iter_mut().for_each(|t| {
401            for v in t.iter_mut() {
402                *v = clamp((u32::from(*v) * scale + 50) / 100, 1, u32::from(u8::MAX)) as u8;
403            }
404        });
405
406        JpegEncoder {
407            writer: BitWriter::new(w),
408
409            components,
410            tables,
411
412            luma_dctable: Cow::Borrowed(&STD_LUMA_DC_HUFF_LUT),
413            luma_actable: Cow::Borrowed(&STD_LUMA_AC_HUFF_LUT),
414            chroma_dctable: Cow::Borrowed(&STD_CHROMA_DC_HUFF_LUT),
415            chroma_actable: Cow::Borrowed(&STD_CHROMA_AC_HUFF_LUT),
416
417            pixel_density: PixelDensity::default(),
418        }
419    }
420
421    /// Set the pixel density of the images the encoder will encode.
422    /// If this method is not called, then a default pixel aspect ratio of 1x1 will be applied,
423    /// and no DPI information will be stored in the image.
424    pub fn set_pixel_density(&mut self, pixel_density: PixelDensity) {
425        self.pixel_density = pixel_density;
426    }
427
428    /// Encodes the image stored in the raw byte buffer ```image```
429    /// that has dimensions ```width``` and ```height```
430    /// and ```ColorType``` ```c```
431    ///
432    /// The Image in encoded with subsampling ratio 4:2:2
433    ///
434    /// # Panics
435    ///
436    /// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`.
437    #[track_caller]
438    pub fn encode(
439        &mut self,
440        image: &[u8],
441        width: u32,
442        height: u32,
443        color_type: ExtendedColorType,
444    ) -> ImageResult<()> {
445        let expected_buffer_len = color_type.buffer_size(width, height);
446        assert_eq!(
447            expected_buffer_len,
448            image.len() as u64,
449            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
450            image.len(),
451        );
452
453        match color_type {
454            ExtendedColorType::L8 => {
455                let image: ImageBuffer<Luma<_>, _> =
456                    ImageBuffer::from_raw(width, height, image).unwrap();
457                self.encode_image(&image)
458            }
459            ExtendedColorType::Rgb8 => {
460                let image: ImageBuffer<Rgb<_>, _> =
461                    ImageBuffer::from_raw(width, height, image).unwrap();
462                self.encode_image(&image)
463            }
464            _ => Err(ImageError::Unsupported(
465                UnsupportedError::from_format_and_kind(
466                    ImageFormat::Jpeg.into(),
467                    UnsupportedErrorKind::Color(color_type),
468                ),
469            )),
470        }
471    }
472
473    /// Encodes the given image.
474    ///
475    /// As a special feature this does not require the whole image to be present in memory at the
476    /// same time such that it may be computed on the fly, which is why this method exists on this
477    /// encoder but not on others. Instead the encoder will iterate over 8-by-8 blocks of pixels at
478    /// a time, inspecting each pixel exactly once. You can rely on this behaviour when calling
479    /// this method.
480    ///
481    /// The Image in encoded with subsampling ratio 4:2:2
482    pub fn encode_image<I: GenericImageView>(&mut self, image: &I) -> ImageResult<()>
483    where
484        I::Pixel: PixelWithColorType,
485    {
486        let n = I::Pixel::CHANNEL_COUNT;
487        let color_type = I::Pixel::COLOR_TYPE;
488        let num_components = if n == 1 || n == 2 { 1 } else { 3 };
489
490        self.writer.write_marker(SOI)?;
491
492        let mut buf = Vec::new();
493
494        build_jfif_header(&mut buf, self.pixel_density);
495        self.writer.write_segment(APP0, &buf)?;
496
497        build_frame_header(
498            &mut buf,
499            8,
500            // TODO: not idiomatic yet. Should be an EncodingError and mention jpg. Further it
501            // should check dimensions prior to writing.
502            u16::try_from(image.width()).map_err(|_| {
503                ImageError::Parameter(ParameterError::from_kind(
504                    ParameterErrorKind::DimensionMismatch,
505                ))
506            })?,
507            u16::try_from(image.height()).map_err(|_| {
508                ImageError::Parameter(ParameterError::from_kind(
509                    ParameterErrorKind::DimensionMismatch,
510                ))
511            })?,
512            &self.components[..num_components],
513        );
514        self.writer.write_segment(SOF0, &buf)?;
515
516        assert_eq!(self.tables.len(), 2);
517        let numtables = if num_components == 1 { 1 } else { 2 };
518
519        for (i, table) in self.tables[..numtables].iter().enumerate() {
520            build_quantization_segment(&mut buf, 8, i as u8, table);
521            self.writer.write_segment(DQT, &buf)?;
522        }
523
524        build_huffman_segment(
525            &mut buf,
526            DCCLASS,
527            LUMADESTINATION,
528            &STD_LUMA_DC_CODE_LENGTHS,
529            &STD_LUMA_DC_VALUES,
530        );
531        self.writer.write_segment(DHT, &buf)?;
532
533        build_huffman_segment(
534            &mut buf,
535            ACCLASS,
536            LUMADESTINATION,
537            &STD_LUMA_AC_CODE_LENGTHS,
538            &STD_LUMA_AC_VALUES,
539        );
540        self.writer.write_segment(DHT, &buf)?;
541
542        if num_components == 3 {
543            build_huffman_segment(
544                &mut buf,
545                DCCLASS,
546                CHROMADESTINATION,
547                &STD_CHROMA_DC_CODE_LENGTHS,
548                &STD_CHROMA_DC_VALUES,
549            );
550            self.writer.write_segment(DHT, &buf)?;
551
552            build_huffman_segment(
553                &mut buf,
554                ACCLASS,
555                CHROMADESTINATION,
556                &STD_CHROMA_AC_CODE_LENGTHS,
557                &STD_CHROMA_AC_VALUES,
558            );
559            self.writer.write_segment(DHT, &buf)?;
560        }
561
562        build_scan_header(&mut buf, &self.components[..num_components]);
563        self.writer.write_segment(SOS, &buf)?;
564
565        if ExtendedColorType::Rgb8 == color_type || ExtendedColorType::Rgba8 == color_type {
566            self.encode_rgb(image)
567        } else {
568            self.encode_gray(image)
569        }?;
570
571        self.writer.pad_byte()?;
572        self.writer.write_marker(EOI)?;
573        Ok(())
574    }
575
576    fn encode_gray<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
577        let mut yblock = [0u8; 64];
578        let mut y_dcprev = 0;
579        let mut dct_yblock = [0i32; 64];
580
581        for y in (0..image.height()).step_by(8) {
582            for x in (0..image.width()).step_by(8) {
583                copy_blocks_gray(image, x, y, &mut yblock);
584
585                // Level shift and fdct
586                // Coeffs are scaled by 8
587                transform::fdct(&yblock, &mut dct_yblock);
588
589                // Quantization
590                for (i, dct) in dct_yblock.iter_mut().enumerate() {
591                    *dct = ((*dct / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
592                }
593
594                let la = &*self.luma_actable;
595                let ld = &*self.luma_dctable;
596
597                y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
598            }
599        }
600
601        Ok(())
602    }
603
604    fn encode_rgb<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
605        let mut y_dcprev = 0;
606        let mut cb_dcprev = 0;
607        let mut cr_dcprev = 0;
608
609        let mut dct_yblock = [0i32; 64];
610        let mut dct_cb_block = [0i32; 64];
611        let mut dct_cr_block = [0i32; 64];
612
613        let mut yblock = [0u8; 64];
614        let mut cb_block = [0u8; 64];
615        let mut cr_block = [0u8; 64];
616
617        for y in (0..image.height()).step_by(8) {
618            for x in (0..image.width()).step_by(8) {
619                // RGB -> YCbCr
620                copy_blocks_ycbcr(image, x, y, &mut yblock, &mut cb_block, &mut cr_block);
621
622                // Level shift and fdct
623                // Coeffs are scaled by 8
624                transform::fdct(&yblock, &mut dct_yblock);
625                transform::fdct(&cb_block, &mut dct_cb_block);
626                transform::fdct(&cr_block, &mut dct_cr_block);
627
628                // Quantization
629                for i in 0usize..64 {
630                    dct_yblock[i] =
631                        ((dct_yblock[i] / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
632                    dct_cb_block[i] = ((dct_cb_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
633                        .round() as i32;
634                    dct_cr_block[i] = ((dct_cr_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
635                        .round() as i32;
636                }
637
638                let la = &*self.luma_actable;
639                let ld = &*self.luma_dctable;
640                let cd = &*self.chroma_dctable;
641                let ca = &*self.chroma_actable;
642
643                y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
644                cb_dcprev = self.writer.write_block(&dct_cb_block, cb_dcprev, cd, ca)?;
645                cr_dcprev = self.writer.write_block(&dct_cr_block, cr_dcprev, cd, ca)?;
646            }
647        }
648
649        Ok(())
650    }
651}
652
653impl<W: Write> ImageEncoder for JpegEncoder<W> {
654    #[track_caller]
655    fn write_image(
656        mut self,
657        buf: &[u8],
658        width: u32,
659        height: u32,
660        color_type: ExtendedColorType,
661    ) -> ImageResult<()> {
662        self.encode(buf, width, height, color_type)
663    }
664}
665
666fn build_jfif_header(m: &mut Vec<u8>, density: PixelDensity) {
667    m.clear();
668    m.extend_from_slice(b"JFIF");
669    m.extend_from_slice(&[
670        0,
671        0x01,
672        0x02,
673        match density.unit {
674            PixelDensityUnit::PixelAspectRatio => 0x00,
675            PixelDensityUnit::Inches => 0x01,
676            PixelDensityUnit::Centimeters => 0x02,
677        },
678    ]);
679    m.extend_from_slice(&density.density.0.to_be_bytes());
680    m.extend_from_slice(&density.density.1.to_be_bytes());
681    m.extend_from_slice(&[0, 0]);
682}
683
684fn build_frame_header(
685    m: &mut Vec<u8>,
686    precision: u8,
687    width: u16,
688    height: u16,
689    components: &[Component],
690) {
691    m.clear();
692
693    m.push(precision);
694    m.extend_from_slice(&height.to_be_bytes());
695    m.extend_from_slice(&width.to_be_bytes());
696    m.push(components.len() as u8);
697
698    for &comp in components {
699        let hv = (comp.h << 4) | comp.v;
700        m.extend_from_slice(&[comp.id, hv, comp.tq]);
701    }
702}
703
704fn build_scan_header(m: &mut Vec<u8>, components: &[Component]) {
705    m.clear();
706
707    m.push(components.len() as u8);
708
709    for &comp in components {
710        let tables = (comp.dc_table << 4) | comp.ac_table;
711        m.extend_from_slice(&[comp.id, tables]);
712    }
713
714    // spectral start and end, approx. high and low
715    m.extend_from_slice(&[0, 63, 0]);
716}
717
718fn build_huffman_segment(
719    m: &mut Vec<u8>,
720    class: u8,
721    destination: u8,
722    numcodes: &[u8; 16],
723    values: &[u8],
724) {
725    m.clear();
726
727    let tcth = (class << 4) | destination;
728    m.push(tcth);
729
730    m.extend_from_slice(numcodes);
731
732    let sum: usize = numcodes.iter().map(|&x| x as usize).sum();
733
734    assert_eq!(sum, values.len());
735
736    m.extend_from_slice(values);
737}
738
739fn build_quantization_segment(m: &mut Vec<u8>, precision: u8, identifier: u8, qtable: &[u8; 64]) {
740    m.clear();
741
742    let p = if precision == 8 { 0 } else { 1 };
743
744    let pqtq = (p << 4) | identifier;
745    m.push(pqtq);
746
747    for &i in &UNZIGZAG[..] {
748        m.push(qtable[i as usize]);
749    }
750}
751
752fn encode_coefficient(coefficient: i32) -> (u8, u16) {
753    let mut magnitude = coefficient.unsigned_abs() as u16;
754    let mut num_bits = 0u8;
755
756    while magnitude > 0 {
757        magnitude >>= 1;
758        num_bits += 1;
759    }
760
761    let mask = (1 << num_bits as usize) - 1;
762
763    let val = if coefficient < 0 {
764        (coefficient - 1) as u16 & mask
765    } else {
766        coefficient as u16 & mask
767    };
768
769    (num_bits, val)
770}
771
772#[inline]
773fn rgb_to_ycbcr<P: Pixel>(pixel: P) -> (u8, u8, u8) {
774    use crate::traits::Primitive;
775    use num_traits::cast::ToPrimitive;
776
777    let [r, g, b] = pixel.to_rgb().0;
778    let max: f32 = P::Subpixel::DEFAULT_MAX_VALUE.to_f32().unwrap();
779    let r: f32 = r.to_f32().unwrap();
780    let g: f32 = g.to_f32().unwrap();
781    let b: f32 = b.to_f32().unwrap();
782
783    // Coefficients from JPEG File Interchange Format (Version 1.02), multiplied for 255 maximum.
784    let y = 76.245 / max * r + 149.685 / max * g + 29.07 / max * b;
785    let cb = -43.0185 / max * r - 84.4815 / max * g + 127.5 / max * b + 128.;
786    let cr = 127.5 / max * r - 106.7685 / max * g - 20.7315 / max * b + 128.;
787
788    (y as u8, cb as u8, cr as u8)
789}
790
791/// Returns the pixel at (x,y) if (x,y) is in the image,
792/// otherwise the closest pixel in the image
793#[inline]
794fn pixel_at_or_near<I: GenericImageView>(source: &I, x: u32, y: u32) -> I::Pixel {
795    if source.in_bounds(x, y) {
796        source.get_pixel(x, y)
797    } else {
798        source.get_pixel(x.min(source.width() - 1), y.min(source.height() - 1))
799    }
800}
801
802fn copy_blocks_ycbcr<I: GenericImageView>(
803    source: &I,
804    x0: u32,
805    y0: u32,
806    yb: &mut [u8; 64],
807    cbb: &mut [u8; 64],
808    crb: &mut [u8; 64],
809) {
810    for y in 0..8 {
811        for x in 0..8 {
812            let pixel = pixel_at_or_near(source, x + x0, y + y0);
813            let (yc, cb, cr) = rgb_to_ycbcr(pixel);
814
815            yb[(y * 8 + x) as usize] = yc;
816            cbb[(y * 8 + x) as usize] = cb;
817            crb[(y * 8 + x) as usize] = cr;
818        }
819    }
820}
821
822fn copy_blocks_gray<I: GenericImageView>(source: &I, x0: u32, y0: u32, gb: &mut [u8; 64]) {
823    use num_traits::cast::ToPrimitive;
824    for y in 0..8 {
825        for x in 0..8 {
826            let pixel = pixel_at_or_near(source, x0 + x, y0 + y);
827            let [luma] = pixel.to_luma().0;
828            gb[(y * 8 + x) as usize] = luma.to_u8().unwrap();
829        }
830    }
831}
832
833#[cfg(test)]
834mod tests {
835    use std::io::Cursor;
836
837    #[cfg(feature = "benchmarks")]
838    extern crate test;
839    #[cfg(feature = "benchmarks")]
840    use test::Bencher;
841
842    use crate::error::ParameterErrorKind::DimensionMismatch;
843    use crate::image::ImageDecoder;
844    use crate::{ExtendedColorType, ImageEncoder, ImageError};
845
846    use super::super::JpegDecoder;
847    use super::{
848        build_frame_header, build_huffman_segment, build_jfif_header, build_quantization_segment,
849        build_scan_header, Component, JpegEncoder, PixelDensity, DCCLASS, LUMADESTINATION,
850        STD_LUMA_DC_CODE_LENGTHS, STD_LUMA_DC_VALUES,
851    };
852
853    fn decode(encoded: &[u8]) -> Vec<u8> {
854        let decoder = JpegDecoder::new(Cursor::new(encoded)).expect("Could not decode image");
855
856        let mut decoded = vec![0; decoder.total_bytes() as usize];
857        decoder
858            .read_image(&mut decoded)
859            .expect("Could not decode image");
860        decoded
861    }
862
863    #[test]
864    fn roundtrip_sanity_check() {
865        // create a 1x1 8-bit image buffer containing a single red pixel
866        let img = [255u8, 0, 0];
867
868        // encode it into a memory buffer
869        let mut encoded_img = Vec::new();
870        {
871            let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
872            encoder
873                .write_image(&img, 1, 1, ExtendedColorType::Rgb8)
874                .expect("Could not encode image");
875        }
876
877        // decode it from the memory buffer
878        {
879            let decoded = decode(&encoded_img);
880            // note that, even with the encode quality set to 100, we do not get the same image
881            // back. Therefore, we're going to assert that it's at least red-ish:
882            assert_eq!(3, decoded.len());
883            assert!(decoded[0] > 0x80);
884            assert!(decoded[1] < 0x80);
885            assert!(decoded[2] < 0x80);
886        }
887    }
888
889    #[test]
890    fn grayscale_roundtrip_sanity_check() {
891        // create a 2x2 8-bit image buffer containing a white diagonal
892        let img = [255u8, 0, 0, 255];
893
894        // encode it into a memory buffer
895        let mut encoded_img = Vec::new();
896        {
897            let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
898            encoder
899                .write_image(&img[..], 2, 2, ExtendedColorType::L8)
900                .expect("Could not encode image");
901        }
902
903        // decode it from the memory buffer
904        {
905            let decoded = decode(&encoded_img);
906            // note that, even with the encode quality set to 100, we do not get the same image
907            // back. Therefore, we're going to assert that the diagonal is at least white-ish:
908            assert_eq!(4, decoded.len());
909            assert!(decoded[0] > 0x80);
910            assert!(decoded[1] < 0x80);
911            assert!(decoded[2] < 0x80);
912            assert!(decoded[3] > 0x80);
913        }
914    }
915
916    #[test]
917    fn jfif_header_density_check() {
918        let mut buffer = Vec::new();
919        build_jfif_header(&mut buffer, PixelDensity::dpi(300));
920        assert_eq!(
921            buffer,
922            vec![
923                b'J',
924                b'F',
925                b'I',
926                b'F',
927                0,
928                1,
929                2, // JFIF version 1.2
930                1, // density is in dpi
931                300u16.to_be_bytes()[0],
932                300u16.to_be_bytes()[1],
933                300u16.to_be_bytes()[0],
934                300u16.to_be_bytes()[1],
935                0,
936                0, // No thumbnail
937            ]
938        );
939    }
940
941    #[test]
942    fn test_image_too_large() {
943        // JPEG cannot encode images larger than 65,535×65,535
944        // create a 65,536×1 8-bit black image buffer
945        let img = [0; 65_536];
946        // Try to encode an image that is too large
947        let mut encoded = Vec::new();
948        let encoder = JpegEncoder::new_with_quality(&mut encoded, 100);
949        let result = encoder.write_image(&img, 65_536, 1, ExtendedColorType::L8);
950        match result {
951            Err(ImageError::Parameter(err)) => {
952                assert_eq!(err.kind(), DimensionMismatch)
953            }
954            other => {
955                panic!(
956                    "Encoding an image that is too large should return a DimensionError \
957                                it returned {:?} instead",
958                    other
959                )
960            }
961        }
962    }
963
964    #[test]
965    fn test_build_jfif_header() {
966        let mut buf = vec![];
967        let density = PixelDensity::dpi(100);
968        build_jfif_header(&mut buf, density);
969        assert_eq!(
970            buf,
971            [0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0, 100, 0, 100, 0, 0]
972        );
973    }
974
975    #[test]
976    fn test_build_frame_header() {
977        let mut buf = vec![];
978        let components = vec![
979            Component {
980                id: 1,
981                h: 1,
982                v: 1,
983                tq: 5,
984                dc_table: 5,
985                ac_table: 5,
986                _dc_pred: 0,
987            },
988            Component {
989                id: 2,
990                h: 1,
991                v: 1,
992                tq: 4,
993                dc_table: 4,
994                ac_table: 4,
995                _dc_pred: 0,
996            },
997        ];
998        build_frame_header(&mut buf, 5, 100, 150, &components);
999        assert_eq!(
1000            buf,
1001            [5, 0, 150, 0, 100, 2, 1, 1 << 4 | 1, 5, 2, 1 << 4 | 1, 4]
1002        );
1003    }
1004
1005    #[test]
1006    fn test_build_scan_header() {
1007        let mut buf = vec![];
1008        let components = vec![
1009            Component {
1010                id: 1,
1011                h: 1,
1012                v: 1,
1013                tq: 5,
1014                dc_table: 5,
1015                ac_table: 5,
1016                _dc_pred: 0,
1017            },
1018            Component {
1019                id: 2,
1020                h: 1,
1021                v: 1,
1022                tq: 4,
1023                dc_table: 4,
1024                ac_table: 4,
1025                _dc_pred: 0,
1026            },
1027        ];
1028        build_scan_header(&mut buf, &components);
1029        assert_eq!(buf, [2, 1, 5 << 4 | 5, 2, 4 << 4 | 4, 0, 63, 0]);
1030    }
1031
1032    #[test]
1033    fn test_build_huffman_segment() {
1034        let mut buf = vec![];
1035        build_huffman_segment(
1036            &mut buf,
1037            DCCLASS,
1038            LUMADESTINATION,
1039            &STD_LUMA_DC_CODE_LENGTHS,
1040            &STD_LUMA_DC_VALUES,
1041        );
1042        assert_eq!(
1043            buf,
1044            vec![
1045                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,
1046                10, 11
1047            ]
1048        );
1049    }
1050
1051    #[test]
1052    fn test_build_quantization_segment() {
1053        let mut buf = vec![];
1054        let qtable = [0u8; 64];
1055        build_quantization_segment(&mut buf, 8, 1, &qtable);
1056        let mut expected = vec![];
1057        expected.push(1);
1058        expected.extend_from_slice(&[0; 64]);
1059        assert_eq!(buf, expected)
1060    }
1061
1062    #[cfg(feature = "benchmarks")]
1063    #[bench]
1064    fn bench_jpeg_encoder_new(b: &mut Bencher) {
1065        b.iter(|| {
1066            let mut y = vec![];
1067            let _x = JpegEncoder::new(&mut y);
1068        })
1069    }
1070}