use crate::modespec::{ModeSpec, SstvMode};
#[allow(clippy::too_many_arguments, clippy::needless_pass_by_value)]
pub(crate) fn decode_line(
spec: ModeSpec,
mode: SstvMode,
line_index: u32,
audio: &[f32],
skip_samples: i64,
line_seconds_offset: f64,
rate_hz: f64,
image: &mut crate::image::SstvImage,
chroma_planes: Option<&mut [Vec<u8>; 2]>,
demod: &mut crate::mode_pd::PdDemod,
snr_est: &mut crate::snr::SnrEstimator,
hedr_shift_hz: f64,
) {
match mode {
SstvMode::Robot72 => {
let _ = chroma_planes;
decode_r72_line(
spec,
line_index,
audio,
skip_samples,
line_seconds_offset,
rate_hz,
image,
demod,
snr_est,
hedr_shift_hz,
);
}
SstvMode::Robot24 | SstvMode::Robot36 => {
#[allow(clippy::expect_used)]
let planes = chroma_planes
.expect("R36/R24 require chroma_planes; DecodingState should populate them");
decode_r36_or_r24_line(
spec,
line_index,
audio,
skip_samples,
line_seconds_offset,
rate_hz,
image,
planes,
demod,
snr_est,
hedr_shift_hz,
);
}
_ => unreachable!("decode_line must be called with a Robot variant"),
}
}
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::too_many_arguments
)]
fn decode_r72_line(
spec: ModeSpec,
line_index: u32,
audio: &[f32],
skip_samples: i64,
line_seconds_offset: f64,
rate_hz: f64,
image: &mut crate::image::SstvImage,
demod: &mut crate::mode_pd::PdDemod,
snr_est: &mut crate::snr::SnrEstimator,
hedr_shift_hz: f64,
) {
let sync_secs = spec.sync_seconds;
let porch_secs = spec.porch_seconds;
let pixel_secs = spec.pixel_seconds;
let septr_secs = spec.septr_seconds;
let width = spec.line_pixels;
let chan_len = f64::from(width) * pixel_secs;
let chan_starts_sec = [
sync_secs + porch_secs, sync_secs + porch_secs + chan_len + septr_secs, sync_secs + porch_secs + 2.0 * chan_len + 2.0 * septr_secs, ];
let width_us = width as usize;
let chan_bounds_abs: [(i64, i64); 3] = std::array::from_fn(|i| {
let start_sec = chan_starts_sec[i];
let end_sec = start_sec + chan_len;
let start_abs = skip_samples + ((line_seconds_offset + start_sec) * rate_hz).round() as i64;
let end_abs = skip_samples + ((line_seconds_offset + end_sec) * rate_hz).round() as i64;
(start_abs, end_abs)
});
let mut y = vec![0_u8; width_us];
let mut cr = vec![0_u8; width_us];
let mut cb = vec![0_u8; width_us];
let buffers: [&mut [u8]; 3] = [&mut y, &mut cr, &mut cb];
for (chan_idx, buf) in buffers.into_iter().enumerate() {
crate::mode_pd::decode_one_channel_into(
buf,
chan_starts_sec[chan_idx],
chan_bounds_abs[chan_idx],
spec,
audio,
skip_samples,
line_seconds_offset,
rate_hz,
demod,
snr_est,
hedr_shift_hz,
);
}
for x in 0..width_us {
let rgb = crate::mode_pd::ycbcr_to_rgb(y[x], cr[x], cb[x]);
image.put_pixel(x as u32, line_index, rgb);
}
}
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
clippy::too_many_arguments
)]
fn decode_r36_or_r24_line(
spec: ModeSpec,
line_index: u32,
audio: &[f32],
skip_samples: i64,
line_seconds_offset: f64,
rate_hz: f64,
image: &mut crate::image::SstvImage,
chroma_planes: &mut [Vec<u8>; 2],
demod: &mut crate::mode_pd::PdDemod,
snr_est: &mut crate::snr::SnrEstimator,
hedr_shift_hz: f64,
) {
let sync_secs = spec.sync_seconds;
let porch_secs = spec.porch_seconds;
let pixel_secs = spec.pixel_seconds;
let septr_secs = spec.septr_seconds;
let width = spec.line_pixels;
let chan_len_y = f64::from(width) * pixel_secs * 2.0;
let chan_len_chroma = f64::from(width) * pixel_secs;
let chan_start_y = sync_secs + porch_secs;
let chan_start_chroma = chan_start_y + chan_len_y + septr_secs;
let width_us = width as usize;
let y_bounds_abs = {
let start_abs =
skip_samples + ((line_seconds_offset + chan_start_y) * rate_hz).round() as i64;
let end_abs = skip_samples
+ ((line_seconds_offset + chan_start_y + chan_len_y) * rate_hz).round() as i64;
(start_abs, end_abs)
};
let chroma_bounds_abs = {
let start_abs =
skip_samples + ((line_seconds_offset + chan_start_chroma) * rate_hz).round() as i64;
let end_abs = skip_samples
+ ((line_seconds_offset + chan_start_chroma + chan_len_chroma) * rate_hz).round()
as i64;
(start_abs, end_abs)
};
let mut y_buf = vec![0_u8; width_us];
let mut chroma_buf = vec![0_u8; width_us];
let mut spec_y = spec;
spec_y.pixel_seconds = pixel_secs * 2.0;
crate::mode_pd::decode_one_channel_into(
&mut y_buf,
chan_start_y,
y_bounds_abs,
spec_y,
audio,
skip_samples,
line_seconds_offset,
rate_hz,
demod,
snr_est,
hedr_shift_hz,
);
crate::mode_pd::decode_one_channel_into(
&mut chroma_buf,
chan_start_chroma,
chroma_bounds_abs,
spec,
audio,
skip_samples,
line_seconds_offset,
rate_hz,
demod,
snr_est,
hedr_shift_hz,
);
let chroma_plane_idx = (line_index % 2) as usize;
let line_off = (line_index as usize) * width_us;
let next_off = ((line_index + 1) as usize) * width_us;
let plane_len = chroma_planes[chroma_plane_idx].len();
chroma_planes[chroma_plane_idx][line_off..line_off + width_us].copy_from_slice(&chroma_buf);
if next_off + width_us <= plane_len {
chroma_planes[chroma_plane_idx][next_off..next_off + width_us].copy_from_slice(&chroma_buf);
}
for x in 0..width_us {
let y = y_buf[x];
let cr = chroma_planes[0][line_off + x];
let cb = chroma_planes[1][line_off + x];
let rgb = crate::mode_pd::ycbcr_to_rgb(y, cr, cb);
image.put_pixel(x as u32, line_index, rgb);
}
}