cranpose 0.9.11

Build desktop, mobile and web apps with composable Rust functions
Documentation
use crate::embed_protocol::PixelRect;
pub(crate) use crate::frame_readback::FrameTarget;
pub(crate) const FRAME_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;

const BYTES_PER_PIXEL: u32 = 4;

pub(crate) fn changed_rect(
    previous: &[u8],
    next: &[u8],
    width: u32,
    height: u32,
) -> Option<PixelRect> {
    let row_bytes = (width * BYTES_PER_PIXEL) as usize;
    if row_bytes == 0 || previous.len() != next.len() {
        return Some(PixelRect::full(width, height));
    }
    let mut bounds: Option<(u32, u32, u32, u32)> = None;
    let rows = previous
        .chunks_exact(row_bytes)
        .zip(next.chunks_exact(row_bytes));
    for (row, (before, after)) in rows.enumerate() {
        if before == after {
            continue;
        }
        let Some((first, last)) = changed_columns(before, after) else {
            continue;
        };
        let row = row as u32;
        bounds = Some(match bounds {
            None => (first, last, row, row),
            Some((first_column, last_column, first_row, _)) => (
                first_column.min(first),
                last_column.max(last),
                first_row,
                row,
            ),
        });
    }
    bounds.map(
        |(first_column, last_column, first_row, last_row)| PixelRect {
            x: first_column,
            y: first_row,
            width: last_column - first_column + 1,
            height: last_row - first_row + 1,
        },
    )
}

fn changed_columns(before: &[u8], after: &[u8]) -> Option<(u32, u32)> {
    let pixel_size = BYTES_PER_PIXEL as usize;
    let pixels = || {
        before
            .chunks_exact(pixel_size)
            .zip(after.chunks_exact(pixel_size))
    };
    let first = pixels().position(|(a, b)| a != b)?;
    let last = pixels().rposition(|(a, b)| a != b)?;
    Some((first as u32, last as u32))
}

#[cfg(test)]
#[path = "tests/embed_frame_tests.rs"]
mod tests;