use core::ops::Range;
pub(super) struct SubsampledPlacement<'a> {
pub(super) input: &'a [f32],
pub(super) input_stride: usize,
pub(super) component_origin: (u32, u32),
pub(super) columns: Range<u32>,
pub(super) rows: Range<u32>,
pub(super) scale: (u32, u32),
pub(super) image_size: (u32, u32),
pub(super) image_offset: (u32, u32),
}
pub(super) fn place_subsampled(placement: &SubsampledPlacement<'_>, output: &mut [f32]) {
let (scale_x, scale_y) = placement.scale;
let (image_width, image_height) = placement.image_size;
let (x_offset, y_offset) = placement.image_offset;
let (x0, x1) = (placement.columns.start, placement.columns.end);
let first_column = (scale_x * x0).max(x_offset);
let end_column = (scale_x * x1).min(image_width + x_offset);
if first_column >= end_column {
return;
}
let width = image_width as usize;
let written = (first_column - x_offset) as usize..(end_column - x_offset) as usize;
let input_columns =
(x0 - placement.component_origin.0) as usize..(x1 - placement.component_origin.0) as usize;
for y in placement.rows.clone() {
let first_row = (scale_y * y).max(y_offset);
let end_row = (scale_y * y + scale_y).min(image_height + y_offset);
if first_row >= end_row {
continue;
}
let input_row = &placement.input
[(y - placement.component_origin.1) as usize * placement.input_stride..];
let out_row = (first_row - y_offset) as usize;
fill_row(
&input_row[input_columns.clone()],
&mut output[out_row * width..][..width],
placement,
);
for row in out_row + 1..(end_row - y_offset) as usize {
output.copy_within(
out_row * width + written.start..out_row * width + written.end,
row * width + written.start,
);
}
}
}
fn fill_row(samples: &[f32], out_row: &mut [f32], placement: &SubsampledPlacement<'_>) {
let scale_x = placement.scale.0;
let x_offset = placement.image_offset.0;
let (x0, x1) = (placement.columns.start, placement.columns.end);
if scale_x * x0 < x_offset || scale_x * x1 > placement.image_size.0 + x_offset {
for (x, &sample) in (x0..x1).zip(samples) {
let first = (scale_x * x).max(x_offset);
let end = (scale_x * x + scale_x).min(placement.image_size.0 + x_offset);
if first < end {
out_row[(first - x_offset) as usize..(end - x_offset) as usize].fill(sample);
}
}
return;
}
let out = &mut out_row[(scale_x * x0 - x_offset) as usize..(scale_x * x1 - x_offset) as usize];
match scale_x {
1 => out.copy_from_slice(samples),
2 => {
for (pair, &sample) in out.chunks_exact_mut(2).zip(samples) {
pair[0] = sample;
pair[1] = sample;
}
}
_ => {
for (footprint, &sample) in out.chunks_exact_mut(scale_x as usize).zip(samples) {
footprint.fill(sample);
}
}
}
}
#[cfg(test)]
mod tests {
use super::{place_subsampled, SubsampledPlacement};
use alloc::vec;
use alloc::vec::Vec;
fn place_reference(placement: &SubsampledPlacement<'_>, output: &mut [f32]) {
let (scale_x, scale_y) = placement.scale;
let (image_width, image_height) = placement.image_size;
let (x_offset, y_offset) = placement.image_offset;
for y in placement.rows.clone() {
let relative_y = (y - placement.component_origin.1) as usize;
for x in placement.columns.clone() {
let relative_x = (x - placement.component_origin.0) as usize;
let sample = placement.input[relative_y * placement.input_stride + relative_x];
for x_position in
(scale_x * x).max(x_offset)..(scale_x * x + scale_x).min(image_width + x_offset)
{
for y_position in (scale_y * y).max(y_offset)
..(scale_y * y + scale_y).min(image_height + y_offset)
{
output[(y_position - y_offset) as usize * image_width as usize
+ (x_position - x_offset) as usize] = sample;
}
}
}
}
}
#[test]
fn placement_matches_the_per_sample_reference() {
for scale in [(1_u32, 1_u32), (2, 1), (1, 2), (2, 2), (3, 2)] {
for (image_offset, origin, columns, rows) in [
((0_u32, 0_u32), (0_u32, 0_u32), 0_u32..5, 0_u32..4),
((1, 1), (0, 0), 0..5, 0..4),
((0, 0), (2, 1), 2..6, 1..4),
((3, 2), (1, 1), 1..6, 1..5),
] {
let (scale_x, scale_y) = scale;
let image_size = (
(scale_x * columns.end)
.saturating_sub(image_offset.0)
.max(1)
- 1,
(scale_y * rows.end).saturating_sub(image_offset.1).max(1),
);
let input_stride = (columns.end - origin.0) as usize;
let input = core::iter::successors(Some(0.5_f32), |value| Some(value + 1.0))
.take(input_stride * (rows.end - origin.1) as usize)
.collect::<Vec<_>>();
let placement = SubsampledPlacement {
input: &input,
input_stride,
component_origin: origin,
columns: columns.clone(),
rows: rows.clone(),
scale,
image_size,
image_offset,
};
let len = (image_size.0 * image_size.1) as usize;
let (mut fast, mut reference) = (vec![-1.0; len], vec![-1.0; len]);
place_subsampled(&placement, &mut fast);
place_reference(&placement, &mut reference);
assert_eq!(fast, reference, "scale {scale:?} offset {image_offset:?}");
}
}
}
}