/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jpeg-encoder-0.6.1/src/encoder.rs
Line | Count | Source |
1 | | use crate::fdct::fdct; |
2 | | use crate::huffman::{CodingClass, HuffmanTable}; |
3 | | use crate::image_buffer::*; |
4 | | use crate::marker::Marker; |
5 | | use crate::quantization::{QuantizationTable, QuantizationTableType}; |
6 | | use crate::writer::{JfifWrite, JfifWriter, ZIGZAG}; |
7 | | use crate::{Density, EncodingError}; |
8 | | |
9 | | use alloc::vec; |
10 | | use alloc::vec::Vec; |
11 | | |
12 | | #[cfg(feature = "std")] |
13 | | use std::io::BufWriter; |
14 | | |
15 | | #[cfg(feature = "std")] |
16 | | use std::fs::File; |
17 | | |
18 | | #[cfg(feature = "std")] |
19 | | use std::path::Path; |
20 | | |
21 | | /// # Color types used in encoding |
22 | | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
23 | | pub enum JpegColorType { |
24 | | /// One component grayscale colorspace |
25 | | Luma, |
26 | | |
27 | | /// Three component YCbCr colorspace |
28 | | Ycbcr, |
29 | | |
30 | | /// 4 Component CMYK colorspace |
31 | | Cmyk, |
32 | | |
33 | | /// 4 Component YCbCrK colorspace |
34 | | Ycck, |
35 | | } |
36 | | |
37 | | impl JpegColorType { |
38 | 0 | pub(crate) fn get_num_components(self) -> usize { |
39 | | use JpegColorType::*; |
40 | | |
41 | 0 | match self { |
42 | 0 | Luma => 1, |
43 | 0 | Ycbcr => 3, |
44 | 0 | Cmyk | Ycck => 4, |
45 | | } |
46 | 0 | } |
47 | | } |
48 | | |
49 | | /// # Color types for input images |
50 | | /// |
51 | | /// Available color input formats for [Encoder::encode]. Other types can be used |
52 | | /// by implementing an [ImageBuffer](crate::ImageBuffer). |
53 | | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
54 | | pub enum ColorType { |
55 | | /// Grayscale with 1 byte per pixel |
56 | | Luma, |
57 | | |
58 | | /// RGB with 3 bytes per pixel |
59 | | Rgb, |
60 | | |
61 | | /// Red, Green, Blue with 4 bytes per pixel. The alpha channel will be ignored during encoding. |
62 | | Rgba, |
63 | | |
64 | | /// RGB with 3 bytes per pixel |
65 | | Bgr, |
66 | | |
67 | | /// RGBA with 4 bytes per pixel. The alpha channel will be ignored during encoding. |
68 | | Bgra, |
69 | | |
70 | | /// YCbCr with 3 bytes per pixel. |
71 | | Ycbcr, |
72 | | |
73 | | /// CMYK with 4 bytes per pixel. |
74 | | Cmyk, |
75 | | |
76 | | /// CMYK with 4 bytes per pixel. Encoded as YCCK (YCbCrK) |
77 | | CmykAsYcck, |
78 | | |
79 | | /// YCCK (YCbCrK) with 4 bytes per pixel. |
80 | | Ycck, |
81 | | } |
82 | | |
83 | | impl ColorType { |
84 | 0 | pub(crate) fn get_bytes_per_pixel(self) -> usize { |
85 | | use ColorType::*; |
86 | | |
87 | 0 | match self { |
88 | 0 | Luma => 1, |
89 | 0 | Rgb | Bgr | Ycbcr => 3, |
90 | 0 | Rgba | Bgra | Cmyk | CmykAsYcck | Ycck => 4, |
91 | | } |
92 | 0 | } |
93 | | } |
94 | | |
95 | | #[repr(u8)] |
96 | | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
97 | | /// # Sampling factors for chroma subsampling |
98 | | /// |
99 | | /// ## Warning |
100 | | /// Sampling factor of 4 are not supported by all decoders or applications |
101 | | #[allow(non_camel_case_types)] |
102 | | pub enum SamplingFactor { |
103 | | F_1_1 = 1 << 4 | 1, |
104 | | F_2_1 = 2 << 4 | 1, |
105 | | F_1_2 = 1 << 4 | 2, |
106 | | F_2_2 = 2 << 4 | 2, |
107 | | F_4_1 = 4 << 4 | 1, |
108 | | F_4_2 = 4 << 4 | 2, |
109 | | F_1_4 = 1 << 4 | 4, |
110 | | F_2_4 = 2 << 4 | 4, |
111 | | |
112 | | /// Alias for F_1_1 |
113 | | R_4_4_4 = 0x80 | 1 << 4 | 1, |
114 | | |
115 | | /// Alias for F_1_2 |
116 | | R_4_4_0 = 0x80 | 1 << 4 | 2, |
117 | | |
118 | | /// Alias for F_1_4 |
119 | | R_4_4_1 = 0x80 | 1 << 4 | 4, |
120 | | |
121 | | /// Alias for F_2_1 |
122 | | R_4_2_2 = 0x80 | 2 << 4 | 1, |
123 | | |
124 | | /// Alias for F_2_2 |
125 | | R_4_2_0 = 0x80 | 2 << 4 | 2, |
126 | | |
127 | | /// Alias for F_2_4 |
128 | | R_4_2_1 = 0x80 | 2 << 4 | 4, |
129 | | |
130 | | /// Alias for F_4_1 |
131 | | R_4_1_1 = 0x80 | 4 << 4 | 1, |
132 | | |
133 | | /// Alias for F_4_2 |
134 | | R_4_1_0 = 0x80 | 4 << 4 | 2, |
135 | | } |
136 | | |
137 | | impl SamplingFactor { |
138 | | /// Get variant for supplied factors or None if not supported |
139 | 0 | pub fn from_factors(horizontal: u8, vertical: u8) -> Option<SamplingFactor> { |
140 | | use SamplingFactor::*; |
141 | | |
142 | 0 | match (horizontal, vertical) { |
143 | 0 | (1, 1) => Some(F_1_1), |
144 | 0 | (1, 2) => Some(F_1_2), |
145 | 0 | (1, 4) => Some(F_1_4), |
146 | 0 | (2, 1) => Some(F_2_1), |
147 | 0 | (2, 2) => Some(F_2_2), |
148 | 0 | (2, 4) => Some(F_2_4), |
149 | 0 | (4, 1) => Some(F_4_1), |
150 | 0 | (4, 2) => Some(F_4_2), |
151 | 0 | _ => None, |
152 | | } |
153 | 0 | } |
154 | | |
155 | 0 | pub(crate) fn get_sampling_factors(self) -> (u8, u8) { |
156 | 0 | let value = self as u8; |
157 | 0 | ((value >> 4) & 0x07, value & 0xf) |
158 | 0 | } |
159 | | |
160 | 0 | pub(crate) fn supports_interleaved(self) -> bool { |
161 | | use SamplingFactor::*; |
162 | | |
163 | | // Interleaved mode is only supported with h/v sampling factors of 1 or 2. |
164 | | // Sampling factors of 4 needs sequential encoding |
165 | 0 | matches!( |
166 | 0 | self, |
167 | | F_1_1 | F_2_1 | F_1_2 | F_2_2 | R_4_4_4 | R_4_4_0 | R_4_2_2 | R_4_2_0 |
168 | | ) |
169 | 0 | } |
170 | | } |
171 | | |
172 | | pub(crate) struct Component { |
173 | | pub id: u8, |
174 | | pub quantization_table: u8, |
175 | | pub dc_huffman_table: u8, |
176 | | pub ac_huffman_table: u8, |
177 | | pub horizontal_sampling_factor: u8, |
178 | | pub vertical_sampling_factor: u8, |
179 | | } |
180 | | |
181 | | macro_rules! add_component { |
182 | | ($components:expr, $id:expr, $dest:expr, $h_sample:expr, $v_sample:expr) => { |
183 | | $components.push(Component { |
184 | | id: $id, |
185 | | quantization_table: $dest, |
186 | | dc_huffman_table: $dest, |
187 | | ac_huffman_table: $dest, |
188 | | horizontal_sampling_factor: $h_sample, |
189 | | vertical_sampling_factor: $v_sample, |
190 | | }); |
191 | | }; |
192 | | } |
193 | | |
194 | | /// # The JPEG encoder |
195 | | pub struct Encoder<W: JfifWrite> { |
196 | | writer: JfifWriter<W>, |
197 | | density: Density, |
198 | | quality: u8, |
199 | | |
200 | | components: Vec<Component>, |
201 | | quantization_tables: [QuantizationTableType; 2], |
202 | | huffman_tables: [(HuffmanTable, HuffmanTable); 2], |
203 | | |
204 | | sampling_factor: SamplingFactor, |
205 | | |
206 | | progressive_scans: Option<u8>, |
207 | | |
208 | | restart_interval: Option<u16>, |
209 | | |
210 | | optimize_huffman_table: bool, |
211 | | |
212 | | app_segments: Vec<(u8, Vec<u8>)>, |
213 | | } |
214 | | |
215 | | impl<W: JfifWrite> Encoder<W> { |
216 | | /// Create a new encoder with the given quality |
217 | | /// |
218 | | /// The quality must be between 1 and 100 where 100 is the highest image quality.<br> |
219 | | /// By default, quality settings below 90 use a chroma subsampling (2x2 / 4:2:0) which can |
220 | | /// be changed with [set_sampling_factor](Encoder::set_sampling_factor) |
221 | 0 | pub fn new(w: W, quality: u8) -> Encoder<W> { |
222 | 0 | let huffman_tables = [ |
223 | 0 | ( |
224 | 0 | HuffmanTable::default_luma_dc(), |
225 | 0 | HuffmanTable::default_luma_ac(), |
226 | 0 | ), |
227 | 0 | ( |
228 | 0 | HuffmanTable::default_chroma_dc(), |
229 | 0 | HuffmanTable::default_chroma_ac(), |
230 | 0 | ), |
231 | 0 | ]; |
232 | | |
233 | 0 | let quantization_tables = [ |
234 | 0 | QuantizationTableType::Default, |
235 | 0 | QuantizationTableType::Default, |
236 | 0 | ]; |
237 | | |
238 | 0 | let sampling_factor = if quality < 90 { |
239 | 0 | SamplingFactor::F_2_2 |
240 | | } else { |
241 | 0 | SamplingFactor::F_1_1 |
242 | | }; |
243 | | |
244 | 0 | Encoder { |
245 | 0 | writer: JfifWriter::new(w), |
246 | 0 | density: Density::None, |
247 | 0 | quality, |
248 | 0 | components: vec![], |
249 | 0 | quantization_tables, |
250 | 0 | huffman_tables, |
251 | 0 | sampling_factor, |
252 | 0 | progressive_scans: None, |
253 | 0 | restart_interval: None, |
254 | 0 | optimize_huffman_table: false, |
255 | 0 | app_segments: Vec::new(), |
256 | 0 | } |
257 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::new Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new |
258 | | |
259 | | /// Set pixel density for the image |
260 | | /// |
261 | | /// By default, this value is None which is equal to "1 pixel per pixel". |
262 | 0 | pub fn set_density(&mut self, density: Density) { |
263 | 0 | self.density = density; |
264 | 0 | } |
265 | | |
266 | | /// Return pixel density |
267 | 0 | pub fn density(&self) -> Density { |
268 | 0 | self.density |
269 | 0 | } |
270 | | |
271 | | /// Set chroma subsampling factor |
272 | 0 | pub fn set_sampling_factor(&mut self, sampling: SamplingFactor) { |
273 | 0 | self.sampling_factor = sampling; |
274 | 0 | } |
275 | | |
276 | | /// Get chroma subsampling factor |
277 | 0 | pub fn sampling_factor(&self) -> SamplingFactor { |
278 | 0 | self.sampling_factor |
279 | 0 | } |
280 | | |
281 | | /// Set quantization tables for luma and chroma components |
282 | 0 | pub fn set_quantization_tables( |
283 | 0 | &mut self, |
284 | 0 | luma: QuantizationTableType, |
285 | 0 | chroma: QuantizationTableType, |
286 | 0 | ) { |
287 | 0 | self.quantization_tables = [luma, chroma]; |
288 | 0 | } |
289 | | |
290 | | /// Get configured quantization tables |
291 | 0 | pub fn quantization_tables(&self) -> &[QuantizationTableType; 2] { |
292 | 0 | &self.quantization_tables |
293 | 0 | } |
294 | | |
295 | | /// Controls if progressive encoding is used. |
296 | | /// |
297 | | /// By default, progressive encoding uses 4 scans.<br> |
298 | | /// Use [set_progressive_scans](Encoder::set_progressive_scans) to use a different number of scans |
299 | 0 | pub fn set_progressive(&mut self, progressive: bool) { |
300 | 0 | self.progressive_scans = if progressive { Some(4) } else { None }; |
301 | 0 | } |
302 | | |
303 | | /// Set number of scans per component for progressive encoding |
304 | | /// |
305 | | /// Number of scans must be between 2 and 64. |
306 | | /// There is at least one scan for the DC coefficients and one for the remaining 63 AC coefficients. |
307 | | /// |
308 | | /// # Panics |
309 | | /// If number of scans is not within valid range |
310 | 0 | pub fn set_progressive_scans(&mut self, scans: u8) { |
311 | 0 | assert!( |
312 | 0 | (2..=64).contains(&scans), |
313 | 0 | "Invalid number of scans: {}", |
314 | | scans |
315 | | ); |
316 | 0 | self.progressive_scans = Some(scans); |
317 | 0 | } |
318 | | |
319 | | /// Return number of progressive scans if progressive encoding is enabled |
320 | 0 | pub fn progressive_scans(&self) -> Option<u8> { |
321 | 0 | self.progressive_scans |
322 | 0 | } |
323 | | |
324 | | /// Set restart interval |
325 | | /// |
326 | | /// Set numbers of MCUs between restart markers. |
327 | 0 | pub fn set_restart_interval(&mut self, interval: u16) { |
328 | 0 | self.restart_interval = if interval == 0 { None } else { Some(interval) }; |
329 | 0 | } |
330 | | |
331 | | /// Return the restart interval |
332 | 0 | pub fn restart_interval(&self) -> Option<u16> { |
333 | 0 | self.restart_interval |
334 | 0 | } |
335 | | |
336 | | /// Set if optimized huffman table should be created |
337 | | /// |
338 | | /// Optimized tables result in slightly smaller file sizes but decrease encoding performance. |
339 | 0 | pub fn set_optimized_huffman_tables(&mut self, optimize_huffman_table: bool) { |
340 | 0 | self.optimize_huffman_table = optimize_huffman_table; |
341 | 0 | } |
342 | | |
343 | | /// Returns if optimized huffman table should be generated |
344 | 0 | pub fn optimized_huffman_tables(&self) -> bool { |
345 | 0 | self.optimize_huffman_table |
346 | 0 | } |
347 | | |
348 | | /// Appends a custom app segment to the JFIF file |
349 | | /// |
350 | | /// Segment numbers need to be in the range between 1 and 15<br> |
351 | | /// The maximum allowed data length is 2^16 - 2 bytes. |
352 | | /// |
353 | | /// # Errors |
354 | | /// |
355 | | /// Returns an error if the segment number is invalid or data exceeds the allowed size |
356 | 0 | pub fn add_app_segment(&mut self, segment_nr: u8, data: &[u8]) -> Result<(), EncodingError> { |
357 | 0 | if segment_nr == 0 || segment_nr > 15 { |
358 | 0 | Err(EncodingError::InvalidAppSegment(segment_nr)) |
359 | 0 | } else if data.len() > 65533 { |
360 | 0 | Err(EncodingError::AppSegmentTooLarge(data.len())) |
361 | | } else { |
362 | 0 | self.app_segments.push((segment_nr, data.to_vec())); |
363 | 0 | Ok(()) |
364 | | } |
365 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::add_app_segment Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::add_app_segment |
366 | | |
367 | | /// Add an ICC profile |
368 | | /// |
369 | | /// The maximum allowed data length is 16,707,345 bytes. |
370 | | /// |
371 | | /// # Errors |
372 | | /// |
373 | | /// Returns an Error if the data exceeds the maximum size for the ICC profile |
374 | 0 | pub fn add_icc_profile(&mut self, data: &[u8]) -> Result<(), EncodingError> { |
375 | | // Based on https://www.color.org/ICC_Minor_Revision_for_Web.pdf |
376 | | // B.4 Embedding ICC profiles in JFIF files |
377 | | |
378 | | const MARKER: &[u8; 12] = b"ICC_PROFILE\0"; |
379 | | const MAX_CHUNK_LENGTH: usize = 65535 - 2 - 12 - 2; |
380 | | |
381 | 0 | let num_chunks = ceil_div(data.len(), MAX_CHUNK_LENGTH); |
382 | | |
383 | | // Sequence number is stored as a byte and starts with 1 |
384 | 0 | if num_chunks >= 255 { |
385 | 0 | return Err(EncodingError::IccTooLarge(data.len())); |
386 | 0 | } |
387 | | |
388 | 0 | let mut chunk_data = Vec::with_capacity(MAX_CHUNK_LENGTH); |
389 | | |
390 | 0 | for (i, data) in data.chunks(MAX_CHUNK_LENGTH).enumerate() { |
391 | 0 | chunk_data.clear(); |
392 | 0 | chunk_data.extend_from_slice(MARKER); |
393 | 0 | chunk_data.push(i as u8 + 1); |
394 | 0 | chunk_data.push(num_chunks as u8); |
395 | 0 | chunk_data.extend_from_slice(data); |
396 | | |
397 | 0 | self.add_app_segment(2, &chunk_data)?; |
398 | | } |
399 | | |
400 | 0 | Ok(()) |
401 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::add_icc_profile Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::add_icc_profile |
402 | | |
403 | | /// Encode an image |
404 | | /// |
405 | | /// Data format and length must conform to specified width, height and color type. |
406 | 0 | pub fn encode( |
407 | 0 | self, |
408 | 0 | data: &[u8], |
409 | 0 | width: u16, |
410 | 0 | height: u16, |
411 | 0 | color_type: ColorType, |
412 | 0 | ) -> Result<(), EncodingError> { |
413 | 0 | let required_data_len = width as usize * height as usize * color_type.get_bytes_per_pixel(); |
414 | | |
415 | 0 | if data.len() < required_data_len { |
416 | 0 | return Err(EncodingError::BadImageData { |
417 | 0 | length: data.len(), |
418 | 0 | required: required_data_len, |
419 | 0 | }); |
420 | 0 | } |
421 | | |
422 | | #[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))] |
423 | | { |
424 | 0 | if std::is_x86_feature_detected!("avx2") { |
425 | | use crate::avx2::*; |
426 | | |
427 | 0 | return match color_type { |
428 | 0 | ColorType::Luma => self |
429 | 0 | .encode_image_internal::<_, AVX2Operations>(GrayImage(data, width, height)), |
430 | 0 | ColorType::Rgb => self.encode_image_internal::<_, AVX2Operations>( |
431 | 0 | RgbImageAVX2(data, width, height), |
432 | | ), |
433 | 0 | ColorType::Rgba => self.encode_image_internal::<_, AVX2Operations>( |
434 | 0 | RgbaImageAVX2(data, width, height), |
435 | | ), |
436 | 0 | ColorType::Bgr => self.encode_image_internal::<_, AVX2Operations>( |
437 | 0 | BgrImageAVX2(data, width, height), |
438 | | ), |
439 | 0 | ColorType::Bgra => self.encode_image_internal::<_, AVX2Operations>( |
440 | 0 | BgraImageAVX2(data, width, height), |
441 | | ), |
442 | 0 | ColorType::Ycbcr => self.encode_image_internal::<_, AVX2Operations>( |
443 | 0 | YCbCrImage(data, width, height), |
444 | | ), |
445 | 0 | ColorType::Cmyk => self |
446 | 0 | .encode_image_internal::<_, AVX2Operations>(CmykImage(data, width, height)), |
447 | 0 | ColorType::CmykAsYcck => self.encode_image_internal::<_, AVX2Operations>( |
448 | 0 | CmykAsYcckImage(data, width, height), |
449 | | ), |
450 | 0 | ColorType::Ycck => self |
451 | 0 | .encode_image_internal::<_, AVX2Operations>(YcckImage(data, width, height)), |
452 | | }; |
453 | 0 | } |
454 | | } |
455 | | |
456 | 0 | match color_type { |
457 | 0 | ColorType::Luma => self.encode_image(GrayImage(data, width, height))?, |
458 | 0 | ColorType::Rgb => self.encode_image(RgbImage(data, width, height))?, |
459 | 0 | ColorType::Rgba => self.encode_image(RgbaImage(data, width, height))?, |
460 | 0 | ColorType::Bgr => self.encode_image(BgrImage(data, width, height))?, |
461 | 0 | ColorType::Bgra => self.encode_image(BgraImage(data, width, height))?, |
462 | 0 | ColorType::Ycbcr => self.encode_image(YCbCrImage(data, width, height))?, |
463 | 0 | ColorType::Cmyk => self.encode_image(CmykImage(data, width, height))?, |
464 | 0 | ColorType::CmykAsYcck => self.encode_image(CmykAsYcckImage(data, width, height))?, |
465 | 0 | ColorType::Ycck => self.encode_image(YcckImage(data, width, height))?, |
466 | | } |
467 | | |
468 | 0 | Ok(()) |
469 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode |
470 | | |
471 | | /// Encode an image |
472 | 0 | pub fn encode_image<I: ImageBuffer>(self, image: I) -> Result<(), EncodingError> { |
473 | | #[cfg(all(feature = "simd", any(target_arch = "x86", target_arch = "x86_64")))] |
474 | | { |
475 | 0 | if std::is_x86_feature_detected!("avx2") { |
476 | | use crate::avx2::*; |
477 | 0 | return self.encode_image_internal::<_, AVX2Operations>(image); |
478 | 0 | } |
479 | | } |
480 | 0 | self.encode_image_internal::<_, DefaultOperations>(image) |
481 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image::<_> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::YCbCrImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::CmykAsYcckImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::BgrImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::RgbImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::BgraImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::CmykImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::GrayImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::RgbaImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image::<jpeg_encoder::image_buffer::YcckImage> |
482 | | |
483 | 0 | fn encode_image_internal<I: ImageBuffer, OP: Operations>( |
484 | 0 | mut self, |
485 | 0 | image: I, |
486 | 0 | ) -> Result<(), EncodingError> { |
487 | 0 | if image.width() == 0 || image.height() == 0 { |
488 | 0 | return Err(EncodingError::ZeroImageDimensions { |
489 | 0 | width: image.width(), |
490 | 0 | height: image.height(), |
491 | 0 | }); |
492 | 0 | } |
493 | | |
494 | 0 | let q_tables = [ |
495 | 0 | QuantizationTable::new_with_quality(&self.quantization_tables[0], self.quality, true), |
496 | 0 | QuantizationTable::new_with_quality(&self.quantization_tables[1], self.quality, false), |
497 | 0 | ]; |
498 | | |
499 | 0 | let jpeg_color_type = image.get_jpeg_color_type(); |
500 | 0 | self.init_components(jpeg_color_type); |
501 | | |
502 | 0 | self.writer.write_marker(Marker::SOI)?; |
503 | | |
504 | 0 | self.writer.write_header(&self.density)?; |
505 | | |
506 | 0 | if jpeg_color_type == JpegColorType::Cmyk { |
507 | | //Set ColorTransform info to "Unknown" |
508 | 0 | let app_14 = b"Adobe\0\0\0\0\0\0\0"; |
509 | 0 | self.writer |
510 | 0 | .write_segment(Marker::APP(14), app_14.as_ref())?; |
511 | 0 | } else if jpeg_color_type == JpegColorType::Ycck { |
512 | | //Set ColorTransform info to YCCK |
513 | 0 | let app_14 = b"Adobe\0\0\0\0\0\0\x02"; |
514 | 0 | self.writer |
515 | 0 | .write_segment(Marker::APP(14), app_14.as_ref())?; |
516 | 0 | } |
517 | | |
518 | 0 | for (nr, data) in &self.app_segments { |
519 | 0 | self.writer.write_segment(Marker::APP(*nr), data)?; |
520 | | } |
521 | | |
522 | 0 | if let Some(scans) = self.progressive_scans { |
523 | 0 | self.encode_image_progressive::<_, OP>(image, scans, &q_tables)?; |
524 | 0 | } else if self.optimize_huffman_table || !self.sampling_factor.supports_interleaved() { |
525 | 0 | self.encode_image_sequential::<_, OP>(image, &q_tables)?; |
526 | | } else { |
527 | 0 | self.encode_image_interleaved::<_, OP>(image, &q_tables)?; |
528 | | } |
529 | | |
530 | 0 | self.writer.write_marker(Marker::EOI)?; |
531 | | |
532 | 0 | Ok(()) |
533 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_internal::<_, _> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_internal::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations> |
534 | | |
535 | 0 | fn init_components(&mut self, color: JpegColorType) { |
536 | 0 | let (horizontal_sampling_factor, vertical_sampling_factor) = |
537 | 0 | self.sampling_factor.get_sampling_factors(); |
538 | | |
539 | 0 | match color { |
540 | 0 | JpegColorType::Luma => { |
541 | 0 | add_component!(self.components, 0, 0, 1, 1); |
542 | 0 | } |
543 | 0 | JpegColorType::Ycbcr => { |
544 | 0 | add_component!( |
545 | 0 | self.components, |
546 | 0 | 0, |
547 | 0 | 0, |
548 | 0 | horizontal_sampling_factor, |
549 | 0 | vertical_sampling_factor |
550 | 0 | ); |
551 | 0 | add_component!(self.components, 1, 1, 1, 1); |
552 | 0 | add_component!(self.components, 2, 1, 1, 1); |
553 | 0 | } |
554 | 0 | JpegColorType::Cmyk => { |
555 | 0 | add_component!(self.components, 0, 1, 1, 1); |
556 | 0 | add_component!(self.components, 1, 1, 1, 1); |
557 | 0 | add_component!(self.components, 2, 1, 1, 1); |
558 | 0 | add_component!( |
559 | 0 | self.components, |
560 | 0 | 3, |
561 | 0 | 0, |
562 | 0 | horizontal_sampling_factor, |
563 | 0 | vertical_sampling_factor |
564 | 0 | ); |
565 | 0 | } |
566 | 0 | JpegColorType::Ycck => { |
567 | 0 | add_component!( |
568 | 0 | self.components, |
569 | 0 | 0, |
570 | 0 | 0, |
571 | 0 | horizontal_sampling_factor, |
572 | 0 | vertical_sampling_factor |
573 | 0 | ); |
574 | 0 | add_component!(self.components, 1, 1, 1, 1); |
575 | 0 | add_component!(self.components, 2, 1, 1, 1); |
576 | 0 | add_component!( |
577 | 0 | self.components, |
578 | 0 | 3, |
579 | 0 | 0, |
580 | 0 | horizontal_sampling_factor, |
581 | 0 | vertical_sampling_factor |
582 | 0 | ); |
583 | 0 | } |
584 | | } |
585 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_components Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_components |
586 | | |
587 | 0 | fn get_max_sampling_size(&self) -> (usize, usize) { |
588 | 0 | let max_h_sampling = self.components.iter().fold(1, |value, component| { |
589 | 0 | value.max(component.horizontal_sampling_factor) |
590 | 0 | }); Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size::{closure#0}Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size::{closure#0} |
591 | | |
592 | 0 | let max_v_sampling = self.components.iter().fold(1, |value, component| { |
593 | 0 | value.max(component.vertical_sampling_factor) |
594 | 0 | }); Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size::{closure#1}Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size::{closure#1} |
595 | | |
596 | 0 | (usize::from(max_h_sampling), usize::from(max_v_sampling)) |
597 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::get_max_sampling_size Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::get_max_sampling_size |
598 | | |
599 | 0 | fn write_frame_header<I: ImageBuffer>( |
600 | 0 | &mut self, |
601 | 0 | image: &I, |
602 | 0 | q_tables: &[QuantizationTable; 2], |
603 | 0 | ) -> Result<(), EncodingError> { |
604 | 0 | self.writer.write_frame_header( |
605 | 0 | image.width(), |
606 | 0 | image.height(), |
607 | 0 | &self.components, |
608 | 0 | self.progressive_scans.is_some(), |
609 | 0 | )?; |
610 | | |
611 | 0 | self.writer.write_quantization_segment(0, &q_tables[0])?; |
612 | 0 | self.writer.write_quantization_segment(1, &q_tables[1])?; |
613 | | |
614 | 0 | self.writer |
615 | 0 | .write_huffman_segment(CodingClass::Dc, 0, &self.huffman_tables[0].0)?; |
616 | | |
617 | 0 | self.writer |
618 | 0 | .write_huffman_segment(CodingClass::Ac, 0, &self.huffman_tables[0].1)?; |
619 | | |
620 | 0 | if image.get_jpeg_color_type().get_num_components() >= 3 { |
621 | 0 | self.writer |
622 | 0 | .write_huffman_segment(CodingClass::Dc, 1, &self.huffman_tables[1].0)?; |
623 | | |
624 | 0 | self.writer |
625 | 0 | .write_huffman_segment(CodingClass::Ac, 1, &self.huffman_tables[1].1)?; |
626 | 0 | } |
627 | | |
628 | 0 | if let Some(restart_interval) = self.restart_interval { |
629 | 0 | self.writer.write_dri(restart_interval)?; |
630 | 0 | } |
631 | | |
632 | 0 | Ok(()) |
633 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::write_frame_header::<_> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::YCbCrImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::CmykAsYcckImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::BgrImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::RgbImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::BgraImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::CmykImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::GrayImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::RgbaImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::image_buffer::YcckImage> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2> |
634 | | |
635 | 0 | fn init_rows(&mut self, buffer_size: usize) -> [Vec<u8>; 4] { |
636 | | // To simplify the code and to give the compiler more infos to optimize stuff we always initialize 4 components |
637 | | // Resource overhead should be minimal because an empty Vec doesn't allocate |
638 | | |
639 | 0 | match self.components.len() { |
640 | 0 | 1 => [ |
641 | 0 | Vec::with_capacity(buffer_size), |
642 | 0 | Vec::new(), |
643 | 0 | Vec::new(), |
644 | 0 | Vec::new(), |
645 | 0 | ], |
646 | 0 | 3 => [ |
647 | 0 | Vec::with_capacity(buffer_size), |
648 | 0 | Vec::with_capacity(buffer_size), |
649 | 0 | Vec::with_capacity(buffer_size), |
650 | 0 | Vec::new(), |
651 | 0 | ], |
652 | 0 | 4 => [ |
653 | 0 | Vec::with_capacity(buffer_size), |
654 | 0 | Vec::with_capacity(buffer_size), |
655 | 0 | Vec::with_capacity(buffer_size), |
656 | 0 | Vec::with_capacity(buffer_size), |
657 | 0 | ], |
658 | 0 | len => unreachable!("Unsupported component length: {}", len), |
659 | | } |
660 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_rows Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_rows |
661 | | |
662 | | /// Encode all components with one scan |
663 | | /// |
664 | | /// This is only valid for sampling factors of 1 and 2 |
665 | 0 | fn encode_image_interleaved<I: ImageBuffer, OP: Operations>( |
666 | 0 | &mut self, |
667 | 0 | image: I, |
668 | 0 | q_tables: &[QuantizationTable; 2], |
669 | 0 | ) -> Result<(), EncodingError> { |
670 | 0 | self.write_frame_header(&image, q_tables)?; |
671 | 0 | self.writer |
672 | 0 | .write_scan_header(&self.components.iter().collect::<Vec<_>>(), None)?; |
673 | | |
674 | 0 | let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size(); |
675 | | |
676 | 0 | let width = image.width(); |
677 | 0 | let height = image.height(); |
678 | | |
679 | 0 | let num_cols = ceil_div(usize::from(width), 8 * max_h_sampling); |
680 | 0 | let num_rows = ceil_div(usize::from(height), 8 * max_v_sampling); |
681 | | |
682 | 0 | let buffer_width = num_cols * 8 * max_h_sampling; |
683 | 0 | let buffer_size = buffer_width * 8 * max_v_sampling; |
684 | | |
685 | 0 | let mut row: [Vec<_>; 4] = self.init_rows(buffer_size); |
686 | | |
687 | 0 | let mut prev_dc = [0i16; 4]; |
688 | | |
689 | 0 | let restart_interval = self.restart_interval.unwrap_or(0); |
690 | 0 | let mut restarts = 0; |
691 | 0 | let mut restarts_to_go = restart_interval; |
692 | | |
693 | 0 | for block_y in 0..num_rows { |
694 | 0 | for r in &mut row { |
695 | 0 | r.clear(); |
696 | 0 | } |
697 | | |
698 | 0 | for y in 0..(8 * max_v_sampling) { |
699 | 0 | let y = y + block_y * 8 * max_v_sampling; |
700 | 0 | let y = (y.min(height as usize - 1)) as u16; |
701 | | |
702 | 0 | image.fill_buffers(y, &mut row); |
703 | | |
704 | 0 | for _ in usize::from(width)..buffer_width { |
705 | 0 | for channel in &mut row { |
706 | 0 | if !channel.is_empty() { |
707 | 0 | channel.push(channel[channel.len() - 1]); |
708 | 0 | } |
709 | | } |
710 | | } |
711 | | } |
712 | | |
713 | 0 | for block_x in 0..num_cols { |
714 | 0 | if restart_interval > 0 && restarts_to_go == 0 { |
715 | 0 | self.writer.finalize_bit_buffer()?; |
716 | 0 | self.writer |
717 | 0 | .write_marker(Marker::RST((restarts % 8) as u8))?; |
718 | | |
719 | 0 | prev_dc[0] = 0; |
720 | 0 | prev_dc[1] = 0; |
721 | 0 | prev_dc[2] = 0; |
722 | 0 | prev_dc[3] = 0; |
723 | 0 | } |
724 | | |
725 | 0 | for (i, component) in self.components.iter().enumerate() { |
726 | 0 | for v_offset in 0..component.vertical_sampling_factor as usize { |
727 | 0 | for h_offset in 0..component.horizontal_sampling_factor as usize { |
728 | 0 | let mut block = get_block( |
729 | 0 | &row[i], |
730 | 0 | block_x * 8 * max_h_sampling + (h_offset * 8), |
731 | 0 | v_offset * 8, |
732 | 0 | max_h_sampling |
733 | 0 | / component.horizontal_sampling_factor as usize, |
734 | 0 | max_v_sampling |
735 | 0 | / component.vertical_sampling_factor as usize, |
736 | 0 | buffer_width, |
737 | | ); |
738 | | |
739 | 0 | OP::fdct(&mut block); |
740 | | |
741 | 0 | let mut q_block = [0i16; 64]; |
742 | | |
743 | 0 | OP::quantize_block( |
744 | 0 | &block, |
745 | 0 | &mut q_block, |
746 | 0 | &q_tables[component.quantization_table as usize], |
747 | | ); |
748 | | |
749 | 0 | self.writer.write_block( |
750 | 0 | &q_block, |
751 | 0 | prev_dc[i], |
752 | 0 | &self.huffman_tables[component.dc_huffman_table as usize].0, |
753 | 0 | &self.huffman_tables[component.ac_huffman_table as usize].1, |
754 | 0 | )?; |
755 | | |
756 | 0 | prev_dc[i] = q_block[0]; |
757 | | } |
758 | | } |
759 | | } |
760 | | |
761 | 0 | if restart_interval > 0 { |
762 | 0 | if restarts_to_go == 0 { |
763 | 0 | restarts_to_go = restart_interval; |
764 | 0 | restarts += 1; |
765 | 0 | restarts &= 7; |
766 | 0 | } |
767 | 0 | restarts_to_go -= 1; |
768 | 0 | } |
769 | | } |
770 | | } |
771 | | |
772 | 0 | self.writer.finalize_bit_buffer()?; |
773 | | |
774 | 0 | Ok(()) |
775 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_interleaved::<_, _> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_interleaved::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations> |
776 | | |
777 | | /// Encode components with one scan per component |
778 | 0 | fn encode_image_sequential<I: ImageBuffer, OP: Operations>( |
779 | 0 | &mut self, |
780 | 0 | image: I, |
781 | 0 | q_tables: &[QuantizationTable; 2], |
782 | 0 | ) -> Result<(), EncodingError> { |
783 | 0 | let blocks = self.encode_blocks::<_, OP>(&image, q_tables); |
784 | | |
785 | 0 | if self.optimize_huffman_table { |
786 | 0 | self.optimize_huffman_table(&blocks); |
787 | 0 | } |
788 | | |
789 | 0 | self.write_frame_header(&image, q_tables)?; |
790 | | |
791 | 0 | for (i, component) in self.components.iter().enumerate() { |
792 | 0 | let restart_interval = self.restart_interval.unwrap_or(0); |
793 | 0 | let mut restarts = 0; |
794 | 0 | let mut restarts_to_go = restart_interval; |
795 | | |
796 | 0 | self.writer.write_scan_header(&[component], None)?; |
797 | | |
798 | 0 | let mut prev_dc = 0; |
799 | | |
800 | 0 | for block in &blocks[i] { |
801 | 0 | if restart_interval > 0 && restarts_to_go == 0 { |
802 | 0 | self.writer.finalize_bit_buffer()?; |
803 | 0 | self.writer |
804 | 0 | .write_marker(Marker::RST((restarts % 8) as u8))?; |
805 | | |
806 | 0 | prev_dc = 0; |
807 | 0 | } |
808 | | |
809 | 0 | self.writer.write_block( |
810 | 0 | block, |
811 | 0 | prev_dc, |
812 | 0 | &self.huffman_tables[component.dc_huffman_table as usize].0, |
813 | 0 | &self.huffman_tables[component.ac_huffman_table as usize].1, |
814 | 0 | )?; |
815 | | |
816 | 0 | prev_dc = block[0]; |
817 | | |
818 | 0 | if restart_interval > 0 { |
819 | 0 | if restarts_to_go == 0 { |
820 | 0 | restarts_to_go = restart_interval; |
821 | 0 | restarts += 1; |
822 | 0 | restarts &= 7; |
823 | 0 | } |
824 | 0 | restarts_to_go -= 1; |
825 | 0 | } |
826 | | } |
827 | | |
828 | 0 | self.writer.finalize_bit_buffer()?; |
829 | | } |
830 | | |
831 | 0 | Ok(()) |
832 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_sequential::<_, _> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_sequential::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations> |
833 | | |
834 | | /// Encode image in progressive mode |
835 | | /// |
836 | | /// This only support spectral selection for now |
837 | 0 | fn encode_image_progressive<I: ImageBuffer, OP: Operations>( |
838 | 0 | &mut self, |
839 | 0 | image: I, |
840 | 0 | scans: u8, |
841 | 0 | q_tables: &[QuantizationTable; 2], |
842 | 0 | ) -> Result<(), EncodingError> { |
843 | 0 | let blocks = self.encode_blocks::<_, OP>(&image, q_tables); |
844 | | |
845 | 0 | if self.optimize_huffman_table { |
846 | 0 | self.optimize_huffman_table(&blocks); |
847 | 0 | } |
848 | | |
849 | 0 | self.write_frame_header(&image, q_tables)?; |
850 | | |
851 | | // Phase 1: DC Scan |
852 | | // Only the DC coefficients can be transfer in the first component scans |
853 | 0 | for (i, component) in self.components.iter().enumerate() { |
854 | 0 | self.writer.write_scan_header(&[component], Some((0, 0)))?; |
855 | | |
856 | 0 | let restart_interval = self.restart_interval.unwrap_or(0); |
857 | 0 | let mut restarts = 0; |
858 | 0 | let mut restarts_to_go = restart_interval; |
859 | | |
860 | 0 | let mut prev_dc = 0; |
861 | | |
862 | 0 | for block in &blocks[i] { |
863 | 0 | if restart_interval > 0 && restarts_to_go == 0 { |
864 | 0 | self.writer.finalize_bit_buffer()?; |
865 | 0 | self.writer |
866 | 0 | .write_marker(Marker::RST((restarts % 8) as u8))?; |
867 | | |
868 | 0 | prev_dc = 0; |
869 | 0 | } |
870 | | |
871 | 0 | self.writer.write_dc( |
872 | 0 | block[0], |
873 | 0 | prev_dc, |
874 | 0 | &self.huffman_tables[component.dc_huffman_table as usize].0, |
875 | 0 | )?; |
876 | | |
877 | 0 | prev_dc = block[0]; |
878 | | |
879 | 0 | if restart_interval > 0 { |
880 | 0 | if restarts_to_go == 0 { |
881 | 0 | restarts_to_go = restart_interval; |
882 | 0 | restarts += 1; |
883 | 0 | restarts &= 7; |
884 | 0 | } |
885 | 0 | restarts_to_go -= 1; |
886 | 0 | } |
887 | | } |
888 | | |
889 | 0 | self.writer.finalize_bit_buffer()?; |
890 | | } |
891 | | |
892 | | // Phase 2: AC scans |
893 | 0 | let scans = scans as usize - 1; |
894 | | |
895 | 0 | let values_per_scan = 64 / scans; |
896 | | |
897 | 0 | for scan in 0..scans { |
898 | 0 | let start = (scan * values_per_scan).max(1); |
899 | 0 | let end = if scan == scans - 1 { |
900 | | // ensure last scan is always transfers the remaining coefficients |
901 | 0 | 64 |
902 | | } else { |
903 | 0 | (scan + 1) * values_per_scan |
904 | | }; |
905 | | |
906 | 0 | for (i, component) in self.components.iter().enumerate() { |
907 | 0 | let restart_interval = self.restart_interval.unwrap_or(0); |
908 | 0 | let mut restarts = 0; |
909 | 0 | let mut restarts_to_go = restart_interval; |
910 | | |
911 | 0 | self.writer |
912 | 0 | .write_scan_header(&[component], Some((start as u8, end as u8 - 1)))?; |
913 | | |
914 | 0 | for block in &blocks[i] { |
915 | 0 | if restart_interval > 0 && restarts_to_go == 0 { |
916 | 0 | self.writer.finalize_bit_buffer()?; |
917 | 0 | self.writer |
918 | 0 | .write_marker(Marker::RST((restarts % 8) as u8))?; |
919 | 0 | } |
920 | | |
921 | 0 | self.writer.write_ac_block( |
922 | 0 | block, |
923 | 0 | start, |
924 | 0 | end, |
925 | 0 | &self.huffman_tables[component.ac_huffman_table as usize].1, |
926 | 0 | )?; |
927 | | |
928 | 0 | if restart_interval > 0 { |
929 | 0 | if restarts_to_go == 0 { |
930 | 0 | restarts_to_go = restart_interval; |
931 | 0 | restarts += 1; |
932 | 0 | restarts &= 7; |
933 | 0 | } |
934 | 0 | restarts_to_go -= 1; |
935 | 0 | } |
936 | | } |
937 | | |
938 | 0 | self.writer.finalize_bit_buffer()?; |
939 | | } |
940 | | } |
941 | | |
942 | 0 | Ok(()) |
943 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_image_progressive::<_, _> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_image_progressive::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations> |
944 | | |
945 | 0 | fn encode_blocks<I: ImageBuffer, OP: Operations>( |
946 | 0 | &mut self, |
947 | 0 | image: &I, |
948 | 0 | q_tables: &[QuantizationTable; 2], |
949 | 0 | ) -> [Vec<[i16; 64]>; 4] { |
950 | 0 | let width = image.width(); |
951 | 0 | let height = image.height(); |
952 | | |
953 | 0 | let (max_h_sampling, max_v_sampling) = self.get_max_sampling_size(); |
954 | | |
955 | 0 | let num_cols = ceil_div(usize::from(width), 8 * max_h_sampling) * max_h_sampling; |
956 | 0 | let num_rows = ceil_div(usize::from(height), 8 * max_v_sampling) * max_v_sampling; |
957 | | |
958 | 0 | debug_assert!(num_cols > 0); |
959 | 0 | debug_assert!(num_rows > 0); |
960 | | |
961 | 0 | let buffer_width = num_cols * 8; |
962 | 0 | let buffer_size = num_cols * num_rows * 64; |
963 | | |
964 | 0 | let mut row: [Vec<_>; 4] = self.init_rows(buffer_size); |
965 | | |
966 | 0 | for y in 0..num_rows * 8 { |
967 | 0 | let y = (y.min(usize::from(height) - 1)) as u16; |
968 | | |
969 | 0 | image.fill_buffers(y, &mut row); |
970 | | |
971 | 0 | for _ in usize::from(width)..num_cols * 8 { |
972 | 0 | for channel in &mut row { |
973 | 0 | if !channel.is_empty() { |
974 | 0 | channel.push(channel[channel.len() - 1]); |
975 | 0 | } |
976 | | } |
977 | | } |
978 | | } |
979 | | |
980 | 0 | let num_cols = ceil_div(usize::from(width), 8); |
981 | 0 | let num_rows = ceil_div(usize::from(height), 8); |
982 | | |
983 | 0 | debug_assert!(num_cols > 0); |
984 | 0 | debug_assert!(num_rows > 0); |
985 | | |
986 | 0 | let mut blocks: [Vec<_>; 4] = self.init_block_buffers(buffer_size / 64); |
987 | | |
988 | 0 | for (i, component) in self.components.iter().enumerate() { |
989 | 0 | let h_scale = max_h_sampling / component.horizontal_sampling_factor as usize; |
990 | 0 | let v_scale = max_v_sampling / component.vertical_sampling_factor as usize; |
991 | | |
992 | 0 | let cols = ceil_div(num_cols, h_scale); |
993 | 0 | let rows = ceil_div(num_rows, v_scale); |
994 | | |
995 | 0 | debug_assert!(cols > 0); |
996 | 0 | debug_assert!(rows > 0); |
997 | | |
998 | 0 | for block_y in 0..rows { |
999 | 0 | for block_x in 0..cols { |
1000 | 0 | let mut block = get_block( |
1001 | 0 | &row[i], |
1002 | 0 | block_x * 8 * h_scale, |
1003 | 0 | block_y * 8 * v_scale, |
1004 | 0 | h_scale, |
1005 | 0 | v_scale, |
1006 | 0 | buffer_width, |
1007 | 0 | ); |
1008 | 0 |
|
1009 | 0 | OP::fdct(&mut block); |
1010 | 0 |
|
1011 | 0 | let mut q_block = [0i16; 64]; |
1012 | 0 |
|
1013 | 0 | OP::quantize_block( |
1014 | 0 | &block, |
1015 | 0 | &mut q_block, |
1016 | 0 | &q_tables[component.quantization_table as usize], |
1017 | 0 | ); |
1018 | 0 |
|
1019 | 0 | blocks[i].push(q_block); |
1020 | 0 | } |
1021 | | } |
1022 | | } |
1023 | 0 | blocks |
1024 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::encode_blocks::<_, _> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YCbCrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykAsYcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgrImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::BgraImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::CmykImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::GrayImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::RgbaImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::encoder::DefaultOperations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::image_buffer::YcckImage, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::BgrImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::RgbImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::BgraImageAVX2, jpeg_encoder::avx2::AVX2Operations> Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_blocks::<jpeg_encoder::avx2::ycbcr::RgbaImageAVX2, jpeg_encoder::avx2::AVX2Operations> |
1025 | | |
1026 | 0 | fn init_block_buffers(&mut self, buffer_size: usize) -> [Vec<[i16; 64]>; 4] { |
1027 | | // To simplify the code and to give the compiler more infos to optimize stuff we always initialize 4 components |
1028 | | // Resource overhead should be minimal because an empty Vec doesn't allocate |
1029 | | |
1030 | 0 | match self.components.len() { |
1031 | 0 | 1 => [ |
1032 | 0 | Vec::with_capacity(buffer_size), |
1033 | 0 | Vec::new(), |
1034 | 0 | Vec::new(), |
1035 | 0 | Vec::new(), |
1036 | 0 | ], |
1037 | 0 | 3 => [ |
1038 | 0 | Vec::with_capacity(buffer_size), |
1039 | 0 | Vec::with_capacity(buffer_size), |
1040 | 0 | Vec::with_capacity(buffer_size), |
1041 | 0 | Vec::new(), |
1042 | 0 | ], |
1043 | 0 | 4 => [ |
1044 | 0 | Vec::with_capacity(buffer_size), |
1045 | 0 | Vec::with_capacity(buffer_size), |
1046 | 0 | Vec::with_capacity(buffer_size), |
1047 | 0 | Vec::with_capacity(buffer_size), |
1048 | 0 | ], |
1049 | 0 | len => unreachable!("Unsupported component length: {}", len), |
1050 | | } |
1051 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::init_block_buffers Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::init_block_buffers |
1052 | | |
1053 | | // Create new huffman tables optimized for this image |
1054 | 0 | fn optimize_huffman_table(&mut self, blocks: &[Vec<[i16; 64]>; 4]) { |
1055 | | // TODO: Find out if it's possible to reuse some code from the writer |
1056 | | |
1057 | 0 | let max_tables = self.components.len().min(2) as u8; |
1058 | | |
1059 | 0 | for table in 0..max_tables { |
1060 | 0 | let mut dc_freq = [0u32; 257]; |
1061 | 0 | dc_freq[256] = 1; |
1062 | 0 | let mut ac_freq = [0u32; 257]; |
1063 | 0 | ac_freq[256] = 1; |
1064 | | |
1065 | 0 | let mut had_ac = false; |
1066 | 0 | let mut had_dc = false; |
1067 | | |
1068 | 0 | for (i, component) in self.components.iter().enumerate() { |
1069 | 0 | if component.dc_huffman_table == table { |
1070 | 0 | had_dc = true; |
1071 | | |
1072 | 0 | let mut prev_dc = 0; |
1073 | | |
1074 | 0 | debug_assert!(!blocks[i].is_empty()); |
1075 | | |
1076 | 0 | for block in &blocks[i] { |
1077 | 0 | let value = block[0]; |
1078 | 0 | let diff = value - prev_dc; |
1079 | 0 | let num_bits = get_num_bits(diff); |
1080 | 0 |
|
1081 | 0 | dc_freq[num_bits as usize] += 1; |
1082 | 0 |
|
1083 | 0 | prev_dc = value; |
1084 | 0 | } |
1085 | 0 | } |
1086 | | |
1087 | 0 | if component.ac_huffman_table == table { |
1088 | 0 | had_ac = true; |
1089 | | |
1090 | 0 | if let Some(scans) = self.progressive_scans { |
1091 | 0 | let scans = scans as usize - 1; |
1092 | | |
1093 | 0 | let values_per_scan = 64 / scans; |
1094 | | |
1095 | 0 | for scan in 0..scans { |
1096 | 0 | let start = (scan * values_per_scan).max(1); |
1097 | 0 | let end = if scan == scans - 1 { |
1098 | | // Due to rounding we might need to transfer more than values_per_scan values in the last scan |
1099 | 0 | 64 |
1100 | | } else { |
1101 | 0 | (scan + 1) * values_per_scan |
1102 | | }; |
1103 | | |
1104 | 0 | debug_assert!(!blocks[i].is_empty()); |
1105 | | |
1106 | 0 | for block in &blocks[i] { |
1107 | 0 | let mut zero_run = 0; |
1108 | | |
1109 | 0 | for &value in &block[start..end] { |
1110 | 0 | if value == 0 { |
1111 | 0 | zero_run += 1; |
1112 | 0 | } else { |
1113 | 0 | while zero_run > 15 { |
1114 | 0 | ac_freq[0xF0] += 1; |
1115 | 0 | zero_run -= 16; |
1116 | 0 | } |
1117 | 0 | let num_bits = get_num_bits(value); |
1118 | 0 | let symbol = (zero_run << 4) | num_bits; |
1119 | | |
1120 | 0 | ac_freq[symbol as usize] += 1; |
1121 | | |
1122 | 0 | zero_run = 0; |
1123 | | } |
1124 | | } |
1125 | | |
1126 | 0 | if zero_run > 0 { |
1127 | 0 | ac_freq[0] += 1; |
1128 | 0 | } |
1129 | | } |
1130 | | } |
1131 | | } else { |
1132 | 0 | for block in &blocks[i] { |
1133 | 0 | let mut zero_run = 0; |
1134 | | |
1135 | 0 | for &value in &block[1..] { |
1136 | 0 | if value == 0 { |
1137 | 0 | zero_run += 1; |
1138 | 0 | } else { |
1139 | 0 | while zero_run > 15 { |
1140 | 0 | ac_freq[0xF0] += 1; |
1141 | 0 | zero_run -= 16; |
1142 | 0 | } |
1143 | 0 | let num_bits = get_num_bits(value); |
1144 | 0 | let symbol = (zero_run << 4) | num_bits; |
1145 | | |
1146 | 0 | ac_freq[symbol as usize] += 1; |
1147 | | |
1148 | 0 | zero_run = 0; |
1149 | | } |
1150 | | } |
1151 | | |
1152 | 0 | if zero_run > 0 { |
1153 | 0 | ac_freq[0] += 1; |
1154 | 0 | } |
1155 | | } |
1156 | | } |
1157 | 0 | } |
1158 | | } |
1159 | | |
1160 | 0 | assert!(had_dc, "Missing DC data for table {}", table); |
1161 | 0 | assert!(had_ac, "Missing AC data for table {}", table); |
1162 | | |
1163 | 0 | self.huffman_tables[table as usize] = ( |
1164 | 0 | HuffmanTable::new_optimized(dc_freq), |
1165 | 0 | HuffmanTable::new_optimized(ac_freq), |
1166 | | ); |
1167 | | } |
1168 | 0 | } Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<_>>::optimize_huffman_table Unexecuted instantiation: <jpeg_encoder::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::optimize_huffman_table |
1169 | | } |
1170 | | |
1171 | | #[cfg(feature = "std")] |
1172 | | impl Encoder<BufWriter<File>> { |
1173 | | /// Create a new decoder that writes into a file |
1174 | | /// |
1175 | | /// See [new](Encoder::new) for further information. |
1176 | | /// |
1177 | | /// # Errors |
1178 | | /// |
1179 | | /// Returns an `IoError(std::io::Error)` if the file can't be created |
1180 | 0 | pub fn new_file<P: AsRef<Path>>( |
1181 | 0 | path: P, |
1182 | 0 | quality: u8, |
1183 | 0 | ) -> Result<Encoder<BufWriter<File>>, EncodingError> { |
1184 | 0 | let file = File::create(path)?; |
1185 | 0 | let buf = BufWriter::new(file); |
1186 | 0 | Ok(Self::new(buf, quality)) |
1187 | 0 | } |
1188 | | } |
1189 | | |
1190 | 0 | fn get_block( |
1191 | 0 | data: &[u8], |
1192 | 0 | start_x: usize, |
1193 | 0 | start_y: usize, |
1194 | 0 | col_stride: usize, |
1195 | 0 | row_stride: usize, |
1196 | 0 | width: usize, |
1197 | 0 | ) -> [i16; 64] { |
1198 | 0 | let mut block = [0i16; 64]; |
1199 | | |
1200 | 0 | for y in 0..8 { |
1201 | 0 | for x in 0..8 { |
1202 | 0 | let ix = start_x + (x * col_stride); |
1203 | 0 | let iy = start_y + (y * row_stride); |
1204 | 0 |
|
1205 | 0 | block[y * 8 + x] = (data[iy * width + ix] as i16) - 128; |
1206 | 0 | } |
1207 | | } |
1208 | | |
1209 | 0 | block |
1210 | 0 | } |
1211 | | |
1212 | 0 | fn ceil_div(value: usize, div: usize) -> usize { |
1213 | 0 | value / div + usize::from(value % div != 0) |
1214 | 0 | } |
1215 | | |
1216 | 0 | fn get_num_bits(mut value: i16) -> u8 { |
1217 | 0 | if value < 0 { |
1218 | 0 | value = -value; |
1219 | 0 | } |
1220 | | |
1221 | 0 | let mut num_bits = 0; |
1222 | | |
1223 | 0 | while value > 0 { |
1224 | 0 | num_bits += 1; |
1225 | 0 | value >>= 1; |
1226 | 0 | } |
1227 | | |
1228 | 0 | num_bits |
1229 | 0 | } |
1230 | | |
1231 | | pub(crate) trait Operations { |
1232 | | #[inline(always)] |
1233 | 0 | fn fdct(data: &mut [i16; 64]) { |
1234 | 0 | fdct(data); |
1235 | 0 | } Unexecuted instantiation: <_ as jpeg_encoder::encoder::Operations>::fdct Unexecuted instantiation: <jpeg_encoder::encoder::DefaultOperations as jpeg_encoder::encoder::Operations>::fdct |
1236 | | |
1237 | | #[inline(always)] |
1238 | 0 | fn quantize_block(block: &[i16; 64], q_block: &mut [i16; 64], table: &QuantizationTable) { |
1239 | 0 | for i in 0..64 { |
1240 | 0 | let z = ZIGZAG[i] as usize & 0x3f; |
1241 | 0 | q_block[i] = table.quantize(block[z], z); |
1242 | 0 | } |
1243 | 0 | } Unexecuted instantiation: <_ as jpeg_encoder::encoder::Operations>::quantize_block Unexecuted instantiation: <jpeg_encoder::avx2::AVX2Operations as jpeg_encoder::encoder::Operations>::quantize_block Unexecuted instantiation: <jpeg_encoder::encoder::DefaultOperations as jpeg_encoder::encoder::Operations>::quantize_block |
1244 | | } |
1245 | | |
1246 | | pub(crate) struct DefaultOperations; |
1247 | | |
1248 | | impl Operations for DefaultOperations {} |
1249 | | |
1250 | | #[cfg(test)] |
1251 | | mod tests { |
1252 | | use alloc::vec; |
1253 | | |
1254 | | use crate::encoder::get_num_bits; |
1255 | | use crate::writer::get_code; |
1256 | | use crate::{Encoder, SamplingFactor}; |
1257 | | |
1258 | | #[test] |
1259 | | fn test_get_num_bits() { |
1260 | | let min_max = 2i16.pow(13); |
1261 | | |
1262 | | for value in -min_max..=min_max { |
1263 | | let num_bits1 = get_num_bits(value); |
1264 | | let (num_bits2, _) = get_code(value); |
1265 | | |
1266 | | assert_eq!( |
1267 | | num_bits1, num_bits2, |
1268 | | "Difference in num bits for value {}: {} vs {}", |
1269 | | value, num_bits1, num_bits2 |
1270 | | ); |
1271 | | } |
1272 | | } |
1273 | | |
1274 | | #[test] |
1275 | | fn sampling_factors() { |
1276 | | assert_eq!(SamplingFactor::F_1_1.get_sampling_factors(), (1, 1)); |
1277 | | assert_eq!(SamplingFactor::F_2_1.get_sampling_factors(), (2, 1)); |
1278 | | assert_eq!(SamplingFactor::F_1_2.get_sampling_factors(), (1, 2)); |
1279 | | assert_eq!(SamplingFactor::F_2_2.get_sampling_factors(), (2, 2)); |
1280 | | assert_eq!(SamplingFactor::F_4_1.get_sampling_factors(), (4, 1)); |
1281 | | assert_eq!(SamplingFactor::F_4_2.get_sampling_factors(), (4, 2)); |
1282 | | assert_eq!(SamplingFactor::F_1_4.get_sampling_factors(), (1, 4)); |
1283 | | assert_eq!(SamplingFactor::F_2_4.get_sampling_factors(), (2, 4)); |
1284 | | |
1285 | | assert_eq!(SamplingFactor::R_4_4_4.get_sampling_factors(), (1, 1)); |
1286 | | assert_eq!(SamplingFactor::R_4_4_0.get_sampling_factors(), (1, 2)); |
1287 | | assert_eq!(SamplingFactor::R_4_4_1.get_sampling_factors(), (1, 4)); |
1288 | | assert_eq!(SamplingFactor::R_4_2_2.get_sampling_factors(), (2, 1)); |
1289 | | assert_eq!(SamplingFactor::R_4_2_0.get_sampling_factors(), (2, 2)); |
1290 | | assert_eq!(SamplingFactor::R_4_2_1.get_sampling_factors(), (2, 4)); |
1291 | | assert_eq!(SamplingFactor::R_4_1_1.get_sampling_factors(), (4, 1)); |
1292 | | assert_eq!(SamplingFactor::R_4_1_0.get_sampling_factors(), (4, 2)); |
1293 | | } |
1294 | | |
1295 | | #[test] |
1296 | | fn test_set_progressive() { |
1297 | | let mut encoder = Encoder::new(vec![], 100); |
1298 | | encoder.set_progressive(true); |
1299 | | assert_eq!(encoder.progressive_scans(), Some(4)); |
1300 | | |
1301 | | encoder.set_progressive(false); |
1302 | | assert_eq!(encoder.progressive_scans(), None); |
1303 | | } |
1304 | | } |