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