use crate::context::Context;
use crate::*;
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[repr(transparent)]
pub struct Transform<InputPixelFormat, OutputPixelFormat, Context = GlobalContext, Flags = AllowCache> {
pub(crate) handle: ffi::HTRANSFORM,
_from: PhantomData<InputPixelFormat>,
_to: PhantomData<OutputPixelFormat>,
_context_ref: PhantomData<Context>,
_flags_ref: PhantomData<Flags>,
}
unsafe impl<F, T, C: Send, Z> Send for Transform<F, T, C, Z> {}
unsafe impl<F, T, C: Send> Sync for Transform<F, T, C, DisallowCache> {}
impl<InputPixelFormat: Copy + Pod, OutputPixelFormat: Copy + Pod> Transform<InputPixelFormat, OutputPixelFormat, GlobalContext, AllowCache> {
#[inline]
pub fn new(input: &Profile,
in_format: PixelFormat,
output: &Profile,
out_format: PixelFormat,
intent: Intent) -> LCMSResult<Self> {
Self::new_flags(input, in_format, output, out_format, intent, Flags::default())
}
#[inline]
pub fn new_flags<Fl: CacheFlag>(input: &Profile,
in_format: PixelFormat,
output: &Profile,
out_format: PixelFormat,
intent: Intent,
flags: Flags<Fl>)
-> LCMSResult<Self> {
Self::new_flags_context(GlobalContext::new(), input, in_format, output, out_format, intent, flags.allow_cache())
}
#[inline]
pub fn new_proofing(input: &Profile, in_format: PixelFormat,
output: &Profile, out_format: PixelFormat,
proofing: &Profile, intent: Intent, proofng_intent: Intent,
flags: Flags)
-> LCMSResult<Self> {
Self::new_proofing_context(GlobalContext::new(), input, in_format, output, out_format, proofing, intent, proofng_intent, flags)
}
#[inline]
pub fn new_multiprofile(profiles: &[&Profile], in_format: PixelFormat, out_format: PixelFormat, intent: Intent, flags: Flags) -> LCMSResult<Self> {
Self::new_multiprofile_context(GlobalContext::new(), profiles, in_format, out_format, intent, flags)
}
}
impl<PixelFormat: Copy + Pod, Ctx: Context, Fl: CacheFlag> Transform<PixelFormat, PixelFormat, Ctx, Fl> {
#[inline]
#[track_caller]
pub fn transform_in_place(&self, srcdst: &mut [PixelFormat]) {
let num_pixels = self.num_pixels(srcdst.len(), srcdst.len());
unsafe {
ffi::cmsDoTransform(self.handle,
srcdst.as_ptr().cast::<c_void>(),
srcdst.as_mut_ptr().cast::<c_void>(),
num_pixels);
}
}
}
impl<InputPixelFormat: Copy + Pod, OutputPixelFormat: Copy + Pod, Ctx: Context> Transform<InputPixelFormat, OutputPixelFormat, Ctx, AllowCache> {
#[inline]
pub fn new_context(context: impl AsRef<Ctx>, input: &Profile<Ctx>, in_format: PixelFormat,
output: &Profile<Ctx>, out_format: PixelFormat, intent: Intent) -> LCMSResult<Self> {
Self::new_flags_context(context, input, in_format, output, out_format, intent, Flags::default())
}
}
impl<InputPixelFormat: Copy + Pod, OutputPixelFormat: Copy + Pod, Ctx: Context, Fl: CacheFlag> Transform<InputPixelFormat, OutputPixelFormat, Ctx, Fl> {
#[inline]
unsafe fn new_handle(handle: ffi::HTRANSFORM) -> LCMSResult<Self> {
if handle.is_null() {
Err(Error::ObjectCreationError)
} else {
Ok(Transform {
handle,
_from: PhantomData,
_to: PhantomData,
_context_ref: PhantomData,
_flags_ref: PhantomData,
})
}
}
#[track_caller]
fn check_formats(in_format: PixelFormat, out_format: PixelFormat) {
Self::check_format::<InputPixelFormat>(in_format, true);
Self::check_format::<OutputPixelFormat>(out_format, false);
}
#[track_caller]
fn check_format<P: Copy + Pod>(format: PixelFormat, input: bool) {
let io = if input {"input"} else {"output"};
assert!(!format.planar(), "Planar {format:?} {io} format not supported");
if is_u8::<P>() {
return;
}
assert_eq!(format.bytes_per_pixel(),
std::mem::size_of::<P>(),
"{format:?} has {} bytes per pixel, but the {io} format has {}",
format.bytes_per_pixel(),
std::mem::size_of::<P>());
}
#[inline]
#[must_use] pub fn input_pixel_format(&self) -> PixelFormat {
unsafe { ffi::cmsGetTransformInputFormat(self.handle) }
}
#[inline]
#[must_use] pub fn output_pixel_format(&self) -> PixelFormat {
unsafe { ffi::cmsGetTransformOutputFormat(self.handle) }
}
#[inline]
#[track_caller]
fn num_pixels(&self, mut src_len: usize, mut dst_len: usize) -> u32 {
if is_u8::<InputPixelFormat>() {
let bpp = self.input_pixel_format().bytes_per_pixel();
if bpp > 1 {
assert_eq!(0, src_len % bpp, "Input [u8] slice's length {src_len} is not a multiple of {bpp}");
src_len /= bpp;
}
}
if is_u8::<OutputPixelFormat>() {
let bpp = self.output_pixel_format().bytes_per_pixel();
if bpp > 1 {
assert_eq!(0, dst_len % bpp, "Output [u8] slice's length {dst_len} is not a multiple of {bpp}");
dst_len /= bpp;
}
}
src_len.min(dst_len).min(u32::MAX as usize) as u32
}
#[inline]
#[track_caller]
pub fn transform_pixels(&self, src: &[InputPixelFormat], dst: &mut [OutputPixelFormat]) {
let num_pixels = self.num_pixels(src.len(), dst.len());
unsafe {
ffi::cmsDoTransform(self.handle,
src.as_ptr().cast::<c_void>(),
dst.as_mut_ptr().cast::<c_void>(),
num_pixels);
}
}
#[inline]
#[track_caller]
pub fn transform_pixels_uninit<'dst>(&self, src: &[InputPixelFormat], dst: &'dst mut [MaybeUninit<OutputPixelFormat>]) -> &'dst mut [OutputPixelFormat] {
let num_pixels = self.num_pixels(src.len(), dst.len());
assert_eq!(num_pixels as usize, dst.len());
unsafe {
ffi::cmsDoTransform(self.handle,
src.as_ptr().cast::<c_void>(),
dst.as_mut_ptr().cast::<c_void>(),
num_pixels);
std::mem::transmute::<&'dst mut [MaybeUninit<OutputPixelFormat>], &'dst mut [OutputPixelFormat]>(dst)
}
}
#[inline]
#[track_caller]
pub fn new_flags_context(context: impl AsRef<Ctx>, input: &Profile<Ctx>, in_format: PixelFormat,
output: &Profile<Ctx>, out_format: PixelFormat,
intent: Intent, flags: Flags<Fl>)
-> LCMSResult<Self> {
Self::check_formats(in_format, out_format);
unsafe {
Self::new_handle(ffi::cmsCreateTransformTHR(context.as_ref().as_ptr(),
input.handle, in_format,
output.handle, out_format,
intent, flags.bits()))
}
}
#[inline]
#[track_caller]
pub fn new_proofing_context(context: impl AsRef<Ctx>, input: &Profile<Ctx>, in_format: PixelFormat,
output: &Profile<Ctx>, out_format: PixelFormat,
proofing: &Profile<Ctx>, intent: Intent, proofng_intent: Intent,
flags: Flags<Fl>)
-> LCMSResult<Self> {
Self::check_formats(in_format, out_format);
unsafe {
Self::new_handle(ffi::cmsCreateProofingTransformTHR(context.as_ref().as_ptr(), input.handle, in_format,
output.handle, out_format,
proofing.handle, intent, proofng_intent, flags.bits()))
}
}
#[inline]
#[track_caller]
pub fn new_multiprofile_context(context: impl AsRef<Ctx>, profiles: &[&Profile<Ctx>],
in_format: PixelFormat, out_format: PixelFormat, intent: Intent, flags: Flags<Fl>) -> LCMSResult<Self> {
Self::check_formats(in_format, out_format);
let mut handles: Vec<_> = profiles.iter().map(|p| p.handle).collect();
unsafe {
Self::new_handle(
ffi::cmsCreateMultiprofileTransformTHR(context.as_ref().as_ptr(), handles.as_mut_ptr(), handles.len() as u32, in_format, out_format, intent, flags.bits()),
)
}
}
}
impl<F, T, C, L> Transform<F, T, C, L> {
#[inline]
#[must_use]
pub fn input_format(&self) -> PixelFormat {
unsafe { ffi::cmsGetTransformInputFormat(self.handle) as PixelFormat }
}
#[inline]
#[must_use]
pub fn output_format(&self) -> PixelFormat {
unsafe { ffi::cmsGetTransformOutputFormat(self.handle) as PixelFormat }
}
}
impl<F, T, L> Transform<F, T, GlobalContext, L> {
#[inline]
#[must_use]
pub fn global_adaptation_state() -> f64 {
unsafe { ffi::cmsSetAdaptationState(-1.) }
}
#[deprecated(note = "Use `ThreadContext::set_adaptation_state()`")]
pub fn set_global_adaptation_state(value: f64) {
unsafe {
ffi::cmsSetAdaptationState(value);
}
}
#[deprecated(note = "Use `ThreadContext::set_alarm_codes()`")]
pub fn set_global_alarm_codes(codes: [u16; ffi::MAXCHANNELS]) {
unsafe { ffi::cmsSetAlarmCodes(codes.as_ptr()) }
}
#[must_use]
pub fn global_alarm_codes() -> [u16; ffi::MAXCHANNELS] {
let mut tmp = [0u16; ffi::MAXCHANNELS];
unsafe {
ffi::cmsGetAlarmCodes(tmp.as_mut_ptr());
}
tmp
}
}
fn is_u8<P: 'static>() -> bool {
std::mem::size_of::<P>() == 1 && std::mem::align_of::<P>() == 1 && std::any::TypeId::of::<P>() == std::any::TypeId::of::<u8>()
}
impl<F, T, C, L> Drop for Transform<F, T, C, L> {
fn drop(&mut self) {
unsafe {
ffi::cmsDeleteTransform(self.handle);
}
}
}
impl<F, T, C, L> fmt::Debug for Transform<F, T, C, L> {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = f.debug_struct(&format!(
"Transform<{}, {}, {}, {}>",
std::any::type_name::<F>(),
std::any::type_name::<T>(),
std::any::type_name::<C>(),
std::any::type_name::<L>(),
));
s.field("input_format", &self.input_format());
s.field("output_format", &self.output_format());
s.finish()
}
}