use core::fmt::Debug;
use embedded_graphics_core::pixelcolor::{Rgb565, RgbColor};
#[derive(Debug, Clone, Copy)]
pub struct DepthDarkenConfig {
pub max_darkness: u8,
pub near: u32,
pub far: u32,
}
impl DepthDarkenConfig {
pub fn new(max_darkness: u8, near: f32, far: f32) -> Self {
Self {
max_darkness,
near: (near * 65536.0) as u32,
far: (far * 65536.0) as u32,
}
}
#[inline]
pub fn apply(&self, base_color: Rgb565, depth: u32) -> Rgb565 {
if self.max_darkness == 0 {
return base_color;
}
let t = if depth <= self.near {
0u32
} else if depth >= self.far {
65536u32
} else {
let numerator = (depth - self.near) as u64;
let denominator = (self.far - self.near).max(1) as u64;
((numerator * 65536) / denominator) as u32
};
let dark = ((t as u64 * self.max_darkness as u64) / 255) as u32;
let scale = 65536u32.saturating_sub(dark);
let r = ((base_color.r() as u32 * scale) / 65536) as u8;
let g = ((base_color.g() as u32 * scale) / 65536) as u8;
let b = ((base_color.b() as u32 * scale) / 65536) as u8;
Rgb565::new(r, g, b)
}
}
#[derive(Debug, Clone, Copy)]
pub struct DepthDarkenShader<'a, S> {
pub inner: S,
pub config: &'a DepthDarkenConfig,
}
impl<'a, S: super::FragmentShader> super::FragmentShader for DepthDarkenShader<'a, S> {
type Interpolants = S::Interpolants;
#[inline(always)]
fn shade(&self, x: i32, y: i32, z: crate::ZDepth, interps: Self::Interpolants) -> Rgb565 {
let base = self.inner.shade(x, y, z, interps);
self.config.apply(base, u32::from(z))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "depth-u16"))]
use crate::shader::{FlatColorShader, FragmentShader};
#[test]
fn test_depth_darken_interpolation() {
let config = DepthDarkenConfig::new(255, 1.0, 10.0);
let color = Rgb565::WHITE;
assert_eq!(config.apply(color, (1.0 * 65536.0) as u32), color);
assert_eq!(config.apply(color, 0), color);
assert_eq!(config.apply(color, (10.0 * 65536.0) as u32), Rgb565::BLACK);
assert_eq!(config.apply(color, (20.0 * 65536.0) as u32), Rgb565::BLACK);
let mid = config.apply(color, (5.5 * 65536.0) as u32);
assert!(mid.r() > 0 && mid.r() < 20);
assert!(mid.g() > 0 && mid.g() < 40);
}
#[test]
fn test_zero_max_darkness_is_identity() {
let config = DepthDarkenConfig::new(0, 1.0, 10.0);
let color = Rgb565::new(10, 20, 30);
assert_eq!(config.apply(color, (100.0 * 65536.0) as u32), color);
}
#[test]
#[cfg(not(feature = "depth-u16"))]
fn test_depth_darken_shader_decorator() {
let config = DepthDarkenConfig::new(255, 1.0, 5.0);
let inner = FlatColorShader { color: Rgb565::RED };
let shader = DepthDarkenShader {
inner,
config: &config,
};
let near = crate::to_zdepth(65536);
let far = crate::to_zdepth(5 * 65536);
assert_eq!(shader.shade(0, 0, near, ()), Rgb565::RED);
assert_eq!(shader.shade(0, 0, far, ()), Rgb565::BLACK);
}
}