use crate::cpu::image::{CpuImage, PixelBox};
use crate::{EffectDef, EffectParams, EffectRuntime, ParamValue, RenderSize};
mod effects;
mod generator;
mod internal;
mod prelude;
mod transitions;
#[allow(unused_imports)]
pub(crate) use prelude::*;
#[derive(Clone, Debug)]
pub(crate) struct CpuLut {
pub(crate) edge: u32,
pub(crate) domain_min: [f32; 3],
pub(crate) domain_max: [f32; 3],
pub(crate) values: Vec<[f32; 3]>,
}
pub(crate) struct KernelContext<'a> {
pub(crate) size: RenderSize,
pub(crate) definition: &'a EffectDef,
pub(crate) params: &'a EffectParams<'a>,
pub(crate) runtime: EffectRuntime,
pub(crate) max_filter_samples: u32,
pub(crate) source_a: &'a CpuImage,
pub(crate) source_b: &'a CpuImage,
pub(crate) inputs: &'a [(&'a str, &'a CpuImage)],
pub(crate) resources: &'a [(&'a str, &'a CpuLut)],
}
impl KernelContext<'_> {
pub(crate) fn param(&self, name: &str) -> ParamValue {
if let Some(value) = self.params.get(name) {
return value.clone();
}
self.definition
.params
.iter()
.find(|spec| spec.name == name)
.map_or(ParamValue::Float(0.0), |spec| spec.default.clone())
}
pub(crate) fn float(&self, name: &str) -> f32 {
match self.param(name) {
ParamValue::Float(value) => value,
_ => 0.0,
}
}
pub(crate) fn vec2(&self, name: &str) -> [f32; 2] {
match self.param(name) {
ParamValue::Vec2(value) => value,
_ => [0.0; 2],
}
}
pub(crate) fn vec4(&self, name: &str) -> [f32; 4] {
match self.param(name) {
ParamValue::Vec4(value) | ParamValue::Color(value) => value,
_ => [0.0; 4],
}
}
pub(crate) fn choice(&self, name: &str) -> u32 {
match self.param(name) {
ParamValue::Enum(value) => value,
_ => 0,
}
}
pub(crate) fn boolean(&self, name: &str) -> bool {
matches!(self.param(name), ParamValue::Bool(true))
}
#[allow(clippy::cast_precision_loss)]
pub(crate) fn resolution(&self) -> [f32; 2] {
[self.size.width as f32, self.size.height as f32]
}
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn time_ticks(&self) -> [u32; 2] {
let ticks = self.runtime.time.ticks().cast_unsigned();
[ticks as u32, (ticks >> 32) as u32]
}
pub(crate) const fn progress(&self) -> f32 {
self.runtime.progress
}
pub(crate) const fn ratio(&self) -> f32 {
self.runtime.ratio
}
#[allow(clippy::cast_precision_loss)]
pub(crate) const fn max_filter_samples(&self) -> f32 {
self.max_filter_samples as f32
}
#[inline]
pub(crate) fn sample_src(&self, uv: [f32; 2]) -> [f32; 4] {
self.source_a.sample(uv[0], uv[1])
}
#[inline]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn from_color(&self, uv: [f32; 2]) -> [f32; 4] {
self.source_a.sample(uv[0], uv[1])
}
#[inline]
pub(crate) fn to_color(&self, uv: [f32; 2]) -> [f32; 4] {
self.source_b.sample(uv[0], uv[1])
}
pub(crate) fn lut(&self, name: &str) -> Option<&CpuLut> {
self.resources
.iter()
.find(|(resource, _)| *resource == name)
.map(|(_, lut)| *lut)
}
}
pub(crate) trait CpuKernel: Sync + Send {
fn bounds(&self, context: &KernelContext<'_>) -> PixelBox {
PixelBox::whole(context.size)
}
fn row(&self, context: &KernelContext<'_>, y: u32, x0: u32, out: &mut [[f32; 4]]);
}
pub(crate) fn kernel(id: &str, version: u32) -> Option<&'static dyn CpuKernel> {
if version != 1 {
return None;
}
effects::kernel(id)
.or_else(|| transitions::kernel(id))
.or_else(|| internal::kernel(id))
.or_else(|| generator::kernel(id))
}