use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ShaderType {
Grayscale,
Sepia,
Invert,
Blur,
Pixelate,
ChromaticAberration,
Vignette,
Scanlines,
CRT,
EdgeDetect,
Emboss,
Sharpen,
OilPainting,
Bloom,
MotionBlur,
Posterize,
HueShift,
Saturation,
Contrast,
Brightness,
GaussianBlur,
Glitch,
Pixelize,
WaveDistortion,
Custom(u32),
}
pub struct Shader {
pub shader_type: ShaderType,
pub intensity: f32,
pub parameters: HashMap<String, f32>,
}
impl Shader {
pub fn new(shader_type: ShaderType) -> Self {
Self {
shader_type,
intensity: 1.0,
parameters: HashMap::new(),
}
}
pub fn with_intensity(mut self, intensity: f32) -> Self {
self.intensity = intensity.clamp(0.0, 1.0);
self
}
pub fn with_parameter(mut self, key: &str, value: f32) -> Self {
self.parameters.insert(key.to_string(), value);
self
}
pub fn apply(&self, pixels: &mut [u8], width: u32, height: u32) {
match self.shader_type {
ShaderType::Grayscale => self.apply_grayscale(pixels),
ShaderType::Sepia => self.apply_sepia(pixels),
ShaderType::Invert => self.apply_invert(pixels),
ShaderType::Blur => self.apply_blur(pixels, width, height),
ShaderType::Pixelate => self.apply_pixelate(pixels, width, height),
ShaderType::ChromaticAberration => self.apply_chromatic_aberration(pixels, width, height),
ShaderType::Vignette => self.apply_vignette(pixels, width, height),
ShaderType::Scanlines => self.apply_scanlines(pixels, width, height),
ShaderType::CRT => self.apply_crt(pixels, width, height),
ShaderType::EdgeDetect => self.apply_edge_detect(pixels, width, height),
ShaderType::Emboss => self.apply_emboss(pixels, width, height),
ShaderType::Sharpen => self.apply_sharpen(pixels, width, height),
ShaderType::OilPainting => self.apply_oil_painting(pixels, width, height),
ShaderType::Bloom => self.apply_bloom(pixels, width, height),
ShaderType::MotionBlur => self.apply_motion_blur(pixels, width, height),
ShaderType::Posterize => self.apply_posterize(pixels),
ShaderType::HueShift => self.apply_hue_shift(pixels),
ShaderType::Saturation => self.apply_saturation(pixels),
ShaderType::Contrast => self.apply_contrast(pixels),
ShaderType::Brightness => self.apply_brightness(pixels),
ShaderType::GaussianBlur => self.apply_gaussian_blur(pixels, width, height),
ShaderType::Glitch => self.apply_glitch(pixels, width, height),
ShaderType::Pixelize => self.apply_pixelize(pixels, width, height),
ShaderType::WaveDistortion => self.apply_wave_distortion(pixels, width, height),
ShaderType::Custom(_) => {}
}
}
fn apply_grayscale(&self, pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let gray = (0.299 * chunk[0] as f32 + 0.587 * chunk[1] as f32 + 0.114 * chunk[2] as f32) as u8;
let intensity = self.intensity;
chunk[0] = Self::lerp(chunk[0], gray, intensity);
chunk[1] = Self::lerp(chunk[1], gray, intensity);
chunk[2] = Self::lerp(chunk[2], gray, intensity);
}
}
fn apply_sepia(&self, pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let r = chunk[0] as f32;
let g = chunk[1] as f32;
let b = chunk[2] as f32;
let tr = (0.393 * r + 0.769 * g + 0.189 * b).min(255.0) as u8;
let tg = (0.349 * r + 0.686 * g + 0.168 * b).min(255.0) as u8;
let tb = (0.272 * r + 0.534 * g + 0.131 * b).min(255.0) as u8;
let intensity = self.intensity;
chunk[0] = Self::lerp(chunk[0], tr, intensity);
chunk[1] = Self::lerp(chunk[1], tg, intensity);
chunk[2] = Self::lerp(chunk[2], tb, intensity);
}
}
fn apply_invert(&self, pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let intensity = self.intensity;
chunk[0] = Self::lerp(chunk[0], 255 - chunk[0], intensity);
chunk[1] = Self::lerp(chunk[1], 255 - chunk[1], intensity);
chunk[2] = Self::lerp(chunk[2], 255 - chunk[2], intensity);
}
}
fn apply_blur(&self, pixels: &mut [u8], width: u32, height: u32) {
let radius = self.parameters.get("radius").copied().unwrap_or(1.0) as i32;
let temp = pixels.to_vec();
for y in 0..height as i32 {
for x in 0..width as i32 {
let mut r_sum = 0u32;
let mut g_sum = 0u32;
let mut b_sum = 0u32;
let mut count = 0u32;
for dy in -radius..=radius {
for dx in -radius..=radius {
let nx = (x + dx).clamp(0, width as i32 - 1);
let ny = (y + dy).clamp(0, height as i32 - 1);
let idx = ((ny * width as i32 + nx) * 4) as usize;
if idx + 2 < temp.len() {
r_sum += temp[idx] as u32;
g_sum += temp[idx + 1] as u32;
b_sum += temp[idx + 2] as u32;
count += 1;
}
}
}
if count > 0 {
let idx = ((y * width as i32 + x) * 4) as usize;
if idx + 2 < pixels.len() {
let r_avg = (r_sum / count) as u8;
let g_avg = (g_sum / count) as u8;
let b_avg = (b_sum / count) as u8;
pixels[idx] = Self::lerp(temp[idx], r_avg, self.intensity);
pixels[idx + 1] = Self::lerp(temp[idx + 1], g_avg, self.intensity);
pixels[idx + 2] = Self::lerp(temp[idx + 2], b_avg, self.intensity);
}
}
}
}
}
fn apply_pixelate(&self, pixels: &mut [u8], width: u32, height: u32) {
let pixel_size = self.parameters.get("size").copied().unwrap_or(4.0) as u32;
for y in (0..height).step_by(pixel_size as usize) {
for x in (0..width).step_by(pixel_size as usize) {
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
let r = pixels[idx];
let g = pixels[idx + 1];
let b = pixels[idx + 2];
for dy in 0..pixel_size.min(height - y) {
for dx in 0..pixel_size.min(width - x) {
let target_idx = (((y + dy) * width + (x + dx)) * 4) as usize;
if target_idx + 2 < pixels.len() {
pixels[target_idx] = r;
pixels[target_idx + 1] = g;
pixels[target_idx + 2] = b;
}
}
}
}
}
}
}
fn apply_chromatic_aberration(&self, pixels: &mut [u8], width: u32, height: u32) {
let offset = self.parameters.get("offset").copied().unwrap_or(2.0) as i32;
let temp = pixels.to_vec();
for y in 0..height as i32 {
for x in 0..width as i32 {
let idx = ((y * width as i32 + x) * 4) as usize;
let rx = (x - offset).clamp(0, width as i32 - 1);
let r_idx = ((y * width as i32 + rx) * 4) as usize;
let bx = (x + offset).clamp(0, width as i32 - 1);
let b_idx = ((y * width as i32 + bx) * 4) as usize;
if idx + 2 < pixels.len() && r_idx < temp.len() && b_idx + 2 < temp.len() {
pixels[idx] = temp[r_idx];
pixels[idx + 2] = temp[b_idx + 2];
}
}
}
}
fn apply_vignette(&self, pixels: &mut [u8], width: u32, height: u32) {
let strength = self.parameters.get("strength").copied().unwrap_or(0.5);
let cx = width as f32 / 2.0;
let cy = height as f32 / 2.0;
let max_dist = ((cx * cx + cy * cy) as f32).sqrt();
for y in 0..height {
for x in 0..width {
let dx = x as f32 - cx;
let dy = y as f32 - cy;
let dist = ((dx * dx + dy * dy) as f32).sqrt();
let vignette = 1.0 - (dist / max_dist * strength).min(1.0);
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
pixels[idx] = (pixels[idx] as f32 * vignette) as u8;
pixels[idx + 1] = (pixels[idx + 1] as f32 * vignette) as u8;
pixels[idx + 2] = (pixels[idx + 2] as f32 * vignette) as u8;
}
}
}
}
fn apply_scanlines(&self, pixels: &mut [u8], width: u32, height: u32) {
let line_height = self.parameters.get("height").copied().unwrap_or(2.0) as u32;
let darkness = self.parameters.get("darkness").copied().unwrap_or(0.5);
for y in (0..height).step_by(line_height as usize * 2) {
for x in 0..width {
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
pixels[idx] = (pixels[idx] as f32 * (1.0 - darkness)) as u8;
pixels[idx + 1] = (pixels[idx + 1] as f32 * (1.0 - darkness)) as u8;
pixels[idx + 2] = (pixels[idx + 2] as f32 * (1.0 - darkness)) as u8;
}
}
}
}
fn apply_crt(&self, pixels: &mut [u8], width: u32, height: u32) {
self.apply_scanlines(pixels, width, height);
self.apply_vignette(pixels, width, height);
let offset = 1;
let mut temp = pixels.to_vec();
for y in 0..height as i32 {
for x in 0..width as i32 {
let idx = ((y * width as i32 + x) * 4) as usize;
let rx = (x - offset).clamp(0, width as i32 - 1);
let r_idx = ((y * width as i32 + rx) * 4) as usize;
if idx < pixels.len() && r_idx < temp.len() {
pixels[idx] = temp[r_idx];
}
}
}
}
fn apply_edge_detect(&self, pixels: &mut [u8], width: u32, height: u32) {
let temp = pixels.to_vec();
let threshold = self.parameters.get("threshold").copied().unwrap_or(50.0);
for y in 1..(height - 1) {
for x in 1..(width - 1) {
let idx = ((y * width + x) * 4) as usize;
let mut gx = 0i32;
let mut gy = 0i32;
for dy in -1..=1 {
for dx in -1..=1 {
let neighbor_idx = (((y as i32 + dy) as u32 * width + (x as i32 + dx) as u32) * 4) as usize;
if neighbor_idx + 2 < temp.len() {
let gray = (temp[neighbor_idx] as i32 + temp[neighbor_idx + 1] as i32 + temp[neighbor_idx + 2] as i32) / 3;
gx += gray * dx;
gy += gray * dy;
}
}
}
let magnitude = ((gx * gx + gy * gy) as f32).sqrt().min(255.0);
let edge = if magnitude > threshold { 255 } else { 0 };
pixels[idx] = Self::lerp(temp[idx], edge, self.intensity);
pixels[idx + 1] = Self::lerp(temp[idx + 1], edge, self.intensity);
pixels[idx + 2] = Self::lerp(temp[idx + 2], edge, self.intensity);
}
}
}
fn apply_emboss(&self, pixels: &mut [u8], width: u32, height: u32) {
let temp = pixels.to_vec();
for y in 1..(height - 1) {
for x in 1..(width - 1) {
let idx = ((y * width + x) * 4) as usize;
let tl = (((y - 1) * width + (x - 1)) * 4) as usize;
let br = (((y + 1) * width + (x + 1)) * 4) as usize;
if tl + 2 < temp.len() && br + 2 < temp.len() && idx + 2 < pixels.len() {
for i in 0..3 {
let diff = temp[tl + i] as i32 - temp[br + i] as i32 + 128;
let result = diff.clamp(0, 255) as u8;
pixels[idx + i] = Self::lerp(temp[idx + i], result, self.intensity);
}
}
}
}
}
fn apply_sharpen(&self, pixels: &mut [u8], width: u32, height: u32) {
let temp = pixels.to_vec();
let amount = self.parameters.get("amount").copied().unwrap_or(1.0);
for y in 1..(height - 1) {
for x in 1..(width - 1) {
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
for i in 0..3 {
let center = temp[idx + i] as i32;
let mut neighbors = 0i32;
let mut count = 0;
for dy in -1..=1 {
for dx in -1..=1 {
if dx != 0 || dy != 0 {
let neighbor_idx = (((y as i32 + dy) as u32 * width + (x as i32 + dx) as u32) * 4) as usize;
if neighbor_idx + i < temp.len() {
neighbors += temp[neighbor_idx + i] as i32;
count += 1;
}
}
}
}
if count > 0 {
let avg = neighbors / count;
let sharpened = center + ((center - avg) as f32 * amount) as i32;
pixels[idx + i] = sharpened.clamp(0, 255) as u8;
}
}
}
}
}
}
fn apply_oil_painting(&self, pixels: &mut [u8], width: u32, height: u32) {
let radius = self.parameters.get("radius").copied().unwrap_or(3.0) as i32;
let temp = pixels.to_vec();
for y in radius..(height as i32 - radius) {
for x in radius..(width as i32 - radius) {
let idx = ((y as u32 * width + x as u32) * 4) as usize;
let mut intensity_count = [0u32; 256];
let mut avg_r = [0u32; 256];
let mut avg_g = [0u32; 256];
let mut avg_b = [0u32; 256];
for dy in -radius..=radius {
for dx in -radius..=radius {
let neighbor_idx = (((y + dy) as u32 * width + (x + dx) as u32) * 4) as usize;
if neighbor_idx + 2 < temp.len() {
let r = temp[neighbor_idx];
let g = temp[neighbor_idx + 1];
let b = temp[neighbor_idx + 2];
let intensity = ((r as u32 + g as u32 + b as u32) / 3) as usize;
intensity_count[intensity] += 1;
avg_r[intensity] += r as u32;
avg_g[intensity] += g as u32;
avg_b[intensity] += b as u32;
}
}
}
let mut max_intensity = 0;
let mut max_count = 0;
for i in 0..256 {
if intensity_count[i] > max_count {
max_count = intensity_count[i];
max_intensity = i;
}
}
if max_count > 0 && idx + 2 < pixels.len() {
pixels[idx] = (avg_r[max_intensity] / max_count) as u8;
pixels[idx + 1] = (avg_g[max_intensity] / max_count) as u8;
pixels[idx + 2] = (avg_b[max_intensity] / max_count) as u8;
}
}
}
}
fn apply_bloom(&self, pixels: &mut [u8], width: u32, height: u32) {
let threshold = self.parameters.get("threshold").copied().unwrap_or(200.0);
let intensity = self.parameters.get("bloom_intensity").copied().unwrap_or(1.5);
let mut bright_pixels = vec![0u8; pixels.len()];
for i in (0..pixels.len()).step_by(4) {
let brightness = (pixels[i] as f32 + pixels[i + 1] as f32 + pixels[i + 2] as f32) / 3.0;
if brightness > threshold {
bright_pixels[i] = pixels[i];
bright_pixels[i + 1] = pixels[i + 1];
bright_pixels[i + 2] = pixels[i + 2];
}
}
let radius = 3;
for y in radius..(height as i32 - radius) {
for x in radius..(width as i32 - radius) {
let idx = ((y as u32 * width + x as u32) * 4) as usize;
let mut r_sum = 0u32;
let mut g_sum = 0u32;
let mut b_sum = 0u32;
let mut count = 0u32;
for dy in -radius..=radius {
for dx in -radius..=radius {
let neighbor_idx = (((y + dy) as u32 * width + (x + dx) as u32) * 4) as usize;
if neighbor_idx + 2 < bright_pixels.len() {
r_sum += bright_pixels[neighbor_idx] as u32;
g_sum += bright_pixels[neighbor_idx + 1] as u32;
b_sum += bright_pixels[neighbor_idx + 2] as u32;
count += 1;
}
}
}
if count > 0 && idx + 2 < pixels.len() {
let bloom_r = ((r_sum / count) as f32 * intensity) as u32;
let bloom_g = ((g_sum / count) as f32 * intensity) as u32;
let bloom_b = ((b_sum / count) as f32 * intensity) as u32;
pixels[idx] = ((pixels[idx] as u32 + bloom_r).min(255)) as u8;
pixels[idx + 1] = ((pixels[idx + 1] as u32 + bloom_g).min(255)) as u8;
pixels[idx + 2] = ((pixels[idx + 2] as u32 + bloom_b).min(255)) as u8;
}
}
}
}
fn apply_motion_blur(&self, pixels: &mut [u8], width: u32, height: u32) {
let direction = self.parameters.get("direction").copied().unwrap_or(0.0);
let samples = self.parameters.get("samples").copied().unwrap_or(5.0) as i32;
let temp = pixels.to_vec();
let dx = direction.cos();
let dy = direction.sin();
for y in 0..height as i32 {
for x in 0..width as i32 {
let idx = ((y * width as i32 + x) * 4) as usize;
let mut r_sum = 0u32;
let mut g_sum = 0u32;
let mut b_sum = 0u32;
let mut count = 0u32;
for i in 0..samples {
let offset = i as f32 - samples as f32 / 2.0;
let sx = (x as f32 + dx * offset).clamp(0.0, width as f32 - 1.0) as i32;
let sy = (y as f32 + dy * offset).clamp(0.0, height as f32 - 1.0) as i32;
let sample_idx = ((sy * width as i32 + sx) * 4) as usize;
if sample_idx + 2 < temp.len() {
r_sum += temp[sample_idx] as u32;
g_sum += temp[sample_idx + 1] as u32;
b_sum += temp[sample_idx + 2] as u32;
count += 1;
}
}
if count > 0 && idx + 2 < pixels.len() {
pixels[idx] = (r_sum / count) as u8;
pixels[idx + 1] = (g_sum / count) as u8;
pixels[idx + 2] = (b_sum / count) as u8;
}
}
}
}
fn apply_posterize(&self, pixels: &mut [u8]) {
let levels = self.parameters.get("levels").copied().unwrap_or(4.0) as u8;
let step = 255 / levels;
for chunk in pixels.chunks_exact_mut(4) {
for i in 0..3 {
chunk[i] = (chunk[i] / step) * step;
}
}
}
fn apply_hue_shift(&self, pixels: &mut [u8]) {
let shift = self.parameters.get("shift").copied().unwrap_or(0.0);
for chunk in pixels.chunks_exact_mut(4) {
let (h, s, v) = Self::rgb_to_hsv(chunk[0], chunk[1], chunk[2]);
let new_h = (h + shift) % 360.0;
let (r, g, b) = Self::hsv_to_rgb(new_h, s, v);
chunk[0] = Self::lerp(chunk[0], r, self.intensity);
chunk[1] = Self::lerp(chunk[1], g, self.intensity);
chunk[2] = Self::lerp(chunk[2], b, self.intensity);
}
}
fn apply_saturation(&self, pixels: &mut [u8]) {
let saturation = self.parameters.get("saturation").copied().unwrap_or(1.5);
for chunk in pixels.chunks_exact_mut(4) {
let (h, s, v) = Self::rgb_to_hsv(chunk[0], chunk[1], chunk[2]);
let new_s = (s * saturation).clamp(0.0, 1.0);
let (r, g, b) = Self::hsv_to_rgb(h, new_s, v);
chunk[0] = Self::lerp(chunk[0], r, self.intensity);
chunk[1] = Self::lerp(chunk[1], g, self.intensity);
chunk[2] = Self::lerp(chunk[2], b, self.intensity);
}
}
fn apply_contrast(&self, pixels: &mut [u8]) {
let contrast = self.parameters.get("contrast").copied().unwrap_or(1.5);
let factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast));
for chunk in pixels.chunks_exact_mut(4) {
for i in 0..3 {
let adjusted = (factor * (chunk[i] as f32 - 128.0) + 128.0).clamp(0.0, 255.0) as u8;
chunk[i] = Self::lerp(chunk[i], adjusted, self.intensity);
}
}
}
fn apply_brightness(&self, pixels: &mut [u8]) {
let brightness = self.parameters.get("brightness").copied().unwrap_or(50.0);
for chunk in pixels.chunks_exact_mut(4) {
for i in 0..3 {
let adjusted = (chunk[i] as f32 + brightness).clamp(0.0, 255.0) as u8;
chunk[i] = Self::lerp(chunk[i], adjusted, self.intensity);
}
}
}
fn apply_gaussian_blur(&self, pixels: &mut [u8], width: u32, height: u32) {
let radius = self.parameters.get("radius").copied().unwrap_or(2.0) as i32;
let sigma = self.parameters.get("sigma").copied().unwrap_or(1.5);
let mut kernel = Vec::new();
let mut kernel_sum = 0.0;
for y in -radius..=radius {
for x in -radius..=radius {
let dist_sq = (x * x + y * y) as f32;
let weight = (-dist_sq / (2.0 * sigma * sigma)).exp();
kernel.push(weight);
kernel_sum += weight;
}
}
for k in &mut kernel {
*k /= kernel_sum;
}
let temp = pixels.to_vec();
for y in radius..(height as i32 - radius) {
for x in radius..(width as i32 - radius) {
let idx = ((y as u32 * width + x as u32) * 4) as usize;
let mut r_sum = 0.0;
let mut g_sum = 0.0;
let mut b_sum = 0.0;
let mut k_idx = 0;
for dy in -radius..=radius {
for dx in -radius..=radius {
let neighbor_idx = (((y + dy) as u32 * width + (x + dx) as u32) * 4) as usize;
if neighbor_idx + 2 < temp.len() {
let weight = kernel[k_idx];
r_sum += temp[neighbor_idx] as f32 * weight;
g_sum += temp[neighbor_idx + 1] as f32 * weight;
b_sum += temp[neighbor_idx + 2] as f32 * weight;
}
k_idx += 1;
}
}
if idx + 2 < pixels.len() {
pixels[idx] = r_sum as u8;
pixels[idx + 1] = g_sum as u8;
pixels[idx + 2] = b_sum as u8;
}
}
}
}
fn apply_glitch(&self, pixels: &mut [u8], width: u32, height: u32) {
use rand::Rng;
let mut rng = rand::thread_rng();
let intensity = self.parameters.get("glitch_intensity").copied().unwrap_or(0.1);
let num_glitches = (height as f32 * intensity) as usize;
for _ in 0..num_glitches {
let y = rng.gen_range(0..height);
let offset = rng.gen_range(-20..20);
for x in 0..width {
let src_x = ((x as i32 + offset).clamp(0, width as i32 - 1)) as u32;
let src_idx = ((y * width + src_x) * 4) as usize;
let dst_idx = ((y * width + x) * 4) as usize;
if src_idx + 2 < pixels.len() && dst_idx + 2 < pixels.len() {
pixels[dst_idx] = pixels[src_idx];
pixels[dst_idx + 1] = pixels[src_idx + 1];
pixels[dst_idx + 2] = pixels[src_idx + 2];
}
}
}
}
fn apply_pixelize(&self, pixels: &mut [u8], width: u32, height: u32) {
let block_size = self.parameters.get("block_size").copied().unwrap_or(8.0) as u32;
for by in (0..height).step_by(block_size as usize) {
for bx in (0..width).step_by(block_size as usize) {
let mut r_sum = 0u32;
let mut g_sum = 0u32;
let mut b_sum = 0u32;
let mut count = 0u32;
for y in by..(by + block_size).min(height) {
for x in bx..(bx + block_size).min(width) {
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
r_sum += pixels[idx] as u32;
g_sum += pixels[idx + 1] as u32;
b_sum += pixels[idx + 2] as u32;
count += 1;
}
}
}
if count > 0 {
let r_avg = (r_sum / count) as u8;
let g_avg = (g_sum / count) as u8;
let b_avg = (b_sum / count) as u8;
for y in by..(by + block_size).min(height) {
for x in bx..(bx + block_size).min(width) {
let idx = ((y * width + x) * 4) as usize;
if idx + 2 < pixels.len() {
pixels[idx] = r_avg;
pixels[idx + 1] = g_avg;
pixels[idx + 2] = b_avg;
}
}
}
}
}
}
}
fn apply_wave_distortion(&self, pixels: &mut [u8], width: u32, height: u32) {
let amplitude = self.parameters.get("amplitude").copied().unwrap_or(10.0);
let frequency = self.parameters.get("frequency").copied().unwrap_or(0.05);
let temp = pixels.to_vec();
for y in 0..height {
for x in 0..width {
let offset = (amplitude * (y as f32 * frequency).sin()) as i32;
let src_x = ((x as i32 + offset).clamp(0, width as i32 - 1)) as u32;
let src_idx = ((y * width + src_x) * 4) as usize;
let dst_idx = ((y * width + x) * 4) as usize;
if src_idx + 2 < temp.len() && dst_idx + 2 < pixels.len() {
pixels[dst_idx] = temp[src_idx];
pixels[dst_idx + 1] = temp[src_idx + 1];
pixels[dst_idx + 2] = temp[src_idx + 2];
}
}
}
}
fn lerp(a: u8, b: u8, t: f32) -> u8 {
(a as f32 * (1.0 - t) + b as f32 * t) as u8
}
fn rgb_to_hsv(r: u8, g: u8, b: u8) -> (f32, f32, f32) {
let r = r as f32 / 255.0;
let g = g as f32 / 255.0;
let b = b as f32 / 255.0;
let max = r.max(g).max(b);
let min = r.min(g).min(b);
let delta = max - min;
let h = if delta == 0.0 {
0.0
} else if max == r {
60.0 * (((g - b) / delta) % 6.0)
} else if max == g {
60.0 * (((b - r) / delta) + 2.0)
} else {
60.0 * (((r - g) / delta) + 4.0)
};
let s = if max == 0.0 { 0.0 } else { delta / max };
let v = max;
(h, s, v)
}
fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (u8, u8, u8) {
let c = v * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = v - c;
let (r, g, b) = if h < 60.0 {
(c, x, 0.0)
} else if h < 120.0 {
(x, c, 0.0)
} else if h < 180.0 {
(0.0, c, x)
} else if h < 240.0 {
(0.0, x, c)
} else if h < 300.0 {
(x, 0.0, c)
} else {
(c, 0.0, x)
};
(
((r + m) * 255.0) as u8,
((g + m) * 255.0) as u8,
((b + m) * 255.0) as u8,
)
}
}
pub struct ShaderManager {
shaders: Vec<Shader>,
enabled: bool,
}
impl ShaderManager {
pub fn new() -> Self {
Self {
shaders: Vec::new(),
enabled: true,
}
}
pub fn add_shader(&mut self, shader: Shader) {
self.shaders.push(shader);
}
pub fn clear_shaders(&mut self) {
self.shaders.clear();
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn apply_all(&self, pixels: &mut [u8], width: u32, height: u32) {
if !self.enabled {
return;
}
for shader in &self.shaders {
shader.apply(pixels, width, height);
}
}
}
impl Default for ShaderManager {
fn default() -> Self {
Self::new()
}
}