1use crate::ljpeg::{self, LjpegHeader};
17use flashraw_core::{Error, Result};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub struct Slices {
22 pub count: u32,
23 pub width: u32,
24 pub last_width: u32,
25}
26
27impl Slices {
28 pub fn from_tag(v: &[u32]) -> Slices {
29 Slices {
30 count: v.first().copied().unwrap_or(0),
31 width: v.get(1).copied().unwrap_or(0),
32 last_width: v.get(2).copied().unwrap_or(0),
33 }
34 }
35 pub fn is_sliced(&self) -> bool {
36 self.count != 0
37 }
38}
39
40struct Placement {
42 raw_width: usize,
43 raw_height: usize,
44 widths: Vec<usize>,
46 starts: Vec<usize>,
48 slice: usize,
49 row: usize,
50 col: usize,
51}
52
53impl Placement {
54 fn new(raw_width: usize, raw_height: usize, s: Slices) -> Result<Placement> {
55 let (widths, starts) = if s.is_sliced() {
56 if s.width == 0 || s.last_width == 0 {
57 return Err(Error::corrupt("CR2: zero slice width"));
58 }
59 if s.count > 64 * 1024 {
60 return Err(Error::corrupt("CR2: implausible slice count"));
61 }
62 let mut w = vec![s.width as usize; s.count as usize];
63 w.push(s.last_width as usize);
64 let starts = (0..w.len()).map(|i| i * s.width as usize).collect();
65 (w, starts)
66 } else {
67 (vec![raw_width], vec![0])
68 };
69 Ok(Placement { raw_width, raw_height, widths, starts, slice: 0, row: 0, col: 0 })
70 }
71
72 #[inline]
74 fn put(&mut self, mut src: &[u16], out: &mut [u16]) -> Result<()> {
75 while !src.is_empty() {
76 if self.row > self.raw_height {
80 return Err(Error::corrupt("CR2: more samples than the slice layout holds"));
81 }
82 let sw = self.widths[self.slice];
83 let take = (sw - self.col).min(src.len());
84 let x = self.starts[self.slice] + self.col;
85 if self.row < self.raw_height && x < self.raw_width {
86 let n = take.min(self.raw_width - x);
87 let o = self.row * self.raw_width + x;
88 out[o..o + n].copy_from_slice(&src[..n]);
89 }
90 src = &src[take..];
91 self.col += take;
92 if self.col == sw {
93 self.col = 0;
94 self.row += 1;
95 if self.row == self.raw_height && self.slice + 1 < self.widths.len() {
96 self.row = 0;
97 self.slice += 1;
98 }
99 }
100 }
101 Ok(())
102 }
103}
104
105fn decode_parallel(
108 data: &[u8],
109 hdr: &LjpegHeader,
110 place: &Placement,
111 out: &mut [u16],
112 chunk: Option<usize>,
113) -> Result<bool> {
114 let frame_len = hdr.row_len() * hdr.height as usize;
115 if place.widths.len() == 1 && frame_len <= out.len() {
116 return crate::ljpeg_par::decode_frame_chunked(data, hdr, hdr.height as usize, &mut out[..frame_len], chunk);
118 }
119 let mut frame = vec![0u16; frame_len];
120 if !crate::ljpeg_par::decode_frame_chunked(data, hdr, hdr.height as usize, &mut frame, chunk)? {
121 return Ok(false);
122 }
123 let (w, h) = (place.raw_width, place.raw_height);
124 let workers = flashraw_core::par::memory_bound_workers();
125 flashraw_core::par::for_each_row_chunk_bounded(out, w, workers, true, |y0, rows| {
126 for (dy, row) in rows.chunks_exact_mut(w).enumerate() {
127 let y = y0 + dy;
128 let mut base = 0usize;
129 for (&sw, &x0) in place.widths.iter().zip(&place.starts) {
130 let src = base + y * sw;
131 base += sw * h;
132 if x0 >= w || src >= frame.len() {
133 continue;
134 }
135 let n = sw.min(w - x0).min(frame.len() - src);
136 row[x0..x0 + n].copy_from_slice(&frame[src..src + n]);
137 }
138 }
139 });
140 Ok(true)
141}
142
143#[allow(clippy::too_many_arguments)]
148pub fn decode(
149 data: &[u8],
150 hdr: &LjpegHeader,
151 raw_width: usize,
152 raw_height: usize,
153 slices: Slices,
154 shift_left_2: bool,
155 parallel: bool,
156 out: &mut [u16],
157) -> Result<()> {
158 let total = raw_width.checked_mul(raw_height).ok_or_else(|| Error::corrupt("CR2: size overflow"))?;
159 if out.len() < total || total == 0 {
160 return Err(Error::corrupt("CR2: output buffer too small"));
161 }
162 let out = &mut out[..total];
163 let mut place = Placement::new(raw_width, raw_height, slices)?;
164 if parallel
165 && total >= crate::ljpeg_par::LJPEG_PARALLEL_MIN_SAMPLES
166 && decode_parallel(data, hdr, &place, out, None)?
167 {
168 if shift_left_2 && total > 2 {
169 out.copy_within(2.., 0);
170 out[total - 2..].fill(0);
171 }
172 return Ok(());
173 }
174 let mut overflow = false;
175 ljpeg::decode_rows(data, hdr, |_, row| {
176 if overflow {
177 return Ok(());
178 }
179 if place.put(row, out).is_err() {
180 overflow = true;
183 }
184 Ok(())
185 })?;
186 if shift_left_2 && total > 2 {
187 out.copy_within(2.., 0);
188 out[total - 2..].fill(0);
189 }
190 Ok(())
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::ljpeg::encoder::encode;
197
198 fn reference(stream: &[u16], w: usize, h: usize, s: Slices) -> Vec<u16> {
200 let mut out = vec![0u16; w * h];
201 let (mut row, mut col) = (0usize, 0usize);
202 for (jidx, &v) in stream.iter().enumerate() {
203 if s.is_sliced() {
204 let per = s.width as usize * h;
205 let mut i = jidx / per;
206 let last = i >= s.count as usize;
207 if last {
208 i = s.count as usize;
209 }
210 let sw = if last { s.last_width } else { s.width } as usize;
211 let k = jidx - i * per;
212 row = k / sw;
213 col = k % sw + i * s.width as usize;
214 }
215 if row < h && col < w {
216 out[row * w + col] = v;
217 }
218 col += 1;
219 if col >= w {
220 col = 0;
221 row += 1;
222 }
223 }
224 out
225 }
226
227 fn pseudo(n: usize) -> Vec<u16> {
228 let mut x = 12345u32;
229 (0..n)
230 .map(|_| {
231 x = x.wrapping_mul(1103515245).wrapping_add(12345);
232 ((x >> 8) & 0x3fff) as u16
233 })
234 .collect()
235 }
236
237 #[test]
238 fn slices_match_layout_formula() {
239 let (w, h) = (14usize, 6usize);
241 let samples = pseudo(w * h);
242 let jpg = encode(7, 6, 2, 14, 1, &samples);
243 let hdr = LjpegHeader::parse(&jpg, Default::default()).unwrap();
244 for s in [
245 Slices { count: 2, width: 5, last_width: 4 },
246 Slices { count: 1, width: 7, last_width: 7 },
247 Slices { count: 0, width: 0, last_width: 14 },
248 ] {
249 let mut out = vec![0u16; w * h];
250 decode(&jpg, &hdr, w, h, s, false, false, &mut out).unwrap();
251 let place = Placement::new(w, h, s).unwrap();
252 let mut par = vec![0u16; w * h];
253 assert!(decode_parallel(&jpg, &hdr, &place, &mut par, Some(9)).unwrap());
254 assert_eq!(par, out, "parallel {s:?}");
255 assert_eq!(out, reference(&samples, w, h, s), "{s:?}");
256 }
257 }
258
259 #[test]
260 fn shift_quirk() {
261 let (w, h) = (8usize, 2usize);
262 let samples = pseudo(w * h);
263 let jpg = encode(8, 2, 1, 12, 1, &samples);
264 let hdr = LjpegHeader::parse(&jpg, Default::default()).unwrap();
265 let mut out = vec![0u16; w * h];
266 decode(&jpg, &hdr, w, h, Slices::default(), true, false, &mut out).unwrap();
267 assert_eq!(&out[..w * h - 2], &samples[2..]);
268 assert_eq!(&out[w * h - 2..], &[0, 0]);
269 }
270
271 #[test]
272 fn bad_slices_rejected() {
273 assert!(Placement::new(10, 10, Slices { count: 1, width: 0, last_width: 3 }).is_err());
274 let mut p = Placement::new(4, 1, Slices { count: 1, width: 2, last_width: 2 }).unwrap();
275 let mut out = vec![0u16; 4];
276 p.put(&[1, 2, 3, 4], &mut out).unwrap();
277 assert_eq!(out, vec![1, 2, 3, 4]);
278 p.put(&[5, 6], &mut out).unwrap(); assert!(p.put(&[7, 8, 9], &mut out).is_err());
280 assert_eq!(out, vec![1, 2, 3, 4]);
281 }
282}