1#![allow(clippy::too_many_arguments)]
2
3use std::borrow::Cow;
4use std::io::{self, Write};
5
6use crate::error::{
7 ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError,
8 UnsupportedErrorKind,
9};
10use crate::image::{ImageEncoder, ImageFormat};
11use crate::utils::clamp;
12use crate::{ColorType, GenericImageView, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba};
13
14use super::entropy::build_huff_lut_const;
15use super::transform;
16use crate::traits::PixelWithColorType;
17
18// Markers
19// Baseline DCT
20static SOF0: u8 = 0xC0;
21// Huffman Tables
22static DHT: u8 = 0xC4;
23// Start of Image (standalone)
24static SOI: u8 = 0xD8;
25// End of image (standalone)
26static EOI: u8 = 0xD9;
27// Start of Scan
28static SOS: u8 = 0xDA;
29// Quantization Tables
30static DQT: u8 = 0xDB;
31// Application segments start and end
32static APP0: u8 = 0xE0;
33
34// section K.1
35// table K.1
36#[rustfmt::skip]
37static STD_LUMA_QTABLE: [u8; 64] = [
38 16, 11, 10, 16, 24, 40, 51, 61,
39 12, 12, 14, 19, 26, 58, 60, 55,
40 14, 13, 16, 24, 40, 57, 69, 56,
41 14, 17, 22, 29, 51, 87, 80, 62,
42 18, 22, 37, 56, 68, 109, 103, 77,
43 24, 35, 55, 64, 81, 104, 113, 92,
44 49, 64, 78, 87, 103, 121, 120, 101,
45 72, 92, 95, 98, 112, 100, 103, 99,
46];
47
48// table K.2
49#[rustfmt::skip]
50static STD_CHROMA_QTABLE: [u8; 64] = [
51 17, 18, 24, 47, 99, 99, 99, 99,
52 18, 21, 26, 66, 99, 99, 99, 99,
53 24, 26, 56, 99, 99, 99, 99, 99,
54 47, 66, 99, 99, 99, 99, 99, 99,
55 99, 99, 99, 99, 99, 99, 99, 99,
56 99, 99, 99, 99, 99, 99, 99, 99,
57 99, 99, 99, 99, 99, 99, 99, 99,
58 99, 99, 99, 99, 99, 99, 99, 99,
59];
60
61// section K.3
62// Code lengths and values for table K.3
63static STD_LUMA_DC_CODE_LENGTHS: [u8; 16] = [
64 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65];
66
67static STD_LUMA_DC_VALUES: [u8; 12] = [
68 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
69];
70
71static STD_LUMA_DC_HUFF_LUT: [(u8, u16); 256] =
72 build_huff_lut_const(&STD_LUMA_DC_CODE_LENGTHS, &STD_LUMA_DC_VALUES);
73
74// Code lengths and values for table K.4
75static STD_CHROMA_DC_CODE_LENGTHS: [u8; 16] = [
76 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
77];
78
79static STD_CHROMA_DC_VALUES: [u8; 12] = [
80 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
81];
82
83static STD_CHROMA_DC_HUFF_LUT: [(u8, u16); 256] =
84 build_huff_lut_const(&STD_CHROMA_DC_CODE_LENGTHS, &STD_CHROMA_DC_VALUES);
85
86// Code lengths and values for table k.5
87static STD_LUMA_AC_CODE_LENGTHS: [u8; 16] = [
88 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D,
89];
90
91static STD_LUMA_AC_VALUES: [u8; 162] = [
92 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
93 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
94 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
95 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
96 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
97 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
98 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
99 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
100 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
101 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
102 0xF9, 0xFA,
103];
104
105static STD_LUMA_AC_HUFF_LUT: [(u8, u16); 256] =
106 build_huff_lut_const(&STD_LUMA_AC_CODE_LENGTHS, &STD_LUMA_AC_VALUES);
107
108// Code lengths and values for table k.6
109static STD_CHROMA_AC_CODE_LENGTHS: [u8; 16] = [
110 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
111];
112static STD_CHROMA_AC_VALUES: [u8; 162] = [
113 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
114 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
115 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
116 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
117 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
118 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
119 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
120 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
121 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
122 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
123 0xF9, 0xFA,
124];
125
126static STD_CHROMA_AC_HUFF_LUT: [(u8, u16); 256] =
127 build_huff_lut_const(&STD_CHROMA_AC_CODE_LENGTHS, &STD_CHROMA_AC_VALUES);
128
129static DCCLASS: u8 = 0;
130static ACCLASS: u8 = 1;
131
132static LUMADESTINATION: u8 = 0;
133static CHROMADESTINATION: u8 = 1;
134
135static LUMAID: u8 = 1;
136static CHROMABLUEID: u8 = 2;
137static CHROMAREDID: u8 = 3;
138
139/// The permutation of dct coefficients.
140#[rustfmt::skip]
141static UNZIGZAG: [u8; 64] = [
142 0, 1, 8, 16, 9, 2, 3, 10,
143 17, 24, 32, 25, 18, 11, 4, 5,
144 12, 19, 26, 33, 40, 48, 41, 34,
145 27, 20, 13, 6, 7, 14, 21, 28,
146 35, 42, 49, 56, 57, 50, 43, 36,
147 29, 22, 15, 23, 30, 37, 44, 51,
148 58, 59, 52, 45, 38, 31, 39, 46,
149 53, 60, 61, 54, 47, 55, 62, 63,
150];
151
152/// A representation of a JPEG component
153#[derive(Copy, Clone)]
154struct Component {
155 /// The Component's identifier
156 id: u8,
157
158 /// Horizontal sampling factor
159 h: u8,
160
161 /// Vertical sampling factor
162 v: u8,
163
164 /// The quantization table selector
165 tq: u8,
166
167 /// Index to the Huffman DC Table
168 dc_table: u8,
169
170 /// Index to the AC Huffman Table
171 ac_table: u8,
172
173 /// The dc prediction of the component
174 _dc_pred: i32,
175}
176
177pub(crate) struct BitWriter<W> {
178 w: W,
179 accumulator: u32,
180 nbits: u8,
181}
182
183impl<W: Write> BitWriter<W> {
184 fn new(w: W) -> Self {
185 BitWriter {
186 w,
187 accumulator: 0,
188 nbits: 0,
189 }
190 }
191
192 fn write_bits(&mut self, bits: u16, size: u8) -> io::Result<()> {
193 if size == 0 {
194 return Ok(());
195 }
196
197 self.nbits += size;
198 self.accumulator |= u32::from(bits) << (32 - self.nbits) as usize;
199
200 while self.nbits >= 8 {
201 let byte = self.accumulator >> 24;
202 self.w.write_all(&[byte as u8])?;
203
204 if byte == 0xFF {
205 self.w.write_all(&[0x00])?;
206 }
207
208 self.nbits -= 8;
209 self.accumulator <<= 8;
210 }
211
212 Ok(())
213 }
214
215 fn pad_byte(&mut self) -> io::Result<()> {
216 self.write_bits(0x7F, 7)
217 }
218
219 fn huffman_encode(&mut self, val: u8, table: &[(u8, u16); 256]) -> io::Result<()> {
220 let (size, code) = table[val as usize];
221
222 if size > 16 {
223 panic!("bad huffman value");
224 }
225
226 self.write_bits(code, size)
227 }
228
229 fn write_block(
230 &mut self,
231 block: &[i32; 64],
232 prevdc: i32,
233 dctable: &[(u8, u16); 256],
234 actable: &[(u8, u16); 256],
235 ) -> io::Result<i32> {
236 // Differential DC encoding
237 let dcval = block[0];
238 let diff = dcval - prevdc;
239 let (size, value) = encode_coefficient(diff);
240
241 self.huffman_encode(size, dctable)?;
242 self.write_bits(value, size)?;
243
244 // Figure F.2
245 let mut zero_run = 0;
246
247 for &k in &UNZIGZAG[1..] {
248 if block[k as usize] == 0 {
249 zero_run += 1;
250 } else {
251 while zero_run > 15 {
252 self.huffman_encode(0xF0, actable)?;
253 zero_run -= 16;
254 }
255
256 let (size, value) = encode_coefficient(block[k as usize]);
257 let symbol = (zero_run << 4) | size;
258
259 self.huffman_encode(symbol, actable)?;
260 self.write_bits(value, size)?;
261
262 zero_run = 0;
263 }
264 }
265
266 if block[UNZIGZAG[63] as usize] == 0 {
267 self.huffman_encode(0x00, actable)?;
268 }
269
270 Ok(dcval)
271 }
272
273 fn write_marker(&mut self, marker: u8) -> io::Result<()> {
274 self.w.write_all(&[0xFF, marker])
275 }
276
277 fn write_segment(&mut self, marker: u8, data: &[u8]) -> io::Result<()> {
278 self.w.write_all(&[0xFF, marker])?;
279 self.w.write_all(&(data.len() as u16 + 2).to_be_bytes())?;
280 self.w.write_all(data)
281 }
282}
283
284/// Represents a unit in which the density of an image is measured
285#[derive(Clone, Copy, Debug, Eq, PartialEq)]
286pub enum PixelDensityUnit {
287 /// Represents the absence of a unit, the values indicate only a
288 /// [pixel aspect ratio](https://en.wikipedia.org/wiki/Pixel_aspect_ratio)
289 PixelAspectRatio,
290
291 /// Pixels per inch (2.54 cm)
292 Inches,
293
294 /// Pixels per centimeter
295 Centimeters,
296}
297
298/// Represents the pixel density of an image
299///
300/// For example, a 300 DPI image is represented by:
301///
302/// ```rust
303/// use image::codecs::jpeg::*;
304/// let hdpi = PixelDensity::dpi(300);
305/// assert_eq!(hdpi, PixelDensity {density: (300,300), unit: PixelDensityUnit::Inches})
306/// ```
307#[derive(Clone, Copy, Debug, Eq, PartialEq)]
308pub struct PixelDensity {
309 /// A couple of values for (Xdensity, Ydensity)
310 pub density: (u16, u16),
311 /// The unit in which the density is measured
312 pub unit: PixelDensityUnit,
313}
314
315impl PixelDensity {
316 /// Creates the most common pixel density type:
317 /// the horizontal and the vertical density are equal,
318 /// and measured in pixels per inch.
319 pub fn dpi(density: u16) -> Self {
320 PixelDensity {
321 density: (density, density),
322 unit: PixelDensityUnit::Inches,
323 }
324 }
325}
326
327impl Default for PixelDensity {
328 /// Returns a pixel density with a pixel aspect ratio of 1
329 fn default() -> Self {
330 PixelDensity {
331 density: (1, 1),
332 unit: PixelDensityUnit::PixelAspectRatio,
333 }
334 }
335}
336
337/// The representation of a JPEG encoder
338pub struct JpegEncoder<W> {
339 writer: BitWriter<W>,
340
341 components: Vec<Component>,
342 tables: Vec<[u8; 64]>,
343
344 luma_dctable: Cow<'static, [(u8, u16); 256]>,
345 luma_actable: Cow<'static, [(u8, u16); 256]>,
346 chroma_dctable: Cow<'static, [(u8, u16); 256]>,
347 chroma_actable: Cow<'static, [(u8, u16); 256]>,
348
349 pixel_density: PixelDensity,
350}
351
352impl<W: Write> JpegEncoder<W> {
353 /// Create a new encoder that writes its output to ```w```
354 pub fn new(w: W) -> JpegEncoder<W> {
355 JpegEncoder::new_with_quality(w, 75)
356 }
357
358 /// Create a new encoder that writes its output to ```w```, and has
359 /// the quality parameter ```quality``` with a value in the range 1-100
360 /// where 1 is the worst and 100 is the best.
361 pub fn new_with_quality(w: W, quality: u8) -> JpegEncoder<W> {
362 let components = vec![
363 Component {
364 id: LUMAID,
365 h: 1,
366 v: 1,
367 tq: LUMADESTINATION,
368 dc_table: LUMADESTINATION,
369 ac_table: LUMADESTINATION,
370 _dc_pred: 0,
371 },
372 Component {
373 id: CHROMABLUEID,
374 h: 1,
375 v: 1,
376 tq: CHROMADESTINATION,
377 dc_table: CHROMADESTINATION,
378 ac_table: CHROMADESTINATION,
379 _dc_pred: 0,
380 },
381 Component {
382 id: CHROMAREDID,
383 h: 1,
384 v: 1,
385 tq: CHROMADESTINATION,
386 dc_table: CHROMADESTINATION,
387 ac_table: CHROMADESTINATION,
388 _dc_pred: 0,
389 },
390 ];
391
392 // Derive our quantization table scaling value using the libjpeg algorithm
393 let scale = u32::from(clamp(quality, 1, 100));
394 let scale = if scale < 50 {
395 5000 / scale
396 } else {
397 200 - scale * 2
398 };
399
400 let mut tables = vec![STD_LUMA_QTABLE, STD_CHROMA_QTABLE];
401 tables.iter_mut().for_each(|t| {
402 t.iter_mut().for_each(|v| {
403 *v = clamp(
404 (u32::from(*v) * scale + 50) / 100,
405 1,
406 u32::from(u8::max_value()),
407 ) as u8;
408 })
409 });
410
411 JpegEncoder {
412 writer: BitWriter::new(w),
413
414 components,
415 tables,
416
417 luma_dctable: Cow::Borrowed(&STD_LUMA_DC_HUFF_LUT),
418 luma_actable: Cow::Borrowed(&STD_LUMA_AC_HUFF_LUT),
419 chroma_dctable: Cow::Borrowed(&STD_CHROMA_DC_HUFF_LUT),
420 chroma_actable: Cow::Borrowed(&STD_CHROMA_AC_HUFF_LUT),
421
422 pixel_density: PixelDensity::default(),
423 }
424 }
425
426 /// Set the pixel density of the images the encoder will encode.
427 /// If this method is not called, then a default pixel aspect ratio of 1x1 will be applied,
428 /// and no DPI information will be stored in the image.
429 pub fn set_pixel_density(&mut self, pixel_density: PixelDensity) {
430 self.pixel_density = pixel_density;
431 }
432
433 /// Encodes the image stored in the raw byte buffer ```image```
434 /// that has dimensions ```width``` and ```height```
435 /// and ```ColorType``` ```c```
436 ///
437 /// The Image in encoded with subsampling ratio 4:2:2
438 ///
439 /// # Panics
440 ///
441 /// Panics if `width * height * color_type.bytes_per_pixel() != image.len()`.
442 #[track_caller]
443 pub fn encode(
444 &mut self,
445 image: &[u8],
446 width: u32,
447 height: u32,
448 color_type: ColorType,
449 ) -> ImageResult<()> {
450 let expected_buffer_len =
451 (width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
452 assert_eq!(
453 expected_buffer_len,
454 image.len() as u64,
455 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
456 image.len(),
457 );
458
459 match color_type {
460 ColorType::L8 => {
461 let image: ImageBuffer<Luma<_>, _> =
462 ImageBuffer::from_raw(width, height, image).unwrap();
463 self.encode_image(&image)
464 }
465 ColorType::La8 => {
466 let image: ImageBuffer<LumaA<_>, _> =
467 ImageBuffer::from_raw(width, height, image).unwrap();
468 self.encode_image(&image)
469 }
470 ColorType::Rgb8 => {
471 let image: ImageBuffer<Rgb<_>, _> =
472 ImageBuffer::from_raw(width, height, image).unwrap();
473 self.encode_image(&image)
474 }
475 ColorType::Rgba8 => {
476 let image: ImageBuffer<Rgba<_>, _> =
477 ImageBuffer::from_raw(width, height, image).unwrap();
478 self.encode_image(&image)
479 }
480 _ => Err(ImageError::Unsupported(
481 UnsupportedError::from_format_and_kind(
482 ImageFormat::Jpeg.into(),
483 UnsupportedErrorKind::Color(color_type.into()),
484 ),
485 )),
486 }
487 }
488
489 /// Encodes the given image.
490 ///
491 /// As a special feature this does not require the whole image to be present in memory at the
492 /// same time such that it may be computed on the fly, which is why this method exists on this
493 /// encoder but not on others. Instead the encoder will iterate over 8-by-8 blocks of pixels at
494 /// a time, inspecting each pixel exactly once. You can rely on this behaviour when calling
495 /// this method.
496 ///
497 /// The Image in encoded with subsampling ratio 4:2:2
498 pub fn encode_image<I: GenericImageView>(&mut self, image: &I) -> ImageResult<()>
499 where
500 I::Pixel: PixelWithColorType,
501 {
502 let n = I::Pixel::CHANNEL_COUNT;
503 let color_type = I::Pixel::COLOR_TYPE;
504 let num_components = if n == 1 || n == 2 { 1 } else { 3 };
505
506 self.writer.write_marker(SOI)?;
507
508 let mut buf = Vec::new();
509
510 build_jfif_header(&mut buf, self.pixel_density);
511 self.writer.write_segment(APP0, &buf)?;
512
513 build_frame_header(
514 &mut buf,
515 8,
516 // TODO: not idiomatic yet. Should be an EncodingError and mention jpg. Further it
517 // should check dimensions prior to writing.
518 u16::try_from(image.width()).map_err(|_| {
519 ImageError::Parameter(ParameterError::from_kind(
520 ParameterErrorKind::DimensionMismatch,
521 ))
522 })?,
523 u16::try_from(image.height()).map_err(|_| {
524 ImageError::Parameter(ParameterError::from_kind(
525 ParameterErrorKind::DimensionMismatch,
526 ))
527 })?,
528 &self.components[..num_components],
529 );
530 self.writer.write_segment(SOF0, &buf)?;
531
532 assert_eq!(self.tables.len(), 2);
533 let numtables = if num_components == 1 { 1 } else { 2 };
534
535 for (i, table) in self.tables[..numtables].iter().enumerate() {
536 build_quantization_segment(&mut buf, 8, i as u8, table);
537 self.writer.write_segment(DQT, &buf)?;
538 }
539
540 build_huffman_segment(
541 &mut buf,
542 DCCLASS,
543 LUMADESTINATION,
544 &STD_LUMA_DC_CODE_LENGTHS,
545 &STD_LUMA_DC_VALUES,
546 );
547 self.writer.write_segment(DHT, &buf)?;
548
549 build_huffman_segment(
550 &mut buf,
551 ACCLASS,
552 LUMADESTINATION,
553 &STD_LUMA_AC_CODE_LENGTHS,
554 &STD_LUMA_AC_VALUES,
555 );
556 self.writer.write_segment(DHT, &buf)?;
557
558 if num_components == 3 {
559 build_huffman_segment(
560 &mut buf,
561 DCCLASS,
562 CHROMADESTINATION,
563 &STD_CHROMA_DC_CODE_LENGTHS,
564 &STD_CHROMA_DC_VALUES,
565 );
566 self.writer.write_segment(DHT, &buf)?;
567
568 build_huffman_segment(
569 &mut buf,
570 ACCLASS,
571 CHROMADESTINATION,
572 &STD_CHROMA_AC_CODE_LENGTHS,
573 &STD_CHROMA_AC_VALUES,
574 );
575 self.writer.write_segment(DHT, &buf)?;
576 }
577
578 build_scan_header(&mut buf, &self.components[..num_components]);
579 self.writer.write_segment(SOS, &buf)?;
580
581 if color_type.has_color() {
582 self.encode_rgb(image)
583 } else {
584 self.encode_gray(image)
585 }?;
586
587 self.writer.pad_byte()?;
588 self.writer.write_marker(EOI)?;
589 Ok(())
590 }
591
592 fn encode_gray<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
593 let mut yblock = [0u8; 64];
594 let mut y_dcprev = 0;
595 let mut dct_yblock = [0i32; 64];
596
597 for y in (0..image.height()).step_by(8) {
598 for x in (0..image.width()).step_by(8) {
599 copy_blocks_gray(image, x, y, &mut yblock);
600
601 // Level shift and fdct
602 // Coeffs are scaled by 8
603 transform::fdct(&yblock, &mut dct_yblock);
604
605 // Quantization
606 for (i, dct) in dct_yblock.iter_mut().enumerate() {
607 *dct = ((*dct / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
608 }
609
610 let la = &*self.luma_actable;
611 let ld = &*self.luma_dctable;
612
613 y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
614 }
615 }
616
617 Ok(())
618 }
619
620 fn encode_rgb<I: GenericImageView>(&mut self, image: &I) -> io::Result<()> {
621 let mut y_dcprev = 0;
622 let mut cb_dcprev = 0;
623 let mut cr_dcprev = 0;
624
625 let mut dct_yblock = [0i32; 64];
626 let mut dct_cb_block = [0i32; 64];
627 let mut dct_cr_block = [0i32; 64];
628
629 let mut yblock = [0u8; 64];
630 let mut cb_block = [0u8; 64];
631 let mut cr_block = [0u8; 64];
632
633 for y in (0..image.height()).step_by(8) {
634 for x in (0..image.width()).step_by(8) {
635 // RGB -> YCbCr
636 copy_blocks_ycbcr(image, x, y, &mut yblock, &mut cb_block, &mut cr_block);
637
638 // Level shift and fdct
639 // Coeffs are scaled by 8
640 transform::fdct(&yblock, &mut dct_yblock);
641 transform::fdct(&cb_block, &mut dct_cb_block);
642 transform::fdct(&cr_block, &mut dct_cr_block);
643
644 // Quantization
645 for i in 0usize..64 {
646 dct_yblock[i] =
647 ((dct_yblock[i] / 8) as f32 / f32::from(self.tables[0][i])).round() as i32;
648 dct_cb_block[i] = ((dct_cb_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
649 .round() as i32;
650 dct_cr_block[i] = ((dct_cr_block[i] / 8) as f32 / f32::from(self.tables[1][i]))
651 .round() as i32;
652 }
653
654 let la = &*self.luma_actable;
655 let ld = &*self.luma_dctable;
656 let cd = &*self.chroma_dctable;
657 let ca = &*self.chroma_actable;
658
659 y_dcprev = self.writer.write_block(&dct_yblock, y_dcprev, ld, la)?;
660 cb_dcprev = self.writer.write_block(&dct_cb_block, cb_dcprev, cd, ca)?;
661 cr_dcprev = self.writer.write_block(&dct_cr_block, cr_dcprev, cd, ca)?;
662 }
663 }
664
665 Ok(())
666 }
667}
668
669impl<W: Write> ImageEncoder for JpegEncoder<W> {
670 #[track_caller]
671 fn write_image(
672 mut self,
673 buf: &[u8],
674 width: u32,
675 height: u32,
676 color_type: ColorType,
677 ) -> ImageResult<()> {
678 self.encode(image:buf, width, height, color_type)
679 }
680}
681
682fn build_jfif_header(m: &mut Vec<u8>, density: PixelDensity) {
683 m.clear();
684 m.extend_from_slice(b"JFIF");
685 m.extend_from_slice(&[
686 0,
687 0x01,
688 0x02,
689 match density.unit {
690 PixelDensityUnit::PixelAspectRatio => 0x00,
691 PixelDensityUnit::Inches => 0x01,
692 PixelDensityUnit::Centimeters => 0x02,
693 },
694 ]);
695 m.extend_from_slice(&density.density.0.to_be_bytes());
696 m.extend_from_slice(&density.density.1.to_be_bytes());
697 m.extend_from_slice(&[0, 0]);
698}
699
700fn build_frame_header(
701 m: &mut Vec<u8>,
702 precision: u8,
703 width: u16,
704 height: u16,
705 components: &[Component],
706) {
707 m.clear();
708
709 m.push(precision);
710 m.extend_from_slice(&height.to_be_bytes());
711 m.extend_from_slice(&width.to_be_bytes());
712 m.push(components.len() as u8);
713
714 for &comp: Component in components.iter() {
715 let hv: u8 = (comp.h << 4) | comp.v;
716 m.extend_from_slice(&[comp.id, hv, comp.tq]);
717 }
718}
719
720fn build_scan_header(m: &mut Vec<u8>, components: &[Component]) {
721 m.clear();
722
723 m.push(components.len() as u8);
724
725 for &comp: Component in components.iter() {
726 let tables: u8 = (comp.dc_table << 4) | comp.ac_table;
727 m.extend_from_slice(&[comp.id, tables]);
728 }
729
730 // spectral start and end, approx. high and low
731 m.extend_from_slice(&[0, 63, 0]);
732}
733
734fn build_huffman_segment(
735 m: &mut Vec<u8>,
736 class: u8,
737 destination: u8,
738 numcodes: &[u8; 16],
739 values: &[u8],
740) {
741 m.clear();
742
743 let tcth: u8 = (class << 4) | destination;
744 m.push(tcth);
745
746 m.extend_from_slice(numcodes);
747
748 let sum: usize = numcodes.iter().map(|&x: u8| x as usize).sum();
749
750 assert_eq!(sum, values.len());
751
752 m.extend_from_slice(values);
753}
754
755fn build_quantization_segment(m: &mut Vec<u8>, precision: u8, identifier: u8, qtable: &[u8; 64]) {
756 m.clear();
757
758 let p: u8 = if precision == 8 { 0 } else { 1 };
759
760 let pqtq: u8 = (p << 4) | identifier;
761 m.push(pqtq);
762
763 for &i: u8 in &UNZIGZAG[..] {
764 m.push(qtable[i as usize]);
765 }
766}
767
768fn encode_coefficient(coefficient: i32) -> (u8, u16) {
769 let mut magnitude: u16 = coefficient.unsigned_abs() as u16;
770 let mut num_bits: u8 = 0u8;
771
772 while magnitude > 0 {
773 magnitude >>= 1;
774 num_bits += 1;
775 }
776
777 let mask: u16 = (1 << num_bits as usize) - 1;
778
779 let val: u16 = if coefficient < 0 {
780 (coefficient - 1) as u16 & mask
781 } else {
782 coefficient as u16 & mask
783 };
784
785 (num_bits, val)
786}
787
788#[inline]
789fn rgb_to_ycbcr<P: Pixel>(pixel: P) -> (u8, u8, u8) {
790 use crate::traits::Primitive;
791 use num_traits::cast::ToPrimitive;
792
793 let [r:

::Subpixel, g:

::Subpixel, b:

::Subpixel] = pixel.to_rgb().0;

794 let max: f32 = P::Subpixel::DEFAULT_MAX_VALUE.to_f32().unwrap();
795 let r: f32 = r.to_f32().unwrap();
796 let g: f32 = g.to_f32().unwrap();
797 let b: f32 = b.to_f32().unwrap();
798
799 // Coefficients from JPEG File Interchange Format (Version 1.02), multiplied for 255 maximum.
800 let y: f32 = 76.245 / max * r + 149.685 / max * g + 29.07 / max * b;
801 let cb: f32 = -43.0185 / max * r - 84.4815 / max * g + 127.5 / max * b + 128.;
802 let cr: f32 = 127.5 / max * r - 106.7685 / max * g - 20.7315 / max * b + 128.;
803
804 (y as u8, cb as u8, cr as u8)
805}
806
807/// Returns the pixel at (x,y) if (x,y) is in the image,
808/// otherwise the closest pixel in the image
809#[inline]
810fn pixel_at_or_near<I: GenericImageView>(source: &I, x: u32, y: u32) -> I::Pixel {
811 if source.in_bounds(x, y) {
812 source.get_pixel(x, y)
813 } else {
814 source.get_pixel(x:x.min(source.width() - 1), y:y.min(source.height() - 1))
815 }
816}
817
818fn copy_blocks_ycbcr<I: GenericImageView>(
819 source: &I,
820 x0: u32,
821 y0: u32,
822 yb: &mut [u8; 64],
823 cbb: &mut [u8; 64],
824 crb: &mut [u8; 64],
825) {
826 for y: u32 in 0..8 {
827 for x: u32 in 0..8 {
828 let pixel: ::Pixel = pixel_at_or_near(source, x:x + x0, y:y + y0);
829 let (yc: u8, cb: u8, cr: u8) = rgb_to_ycbcr(pixel);
830
831 yb[(y * 8 + x) as usize] = yc;
832 cbb[(y * 8 + x) as usize] = cb;
833 crb[(y * 8 + x) as usize] = cr;
834 }
835 }
836}
837
838fn copy_blocks_gray<I: GenericImageView>(source: &I, x0: u32, y0: u32, gb: &mut [u8; 64]) {
839 use num_traits::cast::ToPrimitive;
840 for y: u32 in 0..8 {
841 for x: u32 in 0..8 {
842 let pixel: ::Pixel = pixel_at_or_near(source, x:x0 + x, y:y0 + y);
843 let [luma: <::Pixel as Pixel>::Subpixel] = pixel.to_luma().0;
844 gb[(y * 8 + x) as usize] = luma.to_u8().unwrap();
845 }
846 }
847}
848
849#[cfg(test)]
850mod tests {
851 use std::io::Cursor;
852
853 #[cfg(feature = "benchmarks")]
854 extern crate test;
855 #[cfg(feature = "benchmarks")]
856 use test::Bencher;
857
858 use crate::color::ColorType;
859 use crate::error::ParameterErrorKind::DimensionMismatch;
860 use crate::image::ImageDecoder;
861 use crate::{ImageEncoder, ImageError};
862
863 use super::super::JpegDecoder;
864 use super::{
865 build_frame_header, build_huffman_segment, build_jfif_header, build_quantization_segment,
866 build_scan_header, Component, JpegEncoder, PixelDensity, DCCLASS, LUMADESTINATION,
867 STD_LUMA_DC_CODE_LENGTHS, STD_LUMA_DC_VALUES,
868 };
869
870 fn decode(encoded: &[u8]) -> Vec<u8> {
871 let decoder = JpegDecoder::new(Cursor::new(encoded)).expect("Could not decode image");
872
873 let mut decoded = vec![0; decoder.total_bytes() as usize];
874 decoder
875 .read_image(&mut decoded)
876 .expect("Could not decode image");
877 decoded
878 }
879
880 #[test]
881 fn roundtrip_sanity_check() {
882 // create a 1x1 8-bit image buffer containing a single red pixel
883 let img = [255u8, 0, 0];
884
885 // encode it into a memory buffer
886 let mut encoded_img = Vec::new();
887 {
888 let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
889 encoder
890 .write_image(&img, 1, 1, ColorType::Rgb8)
891 .expect("Could not encode image");
892 }
893
894 // decode it from the memory buffer
895 {
896 let decoded = decode(&encoded_img);
897 // note that, even with the encode quality set to 100, we do not get the same image
898 // back. Therefore, we're going to assert that it's at least red-ish:
899 assert_eq!(3, decoded.len());
900 assert!(decoded[0] > 0x80);
901 assert!(decoded[1] < 0x80);
902 assert!(decoded[2] < 0x80);
903 }
904 }
905
906 #[test]
907 fn grayscale_roundtrip_sanity_check() {
908 // create a 2x2 8-bit image buffer containing a white diagonal
909 let img = [255u8, 0, 0, 255];
910
911 // encode it into a memory buffer
912 let mut encoded_img = Vec::new();
913 {
914 let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
915 encoder
916 .write_image(&img[..], 2, 2, ColorType::L8)
917 .expect("Could not encode image");
918 }
919
920 // decode it from the memory buffer
921 {
922 let decoded = decode(&encoded_img);
923 // note that, even with the encode quality set to 100, we do not get the same image
924 // back. Therefore, we're going to assert that the diagonal is at least white-ish:
925 assert_eq!(4, decoded.len());
926 assert!(decoded[0] > 0x80);
927 assert!(decoded[1] < 0x80);
928 assert!(decoded[2] < 0x80);
929 assert!(decoded[3] > 0x80);
930 }
931 }
932
933 #[test]
934 fn jfif_header_density_check() {
935 let mut buffer = Vec::new();
936 build_jfif_header(&mut buffer, PixelDensity::dpi(300));
937 assert_eq!(
938 buffer,
939 vec![
940 b'J',
941 b'F',
942 b'I',
943 b'F',
944 0,
945 1,
946 2, // JFIF version 1.2
947 1, // density is in dpi
948 300u16.to_be_bytes()[0],
949 300u16.to_be_bytes()[1],
950 300u16.to_be_bytes()[0],
951 300u16.to_be_bytes()[1],
952 0,
953 0, // No thumbnail
954 ]
955 );
956 }
957
958 #[test]
959 fn test_image_too_large() {
960 // JPEG cannot encode images larger than 65,535×65,535
961 // create a 65,536×1 8-bit black image buffer
962 let img = [0; 65_536];
963 // Try to encode an image that is too large
964 let mut encoded = Vec::new();
965 let encoder = JpegEncoder::new_with_quality(&mut encoded, 100);
966 let result = encoder.write_image(&img, 65_536, 1, ColorType::L8);
967 match result {
968 Err(ImageError::Parameter(err)) => {
969 assert_eq!(err.kind(), DimensionMismatch)
970 }
971 other => {
972 panic!(
973 "Encoding an image that is too large should return a DimensionError \
974 it returned {:?} instead",
975 other
976 )
977 }
978 }
979 }
980
981 #[test]
982 fn test_build_jfif_header() {
983 let mut buf = vec![];
984 let density = PixelDensity::dpi(100);
985 build_jfif_header(&mut buf, density);
986 assert_eq!(
987 buf,
988 [0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0, 100, 0, 100, 0, 0]
989 );
990 }
991
992 #[test]
993 fn test_build_frame_header() {
994 let mut buf = vec![];
995 let components = vec![
996 Component {
997 id: 1,
998 h: 1,
999 v: 1,
1000 tq: 5,
1001 dc_table: 5,
1002 ac_table: 5,
1003 _dc_pred: 0,
1004 },
1005 Component {
1006 id: 2,
1007 h: 1,
1008 v: 1,
1009 tq: 4,
1010 dc_table: 4,
1011 ac_table: 4,
1012 _dc_pred: 0,
1013 },
1014 ];
1015 build_frame_header(&mut buf, 5, 100, 150, &components);
1016 assert_eq!(
1017 buf,
1018 [5, 0, 150, 0, 100, 2, 1, 1 << 4 | 1, 5, 2, 1 << 4 | 1, 4]
1019 );
1020 }
1021
1022 #[test]
1023 fn test_build_scan_header() {
1024 let mut buf = vec![];
1025 let components = vec![
1026 Component {
1027 id: 1,
1028 h: 1,
1029 v: 1,
1030 tq: 5,
1031 dc_table: 5,
1032 ac_table: 5,
1033 _dc_pred: 0,
1034 },
1035 Component {
1036 id: 2,
1037 h: 1,
1038 v: 1,
1039 tq: 4,
1040 dc_table: 4,
1041 ac_table: 4,
1042 _dc_pred: 0,
1043 },
1044 ];
1045 build_scan_header(&mut buf, &components);
1046 assert_eq!(buf, [2, 1, 5 << 4 | 5, 2, 4 << 4 | 4, 0, 63, 0]);
1047 }
1048
1049 #[test]
1050 fn test_build_huffman_segment() {
1051 let mut buf = vec![];
1052 build_huffman_segment(
1053 &mut buf,
1054 DCCLASS,
1055 LUMADESTINATION,
1056 &STD_LUMA_DC_CODE_LENGTHS,
1057 &STD_LUMA_DC_VALUES,
1058 );
1059 assert_eq!(
1060 buf,
1061 vec![
1062 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,
1063 10, 11
1064 ]
1065 );
1066 }
1067
1068 #[test]
1069 fn test_build_quantization_segment() {
1070 let mut buf = vec![];
1071 let qtable = [0u8; 64];
1072 build_quantization_segment(&mut buf, 8, 1, &qtable);
1073 let mut expected = vec![];
1074 expected.push(1);
1075 expected.extend_from_slice(&[0; 64]);
1076 assert_eq!(buf, expected)
1077 }
1078
1079 #[cfg(feature = "benchmarks")]
1080 #[bench]
1081 fn bench_jpeg_encoder_new(b: &mut Bencher) {
1082 b.iter(|| {
1083 let mut y = vec![];
1084 let _x = JpegEncoder::new(&mut y);
1085 })
1086 }
1087}
1088