1use colorutils_rs::{TransferFunction, oklab_to_rgb, oklab_to_rgba, rgb_to_oklab, rgba_to_oklab};
30use std::sync::Arc;
31
32use crate::colors::common_splitter::{SplitPlanInterceptor, Splitter};
33use crate::plan::Resampling;
34use crate::validation::PicScaleError;
35use crate::{ImageSize, ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ThreadingPolicy};
36
37#[derive(Debug, Copy, Clone)]
38pub struct OklabScaler {
40 pub(crate) scaler: Scaler,
41 pub(crate) transfer_function: TransferFunction,
42}
43
44impl OklabScaler {
45 pub fn new(filter: ResamplingFunction, transfer_function: TransferFunction) -> Self {
48 OklabScaler {
49 scaler: Scaler::new(filter),
50 transfer_function,
51 }
52 }
53}
54
55struct OklabRgbSplitter {
56 transfer_function: TransferFunction,
57}
58
59impl Splitter<u8, f32, 3> for OklabRgbSplitter {
60 fn split(&self, from: &ImageStore<'_, u8, 3>, into: &mut ImageStoreMut<'_, f32, 3>) {
61 let lab_stride = into.width as u32 * 3u32 * size_of::<f32>() as u32;
62
63 rgb_to_oklab(
64 from.buffer.as_ref(),
65 from.width as u32 * 3u32,
66 into.buffer.borrow_mut(),
67 lab_stride,
68 into.width as u32,
69 into.height as u32,
70 self.transfer_function,
71 );
72 }
73
74 fn merge(&self, from: &ImageStore<'_, f32, 3>, into: &mut ImageStoreMut<'_, u8, 3>) {
75 let new_lab_stride = into.width as u32 * 3 * size_of::<f32>() as u32;
76 oklab_to_rgb(
77 from.buffer.as_ref(),
78 new_lab_stride,
79 into.buffer.borrow_mut(),
80 into.width as u32 * 3,
81 into.width as u32,
82 into.height as u32,
83 self.transfer_function,
84 );
85 }
86
87 fn bit_depth(&self) -> usize {
88 8
89 }
90}
91
92struct OklabRgbaSplitter {
93 transfer_function: TransferFunction,
94}
95
96impl Splitter<u8, f32, 4> for OklabRgbaSplitter {
97 fn split(&self, from: &ImageStore<'_, u8, 4>, into: &mut ImageStoreMut<'_, f32, 4>) {
98 let lab_stride = into.width as u32 * 4u32 * size_of::<f32>() as u32;
99
100 rgba_to_oklab(
101 from.buffer.as_ref(),
102 from.width as u32 * 4u32,
103 into.buffer.borrow_mut(),
104 lab_stride,
105 into.width as u32,
106 into.height as u32,
107 self.transfer_function,
108 );
109 }
110
111 fn merge(&self, from: &ImageStore<'_, f32, 4>, into: &mut ImageStoreMut<'_, u8, 4>) {
112 let new_lab_stride = into.width as u32 * 4 * size_of::<f32>() as u32;
113 oklab_to_rgba(
114 from.buffer.as_ref(),
115 new_lab_stride,
116 into.buffer.borrow_mut(),
117 into.width as u32 * 4,
118 into.width as u32,
119 into.height as u32,
120 self.transfer_function,
121 );
122 }
123
124 fn bit_depth(&self) -> usize {
125 8
126 }
127}
128
129impl OklabScaler {
130 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
131 self.scaler.threading_policy = threading_policy;
132 *self
133 }
134
135 pub fn plan_rgb_resampling(
136 &self,
137 source_size: ImageSize,
138 target_size: ImageSize,
139 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
140 let intercept = self
141 .scaler
142 .plan_rgb_resampling_f32(source_size, target_size)?;
143 let scratch_size = intercept.scratch_size();
144 Ok(Arc::new(SplitPlanInterceptor {
145 intercept,
146 splitter: Arc::new(OklabRgbSplitter {
147 transfer_function: self.transfer_function,
148 }),
149 inner_scratch: scratch_size,
150 }))
151 }
152
153 pub fn plan_rgba_resampling(
154 &self,
155 source_size: ImageSize,
156 target_size: ImageSize,
157 premultiply_alpha: bool,
158 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
159 let intercept =
160 self.scaler
161 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
162 let scratch_size = intercept.scratch_size();
163 Ok(Arc::new(SplitPlanInterceptor {
164 intercept,
165 splitter: Arc::new(OklabRgbaSplitter {
166 transfer_function: self.transfer_function,
167 }),
168 inner_scratch: scratch_size,
169 }))
170 }
171}