Skip to main content

ai_image/codecs/jpeg/
encoder.rs

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