1use colorutils_rs::{
30 jzazbz_to_rgb, jzazbz_to_rgba, rgb_to_jzazbz, rgba_to_jzazbz, TransferFunction,
31};
32
33use crate::pic_scale_error::PicScaleError;
34use crate::scaler::ScalingF32;
35use crate::support::check_image_size_overflow;
36use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
37
38#[derive(Debug, Copy, Clone)]
39pub struct JzazbzScaler {
41 pub(crate) scaler: Scaler,
42 pub(crate) transfer_function: TransferFunction,
43 pub(crate) display_luminance: f32,
44}
45
46impl JzazbzScaler {
47 pub fn new(
51 filter: ResamplingFunction,
52 display_luminance: f32,
53 transfer_function: TransferFunction,
54 ) -> Self {
55 JzazbzScaler {
56 scaler: Scaler::new(filter),
57 transfer_function,
58 display_luminance,
59 }
60 }
61
62 fn rgba_to_laba<'a>(&self, store: &ImageStore<'a, u8, 4>) -> ImageStore<'a, f32, 4> {
63 let mut source_slice = vec![f32::default(); 4 * store.width * store.height];
64 let lab_stride = store.width as u32 * 4u32 * std::mem::size_of::<f32>() as u32;
65 rgba_to_jzazbz(
66 store.buffer.as_ref(),
67 store.width as u32 * 4u32,
68 &mut source_slice,
69 lab_stride,
70 store.width as u32,
71 store.height as u32,
72 self.display_luminance,
73 self.transfer_function,
74 );
75 let new_store = ImageStore::<f32, 4> {
76 buffer: std::borrow::Cow::Owned(source_slice),
77 channels: 4,
78 width: store.width,
79 height: store.height,
80 stride: store.width * 4,
81 bit_depth: store.bit_depth,
82 };
83 new_store
84 }
85
86 fn laba_to_srgba<'a>(
87 &self,
88 store: &ImageStoreMut<'a, f32, 4>,
89 into: &mut ImageStoreMut<'a, u8, 4>,
90 ) {
91 jzazbz_to_rgba(
92 store.buffer.borrow(),
93 store.width as u32 * 4u32 * std::mem::size_of::<f32>() as u32,
94 into.buffer.borrow_mut(),
95 store.width as u32 * 4u32,
96 store.width as u32,
97 store.height as u32,
98 self.display_luminance,
99 self.transfer_function,
100 );
101 }
102}
103
104impl Scaling for JzazbzScaler {
105 fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
106 self.scaler.threading_policy = threading_policy;
107 }
108
109 fn resize_cbcr8<'a>(
110 &'a self,
111 _: &ImageStore<'a, u8, 2>,
112 _: &mut ImageStoreMut<'a, u8, 2>,
113 ) -> Result<(), PicScaleError> {
114 unimplemented!()
115 }
116
117 fn resize_rgb<'a>(
118 &'a self,
119 store: &ImageStore<'a, u8, 3>,
120 into: &mut ImageStoreMut<'a, u8, 3>,
121 ) -> Result<(), PicScaleError> {
122 let new_size = into.get_size();
123 into.validate()?;
124 store.validate()?;
125 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
126 return Err(PicScaleError::ZeroImageDimensions);
127 }
128
129 if check_image_size_overflow(store.width, store.height, store.channels) {
130 return Err(PicScaleError::SourceImageIsTooLarge);
131 }
132
133 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
134 return Err(PicScaleError::DestinationImageIsTooLarge);
135 }
136
137 if store.width == new_size.width && store.height == new_size.height {
138 store.copied_to_mut(into);
139 return Ok(());
140 }
141
142 const COMPONENTS: usize = 3;
143
144 let mut target = vec![f32::default(); store.width * store.height * COMPONENTS];
145
146 let mut lab_store =
147 ImageStoreMut::<f32, COMPONENTS>::from_slice(&mut target, store.width, store.height)?;
148 lab_store.bit_depth = into.bit_depth;
149
150 let lab_stride =
151 lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
152 rgb_to_jzazbz(
153 store.buffer.as_ref(),
154 store.width as u32 * COMPONENTS as u32,
155 lab_store.buffer.borrow_mut(),
156 lab_stride,
157 lab_store.width as u32,
158 lab_store.height as u32,
159 self.display_luminance,
160 self.transfer_function,
161 );
162
163 let new_immutable_store = ImageStore::<f32, COMPONENTS> {
164 buffer: std::borrow::Cow::Owned(target),
165 channels: COMPONENTS,
166 width: store.width,
167 height: store.height,
168 stride: store.width * COMPONENTS,
169 bit_depth: into.bit_depth,
170 };
171
172 let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
173 self.scaler
174 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
175
176 let new_lab_stride =
177 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
178
179 jzazbz_to_rgb(
180 new_store.buffer.borrow(),
181 new_lab_stride,
182 into.buffer.borrow_mut(),
183 into.width as u32 * COMPONENTS as u32,
184 new_store.width as u32,
185 new_store.height as u32,
186 self.display_luminance,
187 self.transfer_function,
188 );
189 Ok(())
190 }
191
192 fn resize_rgba<'a>(
193 &'a self,
194 store: &ImageStore<'a, u8, 4>,
195 into: &mut ImageStoreMut<'a, u8, 4>,
196 premultiply_alpha: bool,
197 ) -> Result<(), PicScaleError> {
198 let new_size = into.get_size();
199 into.validate()?;
200 store.validate()?;
201 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
202 return Err(PicScaleError::ZeroImageDimensions);
203 }
204
205 if check_image_size_overflow(store.width, store.height, store.channels) {
206 return Err(PicScaleError::SourceImageIsTooLarge);
207 }
208
209 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
210 return Err(PicScaleError::DestinationImageIsTooLarge);
211 }
212
213 if store.width == new_size.width && store.height == new_size.height {
214 store.copied_to_mut(into);
215 return Ok(());
216 }
217
218 let lab_store = self.rgba_to_laba(store);
219 let mut new_target_store = ImageStoreMut::alloc(new_size.width, new_size.height);
220
221 self.scaler
222 .resize_rgba_f32(&lab_store, &mut new_target_store, premultiply_alpha)?;
223 self.laba_to_srgba(&new_target_store, into);
224 Ok(())
225 }
226}