/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zune-jpeg-0.5.6/src/mcu_prog.rs
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023. |
3 | | * |
4 | | * This software is free software; |
5 | | * |
6 | | * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license |
7 | | */ |
8 | | |
9 | | //!Routines for progressive decoding |
10 | | /* |
11 | | This file is needlessly complicated, |
12 | | |
13 | | It is that way to ensure we don't burn memory anyhow |
14 | | |
15 | | Memory is a scarce resource in some environments, I would like this to be viable |
16 | | in such environments |
17 | | |
18 | | Half of the complexity comes from the jpeg spec, because progressive decoding, |
19 | | is one hell of a ride. |
20 | | |
21 | | */ |
22 | | use alloc::string::ToString; |
23 | | use alloc::vec::Vec; |
24 | | use alloc::{format, vec}; |
25 | | use core::cmp::min; |
26 | | |
27 | | use zune_core::bytestream::{ZByteReaderTrait, ZReader}; |
28 | | use zune_core::colorspace::ColorSpace; |
29 | | use zune_core::log::{debug, error, warn}; |
30 | | |
31 | | use crate::bitstream::BitStream; |
32 | | use crate::components::SampleRatios; |
33 | | use crate::decoder::{JpegDecoder, MAX_COMPONENTS}; |
34 | | use crate::errors::DecodeErrors; |
35 | | use crate::headers::parse_sos; |
36 | | use crate::marker::Marker; |
37 | | use crate::mcu::DCT_BLOCK; |
38 | | use crate::misc::{calculate_padded_width, setup_component_params}; |
39 | | |
40 | | impl<T: ZByteReaderTrait> JpegDecoder<T> { |
41 | | /// Decode a progressive image |
42 | | /// |
43 | | /// This routine decodes a progressive image, stopping if it finds any error. |
44 | | #[allow( |
45 | | clippy::needless_range_loop, |
46 | | clippy::cast_sign_loss, |
47 | | clippy::redundant_else, |
48 | | clippy::too_many_lines |
49 | | )] |
50 | | #[inline(never)] |
51 | 0 | pub(crate) fn decode_mcu_ycbcr_progressive( |
52 | 0 | &mut self, pixels: &mut [u8] |
53 | 0 | ) -> Result<(), DecodeErrors> { |
54 | 0 | setup_component_params(self)?; |
55 | | |
56 | | let mut mcu_height; |
57 | | |
58 | | // memory location for decoded pixels for components |
59 | 0 | let mut block: [Vec<i16>; MAX_COMPONENTS] = [vec![], vec![], vec![], vec![]]; |
60 | | let mut mcu_width; |
61 | | |
62 | 0 | let mut seen_scans = 1; |
63 | | |
64 | 0 | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { |
65 | 0 | warn!("Grayscale image with down-sampled component, resetting component details"); |
66 | 0 | self.reset_params(); |
67 | 0 | } |
68 | | |
69 | 0 | if self.is_interleaved { |
70 | | // this helps us catch component errors. |
71 | 0 | self.set_upsampling()?; |
72 | 0 | } |
73 | 0 | if self.is_interleaved { |
74 | 0 | mcu_width = self.mcu_x; |
75 | 0 | mcu_height = self.mcu_y; |
76 | 0 | } else { |
77 | 0 | mcu_width = (self.info.width as usize + 7) / 8; |
78 | 0 | mcu_height = (self.info.height as usize + 7) / 8; |
79 | 0 | } |
80 | 0 | if self.is_interleaved |
81 | 0 | && self.input_colorspace.num_components() > 1 |
82 | 0 | && self.options.jpeg_get_out_colorspace().num_components() == 1 |
83 | 0 | && (self.sub_sample_ratio == SampleRatios::V |
84 | 0 | || self.sub_sample_ratio == SampleRatios::HV) |
85 | 0 | { |
86 | 0 | // For a specific set of images, e.g interleaved, |
87 | 0 | // when converting from YcbCr to grayscale, we need to |
88 | 0 | // take into account mcu height since the MCU decoding needs to take |
89 | 0 | // it into account for padding purposes and the post processor |
90 | 0 | // parses two rows per mcu width. |
91 | 0 | // |
92 | 0 | // set coeff to be 2 to ensure that we increment two rows |
93 | 0 | // for every mcu processed also |
94 | 0 | mcu_height *= self.v_max; |
95 | 0 | mcu_height /= self.h_max; |
96 | 0 | self.coeff = 2; |
97 | 0 | } |
98 | | |
99 | 0 | mcu_width *= 64; |
100 | | |
101 | 0 | for i in 0..self.input_colorspace.num_components() { |
102 | 0 | let comp = &self.components[i]; |
103 | 0 | let len = mcu_width * comp.vertical_sample * comp.horizontal_sample * mcu_height; |
104 | 0 |
|
105 | 0 | block[i] = vec![0; len]; |
106 | 0 | } |
107 | | |
108 | 0 | let mut stream = BitStream::new_progressive(self.succ_low, self.spec_start, self.spec_end); |
109 | | |
110 | | // there are multiple scans in the stream, this should resolve the first scan |
111 | 0 | let result = self.parse_entropy_coded_data(&mut stream, &mut block); |
112 | | |
113 | 0 | if result.is_err() { |
114 | 0 | return if self.options.strict_mode() { |
115 | 0 | Err(result.err().unwrap()) |
116 | | } else { |
117 | 0 | error!("{}", result.err().unwrap()); |
118 | | // Go process it and return as much as we can, exiting here |
119 | 0 | return self.finish_progressive_decoding(&block, pixels); |
120 | | }; |
121 | 0 | } |
122 | | |
123 | | // extract marker |
124 | 0 | let mut marker = stream |
125 | 0 | .marker |
126 | 0 | .take() |
127 | 0 | .ok_or(DecodeErrors::FormatStatic("Marker missing where expected"))?; |
128 | | |
129 | | // if marker is EOI, we are done, otherwise continue scanning. |
130 | | // |
131 | | // In case we have a premature image, we print a warning or return |
132 | | // an error, depending on the strictness of the decoder, so there |
133 | | // is that logic to handle too |
134 | 0 | 'eoi: while marker != Marker::EOI { |
135 | 0 | match marker { |
136 | | Marker::SOS => { |
137 | 0 | parse_sos(self)?; |
138 | | |
139 | 0 | stream.update_progressive_params( |
140 | 0 | self.succ_high, |
141 | 0 | self.succ_low, |
142 | 0 | self.spec_start, |
143 | 0 | self.spec_end |
144 | | ); |
145 | | // after every SOS, marker, parse data for that scan. |
146 | 0 | let result = self.parse_entropy_coded_data(&mut stream, &mut block); |
147 | | |
148 | | // Do not error out too fast, allows the decoder to continue as much as possible |
149 | | // even after errors |
150 | 0 | if result.is_err() { |
151 | 0 | return if self.options.strict_mode() { |
152 | 0 | Err(result.err().unwrap()) |
153 | | } else { |
154 | 0 | error!("{}", result.err().unwrap()); |
155 | 0 | break 'eoi; |
156 | | }; |
157 | 0 | } |
158 | | // extract marker, might either indicate end of image or we continue |
159 | | // scanning(hence the continue statement to determine). |
160 | 0 | match get_marker(&mut self.stream, &mut stream) { |
161 | 0 | Ok(marker_n) => { |
162 | 0 | marker = marker_n; |
163 | 0 | seen_scans += 1; |
164 | 0 | if seen_scans > self.options.jpeg_get_max_scans() { |
165 | 0 | return Err(DecodeErrors::Format(format!( |
166 | 0 | "Too many scans, exceeded limit of {}", |
167 | 0 | self.options.jpeg_get_max_scans() |
168 | 0 | ))); |
169 | 0 | } |
170 | | |
171 | 0 | stream.reset(); |
172 | 0 | continue 'eoi; |
173 | | } |
174 | 0 | Err(msg) => { |
175 | 0 | if self.options.strict_mode() { |
176 | 0 | return Err(msg); |
177 | 0 | } |
178 | 0 | error!("{:?}", msg); |
179 | 0 | break 'eoi; |
180 | | } |
181 | | } |
182 | | } |
183 | 0 | Marker::RST(_n) => { |
184 | 0 | self.handle_rst(&mut stream)?; |
185 | | } |
186 | | _ => { |
187 | 0 | self.parse_marker_inner(marker)?; |
188 | | } |
189 | | } |
190 | | |
191 | 0 | match get_marker(&mut self.stream, &mut stream) { |
192 | 0 | Ok(marker_n) => { |
193 | 0 | marker = marker_n; |
194 | 0 | } |
195 | 0 | Err(e) => { |
196 | 0 | if self.options.strict_mode() { |
197 | 0 | return Err(e); |
198 | 0 | } |
199 | 0 | error!("{}", e); |
200 | | // If we can't get the marker, just break away |
201 | | // allows us to decode some corrupt images |
202 | | // e.g https://github.com/etemesi254/zune-image/issues/294 |
203 | 0 | break 'eoi; |
204 | | } |
205 | | } |
206 | | } |
207 | | |
208 | 0 | self.finish_progressive_decoding(&block, pixels) |
209 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::decode_mcu_ycbcr_progressive Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_mcu_ycbcr_progressive |
210 | | |
211 | | /// Reset progressive parameters |
212 | 0 | fn reset_prog_params(&mut self, stream: &mut BitStream) { |
213 | 0 | stream.reset(); |
214 | 0 | self.components.iter_mut().for_each(|x| x.dc_pred = 0); |
215 | | |
216 | | // Also reset JPEG restart intervals |
217 | 0 | self.todo = if self.restart_interval != 0 { self.restart_interval } else { usize::MAX }; |
218 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::reset_prog_params Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::reset_prog_params |
219 | | |
220 | | #[allow(clippy::too_many_lines, clippy::cast_sign_loss)] |
221 | 0 | fn parse_entropy_coded_data( |
222 | 0 | &mut self, stream: &mut BitStream, buffer: &mut [Vec<i16>; MAX_COMPONENTS] |
223 | 0 | ) -> Result<(), DecodeErrors> { |
224 | 0 | self.reset_prog_params(stream); |
225 | | |
226 | 0 | if usize::from(self.num_scans) > self.input_colorspace.num_components() { |
227 | 0 | return Err(DecodeErrors::Format(format!( |
228 | 0 | "Number of scans {} cannot be greater than number of components, {}", |
229 | 0 | self.num_scans, |
230 | 0 | self.input_colorspace.num_components() |
231 | 0 | ))); |
232 | 0 | } |
233 | 0 | if self.num_scans == 1 { |
234 | | // Safety checks |
235 | 0 | if self.spec_end != 0 && self.spec_start == 0 { |
236 | 0 | return Err(DecodeErrors::FormatStatic( |
237 | 0 | "Can't merge DC and AC corrupt jpeg" |
238 | 0 | )); |
239 | 0 | } |
240 | | // non interleaved data, process one block at a time in trivial scanline order |
241 | | |
242 | 0 | let k = self.z_order[0]; |
243 | | |
244 | 0 | if k >= self.components.len() { |
245 | 0 | return Err(DecodeErrors::Format(format!( |
246 | 0 | "Cannot find component {k}, corrupt image" |
247 | 0 | ))); |
248 | 0 | } |
249 | | |
250 | | let (mcu_width, mcu_height); |
251 | | |
252 | 0 | if self.components[k].vertical_sample != 1 |
253 | 0 | || self.components[k].horizontal_sample != 1 |
254 | 0 | || !self.is_interleaved |
255 | 0 | { |
256 | 0 | // For non interleaved scans |
257 | 0 | // mcu's is the image dimensions divided by 8 |
258 | 0 | mcu_width = self.info.width.div_ceil(8) as usize; |
259 | 0 | mcu_height = self.info.height.div_ceil(8) as usize; |
260 | 0 | } else { |
261 | 0 | // For other channels, in an interleaved mcu, number of MCU's |
262 | 0 | // are determined by some weird maths done in headers.rs->parse_sos() |
263 | 0 | mcu_width = self.mcu_x; |
264 | 0 | mcu_height = self.mcu_y; |
265 | 0 | } |
266 | | |
267 | 0 | for i in 0..mcu_height { |
268 | 0 | for j in 0..mcu_width { |
269 | 0 | if self.spec_start != 0 && self.succ_high == 0 && stream.eob_run > 0 { |
270 | 0 | // handle EOB runs here. |
271 | 0 | stream.eob_run -= 1; |
272 | 0 | } else { |
273 | 0 | let start = 64 * (j + i * (self.components[k].width_stride / 8)); |
274 | | |
275 | 0 | let data: &mut [i16; 64] = buffer |
276 | 0 | .get_mut(k) |
277 | 0 | .unwrap() |
278 | 0 | .get_mut(start..start + 64) |
279 | 0 | .ok_or(DecodeErrors::FormatStatic("Slice to Small"))? |
280 | 0 | .try_into() |
281 | 0 | .unwrap(); |
282 | | |
283 | 0 | if self.spec_start == 0 { |
284 | 0 | let pos = self.components[k].dc_huff_table & (MAX_COMPONENTS - 1); |
285 | 0 | let dc_table = self |
286 | 0 | .dc_huffman_tables |
287 | 0 | .get(pos) |
288 | 0 | .ok_or(DecodeErrors::FormatStatic( |
289 | 0 | "No huffman table for DC component" |
290 | 0 | ))? |
291 | 0 | .as_ref() |
292 | 0 | .ok_or(DecodeErrors::FormatStatic( |
293 | 0 | "Huffman table at index {} not initialized" |
294 | 0 | ))?; |
295 | | |
296 | 0 | let dc_pred = &mut self.components[k].dc_pred; |
297 | | |
298 | 0 | if self.succ_high == 0 { |
299 | | // first scan for this mcu |
300 | 0 | stream.decode_prog_dc_first( |
301 | 0 | &mut self.stream, |
302 | 0 | dc_table, |
303 | 0 | &mut data[0], |
304 | 0 | dc_pred |
305 | 0 | )?; |
306 | | } else { |
307 | | // refining scans for this MCU |
308 | 0 | stream.decode_prog_dc_refine(&mut self.stream, &mut data[0])?; |
309 | | } |
310 | | } else { |
311 | 0 | let pos = self.components[k].ac_huff_table; |
312 | 0 | let ac_table = self |
313 | 0 | .ac_huffman_tables |
314 | 0 | .get(pos) |
315 | 0 | .ok_or_else(|| { |
316 | 0 | DecodeErrors::Format(format!( |
317 | 0 | "No huffman table for component:{pos}" |
318 | 0 | )) |
319 | 0 | })? Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#0}Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#0} |
320 | 0 | .as_ref() |
321 | 0 | .ok_or_else(|| { |
322 | 0 | DecodeErrors::Format(format!( |
323 | 0 | "Huffman table at index {pos} not initialized" |
324 | 0 | )) |
325 | 0 | })?; Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#1}Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#1} |
326 | | |
327 | 0 | if self.succ_high == 0 { |
328 | 0 | debug_assert!(stream.eob_run == 0, "EOB run is not zero"); |
329 | | |
330 | 0 | stream.decode_mcu_ac_first(&mut self.stream, ac_table, data)?; |
331 | | } else { |
332 | | // refinement scan |
333 | 0 | stream.decode_mcu_ac_refine(&mut self.stream, ac_table, data)?; |
334 | | } |
335 | | // Check for a marker. |
336 | | // It can appear in stream CC https://github.com/etemesi254/zune-image/issues/300 |
337 | | // if let Some(marker) = stream.marker.take() { |
338 | | // self.parse_marker_inner(marker)?; |
339 | | // } |
340 | | } |
341 | | } |
342 | | |
343 | | // + EOB and investigate effect. |
344 | 0 | self.todo -= 1; |
345 | | |
346 | 0 | self.handle_rst_main(stream)?; |
347 | | } |
348 | | } |
349 | | } else { |
350 | 0 | if self.spec_end != 0 { |
351 | 0 | return Err(DecodeErrors::HuffmanDecode( |
352 | 0 | "Can't merge dc and AC corrupt jpeg".to_string() |
353 | 0 | )); |
354 | 0 | } |
355 | | // process scan n elements in order |
356 | | |
357 | | // Do the error checking with allocs here. |
358 | | // Make the one in the inner loop free of allocations. |
359 | 0 | for k in 0..self.num_scans { |
360 | 0 | let n = self.z_order[k as usize]; |
361 | | |
362 | 0 | if n >= self.components.len() { |
363 | 0 | return Err(DecodeErrors::Format(format!( |
364 | 0 | "Cannot find component {n}, corrupt image" |
365 | 0 | ))); |
366 | 0 | } |
367 | | |
368 | 0 | let component = &mut self.components[n]; |
369 | 0 | let _ = self |
370 | 0 | .dc_huffman_tables |
371 | 0 | .get(component.dc_huff_table) |
372 | 0 | .ok_or_else(|| { |
373 | 0 | DecodeErrors::Format(format!( |
374 | 0 | "No huffman table for component:{}", |
375 | 0 | component.dc_huff_table |
376 | 0 | )) |
377 | 0 | })? Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#2}Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#2} |
378 | 0 | .as_ref() |
379 | 0 | .ok_or_else(|| { |
380 | 0 | DecodeErrors::Format(format!( |
381 | 0 | "Huffman table at index {} not initialized", |
382 | 0 | component.dc_huff_table |
383 | 0 | )) |
384 | 0 | })?; Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#3}Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#3} |
385 | | } |
386 | | // Interleaved scan |
387 | | |
388 | | // Components shall not be interleaved in progressive mode, except for |
389 | | // the DC coefficients in the first scan for each component of a progressive frame. |
390 | 0 | for i in 0..self.mcu_y { |
391 | 0 | for j in 0..self.mcu_x { |
392 | | // process scan n elements in order |
393 | 0 | for k in 0..self.num_scans { |
394 | 0 | let n = self.z_order[k as usize]; |
395 | 0 | let component = &mut self.components[n]; |
396 | 0 | let huff_table = self |
397 | 0 | .dc_huffman_tables |
398 | 0 | .get(component.dc_huff_table) |
399 | 0 | .ok_or(DecodeErrors::FormatStatic("No huffman table for component"))? |
400 | 0 | .as_ref() |
401 | 0 | .ok_or(DecodeErrors::FormatStatic( |
402 | 0 | "Huffman table at index not initialized" |
403 | 0 | ))?; |
404 | | |
405 | 0 | for v_samp in 0..component.vertical_sample { |
406 | 0 | for h_samp in 0..component.horizontal_sample { |
407 | 0 | let x2 = j * component.horizontal_sample + h_samp; |
408 | 0 | let y2 = i * component.vertical_sample + v_samp; |
409 | 0 | let position = 64 * (x2 + y2 * component.width_stride / 8); |
410 | 0 | let buf_n = &mut buffer[n]; |
411 | | |
412 | 0 | let Some(data) = &mut buf_n.get_mut(position) else { |
413 | | // TODO: (CAE), this is another weird sub-sampling bug, so on fix |
414 | | // remove this |
415 | 0 | return Err(DecodeErrors::FormatStatic("Invalid image")); |
416 | | }; |
417 | | |
418 | 0 | if self.succ_high == 0 { |
419 | 0 | stream.decode_prog_dc_first( |
420 | 0 | &mut self.stream, |
421 | 0 | huff_table, |
422 | 0 | data, |
423 | 0 | &mut component.dc_pred |
424 | 0 | )?; |
425 | | } else { |
426 | 0 | stream.decode_prog_dc_refine(&mut self.stream, data)?; |
427 | | } |
428 | | } |
429 | | } |
430 | | } |
431 | | // We want wrapping subtraction here because it means |
432 | | // we get a higher number in the case this underflows |
433 | 0 | self.todo -= 1; |
434 | | // after every scan that's a mcu, count down restart markers. |
435 | 0 | self.handle_rst_main(stream)?; |
436 | | } |
437 | | } |
438 | | } |
439 | 0 | return Ok(()); |
440 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data |
441 | | |
442 | 0 | pub(crate) fn handle_rst_main(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { |
443 | 0 | if self.todo == 0 { |
444 | 0 | stream.refill(&mut self.stream)?; |
445 | 0 | } |
446 | | |
447 | 0 | if self.todo == 0 |
448 | 0 | && self.restart_interval != 0 |
449 | 0 | && stream.marker.is_none() |
450 | 0 | && !stream.seen_eoi |
451 | | { |
452 | | // if no marker and we are to reset RST, look for the marker, this matches |
453 | | // libjpeg-turbo behaviour and allows us to decode images in |
454 | | // https://github.com/etemesi254/zune-image/issues/261 |
455 | 0 | let _start = self.stream.position()?; |
456 | | // skip bytes until we find marker |
457 | 0 | let marker = get_marker(&mut self.stream, stream); |
458 | | |
459 | | // In some images, the RST marker on the last section may not be available |
460 | | // as it is maybe stopped by an EOI marker, see in the case of https://github.com/etemesi254/zune-image/issues/292 |
461 | | // what happened was that we would go looking for the RST marker exhausting all the data |
462 | | // in the image and this would return an error, so for now |
463 | | // translate it to a warning, but return the image decoded up |
464 | | // until that point |
465 | 0 | if let Ok(marker) = marker { |
466 | 0 | let _end = self.stream.position()?; |
467 | 0 | stream.marker = Some(marker); |
468 | | // NB some warnings may be false positives. |
469 | 0 | warn!( |
470 | | "{} Extraneous bytes before marker {:?}", |
471 | | _end - _start, |
472 | | marker |
473 | | ); |
474 | | } else { |
475 | 0 | warn!("RST marker was not found, where expected, image may be garbled") |
476 | | } |
477 | 0 | } |
478 | 0 | if self.todo == 0 { |
479 | 0 | self.handle_rst(stream)? |
480 | 0 | } |
481 | 0 | Ok(()) |
482 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::handle_rst_main Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::handle_rst_main |
483 | | #[allow(clippy::too_many_lines)] |
484 | | #[allow(clippy::needless_range_loop, clippy::cast_sign_loss)] |
485 | 0 | fn finish_progressive_decoding( |
486 | 0 | &mut self, block: &[Vec<i16>; MAX_COMPONENTS], pixels: &mut [u8] |
487 | 0 | ) -> Result<(), DecodeErrors> { |
488 | | // This function is complicated because we need to replicate |
489 | | // the function in mcu.rs |
490 | | // |
491 | | // The advantage is that we do very little allocation and very lot |
492 | | // channel reusing. |
493 | | // The trick is to notice that we repeat the same procedure per MCU |
494 | | // width. |
495 | | // |
496 | | // So we can set it up that we only allocate temporary storage large enough |
497 | | // to store a single mcu width, then reuse it per invocation. |
498 | | // |
499 | | // This is advantageous to us. |
500 | | // |
501 | | // Remember we need to have the whole MCU buffer so we store 3 unprocessed |
502 | | // channels in memory, and then we allocate the whole output buffer in memory, both of |
503 | | // which are huge. |
504 | | // |
505 | | // |
506 | | |
507 | 0 | let mcu_height = if self.is_interleaved { |
508 | 0 | self.mcu_y |
509 | | } else { |
510 | | // For non-interleaved images( (1*1) subsampling) |
511 | | // number of MCU's are the widths (+7 to account for paddings) divided by 8. |
512 | 0 | self.info.height.div_ceil(8) as usize |
513 | | }; |
514 | | |
515 | | // Size of our output image(width*height) |
516 | 0 | let is_hv = usize::from(self.is_interleaved); |
517 | 0 | let upsampler_scratch_size = is_hv * self.components[0].width_stride; |
518 | 0 | let width = usize::from(self.info.width); |
519 | 0 | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); |
520 | | |
521 | 0 | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; |
522 | 0 | let mut tmp = [0_i32; DCT_BLOCK]; |
523 | | |
524 | 0 | for (pos, comp) in self.components.iter_mut().enumerate() { |
525 | | // Allocate only needed components. |
526 | | // |
527 | | // For special colorspaces i.e YCCK and CMYK, just allocate all of the needed |
528 | | // components. |
529 | 0 | if min( |
530 | 0 | self.options.jpeg_get_out_colorspace().num_components() - 1, |
531 | 0 | pos |
532 | 0 | ) == pos |
533 | 0 | || self.input_colorspace == ColorSpace::YCCK |
534 | 0 | || self.input_colorspace == ColorSpace::CMYK |
535 | 0 | { |
536 | 0 | // allocate enough space to hold a whole MCU width |
537 | 0 | // this means we should take into account sampling ratios |
538 | 0 | // `*8` is because each MCU spans 8 widths. |
539 | 0 | let len = comp.width_stride * comp.vertical_sample * 8; |
540 | 0 |
|
541 | 0 | comp.needed = true; |
542 | 0 | comp.raw_coeff = vec![0; len]; |
543 | 0 | } else { |
544 | 0 | comp.needed = false; |
545 | 0 | } |
546 | | } |
547 | | |
548 | 0 | let mut pixels_written = 0; |
549 | | |
550 | | // dequantize, idct and color convert. |
551 | 0 | for i in 0..mcu_height { |
552 | 0 | 'component: for (position, component) in &mut self.components.iter_mut().enumerate() { |
553 | 0 | if !component.needed { |
554 | 0 | continue 'component; |
555 | 0 | } |
556 | 0 | let qt_table = &component.quantization_table; |
557 | | |
558 | | // step is the number of pixels this iteration wil be handling |
559 | | // Given by the number of mcu's height and the length of the component block |
560 | | // Since the component block contains the whole channel as raw pixels |
561 | | // we this evenly divides the pixels into MCU blocks |
562 | | // |
563 | | // For interleaved images, this gives us the exact pixels comprising a whole MCU |
564 | | // block |
565 | 0 | let step = block[position].len() / mcu_height; |
566 | | // where we will be reading our pixels from. |
567 | 0 | let start = i * step; |
568 | | |
569 | 0 | let slice = &block[position][start..start + step]; |
570 | | |
571 | 0 | let temp_channel = &mut component.raw_coeff; |
572 | | |
573 | | // The next logical step is to iterate width wise. |
574 | | // To figure out how many pixels we iterate by we use effective pixels |
575 | | // Given to us by component.x |
576 | | // iterate per effective pixels. |
577 | 0 | let mcu_x = component.width_stride / 8; |
578 | | |
579 | | // iterate per every vertical sample. |
580 | 0 | for k in 0..component.vertical_sample { |
581 | 0 | for j in 0..mcu_x { |
582 | | // after writing a single stride, we need to skip 8 rows. |
583 | | // This does the row calculation |
584 | 0 | let width_stride = k * 8 * component.width_stride; |
585 | 0 | let start = j * 64 + width_stride; |
586 | | |
587 | | // See https://github.com/etemesi254/zune-image/issues/262 sample 3. |
588 | 0 | let Some(qt_slice) = slice.get(start..start + 64) else { |
589 | 0 | return Err(DecodeErrors::FormatStatic( |
590 | 0 | "Invalid slice , would panic, invalid image" |
591 | 0 | )); |
592 | | }; |
593 | | // dequantize |
594 | 0 | for ((x, out), qt_val) in |
595 | 0 | qt_slice.iter().zip(tmp.iter_mut()).zip(qt_table.iter()) |
596 | 0 | { |
597 | 0 | *out = i32::from(*x) * qt_val; |
598 | 0 | } |
599 | | // determine where to write. |
600 | 0 | let sl = &mut temp_channel[component.idct_pos..]; |
601 | | |
602 | 0 | component.idct_pos += 8; |
603 | | // tmp now contains a dequantized block so idct it |
604 | 0 | (self.idct_func)(&mut tmp, sl, component.width_stride); |
605 | | } |
606 | | // after every write of 8, skip 7 since idct write stride wise 8 times. |
607 | | // |
608 | | // Remember each MCU is 8x8 block, so each idct will write 8 strides into |
609 | | // sl |
610 | | // |
611 | | // and component.idct_pos is one stride long |
612 | 0 | component.idct_pos += 7 * component.width_stride; |
613 | | } |
614 | 0 | component.idct_pos = 0; |
615 | | } |
616 | | |
617 | | // process that width up until it's impossible |
618 | 0 | self.post_process( |
619 | 0 | pixels, |
620 | 0 | i, |
621 | 0 | mcu_height, |
622 | 0 | width, |
623 | 0 | padded_width, |
624 | 0 | &mut pixels_written, |
625 | 0 | &mut upsampler_scratch_space |
626 | 0 | )?; |
627 | | } |
628 | | |
629 | | debug!("Finished decoding image"); |
630 | | |
631 | 0 | return Ok(()); |
632 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::finish_progressive_decoding Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::finish_progressive_decoding |
633 | 0 | pub(crate) fn reset_params(&mut self) { |
634 | | /* |
635 | | Apparently, grayscale images which can be down sampled exists, which is weird in the sense |
636 | | that it has one component Y, which is not usually down sampled. |
637 | | |
638 | | This means some calculations will be wrong, so for that we explicitly reset params |
639 | | for such occurrences, warn and reset the image info to appear as if it were |
640 | | a non-sampled image to ensure decoding works |
641 | | */ |
642 | 0 | self.h_max = 1; |
643 | 0 | self.v_max = 1; |
644 | 0 | self.sub_sample_ratio = SampleRatios::None; |
645 | 0 | self.is_interleaved = false; |
646 | 0 | self.components[0].vertical_sample = 1; |
647 | 0 | self.components[0].width_stride = (((self.info.width as usize) + 7) / 8) * 8; |
648 | 0 | self.components[0].horizontal_sample = 1; |
649 | 0 | } Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::reset_params Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::reset_params |
650 | | } |
651 | | |
652 | | ///Get a marker from the bit-stream. |
653 | | /// |
654 | | /// This reads until it gets a marker or end of file is encountered |
655 | 0 | pub fn get_marker<T>( |
656 | 0 | reader: &mut ZReader<T>, stream: &mut BitStream |
657 | 0 | ) -> Result<Marker, DecodeErrors> |
658 | 0 | where |
659 | 0 | T: ZByteReaderTrait |
660 | | { |
661 | 0 | if let Some(marker) = stream.marker { |
662 | 0 | stream.marker = None; |
663 | 0 | return Ok(marker); |
664 | 0 | } |
665 | | |
666 | | // read until we get a marker |
667 | | |
668 | 0 | while !reader.eof()? { |
669 | 0 | let marker = reader.read_u8_err()?; |
670 | | |
671 | 0 | if marker == 255 { |
672 | 0 | let mut r = reader.read_u8_err()?; |
673 | | // 0xFF 0XFF(some images may be like that) |
674 | 0 | while r == 0xFF { |
675 | 0 | r = reader.read_u8_err()?; |
676 | | } |
677 | | |
678 | 0 | if r != 0 { |
679 | 0 | return Marker::from_u8(r) |
680 | 0 | .ok_or_else(|| DecodeErrors::Format(format!("Unknown marker 0xFF{r:X}")));Unexecuted instantiation: zune_jpeg::mcu_prog::get_marker::<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>::{closure#0}Unexecuted instantiation: zune_jpeg::mcu_prog::get_marker::<_>::{closure#0} |
681 | 0 | } |
682 | 0 | } |
683 | | } |
684 | 0 | return Err(DecodeErrors::ExhaustedData); |
685 | 0 | } Unexecuted instantiation: zune_jpeg::mcu_prog::get_marker::<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>> Unexecuted instantiation: zune_jpeg::mcu_prog::get_marker::<_> |
686 | | |
687 | | // #[cfg(test)] |
688 | | // mod tests{ |
689 | | // use zune_core::bytestream::ZCursor; |
690 | | // use crate::JpegDecoder; |
691 | | // |
692 | | // #[test] |
693 | | // fn make_test(){ |
694 | | // let img = "/Users/etemesi/Downloads/wrong_sampling.jpeg"; |
695 | | // let data = ZCursor::new(std::fs::read(img).unwrap()); |
696 | | // let mut decoder = JpegDecoder::new(data); |
697 | | // decoder.decode().unwrap(); |
698 | | // |
699 | | // } |
700 | | // } |