diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/image/src/codecs/jpeg | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/image/src/codecs/jpeg')
-rw-r--r-- | vendor/image/src/codecs/jpeg/decoder.rs | 1289 | ||||
-rw-r--r-- | vendor/image/src/codecs/jpeg/encoder.rs | 1074 | ||||
-rw-r--r-- | vendor/image/src/codecs/jpeg/entropy.rs | 63 | ||||
-rw-r--r-- | vendor/image/src/codecs/jpeg/mod.rs | 16 | ||||
-rw-r--r-- | vendor/image/src/codecs/jpeg/transform.rs | 196 |
5 files changed, 2638 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/jpeg/decoder.rs b/vendor/image/src/codecs/jpeg/decoder.rs new file mode 100644 index 0000000..9625e33 --- /dev/null +++ b/vendor/image/src/codecs/jpeg/decoder.rs @@ -0,0 +1,1289 @@ +use std::convert::TryFrom; +use std::io::{self, Cursor, Read}; +use std::marker::PhantomData; +use std::mem; + +use crate::color::ColorType; +use crate::error::{ + DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, +}; +use crate::image::{ImageDecoder, ImageFormat}; + +/// JPEG decoder +pub struct JpegDecoder<R> { + decoder: jpeg::Decoder<R>, + metadata: jpeg::ImageInfo, +} + +impl<R: Read> JpegDecoder<R> { + /// Create a new decoder that decodes from the stream ```r``` + pub fn new(r: R) -> ImageResult<JpegDecoder<R>> { + let mut decoder = jpeg::Decoder::new(r); + + decoder.read_info().map_err(ImageError::from_jpeg)?; + let mut metadata = decoder.info().ok_or_else(|| { + ImageError::Decoding(DecodingError::from_format_hint(ImageFormat::Jpeg.into())) + })?; + + // We convert CMYK data to RGB before returning it to the user. + if metadata.pixel_format == jpeg::PixelFormat::CMYK32 { + metadata.pixel_format = jpeg::PixelFormat::RGB24; + } + + Ok(JpegDecoder { decoder, metadata }) + } + + /// Configure the decoder to scale the image during decoding. + /// + /// This efficiently scales the image by the smallest supported + /// scale factor that produces an image larger than or equal to + /// the requested size in at least one axis. The currently + /// implemented scale factors are 1/8, 1/4, 1/2 and 1. + /// + /// To generate a thumbnail of an exact size, pass the desired + /// size and then scale to the final size using a traditional + /// resampling algorithm. + /// + /// The size of the image to be loaded, with the scale factor + /// applied, is returned. + pub fn scale( + &mut self, + requested_width: u16, + requested_height: u16, + ) -> ImageResult<(u16, u16)> { + let result = self + .decoder + .scale(requested_width, requested_height) + .map_err(ImageError::from_jpeg)?; + + self.metadata.width = result.0; + self.metadata.height = result.1; + + Ok(result) + } +} + +/// Wrapper struct around a `Cursor<Vec<u8>>` +pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>); +impl<R> Read for JpegReader<R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + if self.0.position() == 0 && buf.is_empty() { + mem::swap(buf, self.0.get_mut()); + Ok(buf.len()) + } else { + self.0.read_to_end(buf) + } + } +} + +impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> { + type Reader = JpegReader<R>; + + fn dimensions(&self) -> (u32, u32) { + ( + u32::from(self.metadata.width), + u32::from(self.metadata.height), + ) + } + + fn color_type(&self) -> ColorType { + ColorType::from_jpeg(self.metadata.pixel_format) + } + + fn icc_profile(&mut self) -> Option<Vec<u8>> { + self.decoder.icc_profile() + } + + fn into_reader(mut self) -> ImageResult<Self::Reader> { + let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?; + data = match self.decoder.info().unwrap().pixel_format { + jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data), + _ => data, + }; + + Ok(JpegReader(Cursor::new(data), PhantomData)) + } + + fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { + assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); + + let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?; + data = match self.decoder.info().unwrap().pixel_format { + jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data), + _ => data, + }; + + buf.copy_from_slice(&data); + Ok(()) + } +} + +fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> { + let count = input.len() / 4; + let mut output = vec![0; 3 * count]; + + let in_pixels = input[..4 * count].chunks_exact(4); + let out_pixels = output[..3 * count].chunks_exact_mut(3); + + for (pixel, outp) in in_pixels.zip(out_pixels) { + let c = 255 - u16::from(pixel[0]); + let m = 255 - u16::from(pixel[1]); + let y = 255 - u16::from(pixel[2]); + let k = 255 - u16::from(pixel[3]); + // CMY -> RGB + let r = (k * c) / 255; + let g = (k * m) / 255; + let b = (k * y) / 255; + + outp[0] = r as u8; + outp[1] = g as u8; + outp[2] = b as u8; + } + + output +} + +impl ColorType { + fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType { + use jpeg::PixelFormat::*; + match pixel_format { + L8 => ColorType::L8, + L16 => ColorType::L16, + RGB24 => ColorType::Rgb8, + CMYK32 => panic!(), + } + } +} + +impl ImageError { + fn from_jpeg(err: jpeg::Error) -> ImageError { + use jpeg::Error::*; + match err { + err @ Format(_) => { + ImageError::Decoding(DecodingError::new(ImageFormat::Jpeg.into(), err)) + } + Unsupported(desc) => ImageError::Unsupported(UnsupportedError::from_format_and_kind( + ImageFormat::Jpeg.into(), + UnsupportedErrorKind::GenericFeature(format!("{:?}", desc)), + )), + Io(err) => ImageError::IoError(err), + Internal(err) => { + ImageError::Decoding(DecodingError::new(ImageFormat::Jpeg.into(), err)) + } + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "benchmarks")] + extern crate test; + + use super::cmyk_to_rgb; + #[cfg(feature = "benchmarks")] + use test::Bencher; + + #[cfg(feature = "benchmarks")] + const W: usize = 256; + #[cfg(feature = "benchmarks")] + const H: usize = 256; + + #[test] + fn cmyk_to_rgb_correct() { + for c in 0..=255 { + for k in 0..=255 { + // Based on R = 255 * (1-C/255) * (1-K/255) + let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0; + let r_u8 = r as u8; + let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0]; + let convert_g = cmyk_to_rgb(&[0, c, 0, k])[1]; + let convert_b = cmyk_to_rgb(&[0, 0, c, k])[2]; + + assert_eq!( + convert_r, r_u8, + "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", + c, k, convert_r, r_u8 + ); + assert_eq!( + convert_g, r_u8, + "m = {}, k = {}, cymk_to_rgb[1] = {}, should be {}", + c, k, convert_g, r_u8 + ); + assert_eq!( + convert_b, r_u8, + "y = {}, k = {}, cymk_to_rgb[2] = {}, should be {}", + c, k, convert_b, r_u8 + ); + } + } + } + + fn single_pix_correct(cmyk_pix: [u8; 4], rgb_pix_true: [u8; 3]) { + let rgb_pix = cmyk_to_rgb(&cmyk_pix); + assert_eq!( + rgb_pix_true[0], rgb_pix[0], + "With CMYK {:?} expected {:?}, got {:?}", + cmyk_pix, rgb_pix_true, rgb_pix + ); + assert_eq!( + rgb_pix_true[1], rgb_pix[1], + "With CMYK {:?} expected {:?}, got {:?}", + cmyk_pix, rgb_pix_true, rgb_pix + ); + assert_eq!( + rgb_pix_true[2], rgb_pix[2], + "With CMYK {:?} expected {:?}, got {:?}", + cmyk_pix, rgb_pix_true, rgb_pix + ); + } + + #[test] + fn test_assorted_colors() { + let cmyk_pixels = vec![ + [0, 51, 102, 65], + [153, 204, 0, 65], + [0, 0, 0, 67], + [0, 85, 170, 69], + [0, 0, 0, 71], + [0, 0, 0, 73], + [0, 17, 34, 75], + [51, 68, 85, 75], + [102, 119, 136, 75], + [153, 170, 187, 75], + [204, 221, 238, 75], + [0, 0, 0, 77], + [0, 0, 0, 79], + [0, 85, 170, 81], + [0, 0, 0, 83], + [0, 3, 6, 85], + [9, 12, 15, 85], + [18, 21, 24, 85], + [27, 30, 33, 85], + [36, 39, 42, 85], + [45, 48, 51, 85], + [54, 57, 60, 85], + [63, 66, 69, 85], + [72, 75, 78, 85], + [81, 84, 87, 85], + [90, 93, 96, 85], + [99, 102, 105, 85], + [108, 111, 114, 85], + [117, 120, 123, 85], + [126, 129, 132, 85], + [135, 138, 141, 85], + [144, 147, 150, 85], + [153, 156, 159, 85], + [162, 165, 168, 85], + [171, 174, 177, 85], + [180, 183, 186, 85], + [189, 192, 195, 85], + [198, 201, 204, 85], + [207, 210, 213, 85], + [216, 219, 222, 85], + [225, 228, 231, 85], + [234, 237, 240, 85], + [243, 246, 249, 85], + [252, 0, 0, 85], + [0, 85, 170, 87], + [0, 0, 0, 89], + [0, 0, 0, 91], + [0, 85, 170, 93], + [0, 51, 102, 95], + [153, 204, 0, 95], + [0, 0, 0, 97], + [0, 85, 170, 99], + [0, 0, 0, 101], + [0, 0, 0, 103], + [0, 17, 34, 105], + [51, 68, 85, 105], + [102, 119, 136, 105], + [153, 170, 187, 105], + [204, 221, 238, 105], + [0, 0, 0, 107], + [0, 0, 0, 109], + [0, 85, 170, 111], + [0, 0, 0, 113], + [0, 51, 102, 115], + [153, 204, 0, 115], + [0, 85, 170, 117], + [0, 15, 30, 119], + [45, 60, 75, 119], + [90, 105, 120, 119], + [135, 150, 165, 119], + [180, 195, 210, 119], + [225, 240, 0, 119], + [0, 0, 0, 121], + [0, 85, 170, 123], + [0, 51, 102, 125], + [153, 204, 0, 125], + [0, 0, 0, 127], + [0, 0, 0, 128], + [0, 85, 170, 129], + [0, 51, 102, 130], + [153, 204, 0, 130], + [0, 0, 0, 131], + [0, 85, 170, 132], + [0, 0, 0, 133], + [0, 0, 0, 134], + [0, 17, 34, 135], + [51, 68, 85, 135], + [102, 119, 136, 135], + [153, 170, 187, 135], + [204, 221, 238, 135], + [0, 15, 30, 136], + [45, 60, 75, 136], + [90, 105, 120, 136], + [135, 150, 165, 136], + [180, 195, 210, 136], + [225, 240, 0, 136], + [0, 0, 0, 137], + [0, 85, 170, 138], + [0, 0, 0, 139], + [0, 51, 102, 140], + [153, 204, 0, 140], + [0, 85, 170, 141], + [0, 0, 0, 142], + [0, 0, 0, 143], + [0, 85, 170, 144], + [0, 51, 102, 145], + [153, 204, 0, 145], + [0, 0, 0, 146], + [0, 85, 170, 147], + [0, 0, 0, 148], + [0, 0, 0, 149], + [0, 17, 34, 150], + [51, 68, 85, 150], + [102, 119, 136, 150], + [153, 170, 187, 150], + [204, 221, 238, 150], + [0, 0, 0, 151], + [0, 0, 0, 152], + [0, 5, 10, 153], + [15, 20, 25, 153], + [30, 35, 40, 153], + [45, 50, 55, 153], + [60, 65, 70, 153], + [75, 80, 85, 153], + [90, 95, 100, 153], + [105, 110, 115, 153], + [120, 125, 130, 153], + [135, 140, 145, 153], + [150, 155, 160, 153], + [165, 170, 175, 153], + [180, 185, 190, 153], + [195, 200, 205, 153], + [210, 215, 220, 153], + [225, 230, 235, 153], + [240, 245, 250, 153], + [0, 0, 0, 154], + [0, 51, 102, 155], + [153, 204, 0, 155], + [0, 85, 170, 156], + [0, 0, 0, 157], + [0, 0, 0, 158], + [0, 85, 170, 159], + [0, 51, 102, 160], + [153, 204, 0, 160], + [0, 0, 0, 161], + [0, 85, 170, 162], + [0, 0, 0, 163], + [0, 0, 0, 164], + [0, 17, 34, 165], + [51, 68, 85, 165], + [102, 119, 136, 165], + [153, 170, 187, 165], + [204, 221, 238, 165], + [0, 0, 0, 166], + [0, 0, 0, 167], + [0, 85, 170, 168], + [0, 0, 0, 169], + [0, 3, 6, 170], + [9, 12, 15, 170], + [18, 21, 24, 170], + [27, 30, 33, 170], + [36, 39, 42, 170], + [45, 48, 51, 170], + [54, 57, 60, 170], + [63, 66, 69, 170], + [72, 75, 78, 170], + [81, 84, 87, 170], + [90, 93, 96, 170], + [99, 102, 105, 170], + [108, 111, 114, 170], + [117, 120, 123, 170], + [126, 129, 132, 170], + [135, 138, 141, 170], + [144, 147, 150, 170], + [153, 156, 159, 170], + [162, 165, 168, 170], + [171, 174, 177, 170], + [180, 183, 186, 170], + [189, 192, 195, 170], + [198, 201, 204, 170], + [207, 210, 213, 170], + [216, 219, 222, 170], + [225, 228, 231, 170], + [234, 237, 240, 170], + [243, 246, 249, 170], + [252, 0, 0, 170], + [0, 85, 170, 171], + [0, 0, 0, 172], + [0, 0, 0, 173], + [0, 85, 170, 174], + [0, 51, 102, 175], + [153, 204, 0, 175], + [0, 0, 0, 176], + [0, 85, 170, 177], + [0, 0, 0, 178], + [0, 0, 0, 179], + [0, 17, 34, 180], + [51, 68, 85, 180], + [102, 119, 136, 180], + [153, 170, 187, 180], + [204, 221, 238, 180], + [0, 0, 0, 181], + [0, 0, 0, 182], + [0, 85, 170, 183], + [0, 0, 0, 184], + [0, 51, 102, 185], + [153, 204, 0, 185], + [0, 85, 170, 186], + [0, 15, 30, 187], + [45, 60, 75, 187], + [90, 105, 120, 187], + [135, 150, 165, 187], + [180, 195, 210, 187], + [225, 240, 0, 187], + [0, 0, 0, 188], + [0, 85, 170, 189], + [0, 51, 102, 190], + [153, 204, 0, 190], + [0, 0, 0, 191], + [0, 85, 170, 192], + [0, 0, 0, 193], + [0, 0, 0, 194], + [0, 17, 34, 195], + [51, 68, 85, 195], + [102, 119, 136, 195], + [153, 170, 187, 195], + [204, 221, 238, 195], + [0, 0, 0, 196], + [0, 0, 0, 197], + [0, 85, 170, 198], + [0, 0, 0, 199], + [0, 51, 102, 200], + [153, 204, 0, 200], + [0, 85, 170, 201], + [0, 0, 0, 202], + [0, 0, 0, 203], + [0, 5, 10, 204], + [15, 20, 25, 204], + [30, 35, 40, 204], + [45, 50, 55, 204], + [60, 65, 70, 204], + [75, 80, 85, 204], + [90, 95, 100, 204], + [105, 110, 115, 204], + [120, 125, 130, 204], + [135, 140, 145, 204], + [150, 155, 160, 204], + [165, 170, 175, 204], + [180, 185, 190, 204], + [195, 200, 205, 204], + [210, 215, 220, 204], + [225, 230, 235, 204], + [240, 245, 250, 204], + [0, 51, 102, 205], + [153, 204, 0, 205], + [0, 0, 0, 206], + [0, 85, 170, 207], + [0, 0, 0, 208], + [0, 0, 0, 209], + [0, 17, 34, 210], + [51, 68, 85, 210], + [102, 119, 136, 210], + [153, 170, 187, 210], + [204, 221, 238, 210], + [0, 0, 0, 211], + [0, 0, 0, 212], + [0, 85, 170, 213], + [0, 0, 0, 214], + [0, 51, 102, 215], + [153, 204, 0, 215], + [0, 85, 170, 216], + [0, 0, 0, 217], + [0, 0, 0, 218], + [0, 85, 170, 219], + [0, 51, 102, 220], + [153, 204, 0, 220], + [0, 15, 30, 221], + [45, 60, 75, 221], + [90, 105, 120, 221], + [135, 150, 165, 221], + [180, 195, 210, 221], + [225, 240, 0, 221], + [0, 85, 170, 222], + [0, 0, 0, 223], + [0, 0, 0, 224], + [0, 17, 34, 225], + [51, 68, 85, 225], + [102, 119, 136, 225], + [153, 170, 187, 225], + [204, 221, 238, 225], + [0, 0, 0, 226], + [0, 0, 0, 227], + [0, 85, 170, 228], + [0, 0, 0, 229], + [0, 51, 102, 230], + [153, 204, 0, 230], + [0, 85, 170, 231], + [0, 0, 0, 232], + [0, 0, 0, 233], + [0, 85, 170, 234], + [0, 51, 102, 235], + [153, 204, 0, 235], + [0, 0, 0, 236], + [0, 85, 170, 237], + [0, 15, 30, 238], + [45, 60, 75, 238], + [90, 105, 120, 238], + [135, 150, 165, 238], + [180, 195, 210, 238], + [225, 240, 0, 238], + [0, 0, 0, 239], + [0, 17, 34, 240], + [51, 68, 85, 240], + [102, 119, 136, 240], + [153, 170, 187, 240], + [204, 221, 238, 240], + [0, 0, 0, 241], + [0, 0, 0, 242], + [0, 85, 170, 243], + [0, 0, 0, 244], + [0, 51, 102, 245], + [153, 204, 0, 245], + [0, 85, 170, 246], + [0, 0, 0, 247], + [0, 0, 0, 248], + [0, 85, 170, 249], + [0, 51, 102, 250], + [153, 204, 0, 250], + [0, 0, 0, 251], + [0, 85, 170, 252], + [0, 0, 0, 253], + [0, 0, 0, 254], + [5, 15, 25, 102], + [35, 40, 45, 102], + [50, 55, 60, 102], + [65, 70, 75, 102], + [80, 85, 90, 102], + [95, 100, 105, 102], + [110, 115, 120, 102], + [125, 130, 135, 102], + [140, 145, 150, 102], + [155, 160, 165, 102], + [170, 175, 180, 102], + [185, 190, 195, 102], + [200, 205, 210, 102], + [215, 220, 225, 102], + [230, 235, 240, 102], + [245, 250, 0, 102], + [15, 45, 60, 68], + [75, 90, 105, 68], + [120, 135, 150, 68], + [165, 180, 195, 68], + [210, 225, 240, 68], + [17, 34, 51, 45], + [68, 85, 102, 45], + [119, 136, 153, 45], + [170, 187, 204, 45], + [221, 238, 0, 45], + [17, 51, 68, 60], + [85, 102, 119, 60], + [136, 153, 170, 60], + [187, 204, 221, 60], + [238, 0, 0, 60], + [17, 34, 51, 90], + [68, 85, 102, 90], + [119, 136, 153, 90], + [170, 187, 204, 90], + [221, 238, 0, 90], + [17, 34, 51, 120], + [68, 85, 102, 120], + [119, 136, 153, 120], + [170, 187, 204, 120], + [221, 238, 0, 120], + [20, 25, 30, 51], + [35, 40, 45, 51], + [50, 55, 60, 51], + [65, 70, 75, 51], + [80, 85, 90, 51], + [95, 100, 105, 51], + [110, 115, 120, 51], + [125, 130, 135, 51], + [140, 145, 150, 51], + [155, 160, 165, 51], + [170, 175, 180, 51], + [185, 190, 195, 51], + [200, 205, 210, 51], + [215, 220, 225, 51], + [230, 235, 240, 51], + [245, 250, 0, 51], + [45, 60, 75, 17], + [90, 105, 120, 17], + [135, 150, 165, 17], + [180, 195, 210, 17], + [225, 240, 0, 17], + [45, 75, 90, 34], + [105, 120, 135, 34], + [150, 165, 180, 34], + [195, 210, 225, 34], + [240, 0, 0, 34], + [51, 153, 204, 20], + [51, 102, 153, 25], + [204, 0, 0, 25], + [51, 85, 119, 30], + [136, 153, 170, 30], + [187, 204, 221, 30], + [238, 0, 0, 30], + [51, 102, 153, 35], + [204, 0, 0, 35], + [51, 102, 153, 40], + [204, 0, 0, 40], + [51, 102, 153, 50], + [204, 0, 0, 50], + [51, 102, 153, 55], + [204, 0, 0, 55], + [51, 102, 153, 70], + [204, 0, 0, 70], + [51, 102, 153, 80], + [204, 0, 0, 80], + [51, 102, 153, 100], + [204, 0, 0, 100], + [51, 102, 153, 110], + [204, 0, 0, 110], + [65, 67, 69, 0], + [71, 73, 75, 0], + [77, 79, 81, 0], + [83, 85, 87, 0], + [89, 91, 93, 0], + [95, 97, 99, 0], + [101, 103, 105, 0], + [107, 109, 111, 0], + [113, 115, 117, 0], + [119, 121, 123, 0], + [125, 127, 128, 0], + [129, 130, 131, 0], + [132, 133, 134, 0], + [135, 136, 137, 0], + [138, 139, 140, 0], + [141, 142, 143, 0], + [144, 145, 146, 0], + [147, 148, 149, 0], + [150, 151, 152, 0], + [153, 154, 155, 0], + [156, 157, 158, 0], + [159, 160, 161, 0], + [162, 163, 164, 0], + [165, 166, 167, 0], + [168, 169, 170, 0], + [171, 172, 173, 0], + [174, 175, 176, 0], + [177, 178, 179, 0], + [180, 181, 182, 0], + [183, 184, 185, 0], + [186, 187, 188, 0], + [189, 190, 191, 0], + [192, 193, 194, 0], + [195, 196, 197, 0], + [198, 199, 200, 0], + [201, 202, 203, 0], + [204, 205, 206, 0], + [207, 208, 209, 0], + [210, 211, 212, 0], + [213, 214, 215, 0], + [216, 217, 218, 0], + [219, 220, 221, 0], + [222, 223, 224, 0], + [225, 226, 227, 0], + [228, 229, 230, 0], + [231, 232, 233, 0], + [234, 235, 236, 0], + [237, 238, 239, 0], + [240, 241, 242, 0], + [243, 244, 245, 0], + [246, 247, 248, 0], + [249, 250, 251, 0], + [252, 253, 254, 0], + [68, 85, 102, 15], + [119, 136, 153, 15], + [170, 187, 204, 15], + [221, 238, 0, 15], + [85, 170, 0, 3], + [85, 170, 0, 6], + [85, 170, 0, 9], + [85, 170, 0, 12], + [85, 170, 0, 18], + [85, 170, 0, 21], + [85, 170, 0, 24], + [85, 170, 0, 27], + [85, 170, 0, 33], + [85, 170, 0, 36], + [85, 170, 0, 39], + [85, 170, 0, 42], + [85, 170, 0, 48], + [85, 170, 0, 54], + [85, 170, 0, 57], + [85, 170, 0, 63], + [85, 170, 0, 66], + [85, 170, 0, 72], + [85, 170, 0, 78], + [85, 170, 0, 84], + [85, 170, 0, 96], + [85, 170, 0, 108], + [85, 170, 0, 114], + [85, 170, 0, 126], + [102, 153, 204, 5], + [153, 204, 0, 10], + ]; + let rgb_pixels = vec![ + [190, 152, 114], + [76, 38, 190], + [188, 188, 188], + [186, 124, 62], + [184, 184, 184], + [182, 182, 182], + [180, 168, 156], + [144, 132, 120], + [108, 96, 84], + [72, 60, 48], + [36, 24, 12], + [178, 178, 178], + [176, 176, 176], + [174, 116, 58], + [172, 172, 172], + [170, 168, 166], + [164, 162, 160], + [158, 156, 154], + [152, 150, 148], + [146, 144, 142], + [140, 138, 136], + [134, 132, 130], + [128, 126, 124], + [122, 120, 118], + [116, 114, 112], + [110, 108, 106], + [104, 102, 100], + [98, 96, 94], + [92, 90, 88], + [86, 84, 82], + [80, 78, 76], + [74, 72, 70], + [68, 66, 64], + [62, 60, 58], + [56, 54, 52], + [50, 48, 46], + [44, 42, 40], + [38, 36, 34], + [32, 30, 28], + [26, 24, 22], + [20, 18, 16], + [14, 12, 10], + [8, 6, 4], + [2, 170, 170], + [168, 112, 56], + [166, 166, 166], + [164, 164, 164], + [162, 108, 54], + [160, 128, 96], + [64, 32, 160], + [158, 158, 158], + [156, 104, 52], + [154, 154, 154], + [152, 152, 152], + [150, 140, 130], + [120, 110, 100], + [90, 80, 70], + [60, 50, 40], + [30, 20, 10], + [148, 148, 148], + [146, 146, 146], + [144, 96, 48], + [142, 142, 142], + [140, 112, 84], + [56, 28, 140], + [138, 92, 46], + [136, 128, 120], + [112, 104, 96], + [88, 80, 72], + [64, 56, 48], + [40, 32, 24], + [16, 8, 136], + [134, 134, 134], + [132, 88, 44], + [130, 104, 78], + [52, 26, 130], + [128, 128, 128], + [127, 127, 127], + [126, 84, 42], + [125, 100, 75], + [50, 25, 125], + [124, 124, 124], + [123, 82, 41], + [122, 122, 122], + [121, 121, 121], + [120, 112, 104], + [96, 88, 80], + [72, 64, 56], + [48, 40, 32], + [24, 16, 8], + [119, 112, 105], + [98, 91, 84], + [77, 70, 63], + [56, 49, 42], + [35, 28, 21], + [14, 7, 119], + [118, 118, 118], + [117, 78, 39], + [116, 116, 116], + [115, 92, 69], + [46, 23, 115], + [114, 76, 38], + [113, 113, 113], + [112, 112, 112], + [111, 74, 37], + [110, 88, 66], + [44, 22, 110], + [109, 109, 109], + [108, 72, 36], + [107, 107, 107], + [106, 106, 106], + [105, 98, 91], + [84, 77, 70], + [63, 56, 49], + [42, 35, 28], + [21, 14, 7], + [104, 104, 104], + [103, 103, 103], + [102, 100, 98], + [96, 94, 92], + [90, 88, 86], + [84, 82, 80], + [78, 76, 74], + [72, 70, 68], + [66, 64, 62], + [60, 58, 56], + [54, 52, 50], + [48, 46, 44], + [42, 40, 38], + [36, 34, 32], + [30, 28, 26], + [24, 22, 20], + [18, 16, 14], + [12, 10, 8], + [6, 4, 2], + [101, 101, 101], + [100, 80, 60], + [40, 20, 100], + [99, 66, 33], + [98, 98, 98], + [97, 97, 97], + [96, 64, 32], + [95, 76, 57], + [38, 19, 95], + [94, 94, 94], + [93, 62, 31], + [92, 92, 92], + [91, 91, 91], + [90, 84, 78], + [72, 66, 60], + [54, 48, 42], + [36, 30, 24], + [18, 12, 6], + [89, 89, 89], + [88, 88, 88], + [87, 58, 29], + [86, 86, 86], + [85, 84, 83], + [82, 81, 80], + [79, 78, 77], + [76, 75, 74], + [73, 72, 71], + [70, 69, 68], + [67, 66, 65], + [64, 63, 62], + [61, 60, 59], + [58, 57, 56], + [55, 54, 53], + [52, 51, 50], + [49, 48, 47], + [46, 45, 44], + [43, 42, 41], + [40, 39, 38], + [37, 36, 35], + [34, 33, 32], + [31, 30, 29], + [28, 27, 26], + [25, 24, 23], + [22, 21, 20], + [19, 18, 17], + [16, 15, 14], + [13, 12, 11], + [10, 9, 8], + [7, 6, 5], + [4, 3, 2], + [1, 85, 85], + [84, 56, 28], + [83, 83, 83], + [82, 82, 82], + [81, 54, 27], + [80, 64, 48], + [32, 16, 80], + [79, 79, 79], + [78, 52, 26], + [77, 77, 77], + [76, 76, 76], + [75, 70, 65], + [60, 55, 50], + [45, 40, 35], + [30, 25, 20], + [15, 10, 5], + [74, 74, 74], + [73, 73, 73], + [72, 48, 24], + [71, 71, 71], + [70, 56, 42], + [28, 14, 70], + [69, 46, 23], + [68, 64, 60], + [56, 52, 48], + [44, 40, 36], + [32, 28, 24], + [20, 16, 12], + [8, 4, 68], + [67, 67, 67], + [66, 44, 22], + [65, 52, 39], + [26, 13, 65], + [64, 64, 64], + [63, 42, 21], + [62, 62, 62], + [61, 61, 61], + [60, 56, 52], + [48, 44, 40], + [36, 32, 28], + [24, 20, 16], + [12, 8, 4], + [59, 59, 59], + [58, 58, 58], + [57, 38, 19], + [56, 56, 56], + [55, 44, 33], + [22, 11, 55], + [54, 36, 18], + [53, 53, 53], + [52, 52, 52], + [51, 50, 49], + [48, 47, 46], + [45, 44, 43], + [42, 41, 40], + [39, 38, 37], + [36, 35, 34], + [33, 32, 31], + [30, 29, 28], + [27, 26, 25], + [24, 23, 22], + [21, 20, 19], + [18, 17, 16], + [15, 14, 13], + [12, 11, 10], + [9, 8, 7], + [6, 5, 4], + [3, 2, 1], + [50, 40, 30], + [20, 10, 50], + [49, 49, 49], + [48, 32, 16], + [47, 47, 47], + [46, 46, 46], + [45, 42, 39], + [36, 33, 30], + [27, 24, 21], + [18, 15, 12], + [9, 6, 3], + [44, 44, 44], + [43, 43, 43], + [42, 28, 14], + [41, 41, 41], + [40, 32, 24], + [16, 8, 40], + [39, 26, 13], + [38, 38, 38], + [37, 37, 37], + [36, 24, 12], + [35, 28, 21], + [14, 7, 35], + [34, 32, 30], + [28, 26, 24], + [22, 20, 18], + [16, 14, 12], + [10, 8, 6], + [4, 2, 34], + [33, 22, 11], + [32, 32, 32], + [31, 31, 31], + [30, 28, 26], + [24, 22, 20], + [18, 16, 14], + [12, 10, 8], + [6, 4, 2], + [29, 29, 29], + [28, 28, 28], + [27, 18, 9], + [26, 26, 26], + [25, 20, 15], + [10, 5, 25], + [24, 16, 8], + [23, 23, 23], + [22, 22, 22], + [21, 14, 7], + [20, 16, 12], + [8, 4, 20], + [19, 19, 19], + [18, 12, 6], + [17, 16, 15], + [14, 13, 12], + [11, 10, 9], + [8, 7, 6], + [5, 4, 3], + [2, 1, 17], + [16, 16, 16], + [15, 14, 13], + [12, 11, 10], + [9, 8, 7], + [6, 5, 4], + [3, 2, 1], + [14, 14, 14], + [13, 13, 13], + [12, 8, 4], + [11, 11, 11], + [10, 8, 6], + [4, 2, 10], + [9, 6, 3], + [8, 8, 8], + [7, 7, 7], + [6, 4, 2], + [5, 4, 3], + [2, 1, 5], + [4, 4, 4], + [3, 2, 1], + [2, 2, 2], + [1, 1, 1], + [150, 144, 138], + [132, 129, 126], + [123, 120, 117], + [114, 111, 108], + [105, 102, 99], + [96, 93, 90], + [87, 84, 81], + [78, 75, 72], + [69, 66, 63], + [60, 57, 54], + [51, 48, 45], + [42, 39, 36], + [33, 30, 27], + [24, 21, 18], + [15, 12, 9], + [6, 3, 153], + [176, 154, 143], + [132, 121, 110], + [99, 88, 77], + [66, 55, 44], + [33, 22, 11], + [196, 182, 168], + [154, 140, 126], + [112, 98, 84], + [70, 56, 42], + [28, 14, 210], + [182, 156, 143], + [130, 117, 104], + [91, 78, 65], + [52, 39, 26], + [13, 195, 195], + [154, 143, 132], + [121, 110, 99], + [88, 77, 66], + [55, 44, 33], + [22, 11, 165], + [126, 117, 108], + [99, 90, 81], + [72, 63, 54], + [45, 36, 27], + [18, 9, 135], + [188, 184, 180], + [176, 172, 168], + [164, 160, 156], + [152, 148, 144], + [140, 136, 132], + [128, 124, 120], + [116, 112, 108], + [104, 100, 96], + [92, 88, 84], + [80, 76, 72], + [68, 64, 60], + [56, 52, 48], + [44, 40, 36], + [32, 28, 24], + [20, 16, 12], + [8, 4, 204], + [196, 182, 168], + [154, 140, 126], + [112, 98, 84], + [70, 56, 42], + [28, 14, 238], + [182, 156, 143], + [130, 117, 104], + [91, 78, 65], + [52, 39, 26], + [13, 221, 221], + [188, 94, 47], + [184, 138, 92], + [46, 230, 230], + [180, 150, 120], + [105, 90, 75], + [60, 45, 30], + [15, 225, 225], + [176, 132, 88], + [44, 220, 220], + [172, 129, 86], + [43, 215, 215], + [164, 123, 82], + [41, 205, 205], + [160, 120, 80], + [40, 200, 200], + [148, 111, 74], + [37, 185, 185], + [140, 105, 70], + [35, 175, 175], + [124, 93, 62], + [31, 155, 155], + [116, 87, 58], + [29, 145, 145], + [190, 188, 186], + [184, 182, 180], + [178, 176, 174], + [172, 170, 168], + [166, 164, 162], + [160, 158, 156], + [154, 152, 150], + [148, 146, 144], + [142, 140, 138], + [136, 134, 132], + [130, 128, 127], + [126, 125, 124], + [123, 122, 121], + [120, 119, 118], + [117, 116, 115], + [114, 113, 112], + [111, 110, 109], + [108, 107, 106], + [105, 104, 103], + [102, 101, 100], + [99, 98, 97], + [96, 95, 94], + [93, 92, 91], + [90, 89, 88], + [87, 86, 85], + [84, 83, 82], + [81, 80, 79], + [78, 77, 76], + [75, 74, 73], + [72, 71, 70], + [69, 68, 67], + [66, 65, 64], + [63, 62, 61], + [60, 59, 58], + [57, 56, 55], + [54, 53, 52], + [51, 50, 49], + [48, 47, 46], + [45, 44, 43], + [42, 41, 40], + [39, 38, 37], + [36, 35, 34], + [33, 32, 31], + [30, 29, 28], + [27, 26, 25], + [24, 23, 22], + [21, 20, 19], + [18, 17, 16], + [15, 14, 13], + [12, 11, 10], + [9, 8, 7], + [6, 5, 4], + [3, 2, 1], + [176, 160, 144], + [128, 112, 96], + [80, 64, 48], + [32, 16, 240], + [168, 84, 252], + [166, 83, 249], + [164, 82, 246], + [162, 81, 243], + [158, 79, 237], + [156, 78, 234], + [154, 77, 231], + [152, 76, 228], + [148, 74, 222], + [146, 73, 219], + [144, 72, 216], + [142, 71, 213], + [138, 69, 207], + [134, 67, 201], + [132, 66, 198], + [128, 64, 192], + [126, 63, 189], + [122, 61, 183], + [118, 59, 177], + [114, 57, 171], + [106, 53, 159], + [98, 49, 147], + [94, 47, 141], + [86, 43, 129], + [150, 100, 50], + [98, 49, 245], + ]; + for (&cmyk_pixel, rgb_pixel) in cmyk_pixels.iter().zip(rgb_pixels) { + single_pix_correct(cmyk_pixel, rgb_pixel); + } + } + + #[cfg(feature = "benchmarks")] + #[bench] + fn bench_cmyk_to_rgb(b: &mut Bencher) { + let mut v = Vec::with_capacity((W * H * 4) as usize); + for c in 0..=255 { + for k in 0..=255 { + v.push(c as u8); + v.push(0); + v.push(0); + v.push(k as u8); + } + } + + b.iter(|| { + cmyk_to_rgb(&v); + }); + } + + #[cfg(feature = "benchmarks")] + #[bench] + fn bench_cmyk_to_rgb_single(b: &mut Bencher) { + b.iter(|| { + cmyk_to_rgb(&[128, 128, 128, 128]); + }); + } +} diff --git a/vendor/image/src/codecs/jpeg/encoder.rs b/vendor/image/src/codecs/jpeg/encoder.rs new file mode 100644 index 0000000..edb2a05 --- /dev/null +++ b/vendor/image/src/codecs/jpeg/encoder.rs @@ -0,0 +1,1074 @@ +#![allow(clippy::too_many_arguments)] + +use std::borrow::Cow; +use std::convert::TryFrom; +use std::io::{self, Write}; + +use crate::error::{ + ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError, + UnsupportedErrorKind, +}; +use crate::image::{ImageEncoder, ImageFormat}; +use crate::utils::clamp; +use crate::{ColorType, GenericImageView, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba}; + +use super::entropy::build_huff_lut_const; +use super::transform; +use crate::traits::PixelWithColorType; + +// Markers +// Baseline DCT +static SOF0: u8 = 0xC0; +// Huffman Tables +static DHT: u8 = 0xC4; +// Start of Image (standalone) +static SOI: u8 = 0xD8; +// End of image (standalone) +static EOI: u8 = 0xD9; +// Start of Scan +static SOS: u8 = 0xDA; +// Quantization Tables +static DQT: u8 = 0xDB; +// Application segments start and end +static APP0: u8 = 0xE0; + +// section K.1 +// table K.1 +#[rustfmt::skip] +static STD_LUMA_QTABLE: [u8; 64] = [ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99, +]; + +// table K.2 +#[rustfmt::skip] +static STD_CHROMA_QTABLE: [u8; 64] = [ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, +]; + +// section K.3 +// Code lengths and values for table K.3 +static STD_LUMA_DC_CODE_LENGTHS: [u8; 16] = [ + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +static STD_LUMA_DC_VALUES: [u8; 12] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +]; + +static STD_LUMA_DC_HUFF_LUT: [(u8, u16); 256] = + build_huff_lut_const(&STD_LUMA_DC_CODE_LENGTHS, &STD_LUMA_DC_VALUES); + +// Code lengths and values for table K.4 +static STD_CHROMA_DC_CODE_LENGTHS: [u8; 16] = [ + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +static STD_CHROMA_DC_VALUES: [u8; 12] = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +]; + +static STD_CHROMA_DC_HUFF_LUT: [(u8, u16); 256] = + build_huff_lut_const(&STD_CHROMA_DC_CODE_LENGTHS, &STD_CHROMA_DC_VALUES); + +// Code lengths and values for table k.5 +static STD_LUMA_AC_CODE_LENGTHS: [u8; 16] = [ + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, +]; + +static STD_LUMA_AC_VALUES: [u8; 162] = [ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA, +]; + +static STD_LUMA_AC_HUFF_LUT: [(u8, u16); 256] = + build_huff_lut_const(&STD_LUMA_AC_CODE_LENGTHS, &STD_LUMA_AC_VALUES); + +// Code lengths and values for table k.6 +static STD_CHROMA_AC_CODE_LENGTHS: [u8; 16] = [ + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, +]; +static STD_CHROMA_AC_VALUES: [u8; 162] = [ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, + 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA, +]; + +static STD_CHROMA_AC_HUFF_LUT: [(u8, u16); 256] = + build_huff_lut_const(&STD_CHROMA_AC_CODE_LENGTHS, &STD_CHROMA_AC_VALUES); + +static DCCLASS: u8 = 0; +static ACCLASS: u8 = 1; + +static LUMADESTINATION: u8 = 0; +static CHROMADESTINATION: u8 = 1; + +static LUMAID: u8 = 1; +static CHROMABLUEID: u8 = 2; +static CHROMAREDID: u8 = 3; + +/// The permutation of dct coefficients. +#[rustfmt::skip] +static UNZIGZAG: [u8; 64] = [ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, +]; + +/// A representation of a JPEG component +#[derive(Copy, Clone)] +struct Component { + /// The Component's identifier + id: u8, + + /// Horizontal sampling factor + h: u8, + + /// Vertical sampling factor + v: u8, + + /// The quantization table selector + tq: u8, + + /// Index to the Huffman DC Table + dc_table: u8, + + /// Index to the AC Huffman Table + ac_table: u8, + + /// The dc prediction of the component + _dc_pred: i32, +} + +pub(crate) struct BitWriter<W> { + w: W, + accumulator: u32, + nbits: u8, +} + +impl<W: Write> BitWriter<W> { + fn new(w: W) -> Self { + BitWriter { + w, + accumulator: 0, + nbits: 0, + } + } + + fn write_bits(&mut self, bits: u16, size: u8) -> io::Result<()> { + if size == 0 { + return Ok(()); + } + + self.nbits += size; + self.accumulator |= u32::from(bits) << (32 - self.nbits) as usize; + + while self.nbits >= 8 { + let byte = self.accumulator >> 24; + self.w.write_all(&[byte as u8])?; + + if byte == 0xFF { + self.w.write_all(&[0x00])?; + } + + self.nbits -= 8; + self.accumulator <<= 8; + } + + Ok(()) + } + + fn pad_byte(&mut self) -> io::Result<()> { + self.write_bits(0x7F, 7) + } + + fn huffman_encode(&mut self, val: u8, table: &[(u8, u16); 256]) -> io::Result<()> { + let (size, code) = table[val as usize]; + + if size > 16 { + panic!("bad huffman value"); + } + + self.write_bits(code, size) + } + + fn write_block( + &mut self, + block: &[i32; 64], + prevdc: i32, + dctable: &[(u8, u16); 256], + actable: &[(u8, u16); 256], + ) -> io::Result<i32> { + // Differential DC encoding + let dcval = block[0]; + let diff = dcval - prevdc; + let (size, value) = encode_coefficient(diff); + + self.huffman_encode(size, dctable)?; + self.write_bits(value, size)?; + + // Figure F.2 + let mut zero_run = 0; + + for &k in &UNZIGZAG[1..] { + if block[k as usize] == 0 { + zero_run += 1; + } else { + while zero_run > 15 { + self.huffman_encode(0xF0, actable)?; + zero_run -= 16; + } + + let (size, value) = encode_coefficient(block[k as usize]); + let symbol = (zero_run << 4) | size; + + self.huffman_encode(symbol, actable)?; + self.write_bits(value, size)?; + + zero_run = 0; + } + } + + if block[UNZIGZAG[63] as usize] == 0 { + self.huffman_encode(0x00, actable)?; + } + + Ok(dcval) + } + + fn write_marker(&mut self, marker: u8) -> io::Result<()> { + self.w.write_all(&[0xFF, marker]) + } + + fn write_segment(&mut self, marker: u8, data: &[u8]) -> io::Result<()> { + self.w.write_all(&[0xFF, marker])?; + self.w.write_all(&(data.len() as u16 + 2).to_be_bytes())?; + self.w.write_all(data) + } +} + +/// Represents a unit in which the density of an image is measured +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PixelDensityUnit { + /// Represents the absence of a unit, the values indicate only a + /// [pixel aspect ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio) + PixelAspectRatio, + + /// Pixels per inch (2.54 cm) + Inches, + + /// Pixels per centimeter + Centimeters, +} + +/// Represents the pixel density of an image +/// +/// For example, a 300 DPI image is represented by: +/// +/// ```rust +/// use image::codecs::jpeg::*; +/// let hdpi = PixelDensity::dpi(300); +/// assert_eq!(hdpi, PixelDensity {density: (300,300), unit: PixelDensityUnit::Inches}) +/// ``` +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct PixelDensity { + /// A couple of values for (Xdensity, Ydensity) + pub density: (u16, u16), + /// The unit in which the density is measured + pub unit: PixelDensityUnit, +} + +impl PixelDensity { + /// Creates the most common pixel density type: + /// the horizontal and the vertical density are equal, + /// and measured in pixels per inch. + pub fn dpi(density: u16) -> Self { + PixelDensity { + density: (density, density), + unit: PixelDensityUnit::Inches, + } + } +} + +impl Default for PixelDensity { + /// Returns a pixel density with a pixel aspect ratio of 1 + fn default() -> Self { + PixelDensity { + density: (1, 1), + unit: PixelDensityUnit::PixelAspectRatio, + } + } +} + +/// The representation of a JPEG encoder +pub struct JpegEncoder<W> { + writer: BitWriter<W>, + + components: Vec<Component>, + tables: Vec<[u8; 64]>, + + luma_dctable: Cow<'static, [(u8, u16); 256]>, + luma_actable: Cow<'static, [(u8, u16); 256]>, + chroma_dctable: Cow<'static, [(u8, u16); 256]>, + chroma_actable: Cow<'static, [(u8, u16); 256]>, + + pixel_density: PixelDensity, +} + +impl<W: Write> JpegEncoder<W> { + /// Create a new encoder that writes its output to ```w``` + pub fn new(w: W) -> JpegEncoder<W> { + JpegEncoder::new_with_quality(w, 75) + } + + /// Create a new encoder that writes its output to ```w```, and has + /// the quality parameter ```quality``` with a value in the range 1-100 + /// where 1 is the worst and 100 is the best. + pub fn new_with_quality(w: W, quality: u8) -> JpegEncoder<W> { + let components = vec![ + Component { + id: LUMAID, + h: 1, + v: 1, + tq: LUMADESTINATION, + dc_table: LUMADESTINATION, + ac_table: LUMADESTINATION, + _dc_pred: 0, + }, + Component { + id: CHROMABLUEID, + h: 1, + v: 1, + tq: CHROMADESTINATION, + dc_table: CHROMADESTINATION, + ac_table: CHROMADESTINATION, + _dc_pred: 0, + }, + Component { + id: CHROMAREDID, + h: 1, + v: 1, + tq: CHROMADESTINATION, + dc_table: CHROMADESTINATION, + ac_table: CHROMADESTINATION, + _dc_pred: 0, + }, + ]; + + // Derive our quantization table scaling value using the libjpeg algorithm + let scale = u32::from(clamp(quality, 1, 100)); + let scale = if scale < 50 { + 5000 / scale + } else { + 200 - scale * 2 + }; + + let mut tables = vec![STD_LUMA_QTABLE, STD_CHROMA_QTABLE]; + tables.iter_mut().for_each(|t| { + t.iter_mut().for_each(|v| { + *v = clamp( + (u32::from(*v) * scale + 50) / 100, + 1, + u32::from(u8::max_value()), + ) as u8; + }) + }); + + JpegEncoder { + writer: BitWriter::new(w), + + components, + tables, + + luma_dctable: Cow::Borrowed(&STD_LUMA_DC_HUFF_LUT), + luma_actable: Cow::Borrowed(&STD_LUMA_AC_HUFF_LUT), + chroma_dctable: Cow::Borrowed(&STD_CHROMA_DC_HUFF_LUT), + chroma_actable: Cow::Borrowed(&STD_CHROMA_AC_HUFF_LUT), + + pixel_density: PixelDensity::default(), + } + } + + /// Set the pixel density of the images the encoder will encode. + /// If this method is not called, then a default pixel aspect ratio of 1x1 will be applied, + /// and no DPI information will be stored in the image. + pub fn set_pixel_density(&mut self, pixel_density: PixelDensity) { + self.pixel_density = pixel_density; + } + + /// Encodes the image stored in the raw byte buffer ```image``` + /// that has dimensions ```width``` and ```height``` + /// and ```ColorType``` ```c``` + /// + /// The Image in encoded with subsampling ratio 4:2:2 + pub fn encode( + &mut self, + image: &[u8], + width: u32, + height: u32, + color_type: ColorType, + ) -> ImageResult<()> { + match color_type { + ColorType::L8 => { + let image: ImageBuffer<Luma<_>, _> = + ImageBuffer::from_raw(width, height, image).unwrap(); + self.encode_image(&image) + } + ColorType::La8 => { + let image: ImageBuffer<LumaA<_>, _> = + ImageBuffer::from_raw(width, height, image).unwrap(); + self.encode_image(&image) + } + ColorType::Rgb8 => { + let image: ImageBuffer<Rgb<_>, _> = + ImageBuffer::from_raw(width, height, image).unwrap(); + self.encode_image(&image) + } + ColorType::Rgba8 => { + let image: ImageBuffer<Rgba<_>, _> = + ImageBuffer::from_raw(width, height, image).unwrap(); + self.encode_image(&image) + } + _ => Err(ImageError::Unsupported( + UnsupportedError::from_format_and_kind( + ImageFormat::Jpeg.into(), + UnsupportedErrorKind::Color(color_type.into()), + ), + )), + } + } + + /// Encodes the given image. + /// + /// As a special feature this does not require the whole image to be present in memory at the + /// same time such that it may be computed on the fly, which is why this method exists on this + /// encoder but not on others. Instead the encoder will iterate over 8-by-8 blocks of pixels at + /// a time, inspecting each pixel exactly once. You can rely on this behaviour when calling + /// this method. + /// + /// The Image in encoded with subsampling ratio 4:2:2 + pub fn encode_image<I: GenericImageView>(&mut self, image: &I) -> ImageResult<()> + where + I::Pixel: PixelWithColorType, + { + let n = I::Pixel::CHANNEL_COUNT; + let color_type = I::Pixel::COLOR_TYPE; + let num_components = if n == 1 || n == 2 { 1 } else { 3 }; + + self.writer.write_marker(SOI)?; + + let mut buf = Vec::new(); + + build_jfif_header(&mut buf, self.pixel_density); + self.writer.write_segment(APP0, &buf)?; + + build_frame_header( + &mut buf, + 8, + // TODO: not idiomatic yet. Should be an EncodingError and mention jpg. Further it + // should check dimensions prior to writing. + u16::try_from(image.width()).map_err(|_| { + ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + )) + })?, + u16::try_from(image.height()).map_err(|_| { + ImageError::Parameter(ParameterError::from_kind( + ParameterErrorKind::DimensionMismatch, + )) + })?, + &self.components[..num_components], + ); + self.writer.write_segment(SOF0, &buf)?; + + assert_eq!(self.tables.len(), 2); + let numtables = if num_components == 1 { 1 } else { 2 }; + + for (i, table) in self.tables[..numtables].iter().enumerate() { + build_quantization_segment(&mut buf, 8, i as u8, table); + self.writer.write_segment(DQT, &buf)?; + } + + build_huffman_segment( + &mut buf, + DCCLASS, + LUMADESTINATION, + &STD_LUMA_DC_CODE_LENGTHS, + &STD_LUMA_DC_VALUES, + ); + self.writer.write_segment(DHT, &buf)?; + + build_huffman_segment( + &mut buf, + ACCLASS, + LUMADESTINATION, + &STD_LUMA_AC_CODE_LENGTHS, + &STD_LUMA_AC_VALUES, + ); + self.writer.write_segment(DHT, &buf)?; + + if num_components == 3 { + build_huffman_segment( + &mut buf, + DCCLASS, + CHROMADESTINATION, + &STD_CHROMA_DC_CODE_LENGTHS, + &STD_CHROMA_DC_VALUES, + ); + self.writer.write_segment(DHT, &buf)?; + + build_huffman_segment( + &mut buf, + ACCLASS, + CHROMADESTINATION, + &STD_CHROMA_AC_CODE_LENGTHS, + &STD_CHROMA_AC_VALUES, + ); + self.writer.write_segment(DHT, &buf)?; + } + + build_scan_header(&mut buf, &self.components[..num_components]); + self.writer.write_segment(SOS, &buf)?; + + if color_type.has_color() { + self.encode_rgb(image) + } else { + self.encode_gray(image) + }?; + + self.writer.pad_byte()?; + self.writer.write_marker(EOI)?; + Ok(()) + } + + fn encode_gray<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> { + let mut yblock = [0u8; 64]; + let mut y_dcprev = 0; + let mut dct_yblock = [0i32; 64]; + + for y in (0..image.height()).step_by(8) { + for x in (0..image.width()).step_by(8) { + copy_blocks_gray(image, x, y, &mut yblock); + + // Level shift and fdct + // Coeffs are scaled by 8 + transform::fdct(&yblock, &mut dct_yblock); + + // Quantization + for (i, dct) in dct_yblock.iter_mut().enumerate() { + *dct = ((*dct / 8) as f32 / f32::from(self.tables[0][i])).round() as i32; + } + + let la = &*self.luma_actable; + let ld = &*self.luma_dctable; + + y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?; + } + } + + Ok(()) + } + + fn encode_rgb<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> { + let mut y_dcprev = 0; + let mut cb_dcprev = 0; + let mut cr_dcprev = 0; + + let mut dct_yblock = [0i32; 64]; + let mut dct_cb_block = [0i32; 64]; + let mut dct_cr_block = [0i32; 64]; + + let mut yblock = [0u8; 64]; + let mut cb_block = [0u8; 64]; + let mut cr_block = [0u8; 64]; + + for y in (0..image.height()).step_by(8) { + for x in (0..image.width()).step_by(8) { + // RGB -> YCbCr + copy_blocks_ycbcr(image, x, y, &mut yblock, &mut cb_block, &mut cr_block); + + // Level shift and fdct + // Coeffs are scaled by 8 + transform::fdct(&yblock, &mut dct_yblock); + transform::fdct(&cb_block, &mut dct_cb_block); + transform::fdct(&cr_block, &mut dct_cr_block); + + // Quantization + for i in 0usize..64 { + dct_yblock[i] = + ((dct_yblock[i] / 8) as f32 / f32::from(self.tables[0][i])).round() as i32; + dct_cb_block[i] = ((dct_cb_block[i] / 8) as f32 / f32::from(self.tables[1][i])) + .round() as i32; + dct_cr_block[i] = ((dct_cr_block[i] / 8) as f32 / f32::from(self.tables[1][i])) + .round() as i32; + } + + let la = &*self.luma_actable; + let ld = &*self.luma_dctable; + let cd = &*self.chroma_dctable; + let ca = &*self.chroma_actable; + + y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?; + cb_dcprev = self.writer.write_block(&dct_cb_block, cb_dcprev, cd, ca)?; + cr_dcprev = self.writer.write_block(&dct_cr_block, cr_dcprev, cd, ca)?; + } + } + + Ok(()) + } +} + +impl<W: Write> ImageEncoder for JpegEncoder<W> { + fn write_image( + mut self, + buf: &[u8], + width: u32, + height: u32, + color_type: ColorType, + ) -> ImageResult<()> { + self.encode(buf, width, height, color_type) + } +} + +fn build_jfif_header(m: &mut Vec<u8>, density: PixelDensity) { + m.clear(); + m.extend_from_slice(b"JFIF"); + m.extend_from_slice(&[ + 0, + 0x01, + 0x02, + match density.unit { + PixelDensityUnit::PixelAspectRatio => 0x00, + PixelDensityUnit::Inches => 0x01, + PixelDensityUnit::Centimeters => 0x02, + }, + ]); + m.extend_from_slice(&density.density.0.to_be_bytes()); + m.extend_from_slice(&density.density.1.to_be_bytes()); + m.extend_from_slice(&[0, 0]); +} + +fn build_frame_header( + m: &mut Vec<u8>, + precision: u8, + width: u16, + height: u16, + components: &[Component], +) { + m.clear(); + + m.push(precision); + m.extend_from_slice(&height.to_be_bytes()); + m.extend_from_slice(&width.to_be_bytes()); + m.push(components.len() as u8); + + for &comp in components.iter() { + let hv = (comp.h << 4) | comp.v; + m.extend_from_slice(&[comp.id, hv, comp.tq]); + } +} + +fn build_scan_header(m: &mut Vec<u8>, components: &[Component]) { + m.clear(); + + m.push(components.len() as u8); + + for &comp in components.iter() { + let tables = (comp.dc_table << 4) | comp.ac_table; + m.extend_from_slice(&[comp.id, tables]); + } + + // spectral start and end, approx. high and low + m.extend_from_slice(&[0, 63, 0]); +} + +fn build_huffman_segment( + m: &mut Vec<u8>, + class: u8, + destination: u8, + numcodes: &[u8; 16], + values: &[u8], +) { + m.clear(); + + let tcth = (class << 4) | destination; + m.push(tcth); + + m.extend_from_slice(numcodes); + + let sum: usize = numcodes.iter().map(|&x| x as usize).sum(); + + assert_eq!(sum, values.len()); + + m.extend_from_slice(values); +} + +fn build_quantization_segment(m: &mut Vec<u8>, precision: u8, identifier: u8, qtable: &[u8; 64]) { + m.clear(); + + let p = if precision == 8 { 0 } else { 1 }; + + let pqtq = (p << 4) | identifier; + m.push(pqtq); + + for &i in &UNZIGZAG[..] { + m.push(qtable[i as usize]); + } +} + +fn encode_coefficient(coefficient: i32) -> (u8, u16) { + let mut magnitude = coefficient.unsigned_abs() as u16; + let mut num_bits = 0u8; + + while magnitude > 0 { + magnitude >>= 1; + num_bits += 1; + } + + let mask = (1 << num_bits as usize) - 1; + + let val = if coefficient < 0 { + (coefficient - 1) as u16 & mask + } else { + coefficient as u16 & mask + }; + + (num_bits, val) +} + +#[inline] +fn rgb_to_ycbcr<P: Pixel>(pixel: P) -> (u8, u8, u8) { + use crate::traits::Primitive; + use num_traits::cast::ToPrimitive; + + let [r, g, b] = pixel.to_rgb().0; + let max: f32 = P::Subpixel::DEFAULT_MAX_VALUE.to_f32().unwrap(); + let r: f32 = r.to_f32().unwrap(); + let g: f32 = g.to_f32().unwrap(); + let b: f32 = b.to_f32().unwrap(); + + // Coefficients from JPEG File Interchange Format (Version 1.02), multiplied for 255 maximum. + let y = 76.245 / max * r + 149.685 / max * g + 29.07 / max * b; + let cb = -43.0185 / max * r - 84.4815 / max * g + 127.5 / max * b + 128.; + let cr = 127.5 / max * r - 106.7685 / max * g - 20.7315 / max * b + 128.; + + (y as u8, cb as u8, cr as u8) +} + +/// Returns the pixel at (x,y) if (x,y) is in the image, +/// otherwise the closest pixel in the image +#[inline] +fn pixel_at_or_near<I: GenericImageView>(source: &I, x: u32, y: u32) -> I::Pixel { + if source.in_bounds(x, y) { + source.get_pixel(x, y) + } else { + source.get_pixel(x.min(source.width() - 1), y.min(source.height() - 1)) + } +} + +fn copy_blocks_ycbcr<I: GenericImageView>( + source: &I, + x0: u32, + y0: u32, + yb: &mut [u8; 64], + cbb: &mut [u8; 64], + crb: &mut [u8; 64], +) { + for y in 0..8 { + for x in 0..8 { + let pixel = pixel_at_or_near(source, x + x0, y + y0); + let (yc, cb, cr) = rgb_to_ycbcr(pixel); + + yb[(y * 8 + x) as usize] = yc; + cbb[(y * 8 + x) as usize] = cb; + crb[(y * 8 + x) as usize] = cr; + } + } +} + +fn copy_blocks_gray<I: GenericImageView>(source: &I, x0: u32, y0: u32, gb: &mut [u8; 64]) { + use num_traits::cast::ToPrimitive; + for y in 0..8 { + for x in 0..8 { + let pixel = pixel_at_or_near(source, x0 + x, y0 + y); + let [luma] = pixel.to_luma().0; + gb[(y * 8 + x) as usize] = luma.to_u8().unwrap(); + } + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + #[cfg(feature = "benchmarks")] + extern crate test; + #[cfg(feature = "benchmarks")] + use test::Bencher; + + use crate::color::ColorType; + use crate::error::ParameterErrorKind::DimensionMismatch; + use crate::image::ImageDecoder; + use crate::{ImageEncoder, ImageError}; + + use super::super::JpegDecoder; + use super::{ + build_frame_header, build_huffman_segment, build_jfif_header, build_quantization_segment, + build_scan_header, Component, JpegEncoder, PixelDensity, DCCLASS, LUMADESTINATION, + STD_LUMA_DC_CODE_LENGTHS, STD_LUMA_DC_VALUES, + }; + + fn decode(encoded: &[u8]) -> Vec<u8> { + let decoder = JpegDecoder::new(Cursor::new(encoded)).expect("Could not decode image"); + + let mut decoded = vec![0; decoder.total_bytes() as usize]; + decoder + .read_image(&mut decoded) + .expect("Could not decode image"); + decoded + } + + #[test] + fn roundtrip_sanity_check() { + // create a 1x1 8-bit image buffer containing a single red pixel + let img = [255u8, 0, 0]; + + // encode it into a memory buffer + let mut encoded_img = Vec::new(); + { + let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100); + encoder + .write_image(&img, 1, 1, ColorType::Rgb8) + .expect("Could not encode image"); + } + + // decode it from the memory buffer + { + let decoded = decode(&encoded_img); + // note that, even with the encode quality set to 100, we do not get the same image + // back. Therefore, we're going to assert that it's at least red-ish: + assert_eq!(3, decoded.len()); + assert!(decoded[0] > 0x80); + assert!(decoded[1] < 0x80); + assert!(decoded[2] < 0x80); + } + } + + #[test] + fn grayscale_roundtrip_sanity_check() { + // create a 2x2 8-bit image buffer containing a white diagonal + let img = [255u8, 0, 0, 255]; + + // encode it into a memory buffer + let mut encoded_img = Vec::new(); + { + let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100); + encoder + .write_image(&img[..], 2, 2, ColorType::L8) + .expect("Could not encode image"); + } + + // decode it from the memory buffer + { + let decoded = decode(&encoded_img); + // note that, even with the encode quality set to 100, we do not get the same image + // back. Therefore, we're going to assert that the diagonal is at least white-ish: + assert_eq!(4, decoded.len()); + assert!(decoded[0] > 0x80); + assert!(decoded[1] < 0x80); + assert!(decoded[2] < 0x80); + assert!(decoded[3] > 0x80); + } + } + + #[test] + fn jfif_header_density_check() { + let mut buffer = Vec::new(); + build_jfif_header(&mut buffer, PixelDensity::dpi(300)); + assert_eq!( + buffer, + vec![ + b'J', + b'F', + b'I', + b'F', + 0, + 1, + 2, // JFIF version 1.2 + 1, // density is in dpi + 300u16.to_be_bytes()[0], + 300u16.to_be_bytes()[1], + 300u16.to_be_bytes()[0], + 300u16.to_be_bytes()[1], + 0, + 0, // No thumbnail + ] + ); + } + + #[test] + fn test_image_too_large() { + // JPEG cannot encode images larger than 65,535×65,535 + // create a 65,536×1 8-bit black image buffer + let img = [0; 65_536]; + // Try to encode an image that is too large + let mut encoded = Vec::new(); + let encoder = JpegEncoder::new_with_quality(&mut encoded, 100); + let result = encoder.write_image(&img, 65_536, 1, ColorType::L8); + match result { + Err(ImageError::Parameter(err)) => { + assert_eq!(err.kind(), DimensionMismatch) + } + other => { + assert!( + false, + "Encoding an image that is too large should return a DimensionError \ + it returned {:?} instead", + other + ) + } + } + } + + #[test] + fn test_build_jfif_header() { + let mut buf = vec![]; + let density = PixelDensity::dpi(100); + build_jfif_header(&mut buf, density); + assert_eq!( + buf, + [0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0, 100, 0, 100, 0, 0] + ); + } + + #[test] + fn test_build_frame_header() { + let mut buf = vec![]; + let components = vec![ + Component { + id: 1, + h: 1, + v: 1, + tq: 5, + dc_table: 5, + ac_table: 5, + _dc_pred: 0, + }, + Component { + id: 2, + h: 1, + v: 1, + tq: 4, + dc_table: 4, + ac_table: 4, + _dc_pred: 0, + }, + ]; + build_frame_header(&mut buf, 5, 100, 150, &components); + assert_eq!( + buf, + [5, 0, 150, 0, 100, 2, 1, 1 << 4 | 1, 5, 2, 1 << 4 | 1, 4] + ); + } + + #[test] + fn test_build_scan_header() { + let mut buf = vec![]; + let components = vec![ + Component { + id: 1, + h: 1, + v: 1, + tq: 5, + dc_table: 5, + ac_table: 5, + _dc_pred: 0, + }, + Component { + id: 2, + h: 1, + v: 1, + tq: 4, + dc_table: 4, + ac_table: 4, + _dc_pred: 0, + }, + ]; + build_scan_header(&mut buf, &components); + assert_eq!(buf, [2, 1, 5 << 4 | 5, 2, 4 << 4 | 4, 0, 63, 0]); + } + + #[test] + fn test_build_huffman_segment() { + let mut buf = vec![]; + build_huffman_segment( + &mut buf, + DCCLASS, + LUMADESTINATION, + &STD_LUMA_DC_CODE_LENGTHS, + &STD_LUMA_DC_VALUES, + ); + assert_eq!( + buf, + vec![ + 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, + 10, 11 + ] + ); + } + + #[test] + fn test_build_quantization_segment() { + let mut buf = vec![]; + let qtable = [0u8; 64]; + build_quantization_segment(&mut buf, 8, 1, &qtable); + let mut expected = vec![]; + expected.push(0 << 4 | 1); + expected.extend_from_slice(&[0; 64]); + assert_eq!(buf, expected) + } + + #[cfg(feature = "benchmarks")] + #[bench] + fn bench_jpeg_encoder_new(b: &mut Bencher) { + b.iter(|| { + let mut y = vec![]; + let x = JpegEncoder::new(&mut y); + }) + } +} diff --git a/vendor/image/src/codecs/jpeg/entropy.rs b/vendor/image/src/codecs/jpeg/entropy.rs new file mode 100644 index 0000000..5bdcef6 --- /dev/null +++ b/vendor/image/src/codecs/jpeg/entropy.rs @@ -0,0 +1,63 @@ +/// Given an array containing the number of codes of each code length, +/// this function generates the huffman codes lengths and their respective +/// code lengths as specified by the JPEG spec. +const fn derive_codes_and_sizes(bits: &[u8; 16]) -> ([u8; 256], [u16; 256]) { + let mut huffsize = [0u8; 256]; + let mut huffcode = [0u16; 256]; + + let mut k = 0; + + // Annex C.2 + // Figure C.1 + // Generate table of individual code lengths + let mut i = 0; + while i < 16 { + let mut j = 0; + while j < bits[i as usize] { + huffsize[k] = i + 1; + k += 1; + j += 1; + } + i += 1; + } + + huffsize[k] = 0; + + // Annex C.2 + // Figure C.2 + // Generate table of huffman codes + k = 0; + let mut code = 0u16; + let mut size = huffsize[0]; + + while huffsize[k] != 0 { + huffcode[k] = code; + code += 1; + k += 1; + + if huffsize[k] == size { + continue; + } + + // FIXME there is something wrong with this code + let diff = huffsize[k].wrapping_sub(size); + code = if diff < 16 { code << diff as usize } else { 0 }; + + size = size.wrapping_add(diff); + } + + (huffsize, huffcode) +} + +pub(crate) const fn build_huff_lut_const(bits: &[u8; 16], huffval: &[u8]) -> [(u8, u16); 256] { + let mut lut = [(17u8, 0u16); 256]; + let (huffsize, huffcode) = derive_codes_and_sizes(bits); + + let mut i = 0; + while i < huffval.len() { + lut[huffval[i] as usize] = (huffsize[i], huffcode[i]); + i += 1; + } + + lut +} diff --git a/vendor/image/src/codecs/jpeg/mod.rs b/vendor/image/src/codecs/jpeg/mod.rs new file mode 100644 index 0000000..4242733 --- /dev/null +++ b/vendor/image/src/codecs/jpeg/mod.rs @@ -0,0 +1,16 @@ +//! Decoding and Encoding of JPEG Images +//! +//! JPEG (Joint Photographic Experts Group) is an image format that supports lossy compression. +//! This module implements the Baseline JPEG standard. +//! +//! # Related Links +//! * <http://www.w3.org/Graphics/JPEG/itu-t81.pdf> - The JPEG specification +//! + +pub use self::decoder::JpegDecoder; +pub use self::encoder::{JpegEncoder, PixelDensity, PixelDensityUnit}; + +mod decoder; +mod encoder; +mod entropy; +mod transform; diff --git a/vendor/image/src/codecs/jpeg/transform.rs b/vendor/image/src/codecs/jpeg/transform.rs new file mode 100644 index 0000000..1ca01a9 --- /dev/null +++ b/vendor/image/src/codecs/jpeg/transform.rs @@ -0,0 +1,196 @@ +/* +fdct is a Rust translation of jfdctint.c from the +Independent JPEG Group's libjpeg version 9a +obtained from http://www.ijg.org/files/jpegsr9a.zip +It comes with the following conditions of distribution and use: + + In plain English: + + 1. We don't promise that this software works. (But if you find any bugs, + please let us know!) + 2. You can use this software for whatever you want. You don't have to pay us. + 3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + + In legalese: + + The authors make NO WARRANTY or representation, either express or implied, + with respect to this software, its quality, accuracy, merchantability, or + fitness for a particular purpose. This software is provided "AS IS", and you, + its user, assume the entire risk as to its quality and accuracy. + + This software is copyright (C) 1991-2014, Thomas G. Lane, Guido Vollbeding. + All Rights Reserved except as specified below. + + Permission is hereby granted to use, copy, modify, and distribute this + software (or portions thereof) for any purpose, without fee, subject to these + conditions: + (1) If any part of the source code for this software is distributed, then this + README file must be included, with this copyright and no-warranty notice + unaltered; and any additions, deletions, or changes to the original files + must be clearly indicated in accompanying documentation. + (2) If only executable code is distributed, then the accompanying + documentation must state that "this software is based in part on the work of + the Independent JPEG Group". + (3) Permission for use of this software is granted only if the user accepts + full responsibility for any undesirable consequences; the authors accept + NO LIABILITY for damages of any kind. + + These conditions apply to any software derived from or based on the IJG code, + not just to the unmodified library. If you use our work, you ought to + acknowledge us. + + Permission is NOT granted for the use of any IJG author's name or company name + in advertising or publicity relating to this software or products derived from + it. This software may be referred to only as "the Independent JPEG Group's + software". + + We specifically permit and encourage the use of this software as the basis of + commercial products, provided that all warranty or liability claims are + assumed by the product vendor. +*/ + +static CONST_BITS: i32 = 13; +static PASS1_BITS: i32 = 2; + +static FIX_0_298631336: i32 = 2446; +static FIX_0_390180644: i32 = 3196; +static FIX_0_541196100: i32 = 4433; +static FIX_0_765366865: i32 = 6270; +static FIX_0_899976223: i32 = 7373; +static FIX_1_175875602: i32 = 9633; +static FIX_1_501321110: i32 = 12_299; +static FIX_1_847759065: i32 = 15_137; +static FIX_1_961570560: i32 = 16_069; +static FIX_2_053119869: i32 = 16_819; +static FIX_2_562915447: i32 = 20_995; +static FIX_3_072711026: i32 = 25_172; + +pub(crate) fn fdct(samples: &[u8; 64], coeffs: &mut [i32; 64]) { + // Pass 1: process rows. + // Results are scaled by sqrt(8) compared to a true DCT + // furthermore we scale the results by 2**PASS1_BITS + for y in 0usize..8 { + let y0 = y * 8; + + // Even part + let t0 = i32::from(samples[y0]) + i32::from(samples[y0 + 7]); + let t1 = i32::from(samples[y0 + 1]) + i32::from(samples[y0 + 6]); + let t2 = i32::from(samples[y0 + 2]) + i32::from(samples[y0 + 5]); + let t3 = i32::from(samples[y0 + 3]) + i32::from(samples[y0 + 4]); + + let t10 = t0 + t3; + let t12 = t0 - t3; + let t11 = t1 + t2; + let t13 = t1 - t2; + + let t0 = i32::from(samples[y0]) - i32::from(samples[y0 + 7]); + let t1 = i32::from(samples[y0 + 1]) - i32::from(samples[y0 + 6]); + let t2 = i32::from(samples[y0 + 2]) - i32::from(samples[y0 + 5]); + let t3 = i32::from(samples[y0 + 3]) - i32::from(samples[y0 + 4]); + + // Apply unsigned -> signed conversion + coeffs[y0] = (t10 + t11 - 8 * 128) << PASS1_BITS as usize; + coeffs[y0 + 4] = (t10 - t11) << PASS1_BITS as usize; + + let mut z1 = (t12 + t13) * FIX_0_541196100; + // Add fudge factor here for final descale + z1 += 1 << (CONST_BITS - PASS1_BITS - 1) as usize; + + coeffs[y0 + 2] = (z1 + t12 * FIX_0_765366865) >> (CONST_BITS - PASS1_BITS) as usize; + coeffs[y0 + 6] = (z1 - t13 * FIX_1_847759065) >> (CONST_BITS - PASS1_BITS) as usize; + + // Odd part + let t12 = t0 + t2; + let t13 = t1 + t3; + + let mut z1 = (t12 + t13) * FIX_1_175875602; + // Add fudge factor here for final descale + z1 += 1 << (CONST_BITS - PASS1_BITS - 1) as usize; + + let mut t12 = t12 * (-FIX_0_390180644); + let mut t13 = t13 * (-FIX_1_961570560); + t12 += z1; + t13 += z1; + + let z1 = (t0 + t3) * (-FIX_0_899976223); + let mut t0 = t0 * FIX_1_501321110; + let mut t3 = t3 * FIX_0_298631336; + t0 += z1 + t12; + t3 += z1 + t13; + + let z1 = (t1 + t2) * (-FIX_2_562915447); + let mut t1 = t1 * FIX_3_072711026; + let mut t2 = t2 * FIX_2_053119869; + t1 += z1 + t13; + t2 += z1 + t12; + + coeffs[y0 + 1] = t0 >> (CONST_BITS - PASS1_BITS) as usize; + coeffs[y0 + 3] = t1 >> (CONST_BITS - PASS1_BITS) as usize; + coeffs[y0 + 5] = t2 >> (CONST_BITS - PASS1_BITS) as usize; + coeffs[y0 + 7] = t3 >> (CONST_BITS - PASS1_BITS) as usize; + } + + // Pass 2: process columns + // We remove the PASS1_BITS scaling but leave the results scaled up an + // overall factor of 8 + for x in (0usize..8).rev() { + // Even part + let t0 = coeffs[x] + coeffs[x + 8 * 7]; + let t1 = coeffs[x + 8] + coeffs[x + 8 * 6]; + let t2 = coeffs[x + 8 * 2] + coeffs[x + 8 * 5]; + let t3 = coeffs[x + 8 * 3] + coeffs[x + 8 * 4]; + + // Add fudge factor here for final descale + let t10 = t0 + t3 + (1 << (PASS1_BITS - 1) as usize); + let t12 = t0 - t3; + let t11 = t1 + t2; + let t13 = t1 - t2; + + let t0 = coeffs[x] - coeffs[x + 8 * 7]; + let t1 = coeffs[x + 8] - coeffs[x + 8 * 6]; + let t2 = coeffs[x + 8 * 2] - coeffs[x + 8 * 5]; + let t3 = coeffs[x + 8 * 3] - coeffs[x + 8 * 4]; + + coeffs[x] = (t10 + t11) >> PASS1_BITS as usize; + coeffs[x + 8 * 4] = (t10 - t11) >> PASS1_BITS as usize; + + let mut z1 = (t12 + t13) * FIX_0_541196100; + // Add fudge factor here for final descale + z1 += 1 << (CONST_BITS + PASS1_BITS - 1) as usize; + + coeffs[x + 8 * 2] = (z1 + t12 * FIX_0_765366865) >> (CONST_BITS + PASS1_BITS) as usize; + coeffs[x + 8 * 6] = (z1 - t13 * FIX_1_847759065) >> (CONST_BITS + PASS1_BITS) as usize; + + // Odd part + let t12 = t0 + t2; + let t13 = t1 + t3; + + let mut z1 = (t12 + t13) * FIX_1_175875602; + // Add fudge factor here for final descale + z1 += 1 << (CONST_BITS - PASS1_BITS - 1) as usize; + + let mut t12 = t12 * (-FIX_0_390180644); + let mut t13 = t13 * (-FIX_1_961570560); + t12 += z1; + t13 += z1; + + let z1 = (t0 + t3) * (-FIX_0_899976223); + let mut t0 = t0 * FIX_1_501321110; + let mut t3 = t3 * FIX_0_298631336; + t0 += z1 + t12; + t3 += z1 + t13; + + let z1 = (t1 + t2) * (-FIX_2_562915447); + let mut t1 = t1 * FIX_3_072711026; + let mut t2 = t2 * FIX_2_053119869; + t1 += z1 + t13; + t2 += z1 + t12; + + coeffs[x + 8] = t0 >> (CONST_BITS + PASS1_BITS) as usize; + coeffs[x + 8 * 3] = t1 >> (CONST_BITS + PASS1_BITS) as usize; + coeffs[x + 8 * 5] = t2 >> (CONST_BITS + PASS1_BITS) as usize; + coeffs[x + 8 * 7] = t3 >> (CONST_BITS + PASS1_BITS) as usize; + } +} |