kahlo 0.0.2

Optimized software rendering library.
Documentation
use crate::{
    bitmap::{BitmapRawData, BitmapRawDataMut},
    colour::{read_from_bytes, write_as_bytes},
    formats::{Abgr32, PixelFormatSpec, Rgba32, Rgba32P, A8},
    math::{clip_to, PixelBox, PixelPoint},
};

use super::{dispatch::BinaryDispatch, BinaryOpSpec, GenericBinary};

pub struct CopyFromOp;

#[cfg(test)]
const _: () = {
    use super::generate;
    impl generate::BinaryInputSpec for CopyFromOp {
        fn gen<'l>() -> impl generate::ValueGeneratorList<'l, OutputTuple = Self::Params<'l>> {
            (generate::src_area, generate::dst_point)
        }
    }
};

impl BinaryOpSpec for CopyFromOp {
    type Params<'l> = (PixelBox, PixelPoint);

    fn build<'l>() -> BinaryDispatch<Self::Params<'l>> {
        BinaryDispatch::from_generic::<Self>()
            .with::<A8, A8>(copy_same_fmt::<{ A8::PIXEL_STRIDE }>)
            .with::<Abgr32, Abgr32>(copy_same_fmt::<{ Abgr32::PIXEL_STRIDE }>)
            .with::<Rgba32, Rgba32>(copy_same_fmt::<{ Rgba32::PIXEL_STRIDE }>)
            .with::<Rgba32P, Rgba32P>(copy_same_fmt::<{ Rgba32P::PIXEL_STRIDE }>)
    }
}

fn copy_same_fmt<const WIDTH: usize>(
    write_data: &mut dyn BitmapRawDataMut,
    read_data: &dyn BitmapRawData,
    params: &(PixelBox, PixelPoint),
) {
    let read_box = params.0;
    let write_box = PixelBox::from_origin_and_size(params.1, read_box.size());

    let Some((read_box, write_box)) = clip_to(
        PixelBox::from_size(read_data.size()),
        PixelBox::from_size(write_data.size()),
        read_box,
        write_box,
    ) else {
        // nothing to do
        return;
    };

    let write_stride = write_data.stride();
    let write_data = write_data.data_mut();
    let read_stride = read_data.stride();
    let read_data = read_data.data();

    let read_line_offset = read_box.min.x as usize * WIDTH;
    let write_line_offset = write_box.min.x as usize * WIDTH;
    let line_width = read_box.width() as usize * WIDTH;

    for y in 0..read_box.height() {
        let write_slice =
            &mut write_data[(y + write_box.min.y) as usize * write_stride + write_line_offset..];
        let read_slice =
            &read_data[(y + read_box.min.y) as usize * read_stride + read_line_offset..];
        (&mut write_slice[..line_width]).copy_from_slice(&read_slice[..line_width]);
    }
}

impl GenericBinary<(PixelBox, PixelPoint)> for CopyFromOp {
    fn perform<Format: PixelFormatSpec, ReadFormat: PixelFormatSpec>(
        write_data: &mut dyn BitmapRawDataMut,
        read_data: &dyn BitmapRawData,
        params: &(PixelBox, PixelPoint),
    ) {
        let read_box = params.0;
        let write_box = PixelBox::from_origin_and_size(params.1, read_box.size());
        let Some((read_box, write_box)) = clip_to(
            PixelBox::from_size(read_data.size()),
            PixelBox::from_size(write_data.size()),
            read_box,
            write_box,
        ) else {
            // nothing to do
            return;
        };

        let write_stride = write_data.stride();
        let write_data = write_data.data_mut();
        let read_stride = read_data.stride();
        let read_data = read_data.data();

        for y in 0..read_box.height() {
            let write_slice = &mut write_data[(y + write_box.min.y) as usize * write_stride..];
            let read_slice = &read_data[(y + read_box.min.y) as usize * read_stride..];
            for x in 0..read_box.width() {
                let col = read_from_bytes::<ReadFormat>(
                    &read_slice[(x + read_box.min.x) as usize * ReadFormat::PIXEL_STRIDE..],
                );
                write_as_bytes::<Format>(
                    &col,
                    &mut write_slice[(x + write_box.min.x) as usize * Format::PIXEL_STRIDE..],
                );
            }
        }
    }
}