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