use colorutils_rs::{
luv_to_rgb, luv_with_alpha_to_rgba, rgb_to_luv, rgba_to_luv_with_alpha, TransferFunction,
SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65,
};
use crate::pic_scale_error::PicScaleError;
use crate::scaler::ScalingF32;
use crate::support::check_image_size_overflow;
use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
#[derive(Debug, Copy, Clone)]
pub struct LuvScaler {
pub(crate) scaler: Scaler,
}
impl LuvScaler {
pub fn new(filter: ResamplingFunction) -> Self {
LuvScaler {
scaler: Scaler::new(filter),
}
}
}
impl Scaling for LuvScaler {
fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
self.scaler.set_threading_policy(threading_policy)
}
fn resize_cbcr8<'a>(
&'a self,
_: &ImageStore<'a, u8, 2>,
_: &mut ImageStoreMut<'a, u8, 2>,
) -> Result<(), PicScaleError> {
unimplemented!()
}
fn resize_rgb<'a>(
&self,
store: &ImageStore<'a, u8, 3>,
into: &mut ImageStoreMut<'a, u8, 3>,
) -> Result<(), PicScaleError> {
let new_size = into.get_size();
into.validate()?;
store.validate()?;
if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
return Err(PicScaleError::ZeroImageDimensions);
}
if check_image_size_overflow(store.width, store.height, store.channels) {
return Err(PicScaleError::SourceImageIsTooLarge);
}
if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
return Err(PicScaleError::DestinationImageIsTooLarge);
}
if store.width == new_size.width && store.height == new_size.height {
store.copied_to_mut(into);
return Ok(());
}
const COMPONENTS: usize = 3;
let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
&mut target_vertical,
store.width,
store.height,
)?;
lab_store.bit_depth = into.bit_depth;
let lab_stride =
lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
rgb_to_luv(
store.buffer.as_ref(),
store.width as u32 * COMPONENTS as u32,
lab_store.buffer.borrow_mut(),
lab_stride,
lab_store.width as u32,
lab_store.height as u32,
&SRGB_TO_XYZ_D65,
TransferFunction::Srgb,
);
let new_immutable_store = ImageStore::<f32, COMPONENTS> {
buffer: std::borrow::Cow::Owned(target_vertical),
channels: COMPONENTS,
width: store.width,
height: store.height,
stride: store.width * COMPONENTS,
bit_depth: into.bit_depth,
};
let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
self.scaler
.resize_rgb_f32(&new_immutable_store, &mut new_store)?;
let new_lab_stride =
new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
luv_to_rgb(
new_store.buffer.borrow(),
new_lab_stride,
into.buffer.borrow_mut(),
into.width as u32 * COMPONENTS as u32,
new_store.width as u32,
new_store.height as u32,
&XYZ_TO_SRGB_D65,
TransferFunction::Srgb,
);
Ok(())
}
fn resize_rgba<'a>(
&'a self,
store: &ImageStore<'a, u8, 4>,
into: &mut ImageStoreMut<'a, u8, 4>,
premultiply_alpha: bool,
) -> Result<(), PicScaleError> {
let new_size = into.get_size();
into.validate()?;
store.validate()?;
if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
return Err(PicScaleError::ZeroImageDimensions);
}
if check_image_size_overflow(store.width, store.height, store.channels) {
return Err(PicScaleError::SourceImageIsTooLarge);
}
if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
return Err(PicScaleError::DestinationImageIsTooLarge);
}
if store.width == new_size.width && store.height == new_size.height {
store.copied_to_mut(into);
return Ok(());
}
const COMPONENTS: usize = 4;
let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
&mut target_vertical,
store.width,
store.height,
)?;
lab_store.bit_depth = into.bit_depth;
let lab_stride =
lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
rgba_to_luv_with_alpha(
store.buffer.as_ref(),
store.width as u32 * COMPONENTS as u32,
lab_store.buffer.borrow_mut(),
lab_stride,
lab_store.width as u32,
lab_store.height as u32,
&SRGB_TO_XYZ_D65,
TransferFunction::Srgb,
);
let new_immutable_store = ImageStore::<f32, COMPONENTS> {
buffer: std::borrow::Cow::Owned(target_vertical),
channels: COMPONENTS,
width: store.width,
height: store.height,
stride: store.width * COMPONENTS,
bit_depth: into.bit_depth,
};
let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
self.scaler
.resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
let new_lab_stride =
new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
luv_with_alpha_to_rgba(
new_store.buffer.borrow(),
new_lab_stride,
into.buffer.borrow_mut(),
into.width as u32 * COMPONENTS as u32,
new_store.width as u32,
new_store.height as u32,
&XYZ_TO_SRGB_D65,
TransferFunction::Srgb,
);
Ok(())
}
}