use half::f16 as F16;
use crate::entities::attrs::Attrs;
use crate::entities::frame::{Frame, PixelBuffer};
pub fn apply(frame: &Frame, attrs: &Attrs) -> Option<Frame> {
let brightness = attrs.get_float("brightness").unwrap_or(0.0);
let contrast = attrs.get_float("contrast").unwrap_or(0.0);
if brightness.abs() < 0.0001 && contrast.abs() < 0.0001 {
return Some(frame.clone());
}
let cf = 1.0 + contrast;
let (width, height) = frame.resolution();
let buffer = frame.buffer();
let out_buffer = match buffer.as_ref() {
PixelBuffer::U8(data) => {
let mut result = Vec::with_capacity(data.len());
for chunk in data.chunks_exact(4) {
let r = chunk[0] as f32 / 255.0;
let g = chunk[1] as f32 / 255.0;
let b = chunk[2] as f32 / 255.0;
let a = chunk[3];
let r_out = ((r - 0.5) * cf + 0.5 + brightness).clamp(0.0, 1.0);
let g_out = ((g - 0.5) * cf + 0.5 + brightness).clamp(0.0, 1.0);
let b_out = ((b - 0.5) * cf + 0.5 + brightness).clamp(0.0, 1.0);
result.push((r_out * 255.0) as u8);
result.push((g_out * 255.0) as u8);
result.push((b_out * 255.0) as u8);
result.push(a);
}
PixelBuffer::U8(result)
}
PixelBuffer::F16(data) => {
let mut result = Vec::with_capacity(data.len());
for chunk in data.chunks_exact(4) {
let r = chunk[0].to_f32();
let g = chunk[1].to_f32();
let b = chunk[2].to_f32();
let a = chunk[3];
let r_out = (r - 0.5) * cf + 0.5 + brightness;
let g_out = (g - 0.5) * cf + 0.5 + brightness;
let b_out = (b - 0.5) * cf + 0.5 + brightness;
result.push(F16::from_f32(r_out));
result.push(F16::from_f32(g_out));
result.push(F16::from_f32(b_out));
result.push(a);
}
PixelBuffer::F16(result)
}
PixelBuffer::F32(data) => {
let mut result = Vec::with_capacity(data.len());
for chunk in data.chunks_exact(4) {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
let a = chunk[3];
let r_out = (r - 0.5) * cf + 0.5 + brightness;
let g_out = (g - 0.5) * cf + 0.5 + brightness;
let b_out = (b - 0.5) * cf + 0.5 + brightness;
result.push(r_out);
result.push(g_out);
result.push(b_out);
result.push(a);
}
PixelBuffer::F32(result)
}
};
Some(Frame::from_buffer(out_buffer, frame.pixel_format(), width, height))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entities::attrs::AttrValue;
use crate::entities::frame::PixelDepth;
#[test]
fn test_no_change() {
let frame = Frame::new(10, 10, PixelDepth::U8);
let mut attrs = Attrs::new();
attrs.set("brightness", AttrValue::Float(0.0));
attrs.set("contrast", AttrValue::Float(0.0));
let result = apply(&frame, &attrs);
assert!(result.is_some());
}
#[test]
fn test_brightness_increase() {
let mut data = vec![0u8; 4 * 4]; data[0] = 128; data[1] = 128; data[2] = 128; data[3] = 255;
let frame = Frame::from_u8_buffer_with_status(
data,
2,
2,
crate::entities::frame::FrameStatus::Loaded,
);
let mut attrs = Attrs::new();
attrs.set("brightness", AttrValue::Float(0.5)); attrs.set("contrast", AttrValue::Float(0.0));
let result = apply(&frame, &attrs).unwrap();
let buffer = result.buffer();
if let PixelBuffer::U8(data) = buffer.as_ref() {
assert!(data[0] > 200); }
}
}