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();
}
#[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);
}
}
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>,
],
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::Abgr32, formats::A8>,
G::perform::<formats::Abgr32, formats::Abgr32>,
G::perform::<formats::Abgr32, formats::Rgba32>,
],
[
G::perform::<formats::Rgba32, formats::A8>,
G::perform::<formats::Rgba32, formats::Abgr32>,
G::perform::<formats::Rgba32, formats::Rgba32>,
],
],
changed: Default::default(),
}
}
}