use itertools::Itertools as _;
use rand::prelude::*;
use rand_pcg::Pcg64Mcg;
use crate::ray::RayResult;
mod software;
pub use software::Image;
#[cfg(feature = "gpu")]
mod vulkan;
#[cfg(feature = "gpu")]
mod vulkan_context;
#[cfg(feature = "gpu")]
pub use vulkan::VulkanImage;
fn calculate_scale(width: f64, height: f64, lightpower: f32, rays: usize, exposure: f64) -> f32 {
let area_scale = f64::sqrt((width * height) / (1024.0 * 576.0));
let intensity_scale = lightpower as f64 / (255.0 * 8192.0);
(f64::exp(1.0 + (10.0 as f64 * exposure)) * area_scale * intensity_scale / rays as f64) as f32
}
pub trait RenderImage: Send + Sync {
fn draw_line(&self, ray: RayResult);
fn prepare_render(&mut self, lightpower: f32);
fn finish_render(&mut self) {}
}
pub trait ExportImage {
fn get_size(&self) -> (usize, usize);
fn get_lightpower(&self) -> f32;
fn to_rgbaf32(&self) -> Vec<f32>;
fn to_rgba8(&self, rays: usize, exposure: f64, exponent: f32) -> Vec<u8> {
let (width, height) = self.get_size();
let scale = calculate_scale(
width as f64,
height as f64,
self.get_lightpower(),
rays,
exposure,
);
let mut rng = Pcg64Mcg::new(0xcafef00dd15ea5e5);
self.to_rgbaf32()
.iter()
.tuples()
.flat_map(|(r, g, b, _a)| {
let u: f32 = 0f32.max(r * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let r8 = 0f32.max(255.9f32.min(v));
let u: f32 = 0f32.max(g * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let g8 = 0f32.max(255.9f32.min(v));
let u: f32 = 0f32.max(b * scale);
let dither = rng.gen_range(0f32..1f32);
let v: f32 = 255.0 * u.powf(exponent) + dither;
let b8 = 0f32.max(255.9f32.min(v));
[r8 as u8, g8 as u8, b8 as u8, 255u8]
})
.collect()
}
}