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