kahlo 0.0.2

Optimized software rendering library.
Documentation
use crate::bitmap::{BitmapRawData, BitmapRawDataMut};
use crate::formats::{self, PixelFormatSpec, NUM_PIXEL_FORMATS};
use crate::op::{self, BinaryOpSpec, UnaryOpSpec};

pub type UnaryFunc<Params> = for<'l> fn(&'l mut dyn BitmapRawDataMut, &'l Params);
pub type BinaryFunc<Params> =
    for<'l> fn(&'l mut dyn BitmapRawDataMut, &'l dyn BitmapRawData, &'l Params);

pub trait GenericUnary<Params> {
    fn perform<'l, Format: PixelFormatSpec>(
        write_data: &'l mut dyn BitmapRawDataMut,
        params: &'l Params,
    );
}

pub trait GenericBinary<Params> {
    fn perform<'l, Format: PixelFormatSpec, ReadFormat: PixelFormatSpec>(
        write_data: &'l mut dyn BitmapRawDataMut,
        read_data: &'l dyn BitmapRawData,
        params: &'l Params,
    );
}

pub struct UnaryDispatch<Params> {
    op_array: [UnaryFunc<Params>; NUM_PIXEL_FORMATS],
    changed: [bool; NUM_PIXEL_FORMATS],
}

impl<Params> UnaryDispatch<Params> {
    pub fn with<Format: PixelFormatSpec>(mut self, specific_imp: UnaryFunc<Params>) -> Self {
        self.op_array[Format::FORMAT_ENUM as usize] = specific_imp;
        self.changed[Format::FORMAT_ENUM as usize] = true;
        self
    }

    pub fn with_avx<Format: PixelFormatSpec>(mut self, avx_imp: UnaryFunc<Params>) -> Self {
        if is_x86_feature_detected!("avx") {
            self.op_array[Format::FORMAT_ENUM as usize] = avx_imp;
            self.changed[Format::FORMAT_ENUM as usize] = true;
            self
        } else {
            self
        }
    }

    pub fn select<Format: PixelFormatSpec>(&self) -> UnaryFunc<Params> {
        self.op_array[Format::FORMAT_ENUM as usize]
    }

    pub fn is_generic<Format: PixelFormatSpec>(&self) -> bool {
        !self.changed[Format::FORMAT_ENUM as usize]
    }
}

pub struct BinaryDispatch<Params> {
    op_array: [[BinaryFunc<Params>; NUM_PIXEL_FORMATS]; NUM_PIXEL_FORMATS],
    changed: [[bool; NUM_PIXEL_FORMATS]; NUM_PIXEL_FORMATS],
}

impl<Params> BinaryDispatch<Params> {
    pub fn with<WriteFormat: PixelFormatSpec, ReadFormat: PixelFormatSpec>(
        mut self,
        imp: BinaryFunc<Params>,
    ) -> Self {
        self.op_array[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize] = imp;
        self.changed[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize] = true;
        self
    }
    pub fn with_avx<WriteFormat: PixelFormatSpec, ReadFormat: PixelFormatSpec>(
        mut self,
        avx_imp: BinaryFunc<Params>,
    ) -> Self {
        if is_x86_feature_detected!("avx2") {
            self.op_array[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize] =
                avx_imp;
            self.changed[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize] =
                true;
            self
        } else {
            self
        }
    }

    pub fn select<WriteFormat: PixelFormatSpec, ReadFormat: PixelFormatSpec>(
        &self,
    ) -> BinaryFunc<Params> {
        self.op_array[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize]
    }

    pub fn is_generic<WriteFormat: PixelFormatSpec, ReadFormat: PixelFormatSpec>(&self) -> bool {
        !self.changed[WriteFormat::FORMAT_ENUM as usize][ReadFormat::FORMAT_ENUM as usize]
    }
}

macro_rules! dispatch_entry {
    (unary, $sn:ident, $opn:ident) => {
        UnaryDispatch < <op::$sn::$opn as UnaryOpSpec>::Params<'l>>
    };

    (binary, $sn:ident, $opn:ident) => {
        BinaryDispatch < <op::$sn::$opn as BinaryOpSpec>::Params<'l>>
    };
}

pub struct KahloDispatch<'l> {
    pub composite: dispatch_entry!(binary, composite, CompositeOp),
    pub copy_from: dispatch_entry!(binary, copy_from, CopyFromOp),
    pub fill: dispatch_entry!(unary, fill, FillOp),
    pub fill_region: dispatch_entry!(unary, fill_region, FillRegionOp),
    pub fill_region_masked: dispatch_entry!(unary, fill_region_masked, FillRegionMaskedOp),
    pub rectangle: dispatch_entry!(unary, rectangle, RectangleOp),
}

impl<'l> Default for KahloDispatch<'l> {
    fn default() -> Self {
        Self {
            composite: op::composite::CompositeOp::build(),
            copy_from: op::copy_from::CopyFromOp::build(),
            fill: op::fill::FillOp::build(),
            fill_region: op::fill_region::FillRegionOp::build(),
            fill_region_masked: op::fill_region_masked::FillRegionMaskedOp::build(),
            rectangle: op::rectangle::RectangleOp::build(),
        }
    }
}

impl<'l> KahloDispatch<'l> {
    pub fn use_lifetime(&self) -> &KahloDispatch<'_> {
        unsafe { std::mem::transmute(self) }
    }
}

lazy_static::lazy_static! {
    pub static ref DISPATCH: KahloDispatch<'static> = KahloDispatch::default();
}

// generating operation acceleration matrices
#[cfg(test)]
mod accel_impls {
    use crate::formats::*;

    use super::{BinaryDispatch, UnaryDispatch, DISPATCH};

    fn matrix_unary<T>(name: &'static str, op: &UnaryDispatch<T>) {
        fn cell<F: PixelFormatSpec, T>(op: &UnaryDispatch<T>) -> &'static str {
            if op.is_generic::<F>() {
                "-"
            } else {
                "X"
            }
        }

        println!("\n\n{name}:");
        println!("-------+---");
        println!("A8     | {}", cell::<A8, _>(op));
        println!("Abgr32 | {}", cell::<Abgr32, _>(op));
        println!("Rgba32 | {}", cell::<Rgba32, _>(op));
        println!("Rgba32 | {}", cell::<Rgba32P, _>(op));
    }

    fn matrix_binary<T>(name: &'static str, op: &BinaryDispatch<T>) {
        fn row<F: PixelFormatSpec, T>(op: &BinaryDispatch<T>) -> String {
            let mut ret = String::new();
            if !op.is_generic::<F, A8>() {
                ret += "|X"
            } else {
                ret += "|-"
            };
            if !op.is_generic::<F, Abgr32>() {
                ret += "|X"
            } else {
                ret += "|-"
            };
            if !op.is_generic::<F, Rgba32>() {
                ret += "|X"
            } else {
                ret += "|-"
            };
            if !op.is_generic::<F, Rgba32P>() {
                ret += "|X"
            } else {
                ret += "|-"
            };
            ret
        }

        println!("\n\n{name}:");

        println!("               R");
        println!("           A R g");
        println!("           b g b");
        println!("           g b a");
        println!("           r a 3");
        println!("         A 3 3 2");
        println!("         8 2 2 P");
        println!("        +-+-+-+");
        println!("A8      {}|", row::<A8, _>(op));
        println!("Abgr32  {}|", row::<Abgr32, _>(op));
        println!("Rgba32  {}|", row::<Rgba32, _>(op));
        println!("Rgba32P {}|", row::<Rgba32P, _>(op))
    }

    #[test]
    fn generate_impl_matrices() {
        matrix_binary("composite", &DISPATCH.composite);
        matrix_binary("copy_from", &DISPATCH.copy_from);
        matrix_unary("fill", &DISPATCH.fill);
        matrix_unary("fill_region", &DISPATCH.fill_region);
        matrix_unary("fill_region_masked", &DISPATCH.fill_region_masked);
        matrix_unary("rectangle", &DISPATCH.rectangle);
    }
}

// long but simple implementations

impl<Params> UnaryDispatch<Params> {
    pub(crate) fn from_generic<G: GenericUnary<Params>>() -> Self {
        Self {
            op_array: [
                G::perform::<formats::A8>,
                G::perform::<formats::Abgr32>,
                G::perform::<formats::Rgba32>,
                // G::perform::<formats::Rgba32P>,
            ],
            changed: Default::default(),
        }
    }
}

impl<Params> BinaryDispatch<Params> {
    pub(crate) fn from_generic<G: GenericBinary<Params>>() -> Self {
        Self {
            op_array: [
                [
                    G::perform::<formats::A8, formats::A8>,
                    G::perform::<formats::A8, formats::Abgr32>,
                    G::perform::<formats::A8, formats::Rgba32>,
                    // G::perform::<formats::A8, formats::Rgba32P>,
                ],
                [
                    G::perform::<formats::Abgr32, formats::A8>,
                    G::perform::<formats::Abgr32, formats::Abgr32>,
                    G::perform::<formats::Abgr32, formats::Rgba32>,
                    // G::perform::<formats::Abgr32, formats::Rgba32P>,
                ],
                [
                    G::perform::<formats::Rgba32, formats::A8>,
                    G::perform::<formats::Rgba32, formats::Abgr32>,
                    G::perform::<formats::Rgba32, formats::Rgba32>,
                    // G::perform::<formats::Rgba32, formats::Rgba32P>,
                ],
                /*[
                    G::perform::<formats::Rgba32P, formats::A8>,
                    G::perform::<formats::Rgba32P, formats::Abgr32>,
                    G::perform::<formats::Rgba32P, formats::Rgba32>,
                    G::perform::<formats::Rgba32P, formats::Rgba32P>,
                ],*/
            ],
            changed: Default::default(),
        }
    }
}