1use crate::ljpeg::{self, LjpegHeader};
19use flashraw_core::{Error, Result};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum SrawConversion {
24 Original { subtract_512: bool },
28 Matrix,
30}
31
32#[derive(Debug, Clone, Copy)]
34pub struct SrawParams {
35 pub width: usize,
37 pub height: usize,
38 pub raw_width: usize,
40 pub slices: [u16; 3],
43 pub conversion: SrawConversion,
44 pub hue: i32,
46 pub mul: [u16; 3],
48}
49
50impl SrawParams {
51 pub fn default_hue(sraw: u32, newer: bool) -> i32 {
53 if newer { (sraw as i32) << 1 } else { ((sraw as i32) + 1) << 2 }
54 }
55}
56
57pub fn decode(data: &[u8], hdr: &LjpegHeader, p: &SrawParams, parallel: bool, out: &mut [u16]) -> Result<()> {
60 let clrs = hdr.clrs as usize;
61 if hdr.sraw == 0 || !(4..=6).contains(&clrs) {
62 return Err(Error::corrupt("sRAW: frame is not subsampled YCbCr"));
63 }
64 let (w, h) = (p.width, p.height);
65 let n = w.checked_mul(h).and_then(|v| v.checked_mul(3)).ok_or_else(|| Error::corrupt("sRAW: size overflow"))?;
66 if out.len() < n || w < 2 || h < 1 {
67 return Err(Error::corrupt("sRAW: output buffer too small"));
68 }
69 let out = &mut out[..n];
70 out.fill(0);
71 let mut jh = hdr.clone();
73 jh.width /= 2;
74 let jwide = jh.width as usize * clrs;
75 if !(32..=65535).contains(&jwide) {
76 return Err(Error::corrupt(format!("sRAW: invalid MCU row length {jwide}")));
77 }
78 if !(parallel && scatter_parallel(data, &jh, p, out)?) {
79 scatter(data, &jh, p, out)?;
80 }
81 interpolate_chroma(out, w, h, hdr.sraw, parallel);
82 convert(out, p, parallel);
83 Ok(())
84}
85
86struct Cursor {
88 slice: usize,
89 slices: usize,
90 scol: usize,
91 ecol: usize,
92 row: usize,
93 col: usize,
94 step: usize,
95 slice_cols: usize,
96 raw_width: usize,
97 height: usize,
98 done: bool,
99}
100
101impl Cursor {
102 fn new(p: &SrawParams, clrs: usize) -> Cursor {
103 let slice_cols = p.slices[1] as usize * 2 / clrs;
104 let mut c = Cursor {
105 slice: 0,
106 slices: p.slices[0] as usize,
107 scol: 0,
108 ecol: 0,
109 row: 0,
110 col: 0,
111 step: (clrs >> 1) - 1,
112 slice_cols,
113 raw_width: p.raw_width,
114 height: p.height,
115 done: false,
116 };
117 c.start_slice(0);
118 c
119 }
120
121 fn start_slice(&mut self, scol: usize) {
122 self.scol = scol;
123 self.ecol = scol + self.slice_cols;
124 if self.slices == 0 || self.ecol + 1 > self.raw_width {
125 self.ecol = self.raw_width & !1;
126 }
127 self.row = 0;
128 self.col = scol;
129 self.skip_empty();
130 }
131
132 fn skip_empty(&mut self) {
134 loop {
135 if self.slice > self.slices || self.height == 0 {
136 self.done = true;
137 return;
138 }
139 if self.col < self.ecol && self.row < self.height {
140 return;
141 }
142 if self.col >= self.ecol && self.row < self.height {
143 self.row += self.step;
144 self.col = self.scol;
145 if self.row < self.height && self.col < self.ecol {
146 return;
147 }
148 }
149 if self.row >= self.height || self.scol >= self.ecol {
150 self.slice += 1;
151 let next = self.ecol;
152 if self.slice > self.slices {
153 self.done = true;
154 return;
155 }
156 self.scol = next;
157 self.ecol = next + self.slice_cols;
158 if self.slices == 0 || self.ecol + 1 > self.raw_width {
159 self.ecol = self.raw_width & !1;
160 }
161 self.row = 0;
162 self.col = self.scol;
163 }
164 }
165 }
166
167 fn advance(&mut self) {
168 self.col += 2;
169 self.skip_empty();
170 }
171}
172
173fn slice_list(p: &SrawParams, clrs: usize) -> Vec<(usize, usize, usize, usize)> {
175 let slice_cols = p.slices[1] as usize * 2 / clrs;
176 let step = (clrs >> 1) - 1;
177 let bands = p.height.div_ceil(step.max(1));
178 let mut out = Vec::new();
179 let (mut first, mut ecol) = (0usize, 0usize);
180 if p.height == 0 {
181 return out;
182 }
183 for _ in 0..=p.slices[0] as usize {
186 let scol = ecol;
187 ecol = scol + slice_cols;
188 if p.slices[0] == 0 || ecol + 1 > p.raw_width {
189 ecol = p.raw_width & !1;
190 }
191 if scol < ecol {
192 let per = (ecol - scol).div_ceil(2);
193 out.push((first, scol, ecol, per));
194 first += per * bands;
195 }
196 }
197 out
198}
199
200fn scatter_parallel(data: &[u8], jh: &LjpegHeader, p: &SrawParams, out: &mut [u16]) -> Result<bool> {
203 let clrs = jh.clrs as usize;
204 let nluma = clrs - 2;
205 let step = (clrs >> 1) - 1;
206 let w = p.width;
207 let slices = slice_list(p, clrs);
208 let mcus = slices.last().map(|s| s.0 + s.3 * p.height.div_ceil(step.max(1))).unwrap_or(0);
209 let rl = jh.row_len();
210 let rows = (mcus * clrs).div_ceil(rl.max(1));
211 if rows == 0 || rows > jh.height as usize || rows * rl < crate::ljpeg_par::LJPEG_PARALLEL_MIN_SAMPLES {
212 return Ok(false);
213 }
214 let mut frame = vec![0u16; rows * rl];
215 if !crate::ljpeg_par::decode_frame_rows(data, jh, rows, &mut frame)? {
216 return Ok(false);
217 }
218 let band_len = w * 3 * step;
219 flashraw_core::par::for_each_row_chunk(out, band_len, 8, true, |b0, chunk| {
220 for (bi, band) in chunk.chunks_mut(band_len).enumerate() {
221 let b = b0 + bi;
222 for &(first, scol, ecol, per) in &slices {
223 for k in 0..per {
224 let col = scol + 2 * k;
225 if col >= ecol || col >= w {
226 break;
227 }
228 let m = first + b * per + k;
229 let mcu = &frame[m * clrs..m * clrs + clrs];
230 for (j, &y) in mcu[..nluma].iter().enumerate() {
231 if let Some(px) = band.get_mut(((j >> 1) * w + col + (j & 1)) * 3) {
232 *px = y;
233 }
234 }
235 if let Some(px) = band.get_mut(col * 3 + 1..col * 3 + 3) {
236 px[0] = (mcu[nluma] as i16).wrapping_sub(16384) as u16;
237 px[1] = (mcu[nluma + 1] as i16).wrapping_sub(16384) as u16;
238 }
239 }
240 }
241 }
242 });
243 Ok(true)
244}
245
246fn scatter(data: &[u8], jh: &LjpegHeader, p: &SrawParams, out: &mut [u16]) -> Result<()> {
247 let clrs = jh.clrs as usize;
248 let nluma = clrs - 2;
249 let w = p.width;
250 let mut cur = Cursor::new(p, clrs);
251 let res = ljpeg::decode_rows(data, jh, |_, row| {
252 for mcu in row.chunks_exact(clrs) {
253 if cur.done {
254 return Ok(());
255 }
256 let (r, c) = (cur.row, cur.col);
257 if c < w {
258 let base = r * w + c;
259 for (k, &y) in mcu[..nluma].iter().enumerate() {
260 let i = base + (k >> 1) * w + (k & 1);
261 if let Some(px) = out.get_mut(i * 3) {
262 *px = y;
263 }
264 }
265 let i = base * 3;
266 if let Some(px) = out.get_mut(i + 1..i + 3) {
267 px[0] = (mcu[nluma] as i16).wrapping_sub(16384) as u16;
268 px[1] = (mcu[nluma + 1] as i16).wrapping_sub(16384) as u16;
269 }
270 }
271 cur.advance();
272 }
273 Ok(())
274 });
275 match res {
276 Err(_) if cur.done => Ok(()),
279 r => r,
280 }
281}
282
283#[inline(always)]
284fn s(v: u16) -> i32 {
285 v as i16 as i32
286}
287
288fn interpolate_chroma(px: &mut [u16], w: usize, h: usize, sraw: u32, parallel: bool) {
289 use flashraw_core::par;
290 let rl = w * 3;
291 if sraw >> 1 != 0 && h > 1 {
292 let src = &*px;
295 let odd: Vec<Vec<u16>> = par::map((1..h).step_by(2).collect(), parallel, |row| {
296 let base = row * rl;
297 let mut v = Vec::with_capacity(w);
298 for col in (0..w).step_by(2) {
299 for c in 1..3 {
300 let i = base + col * 3 + c;
301 let up = s(src[i - rl]);
302 v.push(if row == h - 1 {
303 up as i16 as u16
304 } else {
305 ((up + s(src[i + rl]) + 1) >> 1) as i16 as u16
306 });
307 }
308 }
309 v
310 });
311 par::for_each_row_chunk(px, rl * 2, 16, parallel, |p0, chunk| {
312 for (i, pair) in chunk.chunks_mut(rl * 2).enumerate() {
313 let Some(vals) = odd.get(p0 + i) else { continue };
314 if pair.len() < rl * 2 {
315 continue;
316 }
317 let row = &mut pair[rl..];
318 for (k, col) in (0..w).step_by(2).enumerate() {
319 row[col * 3 + 1] = vals[2 * k];
320 row[col * 3 + 2] = vals[2 * k + 1];
321 }
322 }
323 });
324 }
325 par::for_each_row_chunk(px, rl, 32, parallel, |_, chunk| {
327 for row in chunk.chunks_mut(rl) {
328 for col in (1..w).step_by(2) {
329 for c in 1..3 {
330 let i = col * 3 + c;
331 let left = s(row[i - 3]);
332 row[i] =
333 if col == w - 1 { left as i16 as u16 } else { ((left + s(row[i + 3]) + 1) >> 1) as i16 as u16 };
334 }
335 }
336 }
337 });
338}
339
340#[inline(always)]
341fn clip15(v: i32) -> u16 {
342 v.clamp(0, 32767) as u16
343}
344
345fn convert(px: &mut [u16], p: &SrawParams, parallel: bool) {
346 flashraw_core::par::for_each_row_chunk(px, 3 * 4096, 16, parallel, |_, chunk| convert_chunk(chunk, p));
347}
348
349fn convert_chunk(px: &mut [u16], p: &SrawParams) {
350 let mul = [p.mul[0] as i32, p.mul[1] as i32, p.mul[2] as i32];
351 let scale = |v: i32, m: i32| clip15(v.wrapping_mul(m) >> 10);
352 match p.conversion {
353 SrawConversion::Matrix => {
354 let hue = p.hue;
355 for t in px.chunks_exact_mut(3) {
356 let y = s(t[0]);
357 let cb = ((s(t[1]) << 2) + hue) as i16 as i32;
359 let cr = ((s(t[2]) << 2) + hue) as i16 as i32;
360 let r = y + ((50 * cb + 22929 * cr) >> 14);
361 let g = y + ((-5640 * cb - 11751 * cr) >> 14);
362 let b = y + ((29040 * cb - 101 * cr) >> 14);
363 t[0] = scale(r, mul[0]);
364 t[1] = scale(g, mul[1]);
365 t[2] = scale(b, mul[2]);
366 }
367 }
368 SrawConversion::Original { subtract_512 } => {
369 for t in px.chunks_exact_mut(3) {
370 let mut y = s(t[0]);
371 if subtract_512 {
372 y = (y - 512) as i16 as i32;
373 }
374 let (cb, cr) = (s(t[1]), s(t[2]));
375 let r = y + cr;
376 let b = y + cb;
377 let g = y + ((-778 * cb - (cr << 11)) >> 12);
378 t[0] = scale(r, mul[0]);
379 t[1] = scale(g, mul[1]);
380 t[2] = scale(b, mul[2]);
381 }
382 }
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 fn params(w: usize, h: usize, slices: [u16; 3]) -> SrawParams {
391 SrawParams {
392 width: w,
393 height: h,
394 raw_width: w,
395 slices,
396 conversion: SrawConversion::Original { subtract_512: false },
397 hue: 0,
398 mul: [1024; 3],
399 }
400 }
401
402 #[test]
403 fn cursor_walks_slices_in_order() {
404 let p = params(10, 2, [2, 8, 4]);
406 let mut c = Cursor::new(&p, 4);
407 let mut seen = vec![];
408 while !c.done {
409 seen.push((c.row, c.col));
410 c.advance();
411 }
412 let expect = vec![(0, 0), (0, 2), (1, 0), (1, 2), (0, 4), (0, 6), (1, 4), (1, 6), (0, 8), (1, 8)];
413 assert_eq!(seen, expect);
414 }
415
416 #[test]
417 fn cursor_420_steps_two_rows() {
418 let p = params(4, 4, [0, 0, 0]);
419 let mut c = Cursor::new(&p, 6);
420 let mut seen = vec![];
421 while !c.done {
422 seen.push((c.row, c.col));
423 c.advance();
424 }
425 assert_eq!(seen, vec![(0, 0), (0, 2), (2, 0), (2, 2)]);
426 }
427
428 #[test]
429 fn chroma_interpolation_rounds_up() {
430 let (w, h) = (4, 2);
432 let mut px = vec![0u16; w * h * 3];
433 let set = |px: &mut Vec<u16>, x: usize, y: usize, cb: i16, cr: i16| {
434 px[(y * w + x) * 3 + 1] = cb as u16;
435 px[(y * w + x) * 3 + 2] = cr as u16;
436 };
437 set(&mut px, 0, 0, 10, -10);
438 set(&mut px, 2, 0, 13, -13);
439 interpolate_chroma(&mut px, w, h, 3, false);
440 let get = |px: &Vec<u16>, x: usize, y: usize| (px[(y * w + x) * 3 + 1] as i16, px[(y * w + x) * 3 + 2] as i16);
441 assert_eq!(get(&px, 1, 0), (12, -11)); assert_eq!(get(&px, 3, 0), (13, -13)); assert_eq!(get(&px, 0, 1), (10, -10)); assert_eq!(get(&px, 1, 1), (12, -11));
445 }
446
447 #[test]
448 fn conversion_matches_reference_formulas() {
449 let mut px = vec![4000u16, 100i16 as u16, (-50i16) as u16];
450 let p = SrawParams { conversion: SrawConversion::Original { subtract_512: true }, ..params(1, 1, [0; 3]) };
451 convert(&mut px, &p, false);
452 let y = 4000 - 512;
453 assert_eq!(px, vec![(y - 50) as u16, (y + ((-778 * 100 - (-50 << 11)) >> 12)) as u16, (y + 100) as u16]);
454 let mut px = vec![4000u16, 100i16 as u16, (-50i16) as u16];
455 let p =
456 SrawParams { conversion: SrawConversion::Matrix, hue: 6, mul: [2048, 1024, 512], ..params(1, 1, [0; 3]) };
457 convert(&mut px, &p, false);
458 let (cb, cr) = (406, -194);
459 let r = 4000 + ((50 * cb + 22929 * cr) >> 14);
460 let g = 4000 + ((-5640 * cb - 11751 * cr) >> 14);
461 let b = 4000 + ((29040 * cb - 101 * cr) >> 14);
462 assert_eq!(px, vec![clip15(r * 2), clip15(g), clip15(b / 2)]);
463 }
464
465 #[test]
466 fn rejects_non_sraw_frames() {
467 let hdr_data = crate::ljpeg::encoder::encode(4, 2, 2, 12, 1, &[0; 16]);
468 let hdr = LjpegHeader::parse(&hdr_data, Default::default()).unwrap();
469 let mut out = vec![0u16; 4 * 2 * 3];
470 assert!(decode(&hdr_data, &hdr, ¶ms(4, 2, [0; 3]), false, &mut out).is_err());
471 }
472}