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(
61 &self,
62 from: &ImageStore<'_, u8, 3>,
63 into: &mut ImageStoreMut<'_, f32, 3>,
64 ) -> Result<(), PicScaleError> {
65 let mut dst_buffer = into.to_colorutils_buffer_mut();
66
67 rgb_to_oklab(
68 &from.to_colorutils_buffer(),
69 &mut dst_buffer,
70 self.transfer_function,
71 )
72 .map_err(|x| PicScaleError::Generic(x.to_string()))
73 }
74
75 fn merge(
76 &self,
77 from: &ImageStore<'_, f32, 3>,
78 into: &mut ImageStoreMut<'_, u8, 3>,
79 ) -> Result<(), PicScaleError> {
80 let mut dst_buffer = into.to_colorutils_buffer_mut();
81 oklab_to_rgb(
82 &from.to_colorutils_buffer(),
83 &mut dst_buffer,
84 self.transfer_function,
85 )
86 .map_err(|x| PicScaleError::Generic(x.to_string()))
87 }
88
89 fn bit_depth(&self) -> usize {
90 8
91 }
92}
93
94struct OklabRgbaSplitter {
95 transfer_function: TransferFunction,
96}
97
98impl Splitter<u8, f32, 4> for OklabRgbaSplitter {
99 fn split(
100 &self,
101 from: &ImageStore<'_, u8, 4>,
102 into: &mut ImageStoreMut<'_, f32, 4>,
103 ) -> Result<(), PicScaleError> {
104 let mut dst_buffer = into.to_colorutils_buffer_mut();
105
106 rgba_to_oklab(
107 &from.to_colorutils_buffer(),
108 &mut dst_buffer,
109 self.transfer_function,
110 )
111 .map_err(|x| PicScaleError::Generic(x.to_string()))
112 }
113
114 fn merge(
115 &self,
116 from: &ImageStore<'_, f32, 4>,
117 into: &mut ImageStoreMut<'_, u8, 4>,
118 ) -> Result<(), PicScaleError> {
119 let mut dst_buffer = into.to_colorutils_buffer_mut();
120 oklab_to_rgba(
121 &from.to_colorutils_buffer(),
122 &mut dst_buffer,
123 self.transfer_function,
124 )
125 .map_err(|x| PicScaleError::Generic(x.to_string()))
126 }
127
128 fn bit_depth(&self) -> usize {
129 8
130 }
131}
132
133impl OklabScaler {
134 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
135 self.scaler.threading_policy = threading_policy;
136 *self
137 }
138
139 pub fn plan_rgb_resampling(
140 &self,
141 source_size: ImageSize,
142 target_size: ImageSize,
143 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
144 let intercept = self
145 .scaler
146 .plan_rgb_resampling_f32(source_size, target_size)?;
147 let scratch_size = intercept.scratch_size();
148 Ok(Arc::new(SplitPlanInterceptor {
149 intercept,
150 splitter: Arc::new(OklabRgbSplitter {
151 transfer_function: self.transfer_function,
152 }),
153 inner_scratch: scratch_size,
154 }))
155 }
156
157 pub fn plan_rgba_resampling(
158 &self,
159 source_size: ImageSize,
160 target_size: ImageSize,
161 premultiply_alpha: bool,
162 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
163 let intercept =
164 self.scaler
165 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
166 let scratch_size = intercept.scratch_size();
167 Ok(Arc::new(SplitPlanInterceptor {
168 intercept,
169 splitter: Arc::new(OklabRgbaSplitter {
170 transfer_function: self.transfer_function,
171 }),
172 inner_scratch: scratch_size,
173 }))
174 }
175}