1use crate::ljpeg::{LjpegHeader, LjpegOptions};
13use flashraw_core::bits::{BitPumpJpeg, BitRead};
14use flashraw_core::huffman::HuffTable;
15use flashraw_core::{Error, Result};
16
17pub fn decode_tile(src: &[u8], tile_w: usize, tile_h: usize) -> Result<Vec<u16>> {
19 let hdr = LjpegHeader::parse(src, LjpegOptions::default())?;
20 if hdr.components.len() != 3 {
21 return Err(Error::corrupt(format!("Sony YCbCr tile with {} components", hdr.components.len())));
22 }
23 if hdr.width as usize != tile_w || hdr.height as usize != tile_h || tile_w % 2 != 0 || tile_h % 2 != 0 {
24 return Err(Error::corrupt(format!(
25 "Sony YCbCr tile is {}x{}, expected {tile_w}x{tile_h}",
26 hdr.width, hdr.height
27 )));
28 }
29 let (h, v) = (hdr.components[0].h, hdr.components[0].v);
30 let t = |i: usize| hdr.table(i).ok_or_else(|| Error::corrupt("Sony YCbCr: missing Huffman table"));
31 let scan = src.get(hdr.scan_offset..).ok_or_else(|| Error::corrupt("Sony YCbCr: scan beyond data"))?;
32 let mut bits = BitPumpJpeg::new(scan);
33 let mut out = vec![0u16; tile_w * tile_h * 3];
34 let base = 1i32 << (hdr.bits - 1);
35 match (h, v) {
36 (2, 2) => decode_420(&mut bits, t(0)?, t(4)?, t(5)?, &mut out, tile_w * 3, tile_h, base)?,
37 (2, 1) => decode_422(&mut bits, t(0)?, t(2)?, t(3)?, &mut out, tile_w * 3, tile_h, base)?,
38 _ => return Err(Error::unsupported_compression("ARW", format!("YCbCr sampling {h}x{v}"))),
39 }
40 if bits.overrun() > 64 {
41 return Err(Error::corrupt("truncated Sony YCbCr tile"));
42 }
43 Ok(out)
44}
45
46#[inline(always)]
47fn put(out: &mut [u16], pix: usize, y: i32, cb: i32, cr: i32) {
48 out[pix] = y as u16;
49 out[pix + 1] = cb as u16;
50 out[pix + 2] = cr as u16;
51}
52
53#[allow(clippy::too_many_arguments)]
55fn decode_420(
56 bits: &mut BitPumpJpeg,
57 hy: &HuffTable,
58 hcb: &HuffTable,
59 hcr: &HuffTable,
60 out: &mut [u16],
61 width: usize,
62 height: usize,
63 base: i32,
64) -> Result<()> {
65 let mcu = |out: &mut [u16],
66 bits: &mut BitPumpJpeg,
67 row: usize,
68 col: usize,
69 py1: i32,
70 py3: Option<i32>,
71 pcb: i32,
72 pcr: i32|
73 -> Result<()> {
74 let y1 = py1 + hy.decode_diff(bits)?;
75 let y2 = y1 + hy.decode_diff(bits)?;
76 let y3 = py3.unwrap_or(y1) + hy.decode_diff(bits)?;
77 let y4 = y3 + hy.decode_diff(bits)?;
78 let cb = pcb + hcb.decode_diff(bits)?;
79 let cr = pcr + hcr.decode_diff(bits)?;
80 let p1 = row * width + col;
81 let p3 = (row + 1) * width + col;
82 put(out, p1, y1, cb, cr);
83 put(out, p1 + 3, y2, cb, cr);
84 put(out, p3, y3, cb, cr);
85 put(out, p3 + 3, y4, cb, cr);
86 Ok(())
87 };
88 mcu(out, bits, 0, 0, base, None, base, base)?;
89 for row in (0..height).step_by(2) {
90 let start = if row == 0 { 6 } else { 0 };
91 for col in (start..width).step_by(6) {
92 if col == 0 {
93 let pos = (row - 2) * width;
94 let (py1, pcb, pcr) = (out[pos] as i32, out[pos + 1] as i32, out[pos + 2] as i32);
95 mcu(out, bits, row, col, py1, None, pcb, pcr)?;
96 } else {
97 let p1 = row * width + col - 3;
98 let p3 = (row + 1) * width + col - 3;
99 let (py1, py3, pcb, pcr) = (out[p1] as i32, out[p3] as i32, out[p1 + 1] as i32, out[p1 + 2] as i32);
100 mcu(out, bits, row, col, py1, Some(py3), pcb, pcr)?;
101 }
102 }
103 if bits.overrun() > 64 {
104 return Err(Error::corrupt("truncated Sony YCbCr tile"));
105 }
106 }
107 Ok(())
108}
109
110#[allow(clippy::too_many_arguments)]
112fn decode_422(
113 bits: &mut BitPumpJpeg,
114 hy: &HuffTable,
115 hcb: &HuffTable,
116 hcr: &HuffTable,
117 out: &mut [u16],
118 width: usize,
119 height: usize,
120 base: i32,
121) -> Result<()> {
122 let mcu =
123 |out: &mut [u16], bits: &mut BitPumpJpeg, row: usize, col: usize, py: i32, pcb: i32, pcr: i32| -> Result<()> {
124 let y1 = py + hy.decode_diff(bits)?;
125 let y2 = y1 + hy.decode_diff(bits)?;
126 let cb = pcb + hcb.decode_diff(bits)?;
127 let cr = pcr + hcr.decode_diff(bits)?;
128 let p = row * width + col;
129 put(out, p, y1, cb, cr);
130 put(out, p + 3, y2, cb, cr);
131 Ok(())
132 };
133 mcu(out, bits, 0, 0, base, base, base)?;
134 for row in 0..height {
135 let start = if row == 0 { 6 } else { 0 };
136 for col in (start..width).step_by(6) {
137 let pos = if col == 0 { (row - 1) * width } else { row * width + col - 3 };
138 let (py, pcb, pcr) = (out[pos] as i32, out[pos + 1] as i32, out[pos + 2] as i32);
139 mcu(out, bits, row, col, py, pcb, pcr)?;
140 }
141 if bits.overrun() > 64 {
142 return Err(Error::corrupt("truncated Sony YCbCr tile"));
143 }
144 }
145 Ok(())
146}
147
148#[inline]
151pub fn to_rgb(y: u16, cb: u16, cr: u16) -> [u16; 3] {
152 const CDELTA: i32 = 16383;
153 let y = y as f32;
154 let cb = (cb as i32 - CDELTA) as f32;
155 let cr = (cr as i32 - CDELTA) as f32;
156 let r = y + 1.40200f32 * cr;
157 let g = y - 0.34414f32 * cb - 0.71414f32 * cr;
158 let b = y + 1.77200f32 * cb;
159 [lim16(r), lim16(g), lim16(b)]
160}
161
162#[inline(always)]
163fn lim16(q: f32) -> u16 {
164 if q < 0.0 {
165 0
166 } else if q > 65535.0 {
167 65535
168 } else {
169 q as u32 as u16
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 struct Bw {
178 out: Vec<u8>,
179 acc: u32,
180 n: u32,
181 }
182 impl Bw {
183 fn put(&mut self, v: u32, len: u32) {
184 for i in (0..len).rev() {
185 self.acc = self.acc << 1 | ((v >> i) & 1);
186 self.n += 1;
187 if self.n == 8 {
188 self.out.push(self.acc as u8);
189 if self.acc as u8 == 0xFF {
190 self.out.push(0);
191 }
192 self.acc = 0;
193 self.n = 0;
194 }
195 }
196 }
197 fn diff(&mut self, d: i32) {
198 let len = if d == 0 { 0 } else { 32 - d.unsigned_abs().leading_zeros() };
200 self.put(len, 5);
201 if len > 0 {
202 let v = if d < 0 { (d - 1) as u32 & ((1 << len) - 1) } else { d as u32 };
203 self.put(v, len);
204 }
205 }
206 }
207
208 fn stream(w: usize, h: usize, hs: u8, vs: u8, body: &[u8]) -> Vec<u8> {
210 let mut o = vec![0xFF, 0xD8, 0xFF, 0xC3, 0, 17, 14];
211 o.extend((h as u16).to_be_bytes());
212 o.extend((w as u16).to_be_bytes());
213 o.extend([3, 1, hs << 4 | vs, 0, 2, 0x11, 0, 3, 0x11, 0]);
214 let mut counts = [0u8; 16];
216 counts[4] = 17;
217 o.extend([0xFF, 0xC4]);
218 o.extend(((2 + 17 + 17) as u16).to_be_bytes());
219 o.push(0);
220 o.extend(counts);
221 o.extend(0u8..17);
222 o.extend([0xFF, 0xDA, 0, 12, 3, 1, 0, 2, 0, 3, 0, 1, 0, 0]);
223 o.extend_from_slice(body);
224 o.extend([0xFF, 0xD9]);
225 o
226 }
227
228 #[test]
229 fn decode_420_matches_reference_prediction() {
230 let (w, h) = (8usize, 4usize);
231 let y = |r: usize, c: usize| (1000 + r * 37 + c * 11) as i32;
233 let cb = |r: usize, c: usize| (16000 + (r / 2) * 5 + (c / 2) * 3) as i32;
234 let cr = |r: usize, c: usize| (16500 - (r / 2) * 7 - (c / 2) * 2) as i32;
235 let base = 1 << 13; let mut bw = Bw { out: vec![], acc: 0, n: 0 };
237 for row in (0..h).step_by(2) {
238 for col in (0..w).step_by(2) {
239 let (py1, py3, pcb, pcr) = if row == 0 && col == 0 {
240 (base, None, base, base)
241 } else if col == 0 {
242 (y(row - 2, 0), None, cb(row - 2, 0), cr(row - 2, 0))
243 } else {
244 (y(row, col - 1), Some(y(row + 1, col - 1)), cb(row, col - 1), cr(row, col - 1))
245 };
246 bw.diff(y(row, col) - py1);
247 bw.diff(y(row, col + 1) - y(row, col));
248 bw.diff(y(row + 1, col) - py3.unwrap_or(y(row, col)));
249 bw.diff(y(row + 1, col + 1) - y(row + 1, col));
250 bw.diff(cb(row, col) - pcb);
251 bw.diff(cr(row, col) - pcr);
252 }
253 }
254 bw.put(0x7F, 7);
255 let s = stream(w, h, 2, 2, &bw.out);
256 let out = decode_tile(&s, w, h).unwrap();
257 for r in 0..h {
258 for c in 0..w {
259 let p = (r * w + c) * 3;
260 assert_eq!(out[p] as i32, y(r, c), "Y at {r},{c}");
261 assert_eq!(out[p + 1] as i32, cb(r, c));
262 assert_eq!(out[p + 2] as i32, cr(r, c));
263 }
264 }
265 assert!(decode_tile(&s[..s.len() / 2], w, h).is_err());
267 }
268
269 #[test]
270 fn rgb_conversion_clamps() {
271 assert_eq!(to_rgb(1000, 16383, 16383), [1000, 1000, 1000]);
272 assert_eq!(to_rgb(0, 0, 0)[0], 0);
273 assert_eq!(to_rgb(65535, 65535, 65535)[2], 65535);
274 }
275}