#[allow(clippy::too_many_arguments)]
pub(crate) fn bayer_to_rgb_row(
above: &[u8],
mid: &[u8],
below: &[u8],
row_parity: u32,
pattern: crate::raw::BayerPattern,
_demosaic: crate::raw::BayerDemosaic,
m: &[[f32; 3]; 3],
rgb_out: &mut [u8],
) {
let w = mid.len();
debug_assert_eq!(above.len(), w, "above row length must match mid");
debug_assert_eq!(below.len(), w, "below row length must match mid");
debug_assert!(rgb_out.len() >= 3 * w, "rgb_out too short");
let (r_par, b_par) = pattern_phases(pattern);
let rp = (row_parity & 1) as usize;
for x in 0..w {
let cp = x & 1;
let (r, g, b) = bilinear_demosaic_at(w, x, rp, cp, r_par, b_par, |sel, i| match sel {
BayerRowSel::Above => above[i] as f32,
BayerRowSel::Mid => mid[i] as f32,
BayerRowSel::Below => below[i] as f32,
});
let r_out = m[0][0] * r + m[0][1] * g + m[0][2] * b;
let g_out = m[1][0] * r + m[1][1] * g + m[1][2] * b;
let b_out = m[2][0] * r + m[2][1] * g + m[2][2] * b;
rgb_out[3 * x] = clamp_u8_round(r_out);
rgb_out[3 * x + 1] = clamp_u8_round(g_out);
rgb_out[3 * x + 2] = clamp_u8_round(b_out);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn clamp_u8_round(v: f32) -> u8 {
if v <= 0.0 {
0
} else if v >= 255.0 {
255
} else {
(v + 0.5) as u8
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn pattern_phases(p: crate::raw::BayerPattern) -> ((usize, usize), (usize, usize)) {
use crate::raw::BayerPattern::*;
match p {
Rggb => ((0, 0), (1, 1)),
Bggr => ((1, 1), (0, 0)),
Grbg => ((0, 1), (1, 0)),
Gbrg => ((1, 0), (0, 1)),
_ => unreachable!("invalid BayerPattern"),
}
}
#[derive(Clone, Copy)]
enum BayerRowSel {
Above,
Mid,
Below,
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn bilinear_demosaic_at<F>(
width: usize,
x: usize,
rp: usize,
cp: usize,
r_par: (usize, usize),
b_par: (usize, usize),
read: F,
) -> (f32, f32, f32)
where
F: Fn(BayerRowSel, usize) -> f32,
{
let center = read(BayerRowSel::Mid, x);
let n = read(BayerRowSel::Above, x);
let s = read(BayerRowSel::Below, x);
let w_idx = if x == 0 {
if width >= 2 { 1 } else { 0 }
} else {
x - 1
};
let e_idx = if x + 1 == width {
if width >= 2 { width - 2 } else { width - 1 }
} else {
x + 1
};
let west = read(BayerRowSel::Mid, w_idx);
let east = read(BayerRowSel::Mid, e_idx);
let nw = read(BayerRowSel::Above, w_idx);
let ne = read(BayerRowSel::Above, e_idx);
let sw = read(BayerRowSel::Below, w_idx);
let se = read(BayerRowSel::Below, e_idx);
if (rp, cp) == r_par {
(
center,
(n + s + west + east) * 0.25,
(nw + ne + sw + se) * 0.25,
)
} else if (rp, cp) == b_par {
(
(nw + ne + sw + se) * 0.25,
(n + s + west + east) * 0.25,
center,
)
} else {
let on_red_row = rp == r_par.0;
if on_red_row {
((west + east) * 0.5, center, (n + s) * 0.5)
} else {
((n + s) * 0.5, center, (west + east) * 0.5)
}
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn bayer16_to_rgb_row<const BITS: u32>(
above: &[u16],
mid: &[u16],
below: &[u16],
row_parity: u32,
pattern: crate::raw::BayerPattern,
_demosaic: crate::raw::BayerDemosaic,
m: &[[f32; 3]; 3],
rgb_out: &mut [u8],
) {
const { assert!(BITS == 10 || BITS == 12 || BITS == 14 || BITS == 16) };
let w = mid.len();
debug_assert_eq!(above.len(), w);
debug_assert_eq!(below.len(), w);
debug_assert!(rgb_out.len() >= 3 * w);
let (r_par, b_par) = pattern_phases(pattern);
let rp = (row_parity & 1) as usize;
let max_valid: u16 = ((1u32 << BITS) - 1) as u16;
let max_in = max_valid as f32;
let out_scale = 255.0 / max_in;
for x in 0..w {
let cp = x & 1;
let (r, g, b) = bilinear_demosaic_at(w, x, rp, cp, r_par, b_par, |sel, i| match sel {
BayerRowSel::Above => above[i] as f32,
BayerRowSel::Mid => mid[i] as f32,
BayerRowSel::Below => below[i] as f32,
});
let r_out = (m[0][0] * r + m[0][1] * g + m[0][2] * b) * out_scale;
let g_out = (m[1][0] * r + m[1][1] * g + m[1][2] * b) * out_scale;
let b_out = (m[2][0] * r + m[2][1] * g + m[2][2] * b) * out_scale;
rgb_out[3 * x] = clamp_u8_round(r_out);
rgb_out[3 * x + 1] = clamp_u8_round(g_out);
rgb_out[3 * x + 2] = clamp_u8_round(b_out);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn bayer16_to_rgb_u16_row<const BITS: u32>(
above: &[u16],
mid: &[u16],
below: &[u16],
row_parity: u32,
pattern: crate::raw::BayerPattern,
_demosaic: crate::raw::BayerDemosaic,
m: &[[f32; 3]; 3],
rgb_out: &mut [u16],
) {
const { assert!(BITS == 10 || BITS == 12 || BITS == 14 || BITS == 16) };
let w = mid.len();
debug_assert_eq!(above.len(), w);
debug_assert_eq!(below.len(), w);
debug_assert!(rgb_out.len() >= 3 * w);
let (r_par, b_par) = pattern_phases(pattern);
let rp = (row_parity & 1) as usize;
let max_valid: u16 = ((1u32 << BITS) - 1) as u16;
let max_out = max_valid as f32;
for x in 0..w {
let cp = x & 1;
let (r, g, b) = bilinear_demosaic_at(w, x, rp, cp, r_par, b_par, |sel, i| match sel {
BayerRowSel::Above => above[i] as f32,
BayerRowSel::Mid => mid[i] as f32,
BayerRowSel::Below => below[i] as f32,
});
let r_out = m[0][0] * r + m[0][1] * g + m[0][2] * b;
let g_out = m[1][0] * r + m[1][1] * g + m[1][2] * b;
let b_out = m[2][0] * r + m[2][1] * g + m[2][2] * b;
rgb_out[3 * x] = clamp_u16_round(r_out, max_out);
rgb_out[3 * x + 1] = clamp_u16_round(g_out, max_out);
rgb_out[3 * x + 2] = clamp_u16_round(b_out, max_out);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn clamp_u16_round(v: f32, max: f32) -> u16 {
if v <= 0.0 {
0
} else if v >= max {
max as u16
} else {
(v + 0.5) as u16
}
}