1 | #![allow(clippy::too_many_arguments)] |
---|---|
2 | |
3 | use std::borrow::Cow; |
4 | use std::io::{self, Write}; |
5 | |
6 | use crate::error::{ |
7 | ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError, |
8 | UnsupportedErrorKind, |
9 | }; |
10 | use crate::image::{ImageEncoder, ImageFormat}; |
11 | use crate::utils::clamp; |
12 | use crate::{ColorType, GenericImageView, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba}; |
13 | |
14 | use super::entropy::build_huff_lut_const; |
15 | use super::transform; |
16 | use crate::traits::PixelWithColorType; |
17 | |
18 | // Markers |
19 | // Baseline DCT |
20 | static SOF0: u8 = 0xC0; |
21 | // Huffman Tables |
22 | static DHT: u8 = 0xC4; |
23 | // Start of Image (standalone) |
24 | static SOI: u8 = 0xD8; |
25 | // End of image (standalone) |
26 | static EOI: u8 = 0xD9; |
27 | // Start of Scan |
28 | static SOS: u8 = 0xDA; |
29 | // Quantization Tables |
30 | static DQT: u8 = 0xDB; |
31 | // Application segments start and end |
32 | static APP0: u8 = 0xE0; |
33 | |
34 | // section K.1 |
35 | // table K.1 |
36 | #[rustfmt::skip] |
37 | static 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] |
50 | static 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 |
63 | static 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 | |
67 | static STD_LUMA_DC_VALUES: [u8; 12] = [ |
68 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, |
69 | ]; |
70 | |
71 | static 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 |
75 | static 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 | |
79 | static STD_CHROMA_DC_VALUES: [u8; 12] = [ |
80 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, |
81 | ]; |
82 | |
83 | static 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 |
87 | static 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 | |
91 | static 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 | |
105 | static 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 |
109 | static 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 | ]; |
112 | static 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 | |
126 | static STD_CHROMA_AC_HUFF_LUT: [(u8, u16); 256] = |
127 | build_huff_lut_const(&STD_CHROMA_AC_CODE_LENGTHS, &STD_CHROMA_AC_VALUES); |
128 | |
129 | static DCCLASS: u8 = 0; |
130 | static ACCLASS: u8 = 1; |
131 | |
132 | static LUMADESTINATION: u8 = 0; |
133 | static CHROMADESTINATION: u8 = 1; |
134 | |
135 | static LUMAID: u8 = 1; |
136 | static CHROMABLUEID: u8 = 2; |
137 | static CHROMAREDID: u8 = 3; |
138 | |
139 | /// The permutation of dct coefficients. |
140 | #[rustfmt::skip] |
141 | static 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)] |
154 | struct 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 | |
177 | pub(crate) struct BitWriter<W> { |
178 | w: W, |
179 | accumulator: u32, |
180 | nbits: u8, |
181 | } |
182 | |
183 | impl<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)] |
286 | pub 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)] |
308 | pub 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 | |
315 | impl 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 | |
327 | impl 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 |
338 | pub 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 | |
352 | impl<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 | |
669 | impl<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 | |
682 | fn 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 | |
700 | fn 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 | |
720 | fn 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 | |
734 | fn 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 | |
755 | fn 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 | |
768 | fn 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] |
789 | fn 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] |
810 | fn 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 | |
818 | fn 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 | |
838 | fn 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)] |
850 | mod 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 |