use crate::Rgb;
#[must_use]
pub fn temperature_to_rgb(temperature: u32) -> Rgb {
let temp = (temperature as f64 / 100.0).clamp(10.0, 400.0);
let red = if temp <= 66.0 {
1.0
} else {
let r = temp - 60.0;
(329.698_727_446 * r.powf(-0.133_204_759_2)) / 255.0
};
let green = if temp <= 66.0 {
let g = temp;
(99.470_802_586_1 * g.ln() - 161.119_568_166_1) / 255.0
} else {
let g = temp - 60.0;
(288.122_169_53 * g.powf(-0.075_514_849_2)) / 255.0
};
let blue = if temp >= 66.0 {
1.0
} else if temp <= 19.0 {
0.0
} else {
let b = temp - 10.0;
(138.517_731_223_1 * b.ln() - 305.044_792_730_7) / 255.0
};
[
red.clamp(0.0, 1.0),
green.clamp(0.0, 1.0),
blue.clamp(0.0, 1.0),
]
}
#[must_use]
pub fn apply_temperature_shift(rgb: &Rgb, from_temp: u32, to_temp: u32) -> Rgb {
let from_rgb = temperature_to_rgb(from_temp);
let to_rgb = temperature_to_rgb(to_temp);
let r_mult = to_rgb[0] / from_rgb[0].max(0.001);
let g_mult = to_rgb[1] / from_rgb[1].max(0.001);
let b_mult = to_rgb[2] / from_rgb[2].max(0.001);
[
(rgb[0] * r_mult).clamp(0.0, 1.0),
(rgb[1] * g_mult).clamp(0.0, 1.0),
(rgb[2] * b_mult).clamp(0.0, 1.0),
]
}
#[must_use]
pub fn apply_temperature_shift_to_image(
image_data: &[u8],
from_temp: u32,
to_temp: u32,
) -> Vec<u8> {
let mut output = Vec::with_capacity(image_data.len());
for chunk in image_data.chunks_exact(3) {
let r = f64::from(chunk[0]) / 255.0;
let g = f64::from(chunk[1]) / 255.0;
let b = f64::from(chunk[2]) / 255.0;
let shifted = apply_temperature_shift(&[r, g, b], from_temp, to_temp);
output.push((shifted[0] * 255.0).round() as u8);
output.push((shifted[1] * 255.0).round() as u8);
output.push((shifted[2] * 255.0).round() as u8);
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temperature_to_rgb_daylight() {
let rgb = temperature_to_rgb(6500);
assert!(rgb[0] > 0.9);
assert!(rgb[1] > 0.9);
assert!(rgb[2] > 0.9);
}
#[test]
fn test_temperature_to_rgb_tungsten() {
let rgb = temperature_to_rgb(3000);
assert!(rgb[0] > rgb[2]);
}
#[test]
fn test_temperature_to_rgb_cool() {
let rgb = temperature_to_rgb(9000);
assert!(rgb[2] > rgb[0]);
}
#[test]
fn test_temperature_to_rgb_range() {
for temp in (2000..=10000).step_by(1000) {
let rgb = temperature_to_rgb(temp);
assert!(rgb[0] >= 0.0 && rgb[0] <= 1.0);
assert!(rgb[1] >= 0.0 && rgb[1] <= 1.0);
assert!(rgb[2] >= 0.0 && rgb[2] <= 1.0);
}
}
#[test]
fn test_apply_temperature_shift_same() {
let rgb = [0.5, 0.5, 0.5];
let result = apply_temperature_shift(&rgb, 6500, 6500);
assert!((result[0] - rgb[0]).abs() < 0.1);
assert!((result[1] - rgb[1]).abs() < 0.1);
assert!((result[2] - rgb[2]).abs() < 0.1);
}
#[test]
fn test_apply_temperature_shift_warm_to_cool() {
let rgb = [0.5, 0.5, 0.5];
let result = apply_temperature_shift(&rgb, 3000, 6500);
assert!(result[2] > result[0]);
}
#[test]
fn test_apply_temperature_shift_to_image() {
let image = vec![128, 128, 128, 255, 0, 0];
let output = apply_temperature_shift_to_image(&image, 6500, 6500);
assert_eq!(output.len(), image.len());
}
}