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