1use crate::colors::common_splitter::{SplitPlanInterceptor, Splitter};
30use crate::plan::Resampling;
31use crate::validation::PicScaleError;
32use crate::{ImageSize, ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ThreadingPolicy};
33use colorutils_rs::{
34 SRGB_TO_XYZ_D65, TransferFunction, XYZ_TO_SRGB_D65, lch_to_rgb, lch_with_alpha_to_rgba,
35 rgb_to_lch, rgba_to_lch_with_alpha,
36};
37use std::sync::Arc;
38
39#[derive(Debug, Copy, Clone)]
40pub struct LChScaler {
42 pub(crate) scaler: Scaler,
43}
44
45struct LchRgbSplitter {}
46
47impl Splitter<u8, f32, 3> for LchRgbSplitter {
48 fn split(
49 &self,
50 from: &ImageStore<'_, u8, 3>,
51 into: &mut ImageStoreMut<'_, f32, 3>,
52 ) -> Result<(), PicScaleError> {
53 let mut dst_buffer = into.to_colorutils_buffer_mut();
54 rgb_to_lch(
55 &from.to_colorutils_buffer(),
56 &mut dst_buffer,
57 &SRGB_TO_XYZ_D65,
58 TransferFunction::Srgb,
59 )
60 .map_err(|x| PicScaleError::Generic(x.to_string()))
61 }
62
63 fn merge(
64 &self,
65 from: &ImageStore<'_, f32, 3>,
66 into: &mut ImageStoreMut<'_, u8, 3>,
67 ) -> Result<(), PicScaleError> {
68 let mut dst_buffer = into.to_colorutils_buffer_mut();
69 lch_to_rgb(
70 &from.to_colorutils_buffer(),
71 &mut dst_buffer,
72 &XYZ_TO_SRGB_D65,
73 TransferFunction::Srgb,
74 )
75 .map_err(|x| PicScaleError::Generic(x.to_string()))
76 }
77
78 fn bit_depth(&self) -> usize {
79 8
80 }
81}
82
83struct LchRgbaSplitter {}
84
85impl Splitter<u8, f32, 4> for LchRgbaSplitter {
86 fn split(
87 &self,
88 from: &ImageStore<'_, u8, 4>,
89 into: &mut ImageStoreMut<'_, f32, 4>,
90 ) -> Result<(), PicScaleError> {
91 let mut dst_buffer = into.to_colorutils_buffer_mut();
92 rgba_to_lch_with_alpha(
93 &from.to_colorutils_buffer(),
94 &mut dst_buffer,
95 &SRGB_TO_XYZ_D65,
96 TransferFunction::Srgb,
97 )
98 .map_err(|x| PicScaleError::Generic(x.to_string()))
99 }
100
101 fn merge(
102 &self,
103 from: &ImageStore<'_, f32, 4>,
104 into: &mut ImageStoreMut<'_, u8, 4>,
105 ) -> Result<(), PicScaleError> {
106 let mut dst_buffer = into.to_colorutils_buffer_mut();
107 lch_with_alpha_to_rgba(
108 &from.to_colorutils_buffer(),
109 &mut dst_buffer,
110 &XYZ_TO_SRGB_D65,
111 TransferFunction::Srgb,
112 )
113 .map_err(|x| PicScaleError::Generic(x.to_string()))
114 }
115
116 fn bit_depth(&self) -> usize {
117 8
118 }
119}
120
121impl LChScaler {
122 pub fn new(filter: ResamplingFunction) -> Self {
123 LChScaler {
124 scaler: Scaler::new(filter),
125 }
126 }
127}
128
129impl LChScaler {
130 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
131 self.scaler.set_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(LchRgbSplitter {}),
147 inner_scratch: scratch_size,
148 }))
149 }
150
151 pub fn plan_rgba_resampling(
152 &self,
153 source_size: ImageSize,
154 target_size: ImageSize,
155 premultiply_alpha: bool,
156 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
157 let intercept =
158 self.scaler
159 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
160 let scratch_size = intercept.scratch_size();
161 Ok(Arc::new(SplitPlanInterceptor {
162 intercept,
163 splitter: Arc::new(LchRgbaSplitter {}),
164 inner_scratch: scratch_size,
165 }))
166 }
167}