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(&self, from: &ImageStore<'_, u8, 3>, into: &mut ImageStoreMut<'_, f32, 3>) {
49 let lab_stride = into.width as u32 * 3u32 * size_of::<f32>() as u32;
50
51 rgb_to_lch(
52 from.buffer.as_ref(),
53 from.width as u32 * 3u32,
54 into.buffer.borrow_mut(),
55 lab_stride,
56 into.width as u32,
57 into.height as u32,
58 &SRGB_TO_XYZ_D65,
59 TransferFunction::Srgb,
60 );
61 }
62
63 fn merge(&self, from: &ImageStore<'_, f32, 3>, into: &mut ImageStoreMut<'_, u8, 3>) {
64 let new_lab_stride = into.width as u32 * 3 * size_of::<f32>() as u32;
65 lch_to_rgb(
66 from.buffer.as_ref(),
67 new_lab_stride,
68 into.buffer.borrow_mut(),
69 into.width as u32 * 3,
70 into.width as u32,
71 into.height as u32,
72 &XYZ_TO_SRGB_D65,
73 TransferFunction::Srgb,
74 );
75 }
76
77 fn bit_depth(&self) -> usize {
78 8
79 }
80}
81
82struct LchRgbaSplitter {}
83
84impl Splitter<u8, f32, 4> for LchRgbaSplitter {
85 fn split(&self, from: &ImageStore<'_, u8, 4>, into: &mut ImageStoreMut<'_, f32, 4>) {
86 let lab_stride = into.width as u32 * 4u32 * size_of::<f32>() as u32;
87
88 rgba_to_lch_with_alpha(
89 from.buffer.as_ref(),
90 from.width as u32 * 4u32,
91 into.buffer.borrow_mut(),
92 lab_stride,
93 into.width as u32,
94 into.height as u32,
95 &SRGB_TO_XYZ_D65,
96 TransferFunction::Srgb,
97 );
98 }
99
100 fn merge(&self, from: &ImageStore<'_, f32, 4>, into: &mut ImageStoreMut<'_, u8, 4>) {
101 let new_lab_stride = into.width as u32 * 4 * size_of::<f32>() as u32;
102 lch_with_alpha_to_rgba(
103 from.buffer.as_ref(),
104 new_lab_stride,
105 into.buffer.borrow_mut(),
106 into.width as u32 * 4,
107 into.width as u32,
108 into.height as u32,
109 &XYZ_TO_SRGB_D65,
110 TransferFunction::Srgb,
111 );
112 }
113
114 fn bit_depth(&self) -> usize {
115 8
116 }
117}
118
119impl LChScaler {
120 pub fn new(filter: ResamplingFunction) -> Self {
121 LChScaler {
122 scaler: Scaler::new(filter),
123 }
124 }
125}
126
127impl LChScaler {
128 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
129 self.scaler.set_threading_policy(threading_policy);
130 *self
131 }
132
133 pub fn plan_rgb_resampling(
134 &self,
135 source_size: ImageSize,
136 target_size: ImageSize,
137 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
138 let intercept = self
139 .scaler
140 .plan_rgb_resampling_f32(source_size, target_size)?;
141 let scratch_size = intercept.scratch_size();
142 Ok(Arc::new(SplitPlanInterceptor {
143 intercept,
144 splitter: Arc::new(LchRgbSplitter {}),
145 inner_scratch: scratch_size,
146 }))
147 }
148
149 pub fn plan_rgba_resampling(
150 &self,
151 source_size: ImageSize,
152 target_size: ImageSize,
153 premultiply_alpha: bool,
154 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
155 let intercept =
156 self.scaler
157 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
158 let scratch_size = intercept.scratch_size();
159 Ok(Arc::new(SplitPlanInterceptor {
160 intercept,
161 splitter: Arc::new(LchRgbaSplitter {}),
162 inner_scratch: scratch_size,
163 }))
164 }
165}