kahlo 0.0.2

Optimized software rendering library.
Documentation
use crate::{
    bitmap::BitmapRawDataMut,
    colour::{write_as_bytes, Colour},
    formats::A8,
};

use super::{GenericUnary, UnaryDispatch, UnaryOpSpec};

pub struct FillOp;

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

impl UnaryOpSpec for FillOp {
    type Params<'l> = (Colour,);
    fn build<'l>() -> UnaryDispatch<Self::Params<'l>> {
        UnaryDispatch::from_generic::<Self>().with::<A8>(fill_a8)
    }
}

fn fill_a8(target: &mut dyn BitmapRawDataMut, with: &(Colour,)) {
    let height = target.height();
    let row_width = target.width();
    let stride = target.stride();
    let data = target.data_mut();

    let alpha = with.0.a;

    // optimization: can we fill the entire thing in one go?
    if stride == row_width {
        data[0..(stride * height)].fill(alpha);
        return;
    }

    for y in 0..height {
        let yoff = y * stride;

        data[yoff..(yoff + row_width)].fill(alpha);
    }
}

fn fill_helper<const WIDTH: usize>(target: &mut dyn BitmapRawDataMut, pattern: &[u8]) {
    let width = target.width();
    let row_width = width * WIDTH;
    let stride = target.stride();
    let height = target.height();
    let target_data = target.data_mut();

    // first, fill in the first row
    let mut filled = WIDTH;
    target_data[0..WIDTH].copy_from_slice(pattern);
    while filled < row_width {
        let to_fill = row_width - filled;

        let to_copy = to_fill.min(filled);
        target_data.copy_within(0..to_copy, filled);
        filled += to_copy;
    }

    // then copy the first row to the rest
    for y in 1..height {
        let yoff = y * stride;
        target_data.copy_within(0..row_width, yoff);
    }
}

impl<'l> GenericUnary<(Colour,)> for FillOp {
    fn perform<Format: crate::formats::PixelFormatSpec>(
        write_data: &mut dyn BitmapRawDataMut,
        params: &(Colour,),
    ) {
        if write_data.width() == 0 || write_data.height() == 0 {
            return;
        }

        let mut cdata = [0u8; 4];
        write_as_bytes::<Format>(&params.0, &mut cdata);

        let cdata = &cdata[0..Format::PIXEL_STRIDE];

        if Format::PIXEL_STRIDE == 1 {
            fill_helper::<1>(write_data, cdata);
        } else if Format::PIXEL_STRIDE == 2 {
            fill_helper::<2>(write_data, cdata);
        } else if Format::PIXEL_STRIDE == 3 {
            fill_helper::<3>(write_data, cdata);
        } else if Format::PIXEL_STRIDE == 4 {
            fill_helper::<4>(write_data, cdata);
        } else {
            unreachable!()
        }
    }
}