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