moxcms/conversions/katana/
rgb_xyz.rs1use crate::conversions::katana::pcs_stages::KatanaMatrixStage;
30use crate::conversions::katana::{KatanaInitialStage, KatanaIntermediateStage};
31use crate::err::try_vec;
32use crate::{CmsError, ColorProfile, Layout, Matrix3f, PointeeSizeExpressible, TransformOptions};
33use num_traits::AsPrimitive;
34use std::marker::PhantomData;
35
36struct KatanaRgbLinearizationStage<T: Clone, const LAYOUT: u8, const LINEAR_CAP: usize> {
37 r_lin: Box<[f32; LINEAR_CAP]>,
38 g_lin: Box<[f32; LINEAR_CAP]>,
39 b_lin: Box<[f32; LINEAR_CAP]>,
40 linear_cap: usize,
41 bit_depth: usize,
42 _phantom: PhantomData<T>,
43}
44
45impl<
46 T: Clone + AsPrimitive<f32> + PointeeSizeExpressible,
47 const LAYOUT: u8,
48 const LINEAR_CAP: usize,
49> KatanaInitialStage<f32, T> for KatanaRgbLinearizationStage<T, LAYOUT, LINEAR_CAP>
50{
51 fn to_pcs(&self, input: &[T]) -> Result<Vec<f32>, CmsError> {
52 let src_layout = Layout::from(LAYOUT);
53 if input.len() % src_layout.channels() != 0 {
54 return Err(CmsError::LaneMultipleOfChannels);
55 }
56 let mut dst = try_vec![0.; input.len() / src_layout.channels() * 3];
57
58 let scale = if T::FINITE {
59 (self.linear_cap as f32 - 1.) / ((1 << self.bit_depth) - 1) as f32
60 } else {
61 (T::NOT_FINITE_LINEAR_TABLE_SIZE - 1) as f32
62 };
63
64 let cap_value = if T::FINITE {
65 ((1 << self.bit_depth) - 1) as f32
66 } else {
67 (T::NOT_FINITE_LINEAR_TABLE_SIZE - 1) as f32
68 };
69
70 for (src, dst) in input
71 .chunks_exact(src_layout.channels())
72 .zip(dst.chunks_exact_mut(3))
73 {
74 let j_r = src[0].as_() * scale;
75 let j_g = src[1].as_() * scale;
76 let j_b = src[2].as_() * scale;
77 dst[0] = self.r_lin[(j_r.round().min(cap_value).max(0.) as u16) as usize];
78 dst[1] = self.g_lin[(j_g.round().min(cap_value).max(0.) as u16) as usize];
79 dst[2] = self.b_lin[(j_b.round().min(cap_value).max(0.) as u16) as usize];
80 }
81 Ok(dst)
82 }
83}
84
85pub(crate) struct KatanaRgbLinearizationState<T> {
86 pub(crate) stages: Vec<Box<dyn KatanaIntermediateStage<f32> + Send + Sync>>,
87 pub(crate) initial_stage: Box<dyn KatanaInitialStage<f32, T> + Send + Sync>,
88}
89
90pub(crate) fn katana_create_rgb_lin_lut<
91 T: Copy + Default + AsPrimitive<f32> + Send + Sync + AsPrimitive<usize> + PointeeSizeExpressible,
92 const BIT_DEPTH: usize,
93 const LINEAR_CAP: usize,
94>(
95 layout: Layout,
96 source: &ColorProfile,
97 opts: TransformOptions,
98) -> Result<KatanaRgbLinearizationState<T>, CmsError>
99where
100 u32: AsPrimitive<T>,
101 f32: AsPrimitive<T>,
102{
103 let lin_r =
104 source.build_r_linearize_table::<T, LINEAR_CAP, BIT_DEPTH>(opts.allow_use_cicp_transfer)?;
105 let lin_g =
106 source.build_g_linearize_table::<T, LINEAR_CAP, BIT_DEPTH>(opts.allow_use_cicp_transfer)?;
107 let lin_b =
108 source.build_b_linearize_table::<T, LINEAR_CAP, BIT_DEPTH>(opts.allow_use_cicp_transfer)?;
109
110 let lin_stage: Box<dyn KatanaInitialStage<f32, T> + Send + Sync> = match layout {
111 Layout::Rgb => {
112 Box::new(
113 KatanaRgbLinearizationStage::<T, { Layout::Rgb as u8 }, LINEAR_CAP> {
114 r_lin: lin_r,
115 g_lin: lin_g,
116 b_lin: lin_b,
117 bit_depth: BIT_DEPTH,
118 linear_cap: LINEAR_CAP,
119 _phantom: PhantomData,
120 },
121 )
122 }
123 Layout::Rgba => {
124 Box::new(
125 KatanaRgbLinearizationStage::<T, { Layout::Rgba as u8 }, LINEAR_CAP> {
126 r_lin: lin_r,
127 g_lin: lin_g,
128 b_lin: lin_b,
129 bit_depth: BIT_DEPTH,
130 linear_cap: LINEAR_CAP,
131 _phantom: PhantomData,
132 },
133 )
134 }
135 Layout::Gray => return Err(CmsError::UnsupportedProfileConnection),
136 Layout::GrayAlpha => {
137 return Err(CmsError::UnsupportedProfileConnection);
138 }
139 _ => return Err(CmsError::UnsupportedProfileConnection),
140 };
141
142 let xyz_to_rgb = source.rgb_to_xyz_matrix();
143
144 let matrices: Vec<Box<dyn KatanaIntermediateStage<f32> + Send + Sync>> =
145 vec![Box::new(KatanaMatrixStage {
146 matrices: vec![
147 xyz_to_rgb.to_f32(),
148 Matrix3f {
149 v: [
150 [32768.0 / 65535.0, 0.0, 0.0],
151 [0.0, 32768.0 / 65535.0, 0.0],
152 [0.0, 0.0, 32768.0 / 65535.0],
153 ],
154 },
155 ],
156 })];
157
158 Ok(KatanaRgbLinearizationState {
159 stages: matrices,
160 initial_stage: lin_stage,
161 })
162}