/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 | 3.59k | pub(crate) fn decode_mcu_ycbcr_progressive( |
52 | 3.59k | &mut self, pixels: &mut [u8] |
53 | 3.59k | ) -> Result<(), DecodeErrors> { |
54 | 3.59k | setup_component_params(self)?; |
55 | | |
56 | | let mut mcu_height; |
57 | | |
58 | | // memory location for decoded pixels for components |
59 | 3.54k | let mut block: [Vec<i16>; MAX_COMPONENTS] = [vec![], vec![], vec![], vec![]]; |
60 | | let mut mcu_width; |
61 | | |
62 | 3.54k | let mut seen_scans = 1; |
63 | | |
64 | 3.54k | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { |
65 | 1.57k | warn!("Grayscale image with down-sampled component, resetting component details"); |
66 | 1.57k | self.reset_params(); |
67 | 1.97k | } |
68 | | |
69 | 3.54k | if self.is_interleaved { |
70 | | // this helps us catch component errors. |
71 | 1.05k | self.set_upsampling()?; |
72 | 2.49k | } |
73 | 3.54k | if self.is_interleaved { |
74 | 1.05k | mcu_width = self.mcu_x; |
75 | 1.05k | mcu_height = self.mcu_y; |
76 | 2.49k | } else { |
77 | 2.49k | mcu_width = (self.info.width as usize + 7) / 8; |
78 | 2.49k | mcu_height = (self.info.height as usize + 7) / 8; |
79 | 2.49k | } |
80 | 3.54k | if self.is_interleaved |
81 | 1.05k | && self.input_colorspace.num_components() > 1 |
82 | 1.05k | && 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 | 3.54k | } |
98 | | |
99 | 3.54k | mcu_width *= 64; |
100 | | |
101 | 5.86k | for i in 0..self.input_colorspace.num_components() { |
102 | 5.86k | let comp = &self.components[i]; |
103 | 5.86k | let len = mcu_width * comp.vertical_sample * comp.horizontal_sample * mcu_height; |
104 | 5.86k | |
105 | 5.86k | block[i] = vec![0; len]; |
106 | 5.86k | } |
107 | | |
108 | 3.54k | 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 | 3.54k | let result = self.parse_entropy_coded_data(&mut stream, &mut block); |
112 | | |
113 | 3.54k | if result.is_err() { |
114 | 945 | return if self.options.strict_mode() { |
115 | 0 | Err(result.err().unwrap()) |
116 | | } else { |
117 | 945 | error!("{}", result.err().unwrap()); |
118 | | // Go process it and return as much as we can, exiting here |
119 | 945 | return self.finish_progressive_decoding(&block, pixels); |
120 | | }; |
121 | 2.60k | } |
122 | | |
123 | | // extract marker |
124 | 2.60k | let mut marker = stream |
125 | 2.60k | .marker |
126 | 2.60k | .take() |
127 | 2.60k | .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 | 69.2k | 'eoi: while marker != Marker::EOI { |
135 | 69.0k | match marker { |
136 | | Marker::SOS => { |
137 | 13.4k | parse_sos(self)?; |
138 | | |
139 | 13.2k | stream.update_progressive_params( |
140 | 13.2k | self.succ_high, |
141 | 13.2k | self.succ_low, |
142 | 13.2k | self.spec_start, |
143 | 13.2k | self.spec_end |
144 | | ); |
145 | | // after every SOS, marker, parse data for that scan. |
146 | 13.2k | 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 | 13.2k | if result.is_err() { |
151 | 651 | return if self.options.strict_mode() { |
152 | 0 | Err(result.err().unwrap()) |
153 | | } else { |
154 | 651 | error!("{}", result.err().unwrap()); |
155 | 651 | break 'eoi; |
156 | | }; |
157 | 12.6k | } |
158 | | // extract marker, might either indicate end of image or we continue |
159 | | // scanning(hence the continue statement to determine). |
160 | 12.6k | match get_marker(&mut self.stream, &mut stream) { |
161 | 12.1k | Ok(marker_n) => { |
162 | 12.1k | marker = marker_n; |
163 | 12.1k | seen_scans += 1; |
164 | 12.1k | if seen_scans > self.options.jpeg_get_max_scans() { |
165 | 5 | return Err(DecodeErrors::Format(format!( |
166 | 5 | "Too many scans, exceeded limit of {}", |
167 | 5 | self.options.jpeg_get_max_scans() |
168 | 5 | ))); |
169 | 12.1k | } |
170 | | |
171 | 12.1k | stream.reset(); |
172 | 12.1k | continue 'eoi; |
173 | | } |
174 | 447 | Err(msg) => { |
175 | 447 | if self.options.strict_mode() { |
176 | 0 | return Err(msg); |
177 | 447 | } |
178 | 447 | error!("{:?}", msg); |
179 | 447 | break 'eoi; |
180 | | } |
181 | | } |
182 | | } |
183 | 10.2k | Marker::RST(_n) => { |
184 | 10.2k | self.handle_rst(&mut stream)?; |
185 | | } |
186 | | _ => { |
187 | 45.2k | self.parse_marker_inner(marker)?; |
188 | | } |
189 | | } |
190 | | |
191 | 55.3k | match get_marker(&mut self.stream, &mut stream) { |
192 | 55.0k | Ok(marker_n) => { |
193 | 55.0k | marker = marker_n; |
194 | 55.0k | } |
195 | 257 | Err(e) => { |
196 | 257 | if self.options.strict_mode() { |
197 | 0 | return Err(e); |
198 | 257 | } |
199 | 257 | 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 | 257 | break 'eoi; |
204 | | } |
205 | | } |
206 | | } |
207 | | |
208 | 1.58k | self.finish_progressive_decoding(&block, pixels) |
209 | 3.59k | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::decode_mcu_ycbcr_progressive Line | Count | Source | 51 | 3.59k | pub(crate) fn decode_mcu_ycbcr_progressive( | 52 | 3.59k | &mut self, pixels: &mut [u8] | 53 | 3.59k | ) -> Result<(), DecodeErrors> { | 54 | 3.59k | setup_component_params(self)?; | 55 | | | 56 | | let mut mcu_height; | 57 | | | 58 | | // memory location for decoded pixels for components | 59 | 3.54k | let mut block: [Vec<i16>; MAX_COMPONENTS] = [vec![], vec![], vec![], vec![]]; | 60 | | let mut mcu_width; | 61 | | | 62 | 3.54k | let mut seen_scans = 1; | 63 | | | 64 | 3.54k | if self.input_colorspace == ColorSpace::Luma && self.is_interleaved { | 65 | 1.57k | warn!("Grayscale image with down-sampled component, resetting component details"); | 66 | 1.57k | self.reset_params(); | 67 | 1.97k | } | 68 | | | 69 | 3.54k | if self.is_interleaved { | 70 | | // this helps us catch component errors. | 71 | 1.05k | self.set_upsampling()?; | 72 | 2.49k | } | 73 | 3.54k | if self.is_interleaved { | 74 | 1.05k | mcu_width = self.mcu_x; | 75 | 1.05k | mcu_height = self.mcu_y; | 76 | 2.49k | } else { | 77 | 2.49k | mcu_width = (self.info.width as usize + 7) / 8; | 78 | 2.49k | mcu_height = (self.info.height as usize + 7) / 8; | 79 | 2.49k | } | 80 | 3.54k | if self.is_interleaved | 81 | 1.05k | && self.input_colorspace.num_components() > 1 | 82 | 1.05k | && 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 | 3.54k | } | 98 | | | 99 | 3.54k | mcu_width *= 64; | 100 | | | 101 | 5.86k | for i in 0..self.input_colorspace.num_components() { | 102 | 5.86k | let comp = &self.components[i]; | 103 | 5.86k | let len = mcu_width * comp.vertical_sample * comp.horizontal_sample * mcu_height; | 104 | 5.86k | | 105 | 5.86k | block[i] = vec![0; len]; | 106 | 5.86k | } | 107 | | | 108 | 3.54k | 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 | 3.54k | let result = self.parse_entropy_coded_data(&mut stream, &mut block); | 112 | | | 113 | 3.54k | if result.is_err() { | 114 | 945 | return if self.options.strict_mode() { | 115 | 0 | Err(result.err().unwrap()) | 116 | | } else { | 117 | 945 | error!("{}", result.err().unwrap()); | 118 | | // Go process it and return as much as we can, exiting here | 119 | 945 | return self.finish_progressive_decoding(&block, pixels); | 120 | | }; | 121 | 2.60k | } | 122 | | | 123 | | // extract marker | 124 | 2.60k | let mut marker = stream | 125 | 2.60k | .marker | 126 | 2.60k | .take() | 127 | 2.60k | .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 | 69.2k | 'eoi: while marker != Marker::EOI { | 135 | 69.0k | match marker { | 136 | | Marker::SOS => { | 137 | 13.4k | parse_sos(self)?; | 138 | | | 139 | 13.2k | stream.update_progressive_params( | 140 | 13.2k | self.succ_high, | 141 | 13.2k | self.succ_low, | 142 | 13.2k | self.spec_start, | 143 | 13.2k | self.spec_end | 144 | | ); | 145 | | // after every SOS, marker, parse data for that scan. | 146 | 13.2k | 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 | 13.2k | if result.is_err() { | 151 | 651 | return if self.options.strict_mode() { | 152 | 0 | Err(result.err().unwrap()) | 153 | | } else { | 154 | 651 | error!("{}", result.err().unwrap()); | 155 | 651 | break 'eoi; | 156 | | }; | 157 | 12.6k | } | 158 | | // extract marker, might either indicate end of image or we continue | 159 | | // scanning(hence the continue statement to determine). | 160 | 12.6k | match get_marker(&mut self.stream, &mut stream) { | 161 | 12.1k | Ok(marker_n) => { | 162 | 12.1k | marker = marker_n; | 163 | 12.1k | seen_scans += 1; | 164 | 12.1k | if seen_scans > self.options.jpeg_get_max_scans() { | 165 | 5 | return Err(DecodeErrors::Format(format!( | 166 | 5 | "Too many scans, exceeded limit of {}", | 167 | 5 | self.options.jpeg_get_max_scans() | 168 | 5 | ))); | 169 | 12.1k | } | 170 | | | 171 | 12.1k | stream.reset(); | 172 | 12.1k | continue 'eoi; | 173 | | } | 174 | 447 | Err(msg) => { | 175 | 447 | if self.options.strict_mode() { | 176 | 0 | return Err(msg); | 177 | 447 | } | 178 | 447 | error!("{:?}", msg); | 179 | 447 | break 'eoi; | 180 | | } | 181 | | } | 182 | | } | 183 | 10.2k | Marker::RST(_n) => { | 184 | 10.2k | self.handle_rst(&mut stream)?; | 185 | | } | 186 | | _ => { | 187 | 45.2k | self.parse_marker_inner(marker)?; | 188 | | } | 189 | | } | 190 | | | 191 | 55.3k | match get_marker(&mut self.stream, &mut stream) { | 192 | 55.0k | Ok(marker_n) => { | 193 | 55.0k | marker = marker_n; | 194 | 55.0k | } | 195 | 257 | Err(e) => { | 196 | 257 | if self.options.strict_mode() { | 197 | 0 | return Err(e); | 198 | 257 | } | 199 | 257 | 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 | 257 | break 'eoi; | 204 | | } | 205 | | } | 206 | | } | 207 | | | 208 | 1.58k | self.finish_progressive_decoding(&block, pixels) | 209 | 3.59k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_mcu_ycbcr_progressive |
210 | | |
211 | | /// Reset progressive parameters |
212 | 16.8k | fn reset_prog_params(&mut self, stream: &mut BitStream) { |
213 | 16.8k | stream.reset(); |
214 | 16.8k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); |
215 | | |
216 | | // Also reset JPEG restart intervals |
217 | 16.8k | self.todo = if self.restart_interval != 0 { self.restart_interval } else { usize::MAX }; |
218 | 16.8k | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::reset_prog_params Line | Count | Source | 212 | 16.8k | fn reset_prog_params(&mut self, stream: &mut BitStream) { | 213 | 16.8k | stream.reset(); | 214 | 16.8k | self.components.iter_mut().for_each(|x| x.dc_pred = 0); | 215 | | | 216 | | // Also reset JPEG restart intervals | 217 | 16.8k | self.todo = if self.restart_interval != 0 { self.restart_interval } else { usize::MAX }; | 218 | 16.8k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::reset_prog_params |
219 | | |
220 | | #[allow(clippy::too_many_lines, clippy::cast_sign_loss)] |
221 | 16.8k | fn parse_entropy_coded_data( |
222 | 16.8k | &mut self, stream: &mut BitStream, buffer: &mut [Vec<i16>; MAX_COMPONENTS] |
223 | 16.8k | ) -> Result<(), DecodeErrors> { |
224 | 16.8k | self.reset_prog_params(stream); |
225 | | |
226 | 16.8k | 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 | 16.8k | } |
233 | 16.8k | if self.num_scans == 1 { |
234 | | // Safety checks |
235 | 13.2k | if self.spec_end != 0 && self.spec_start == 0 { |
236 | 4 | return Err(DecodeErrors::FormatStatic( |
237 | 4 | "Can't merge DC and AC corrupt jpeg" |
238 | 4 | )); |
239 | 13.2k | } |
240 | | // non interleaved data, process one block at a time in trivial scanline order |
241 | | |
242 | 13.2k | let k = self.z_order[0]; |
243 | | |
244 | 13.2k | if k >= self.components.len() { |
245 | 0 | return Err(DecodeErrors::Format(format!( |
246 | 0 | "Cannot find component {k}, corrupt image" |
247 | 0 | ))); |
248 | 13.2k | } |
249 | | |
250 | | let (mcu_width, mcu_height); |
251 | | |
252 | 13.2k | if self.components[k].vertical_sample != 1 |
253 | 9.78k | || self.components[k].horizontal_sample != 1 |
254 | 9.53k | || !self.is_interleaved |
255 | 12.7k | { |
256 | 12.7k | // For non interleaved scans |
257 | 12.7k | // mcu's is the image dimensions divided by 8 |
258 | 12.7k | mcu_width = self.info.width.div_ceil(8) as usize; |
259 | 12.7k | mcu_height = self.info.height.div_ceil(8) as usize; |
260 | 12.7k | } else { |
261 | 468 | // For other channels, in an interleaved mcu, number of MCU's |
262 | 468 | // are determined by some weird maths done in headers.rs->parse_sos() |
263 | 468 | mcu_width = self.mcu_x; |
264 | 468 | mcu_height = self.mcu_y; |
265 | 468 | } |
266 | | |
267 | 5.86M | for i in 0..mcu_height { |
268 | 298M | for j in 0..mcu_width { |
269 | 298M | if self.spec_start != 0 && self.succ_high == 0 && stream.eob_run > 0 { |
270 | 7.29M | // handle EOB runs here. |
271 | 7.29M | stream.eob_run -= 1; |
272 | 7.29M | } else { |
273 | 290M | let start = 64 * (j + i * (self.components[k].width_stride / 8)); |
274 | | |
275 | 290M | let data: &mut [i16; 64] = buffer |
276 | 290M | .get_mut(k) |
277 | 290M | .unwrap() |
278 | 290M | .get_mut(start..start + 64) |
279 | 290M | .ok_or(DecodeErrors::FormatStatic("Slice to Small"))? |
280 | 290M | .try_into() |
281 | 290M | .unwrap(); |
282 | | |
283 | 290M | if self.spec_start == 0 { |
284 | 200M | let pos = self.components[k].dc_huff_table & (MAX_COMPONENTS - 1); |
285 | 200M | let dc_table = self |
286 | 200M | .dc_huffman_tables |
287 | 200M | .get(pos) |
288 | 200M | .ok_or(DecodeErrors::FormatStatic( |
289 | 200M | "No huffman table for DC component" |
290 | 200M | ))? |
291 | 200M | .as_ref() |
292 | 200M | .ok_or(DecodeErrors::FormatStatic( |
293 | 200M | "Huffman table at index {} not initialized" |
294 | 200M | ))?; |
295 | | |
296 | 200M | let dc_pred = &mut self.components[k].dc_pred; |
297 | | |
298 | 200M | if self.succ_high == 0 { |
299 | | // first scan for this mcu |
300 | 70.6M | stream.decode_prog_dc_first( |
301 | 70.6M | &mut self.stream, |
302 | 70.6M | dc_table, |
303 | 70.6M | &mut data[0], |
304 | 70.6M | dc_pred |
305 | 145 | )?; |
306 | | } else { |
307 | | // refining scans for this MCU |
308 | 129M | stream.decode_prog_dc_refine(&mut self.stream, &mut data[0])?; |
309 | | } |
310 | | } else { |
311 | 90.6M | let pos = self.components[k].ac_huff_table; |
312 | 90.6M | let ac_table = self |
313 | 90.6M | .ac_huffman_tables |
314 | 90.6M | .get(pos) |
315 | 90.6M | .ok_or_else(|| { |
316 | 1 | DecodeErrors::Format(format!( |
317 | 1 | "No huffman table for component:{pos}" |
318 | 1 | )) |
319 | 1 | })? <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#0}Line | Count | Source | 315 | 1 | .ok_or_else(|| { | 316 | 1 | DecodeErrors::Format(format!( | 317 | 1 | "No huffman table for component:{pos}" | 318 | 1 | )) | 319 | 1 | })? |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#0} |
320 | 90.6M | .as_ref() |
321 | 90.6M | .ok_or_else(|| { |
322 | 25 | DecodeErrors::Format(format!( |
323 | 25 | "Huffman table at index {pos} not initialized" |
324 | 25 | )) |
325 | 25 | })?; <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#1}Line | Count | Source | 321 | 25 | .ok_or_else(|| { | 322 | 25 | DecodeErrors::Format(format!( | 323 | 25 | "Huffman table at index {pos} not initialized" | 324 | 25 | )) | 325 | 25 | })?; |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#1} |
326 | | |
327 | 90.6M | if self.succ_high == 0 { |
328 | 62.1M | debug_assert!(stream.eob_run == 0, "EOB run is not zero"); |
329 | | |
330 | 62.1M | stream.decode_mcu_ac_first(&mut self.stream, ac_table, data)?; |
331 | | } else { |
332 | | // refinement scan |
333 | 28.5M | 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 | 298M | self.todo -= 1; |
345 | | |
346 | 298M | self.handle_rst_main(stream)?; |
347 | | } |
348 | | } |
349 | | } else { |
350 | 3.60k | if self.spec_end != 0 { |
351 | 4 | return Err(DecodeErrors::HuffmanDecode( |
352 | 4 | "Can't merge dc and AC corrupt jpeg".to_string() |
353 | 4 | )); |
354 | 3.59k | } |
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 | 10.9k | for k in 0..self.num_scans { |
360 | 10.9k | let n = self.z_order[k as usize]; |
361 | | |
362 | 10.9k | if n >= self.components.len() { |
363 | 0 | return Err(DecodeErrors::Format(format!( |
364 | 0 | "Cannot find component {n}, corrupt image" |
365 | 0 | ))); |
366 | 10.9k | } |
367 | | |
368 | 10.9k | let component = &mut self.components[n]; |
369 | 10.9k | let _ = self |
370 | 10.9k | .dc_huffman_tables |
371 | 10.9k | .get(component.dc_huff_table) |
372 | 10.9k | .ok_or_else(|| { |
373 | 6 | DecodeErrors::Format(format!( |
374 | 6 | "No huffman table for component:{}", |
375 | 6 | component.dc_huff_table |
376 | 6 | )) |
377 | 6 | })? <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#2}Line | Count | Source | 372 | 6 | .ok_or_else(|| { | 373 | 6 | DecodeErrors::Format(format!( | 374 | 6 | "No huffman table for component:{}", | 375 | 6 | component.dc_huff_table | 376 | 6 | )) | 377 | 6 | })? |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data::{closure#2} |
378 | 10.9k | .as_ref() |
379 | 10.9k | .ok_or_else(|| { |
380 | 19 | DecodeErrors::Format(format!( |
381 | 19 | "Huffman table at index {} not initialized", |
382 | 19 | component.dc_huff_table |
383 | 19 | )) |
384 | 19 | })?; <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data::{closure#3}Line | Count | Source | 379 | 19 | .ok_or_else(|| { | 380 | 19 | DecodeErrors::Format(format!( | 381 | 19 | "Huffman table at index {} not initialized", | 382 | 19 | component.dc_huff_table | 383 | 19 | )) | 384 | 19 | })?; |
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 | 580k | for i in 0..self.mcu_y { |
391 | 29.0M | for j in 0..self.mcu_x { |
392 | | // process scan n elements in order |
393 | 99.0M | for k in 0..self.num_scans { |
394 | 99.0M | let n = self.z_order[k as usize]; |
395 | 99.0M | let component = &mut self.components[n]; |
396 | 99.0M | let huff_table = self |
397 | 99.0M | .dc_huffman_tables |
398 | 99.0M | .get(component.dc_huff_table) |
399 | 99.0M | .ok_or(DecodeErrors::FormatStatic("No huffman table for component"))? |
400 | 99.0M | .as_ref() |
401 | 99.0M | .ok_or(DecodeErrors::FormatStatic( |
402 | 99.0M | "Huffman table at index not initialized" |
403 | 99.0M | ))?; |
404 | | |
405 | 111M | for v_samp in 0..component.vertical_sample { |
406 | 168M | for h_samp in 0..component.horizontal_sample { |
407 | 168M | let x2 = j * component.horizontal_sample + h_samp; |
408 | 168M | let y2 = i * component.vertical_sample + v_samp; |
409 | 168M | let position = 64 * (x2 + y2 * component.width_stride / 8); |
410 | 168M | let buf_n = &mut buffer[n]; |
411 | | |
412 | 168M | 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 | 15 | return Err(DecodeErrors::FormatStatic("Invalid image")); |
416 | | }; |
417 | | |
418 | 168M | if self.succ_high == 0 { |
419 | 89.5M | stream.decode_prog_dc_first( |
420 | 89.5M | &mut self.stream, |
421 | 89.5M | huff_table, |
422 | 89.5M | data, |
423 | 89.5M | &mut component.dc_pred |
424 | 192 | )?; |
425 | | } else { |
426 | 78.6M | 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 | 29.0M | self.todo -= 1; |
434 | | // after every scan that's a mcu, count down restart markers. |
435 | 29.0M | self.handle_rst_main(stream)?; |
436 | | } |
437 | | } |
438 | | } |
439 | 15.2k | return Ok(()); |
440 | 16.8k | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_entropy_coded_data Line | Count | Source | 221 | 16.8k | fn parse_entropy_coded_data( | 222 | 16.8k | &mut self, stream: &mut BitStream, buffer: &mut [Vec<i16>; MAX_COMPONENTS] | 223 | 16.8k | ) -> Result<(), DecodeErrors> { | 224 | 16.8k | self.reset_prog_params(stream); | 225 | | | 226 | 16.8k | 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 | 16.8k | } | 233 | 16.8k | if self.num_scans == 1 { | 234 | | // Safety checks | 235 | 13.2k | if self.spec_end != 0 && self.spec_start == 0 { | 236 | 4 | return Err(DecodeErrors::FormatStatic( | 237 | 4 | "Can't merge DC and AC corrupt jpeg" | 238 | 4 | )); | 239 | 13.2k | } | 240 | | // non interleaved data, process one block at a time in trivial scanline order | 241 | | | 242 | 13.2k | let k = self.z_order[0]; | 243 | | | 244 | 13.2k | if k >= self.components.len() { | 245 | 0 | return Err(DecodeErrors::Format(format!( | 246 | 0 | "Cannot find component {k}, corrupt image" | 247 | 0 | ))); | 248 | 13.2k | } | 249 | | | 250 | | let (mcu_width, mcu_height); | 251 | | | 252 | 13.2k | if self.components[k].vertical_sample != 1 | 253 | 9.78k | || self.components[k].horizontal_sample != 1 | 254 | 9.53k | || !self.is_interleaved | 255 | 12.7k | { | 256 | 12.7k | // For non interleaved scans | 257 | 12.7k | // mcu's is the image dimensions divided by 8 | 258 | 12.7k | mcu_width = self.info.width.div_ceil(8) as usize; | 259 | 12.7k | mcu_height = self.info.height.div_ceil(8) as usize; | 260 | 12.7k | } else { | 261 | 468 | // For other channels, in an interleaved mcu, number of MCU's | 262 | 468 | // are determined by some weird maths done in headers.rs->parse_sos() | 263 | 468 | mcu_width = self.mcu_x; | 264 | 468 | mcu_height = self.mcu_y; | 265 | 468 | } | 266 | | | 267 | 5.86M | for i in 0..mcu_height { | 268 | 298M | for j in 0..mcu_width { | 269 | 298M | if self.spec_start != 0 && self.succ_high == 0 && stream.eob_run > 0 { | 270 | 7.29M | // handle EOB runs here. | 271 | 7.29M | stream.eob_run -= 1; | 272 | 7.29M | } else { | 273 | 290M | let start = 64 * (j + i * (self.components[k].width_stride / 8)); | 274 | | | 275 | 290M | let data: &mut [i16; 64] = buffer | 276 | 290M | .get_mut(k) | 277 | 290M | .unwrap() | 278 | 290M | .get_mut(start..start + 64) | 279 | 290M | .ok_or(DecodeErrors::FormatStatic("Slice to Small"))? | 280 | 290M | .try_into() | 281 | 290M | .unwrap(); | 282 | | | 283 | 290M | if self.spec_start == 0 { | 284 | 200M | let pos = self.components[k].dc_huff_table & (MAX_COMPONENTS - 1); | 285 | 200M | let dc_table = self | 286 | 200M | .dc_huffman_tables | 287 | 200M | .get(pos) | 288 | 200M | .ok_or(DecodeErrors::FormatStatic( | 289 | 200M | "No huffman table for DC component" | 290 | 200M | ))? | 291 | 200M | .as_ref() | 292 | 200M | .ok_or(DecodeErrors::FormatStatic( | 293 | 200M | "Huffman table at index {} not initialized" | 294 | 200M | ))?; | 295 | | | 296 | 200M | let dc_pred = &mut self.components[k].dc_pred; | 297 | | | 298 | 200M | if self.succ_high == 0 { | 299 | | // first scan for this mcu | 300 | 70.6M | stream.decode_prog_dc_first( | 301 | 70.6M | &mut self.stream, | 302 | 70.6M | dc_table, | 303 | 70.6M | &mut data[0], | 304 | 70.6M | dc_pred | 305 | 145 | )?; | 306 | | } else { | 307 | | // refining scans for this MCU | 308 | 129M | stream.decode_prog_dc_refine(&mut self.stream, &mut data[0])?; | 309 | | } | 310 | | } else { | 311 | 90.6M | let pos = self.components[k].ac_huff_table; | 312 | 90.6M | let ac_table = self | 313 | 90.6M | .ac_huffman_tables | 314 | 90.6M | .get(pos) | 315 | 90.6M | .ok_or_else(|| { | 316 | | DecodeErrors::Format(format!( | 317 | | "No huffman table for component:{pos}" | 318 | | )) | 319 | 1 | })? | 320 | 90.6M | .as_ref() | 321 | 90.6M | .ok_or_else(|| { | 322 | | DecodeErrors::Format(format!( | 323 | | "Huffman table at index {pos} not initialized" | 324 | | )) | 325 | 25 | })?; | 326 | | | 327 | 90.6M | if self.succ_high == 0 { | 328 | 62.1M | debug_assert!(stream.eob_run == 0, "EOB run is not zero"); | 329 | | | 330 | 62.1M | stream.decode_mcu_ac_first(&mut self.stream, ac_table, data)?; | 331 | | } else { | 332 | | // refinement scan | 333 | 28.5M | 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 | 298M | self.todo -= 1; | 345 | | | 346 | 298M | self.handle_rst_main(stream)?; | 347 | | } | 348 | | } | 349 | | } else { | 350 | 3.60k | if self.spec_end != 0 { | 351 | 4 | return Err(DecodeErrors::HuffmanDecode( | 352 | 4 | "Can't merge dc and AC corrupt jpeg".to_string() | 353 | 4 | )); | 354 | 3.59k | } | 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 | 10.9k | for k in 0..self.num_scans { | 360 | 10.9k | let n = self.z_order[k as usize]; | 361 | | | 362 | 10.9k | if n >= self.components.len() { | 363 | 0 | return Err(DecodeErrors::Format(format!( | 364 | 0 | "Cannot find component {n}, corrupt image" | 365 | 0 | ))); | 366 | 10.9k | } | 367 | | | 368 | 10.9k | let component = &mut self.components[n]; | 369 | 10.9k | let _ = self | 370 | 10.9k | .dc_huffman_tables | 371 | 10.9k | .get(component.dc_huff_table) | 372 | 10.9k | .ok_or_else(|| { | 373 | | DecodeErrors::Format(format!( | 374 | | "No huffman table for component:{}", | 375 | | component.dc_huff_table | 376 | | )) | 377 | 6 | })? | 378 | 10.9k | .as_ref() | 379 | 10.9k | .ok_or_else(|| { | 380 | | DecodeErrors::Format(format!( | 381 | | "Huffman table at index {} not initialized", | 382 | | component.dc_huff_table | 383 | | )) | 384 | 19 | })?; | 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 | 580k | for i in 0..self.mcu_y { | 391 | 29.0M | for j in 0..self.mcu_x { | 392 | | // process scan n elements in order | 393 | 99.0M | for k in 0..self.num_scans { | 394 | 99.0M | let n = self.z_order[k as usize]; | 395 | 99.0M | let component = &mut self.components[n]; | 396 | 99.0M | let huff_table = self | 397 | 99.0M | .dc_huffman_tables | 398 | 99.0M | .get(component.dc_huff_table) | 399 | 99.0M | .ok_or(DecodeErrors::FormatStatic("No huffman table for component"))? | 400 | 99.0M | .as_ref() | 401 | 99.0M | .ok_or(DecodeErrors::FormatStatic( | 402 | 99.0M | "Huffman table at index not initialized" | 403 | 99.0M | ))?; | 404 | | | 405 | 111M | for v_samp in 0..component.vertical_sample { | 406 | 168M | for h_samp in 0..component.horizontal_sample { | 407 | 168M | let x2 = j * component.horizontal_sample + h_samp; | 408 | 168M | let y2 = i * component.vertical_sample + v_samp; | 409 | 168M | let position = 64 * (x2 + y2 * component.width_stride / 8); | 410 | 168M | let buf_n = &mut buffer[n]; | 411 | | | 412 | 168M | 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 | 15 | return Err(DecodeErrors::FormatStatic("Invalid image")); | 416 | | }; | 417 | | | 418 | 168M | if self.succ_high == 0 { | 419 | 89.5M | stream.decode_prog_dc_first( | 420 | 89.5M | &mut self.stream, | 421 | 89.5M | huff_table, | 422 | 89.5M | data, | 423 | 89.5M | &mut component.dc_pred | 424 | 192 | )?; | 425 | | } else { | 426 | 78.6M | 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 | 29.0M | self.todo -= 1; | 434 | | // after every scan that's a mcu, count down restart markers. | 435 | 29.0M | self.handle_rst_main(stream)?; | 436 | | } | 437 | | } | 438 | | } | 439 | 15.2k | return Ok(()); | 440 | 16.8k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_entropy_coded_data |
441 | | |
442 | 327M | pub(crate) fn handle_rst_main(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { |
443 | 327M | if self.todo == 0 { |
444 | 1.24M | stream.refill(&mut self.stream)?; |
445 | 326M | } |
446 | | |
447 | 327M | if self.todo == 0 |
448 | 1.24M | && self.restart_interval != 0 |
449 | 1.24M | && stream.marker.is_none() |
450 | 1.08M | && !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 | 960k | let _start = self.stream.position()?; |
456 | | // skip bytes until we find marker |
457 | 960k | 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 | 960k | if let Ok(marker) = marker { |
466 | 5.77k | let _end = self.stream.position()?; |
467 | 5.77k | stream.marker = Some(marker); |
468 | | // NB some warnings may be false positives. |
469 | 5.77k | warn!( |
470 | | "{} Extraneous bytes before marker {:?}", |
471 | | _end - _start, |
472 | | marker |
473 | | ); |
474 | | } else { |
475 | 954k | warn!("RST marker was not found, where expected, image may be garbled") |
476 | | } |
477 | 326M | } |
478 | 327M | if self.todo == 0 { |
479 | 1.24M | self.handle_rst(stream)? |
480 | 326M | } |
481 | 327M | Ok(()) |
482 | 327M | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::handle_rst_main Line | Count | Source | 442 | 327M | pub(crate) fn handle_rst_main(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> { | 443 | 327M | if self.todo == 0 { | 444 | 1.24M | stream.refill(&mut self.stream)?; | 445 | 326M | } | 446 | | | 447 | 327M | if self.todo == 0 | 448 | 1.24M | && self.restart_interval != 0 | 449 | 1.24M | && stream.marker.is_none() | 450 | 1.08M | && !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 | 960k | let _start = self.stream.position()?; | 456 | | // skip bytes until we find marker | 457 | 960k | 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 | 960k | if let Ok(marker) = marker { | 466 | 5.77k | let _end = self.stream.position()?; | 467 | 5.77k | stream.marker = Some(marker); | 468 | | // NB some warnings may be false positives. | 469 | 5.77k | warn!( | 470 | | "{} Extraneous bytes before marker {:?}", | 471 | | _end - _start, | 472 | | marker | 473 | | ); | 474 | | } else { | 475 | 954k | warn!("RST marker was not found, where expected, image may be garbled") | 476 | | } | 477 | 326M | } | 478 | 327M | if self.todo == 0 { | 479 | 1.24M | self.handle_rst(stream)? | 480 | 326M | } | 481 | 327M | Ok(()) | 482 | 327M | } |
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 | 2.53k | fn finish_progressive_decoding( |
486 | 2.53k | &mut self, block: &[Vec<i16>; MAX_COMPONENTS], pixels: &mut [u8] |
487 | 2.53k | ) -> 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 | 2.53k | let mcu_height = if self.is_interleaved { |
508 | 754 | 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 | 1.78k | self.info.height.div_ceil(8) as usize |
513 | | }; |
514 | | |
515 | | // Size of our output image(width*height) |
516 | 2.53k | let is_hv = usize::from(self.is_interleaved); |
517 | 2.53k | let upsampler_scratch_size = is_hv * self.components[0].width_stride; |
518 | 2.53k | let width = usize::from(self.info.width); |
519 | 2.53k | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); |
520 | | |
521 | 2.53k | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; |
522 | 2.53k | let mut tmp = [0_i32; DCT_BLOCK]; |
523 | | |
524 | 4.21k | 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 | 4.21k | if min( |
530 | 4.21k | self.options.jpeg_get_out_colorspace().num_components() - 1, |
531 | 4.21k | pos |
532 | 4.21k | ) == pos |
533 | 28 | || self.input_colorspace == ColorSpace::YCCK |
534 | 27 | || self.input_colorspace == ColorSpace::CMYK |
535 | 4.21k | { |
536 | 4.21k | // allocate enough space to hold a whole MCU width |
537 | 4.21k | // this means we should take into account sampling ratios |
538 | 4.21k | // `*8` is because each MCU spans 8 widths. |
539 | 4.21k | let len = comp.width_stride * comp.vertical_sample * 8; |
540 | 4.21k | |
541 | 4.21k | comp.needed = true; |
542 | 4.21k | comp.raw_coeff = vec![0; len]; |
543 | 4.21k | } else { |
544 | 0 | comp.needed = false; |
545 | 0 | } |
546 | | } |
547 | | |
548 | 2.53k | let mut pixels_written = 0; |
549 | | |
550 | | // dequantize, idct and color convert. |
551 | 1.74M | for i in 0..mcu_height { |
552 | 2.24M | 'component: for (position, component) in &mut self.components.iter_mut().enumerate() { |
553 | 2.24M | if !component.needed { |
554 | 0 | continue 'component; |
555 | 2.24M | } |
556 | 2.24M | 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 | 2.24M | let step = block[position].len() / mcu_height; |
566 | | // where we will be reading our pixels from. |
567 | 2.24M | let start = i * step; |
568 | | |
569 | 2.24M | let slice = &block[position][start..start + step]; |
570 | | |
571 | 2.24M | 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 | 2.24M | let mcu_x = component.width_stride / 8; |
578 | | |
579 | | // iterate per every vertical sample. |
580 | 2.35M | for k in 0..component.vertical_sample { |
581 | 268M | 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 | 268M | let width_stride = k * 8 * component.width_stride; |
585 | 268M | let start = j * 64 + width_stride; |
586 | | |
587 | | // See https://github.com/etemesi254/zune-image/issues/262 sample 3. |
588 | 268M | let Some(qt_slice) = slice.get(start..start + 64) else { |
589 | 19 | return Err(DecodeErrors::FormatStatic( |
590 | 19 | "Invalid slice , would panic, invalid image" |
591 | 19 | )); |
592 | | }; |
593 | | // dequantize |
594 | 17.1G | for ((x, out), qt_val) in |
595 | 268M | qt_slice.iter().zip(tmp.iter_mut()).zip(qt_table.iter()) |
596 | 17.1G | { |
597 | 17.1G | *out = i32::from(*x) * qt_val; |
598 | 17.1G | } |
599 | | // determine where to write. |
600 | 268M | let sl = &mut temp_channel[component.idct_pos..]; |
601 | | |
602 | 268M | component.idct_pos += 8; |
603 | | // tmp now contains a dequantized block so idct it |
604 | 268M | (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 | 2.35M | component.idct_pos += 7 * component.width_stride; |
613 | | } |
614 | 2.24M | component.idct_pos = 0; |
615 | | } |
616 | | |
617 | | // process that width up until it's impossible |
618 | 1.74M | self.post_process( |
619 | 1.74M | pixels, |
620 | 1.74M | i, |
621 | 1.74M | mcu_height, |
622 | 1.74M | width, |
623 | 1.74M | padded_width, |
624 | 1.74M | &mut pixels_written, |
625 | 1.74M | &mut upsampler_scratch_space |
626 | 30 | )?; |
627 | | } |
628 | | |
629 | | debug!("Finished decoding image"); |
630 | | |
631 | 2.48k | return Ok(()); |
632 | 2.53k | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::finish_progressive_decoding Line | Count | Source | 485 | 2.53k | fn finish_progressive_decoding( | 486 | 2.53k | &mut self, block: &[Vec<i16>; MAX_COMPONENTS], pixels: &mut [u8] | 487 | 2.53k | ) -> 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 | 2.53k | let mcu_height = if self.is_interleaved { | 508 | 754 | 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 | 1.78k | self.info.height.div_ceil(8) as usize | 513 | | }; | 514 | | | 515 | | // Size of our output image(width*height) | 516 | 2.53k | let is_hv = usize::from(self.is_interleaved); | 517 | 2.53k | let upsampler_scratch_size = is_hv * self.components[0].width_stride; | 518 | 2.53k | let width = usize::from(self.info.width); | 519 | 2.53k | let padded_width = calculate_padded_width(width, self.sub_sample_ratio); | 520 | | | 521 | 2.53k | let mut upsampler_scratch_space = vec![0; upsampler_scratch_size]; | 522 | 2.53k | let mut tmp = [0_i32; DCT_BLOCK]; | 523 | | | 524 | 4.21k | 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 | 4.21k | if min( | 530 | 4.21k | self.options.jpeg_get_out_colorspace().num_components() - 1, | 531 | 4.21k | pos | 532 | 4.21k | ) == pos | 533 | 28 | || self.input_colorspace == ColorSpace::YCCK | 534 | 27 | || self.input_colorspace == ColorSpace::CMYK | 535 | 4.21k | { | 536 | 4.21k | // allocate enough space to hold a whole MCU width | 537 | 4.21k | // this means we should take into account sampling ratios | 538 | 4.21k | // `*8` is because each MCU spans 8 widths. | 539 | 4.21k | let len = comp.width_stride * comp.vertical_sample * 8; | 540 | 4.21k | | 541 | 4.21k | comp.needed = true; | 542 | 4.21k | comp.raw_coeff = vec![0; len]; | 543 | 4.21k | } else { | 544 | 0 | comp.needed = false; | 545 | 0 | } | 546 | | } | 547 | | | 548 | 2.53k | let mut pixels_written = 0; | 549 | | | 550 | | // dequantize, idct and color convert. | 551 | 1.74M | for i in 0..mcu_height { | 552 | 2.24M | 'component: for (position, component) in &mut self.components.iter_mut().enumerate() { | 553 | 2.24M | if !component.needed { | 554 | 0 | continue 'component; | 555 | 2.24M | } | 556 | 2.24M | 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 | 2.24M | let step = block[position].len() / mcu_height; | 566 | | // where we will be reading our pixels from. | 567 | 2.24M | let start = i * step; | 568 | | | 569 | 2.24M | let slice = &block[position][start..start + step]; | 570 | | | 571 | 2.24M | 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 | 2.24M | let mcu_x = component.width_stride / 8; | 578 | | | 579 | | // iterate per every vertical sample. | 580 | 2.35M | for k in 0..component.vertical_sample { | 581 | 268M | 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 | 268M | let width_stride = k * 8 * component.width_stride; | 585 | 268M | let start = j * 64 + width_stride; | 586 | | | 587 | | // See https://github.com/etemesi254/zune-image/issues/262 sample 3. | 588 | 268M | let Some(qt_slice) = slice.get(start..start + 64) else { | 589 | 19 | return Err(DecodeErrors::FormatStatic( | 590 | 19 | "Invalid slice , would panic, invalid image" | 591 | 19 | )); | 592 | | }; | 593 | | // dequantize | 594 | 17.1G | for ((x, out), qt_val) in | 595 | 268M | qt_slice.iter().zip(tmp.iter_mut()).zip(qt_table.iter()) | 596 | 17.1G | { | 597 | 17.1G | *out = i32::from(*x) * qt_val; | 598 | 17.1G | } | 599 | | // determine where to write. | 600 | 268M | let sl = &mut temp_channel[component.idct_pos..]; | 601 | | | 602 | 268M | component.idct_pos += 8; | 603 | | // tmp now contains a dequantized block so idct it | 604 | 268M | (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 | 2.35M | component.idct_pos += 7 * component.width_stride; | 613 | | } | 614 | 2.24M | component.idct_pos = 0; | 615 | | } | 616 | | | 617 | | // process that width up until it's impossible | 618 | 1.74M | self.post_process( | 619 | 1.74M | pixels, | 620 | 1.74M | i, | 621 | 1.74M | mcu_height, | 622 | 1.74M | width, | 623 | 1.74M | padded_width, | 624 | 1.74M | &mut pixels_written, | 625 | 1.74M | &mut upsampler_scratch_space | 626 | 30 | )?; | 627 | | } | 628 | | | 629 | | debug!("Finished decoding image"); | 630 | | | 631 | 2.48k | return Ok(()); | 632 | 2.53k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::finish_progressive_decoding |
633 | 1.86k | 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 | 1.86k | self.h_max = 1; |
643 | 1.86k | self.v_max = 1; |
644 | 1.86k | self.sub_sample_ratio = SampleRatios::None; |
645 | 1.86k | self.is_interleaved = false; |
646 | 1.86k | self.components[0].vertical_sample = 1; |
647 | 1.86k | self.components[0].width_stride = (((self.info.width as usize) + 7) / 8) * 8; |
648 | 1.86k | self.components[0].horizontal_sample = 1; |
649 | 1.86k | } <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::reset_params Line | Count | Source | 633 | 1.86k | 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 | 1.86k | self.h_max = 1; | 643 | 1.86k | self.v_max = 1; | 644 | 1.86k | self.sub_sample_ratio = SampleRatios::None; | 645 | 1.86k | self.is_interleaved = false; | 646 | 1.86k | self.components[0].vertical_sample = 1; | 647 | 1.86k | self.components[0].width_stride = (((self.info.width as usize) + 7) / 8) * 8; | 648 | 1.86k | self.components[0].horizontal_sample = 1; | 649 | 1.86k | } |
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 | 1.02M | pub fn get_marker<T>( |
656 | 1.02M | reader: &mut ZReader<T>, stream: &mut BitStream |
657 | 1.02M | ) -> Result<Marker, DecodeErrors> |
658 | 1.02M | where |
659 | 1.02M | T: ZByteReaderTrait |
660 | | { |
661 | 1.02M | if let Some(marker) = stream.marker { |
662 | 11.1k | stream.marker = None; |
663 | 11.1k | return Ok(marker); |
664 | 1.01M | } |
665 | | |
666 | | // read until we get a marker |
667 | | |
668 | 6.92M | while !reader.eof()? { |
669 | 5.97M | let marker = reader.read_u8_err()?; |
670 | | |
671 | 5.97M | if marker == 255 { |
672 | 158k | let mut r = reader.read_u8_err()?; |
673 | | // 0xFF 0XFF(some images may be like that) |
674 | 263k | while r == 0xFF { |
675 | 105k | r = reader.read_u8_err()?; |
676 | | } |
677 | | |
678 | 158k | if r != 0 { |
679 | 62.0k | return Marker::from_u8(r) |
680 | 62.0k | .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 | 96.0k | } |
682 | 5.81M | } |
683 | | } |
684 | 955k | return Err(DecodeErrors::ExhaustedData); |
685 | 1.02M | } zune_jpeg::mcu_prog::get_marker::<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>> Line | Count | Source | 655 | 1.02M | pub fn get_marker<T>( | 656 | 1.02M | reader: &mut ZReader<T>, stream: &mut BitStream | 657 | 1.02M | ) -> Result<Marker, DecodeErrors> | 658 | 1.02M | where | 659 | 1.02M | T: ZByteReaderTrait | 660 | | { | 661 | 1.02M | if let Some(marker) = stream.marker { | 662 | 11.1k | stream.marker = None; | 663 | 11.1k | return Ok(marker); | 664 | 1.01M | } | 665 | | | 666 | | // read until we get a marker | 667 | | | 668 | 6.92M | while !reader.eof()? { | 669 | 5.97M | let marker = reader.read_u8_err()?; | 670 | | | 671 | 5.97M | if marker == 255 { | 672 | 158k | let mut r = reader.read_u8_err()?; | 673 | | // 0xFF 0XFF(some images may be like that) | 674 | 263k | while r == 0xFF { | 675 | 105k | r = reader.read_u8_err()?; | 676 | | } | 677 | | | 678 | 158k | if r != 0 { | 679 | 62.0k | return Marker::from_u8(r) | 680 | 62.0k | .ok_or_else(|| DecodeErrors::Format(format!("Unknown marker 0xFF{r:X}"))); | 681 | 96.0k | } | 682 | 5.81M | } | 683 | | } | 684 | 955k | return Err(DecodeErrors::ExhaustedData); | 685 | 1.02M | } |
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 | | // } |