1use crate::colors::common_splitter::{SplitPlanInterceptor, Splitter};
30use crate::mixed_storage::CpuRound;
31use crate::plan::Resampling;
32use crate::validation::PicScaleError;
33use crate::{ImageSize, ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ThreadingPolicy};
34use colorutils_rs::TransferFunction;
35use std::sync::Arc;
36
37#[derive(Debug, Copy, Clone)]
38pub struct LinearScaler {
45 pub(crate) scaler: Scaler,
46 pub(crate) transfer_function: TransferFunction,
47}
48
49impl LinearScaler {
50 pub fn new(filter: ResamplingFunction) -> Self {
52 LinearScaler {
53 scaler: Scaler::new(filter),
54 transfer_function: TransferFunction::Srgb,
55 }
56 }
57
58 pub fn new_with_transfer(
60 filter: ResamplingFunction,
61 transfer_function: TransferFunction,
62 ) -> Self {
63 LinearScaler {
64 scaler: Scaler::new(filter),
65 transfer_function,
66 }
67 }
68}
69
70struct Common8BitSplitter<const N: usize> {
71 linearization: Box<[f32; 256]>,
72 gamma: Box<[u8; 65536]>,
73 has_alpha: bool,
74}
75
76impl<const N: usize> Splitter<u8, f32, N> for Common8BitSplitter<N> {
77 fn split(&self, from: &ImageStore<'_, u8, N>, into: &mut ImageStoreMut<'_, f32, N>) {
78 const S: f32 = 1. / 255.;
79 if N == 4 {
80 for (src, dst) in from
81 .as_bytes()
82 .chunks_exact(4)
83 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
84 {
85 dst[0] = self.linearization[src[0] as usize];
86 dst[1] = self.linearization[src[1] as usize];
87 dst[2] = self.linearization[src[2] as usize];
88 dst[3] = src[3] as f32 * S;
89 }
90 } else if N == 2 && self.has_alpha {
91 for (src, dst) in from
92 .as_bytes()
93 .chunks_exact(2)
94 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
95 {
96 dst[0] = self.linearization[src[0] as usize];
97 dst[1] = src[1] as f32 * S;
98 }
99 } else {
100 for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
101 *dst = self.linearization[src as usize];
102 }
103 }
104 }
105
106 fn merge(&self, from: &ImageStore<'_, f32, N>, into: &mut ImageStoreMut<'_, u8, N>) {
107 if N == 4 {
108 for (src, dst) in from
109 .as_bytes()
110 .chunks_exact(4)
111 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
112 {
113 let v0 = (src[0] * 65535.).cpu_round() as u16;
114 let v1 = (src[1] * 65535.).cpu_round() as u16;
115 let v2 = (src[2] * 65535.).cpu_round() as u16;
116
117 dst[0] = self.gamma[v0 as usize];
118 dst[1] = self.gamma[v1 as usize];
119 dst[2] = self.gamma[v2 as usize];
120 dst[3] = (src[3] * 255.).cpu_round() as u8;
121 }
122 } else if N == 2 && self.has_alpha {
123 for (src, dst) in from
124 .as_bytes()
125 .chunks_exact(2)
126 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
127 {
128 let v0 = (src[0] * 65535.).cpu_round() as u16;
129
130 dst[0] = self.gamma[v0 as usize];
131 dst[1] = (src[1] * 255.).cpu_round() as u8;
132 }
133 } else {
134 for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
135 let v = (src * 65535.).cpu_round() as u16;
136 *dst = self.gamma[v as usize];
137 }
138 }
139 }
140
141 fn bit_depth(&self) -> usize {
142 8
143 }
144}
145
146struct Common16BitSplitter<const N: usize> {
147 linearization: Box<[f32; 65536]>,
148 gamma: Box<[u16; 262144]>,
149 has_alpha: bool,
150 bit_depth: usize,
151}
152
153impl<const N: usize> Splitter<u16, f32, N> for Common16BitSplitter<N> {
154 fn split(&self, from: &ImageStore<'_, u16, N>, into: &mut ImageStoreMut<'_, f32, N>) {
155 if N == 4 {
156 let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
157
158 let v_recip = 1. / max_bit_depth_value;
159
160 for (src, dst) in from
161 .as_bytes()
162 .chunks_exact(4)
163 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
164 {
165 dst[0] = self.linearization[src[0] as usize];
166 dst[1] = self.linearization[src[1] as usize];
167 dst[2] = self.linearization[src[2] as usize];
168 dst[3] = src[3] as f32 * v_recip;
169 }
170 } else if N == 2 && self.has_alpha {
171 let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
172 let v_recip = 1. / max_bit_depth_value;
173
174 for (src, dst) in from
175 .as_bytes()
176 .chunks_exact(2)
177 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
178 {
179 dst[0] = self.linearization[src[0] as usize];
180 dst[1] = src[1] as f32 * v_recip;
181 }
182 } else {
183 for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
184 *dst = self.linearization[src as usize];
185 }
186 }
187 }
188
189 fn merge(&self, from: &ImageStore<'_, f32, N>, into: &mut ImageStoreMut<'_, u16, N>) {
190 if N == 4 {
191 let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
192 for (src, dst) in from
193 .as_bytes()
194 .chunks_exact(4)
195 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
196 {
197 let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
198 let v1 = ((src[1] * 262143.).cpu_round().max(0.) as u32).min(262143);
199 let v2 = ((src[2] * 262143.).cpu_round().max(0.) as u32).min(262143);
200
201 dst[0] = self.gamma[v0 as usize];
202 dst[1] = self.gamma[v1 as usize];
203 dst[2] = self.gamma[v2 as usize];
204 dst[3] = (src[3] * max_bit_depth_value)
205 .cpu_round()
206 .min(max_bit_depth_value)
207 .max(0.) as u16;
208 }
209 } else if N == 2 && self.has_alpha {
210 let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
211 for (src, dst) in from
212 .as_bytes()
213 .chunks_exact(2)
214 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
215 {
216 let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
217
218 dst[0] = self.gamma[v0 as usize];
219 dst[1] = (src[1] * max_bit_depth_value)
220 .cpu_round()
221 .min(max_bit_depth_value)
222 .max(0.) as u16;
223 }
224 } else {
225 for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
226 let v = ((src * 262143.).cpu_round().max(0.) as u32).min(262143);
227 *dst = self.gamma[v as usize];
228 }
229 }
230 }
231
232 fn bit_depth(&self) -> usize {
233 8
234 }
235}
236
237struct Linearization {
238 linearization: Box<[f32; 256]>,
239 gamma: Box<[u8; 65536]>,
240}
241
242struct Linearization16 {
243 linearization: Box<[f32; 65536]>,
244 gamma: Box<[u16; 262144]>,
245}
246
247fn make_linearization16(
248 transfer_function: TransferFunction,
249 bit_depth: usize,
250) -> Result<Linearization16, PicScaleError> {
251 if bit_depth < 8 {
252 return Err(PicScaleError::UnsupportedBitDepth(bit_depth));
253 }
254 let mut linearizing = Box::new([0f32; 65536]);
255 let max_lin_depth = (1u32 << bit_depth) - 1;
256 let keep_max = 1u32 << bit_depth;
257 let mut gamma = Box::new([0u16; 262144]);
258
259 let rcp_bit_depth = 1. / max_lin_depth as f32;
260
261 for (i, dst) in linearizing.iter_mut().take(keep_max as usize).enumerate() {
262 *dst = transfer_function.linearize(i as f32 * rcp_bit_depth);
263 }
264
265 const RCP_LUT_CAP: f32 = 1. / 262143.;
266
267 for (i, dst) in gamma.iter_mut().enumerate() {
268 *dst = (transfer_function.gamma(i as f32 * RCP_LUT_CAP) * max_lin_depth as f32)
269 .cpu_round()
270 .min(max_lin_depth as f32) as u16;
271 }
272
273 Ok(Linearization16 {
274 linearization: linearizing,
275 gamma,
276 })
277}
278
279fn make_linearization(transfer_function: TransferFunction) -> Linearization {
280 let mut linearizing = Box::new([0f32; 256]);
281 let mut gamma = Box::new([0u8; 65536]);
282
283 const S: f32 = 1. / 255.;
284
285 for (i, dst) in linearizing.iter_mut().enumerate() {
286 *dst = transfer_function.linearize(i as f32 * S);
287 }
288
289 const RCP_LUT_CAP: f32 = 1. / 65535.;
290
291 for (i, dst) in gamma.iter_mut().enumerate() {
292 *dst = (transfer_function.gamma(i as f32 * RCP_LUT_CAP) * 255.)
293 .cpu_round()
294 .min(255.) as u8;
295 }
296
297 Linearization {
298 linearization: linearizing,
299 gamma,
300 }
301}
302
303impl LinearScaler {
304 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
305 self.scaler.threading_policy = threading_policy;
306 *self
307 }
308
309 pub fn plan_planar_resampling(
310 &self,
311 source_size: ImageSize,
312 target_size: ImageSize,
313 ) -> Result<Arc<Resampling<u8, 1>>, PicScaleError> {
314 let intercept = self
315 .scaler
316 .plan_planar_resampling_f32(source_size, target_size)?;
317 let scratch_size = intercept.scratch_size();
318 let lin = make_linearization(self.transfer_function);
319 Ok(Arc::new(SplitPlanInterceptor {
320 intercept,
321 splitter: Arc::new(Common8BitSplitter {
322 linearization: lin.linearization,
323 gamma: lin.gamma,
324 has_alpha: false,
325 }),
326 inner_scratch: scratch_size,
327 }))
328 }
329
330 pub fn plan_cbcr_resampling(
331 &self,
332 source_size: ImageSize,
333 target_size: ImageSize,
334 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
335 let intercept = self
336 .scaler
337 .plan_cbcr_resampling_f32(source_size, target_size)?;
338 let scratch_size = intercept.scratch_size();
339 let lin = make_linearization(self.transfer_function);
340 Ok(Arc::new(SplitPlanInterceptor {
341 intercept,
342 splitter: Arc::new(Common8BitSplitter {
343 linearization: lin.linearization,
344 gamma: lin.gamma,
345 has_alpha: false,
346 }),
347 inner_scratch: scratch_size,
348 }))
349 }
350
351 pub fn plan_gray_alpha_resampling(
352 &self,
353 source_size: ImageSize,
354 target_size: ImageSize,
355 premultiply_alpha: bool,
356 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
357 let intercept = self.scaler.plan_gray_alpha_resampling_f32(
358 source_size,
359 target_size,
360 premultiply_alpha,
361 )?;
362 let scratch_size = intercept.scratch_size();
363 let lin = make_linearization(self.transfer_function);
364 Ok(Arc::new(SplitPlanInterceptor {
365 intercept,
366 splitter: Arc::new(Common8BitSplitter {
367 linearization: lin.linearization,
368 gamma: lin.gamma,
369 has_alpha: true,
370 }),
371 inner_scratch: scratch_size,
372 }))
373 }
374
375 pub fn plan_rgb_resampling(
376 &self,
377 source_size: ImageSize,
378 target_size: ImageSize,
379 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
380 let intercept = self
381 .scaler
382 .plan_rgb_resampling_f32(source_size, target_size)?;
383 let scratch_size = intercept.scratch_size();
384 let lin = make_linearization(self.transfer_function);
385 Ok(Arc::new(SplitPlanInterceptor {
386 intercept,
387 splitter: Arc::new(Common8BitSplitter {
388 linearization: lin.linearization,
389 gamma: lin.gamma,
390 has_alpha: false,
391 }),
392 inner_scratch: scratch_size,
393 }))
394 }
395
396 pub fn plan_rgba_resampling(
397 &self,
398 source_size: ImageSize,
399 target_size: ImageSize,
400 premultiply_alpha: bool,
401 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
402 let intercept =
403 self.scaler
404 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
405 let scratch_size = intercept.scratch_size();
406 let lin = make_linearization(self.transfer_function);
407 Ok(Arc::new(SplitPlanInterceptor {
408 intercept,
409 splitter: Arc::new(Common8BitSplitter {
410 linearization: lin.linearization,
411 gamma: lin.gamma,
412 has_alpha: true,
413 }),
414 inner_scratch: scratch_size,
415 }))
416 }
417
418 pub fn plan_planar_resampling16(
419 &self,
420 source_size: ImageSize,
421 target_size: ImageSize,
422 bit_depth: usize,
423 ) -> Result<Arc<Resampling<u16, 1>>, PicScaleError> {
424 let intercept = self
425 .scaler
426 .plan_planar_resampling_f32(source_size, target_size)?;
427 let scratch_size = intercept.scratch_size();
428 let lin = make_linearization16(self.transfer_function, bit_depth)?;
429 Ok(Arc::new(SplitPlanInterceptor {
430 intercept,
431 splitter: Arc::new(Common16BitSplitter {
432 linearization: lin.linearization,
433 gamma: lin.gamma,
434 has_alpha: false,
435 bit_depth,
436 }),
437 inner_scratch: scratch_size,
438 }))
439 }
440
441 pub fn plan_gray_alpha_resampling16(
442 &self,
443 source_size: ImageSize,
444 target_size: ImageSize,
445 premultiply_alpha: bool,
446 bit_depth: usize,
447 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
448 let intercept = self.scaler.plan_gray_alpha_resampling_f32(
449 source_size,
450 target_size,
451 premultiply_alpha,
452 )?;
453 let scratch_size = intercept.scratch_size();
454 let lin = make_linearization16(self.transfer_function, bit_depth)?;
455 Ok(Arc::new(SplitPlanInterceptor {
456 intercept,
457 splitter: Arc::new(Common16BitSplitter {
458 linearization: lin.linearization,
459 gamma: lin.gamma,
460 has_alpha: true,
461 bit_depth,
462 }),
463 inner_scratch: scratch_size,
464 }))
465 }
466
467 pub fn plan_cbcr_resampling16(
468 &self,
469 source_size: ImageSize,
470 target_size: ImageSize,
471 bit_depth: usize,
472 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
473 let intercept = self
474 .scaler
475 .plan_cbcr_resampling_f32(source_size, target_size)?;
476 let scratch_size = intercept.scratch_size();
477 let lin = make_linearization16(self.transfer_function, bit_depth)?;
478 Ok(Arc::new(SplitPlanInterceptor {
479 intercept,
480 splitter: Arc::new(Common16BitSplitter {
481 linearization: lin.linearization,
482 gamma: lin.gamma,
483 has_alpha: false,
484 bit_depth,
485 }),
486 inner_scratch: scratch_size,
487 }))
488 }
489
490 pub fn plan_rgb_resampling16(
491 &self,
492 source_size: ImageSize,
493 target_size: ImageSize,
494 bit_depth: usize,
495 ) -> Result<Arc<Resampling<u16, 3>>, PicScaleError> {
496 let intercept = self
497 .scaler
498 .plan_rgb_resampling_f32(source_size, target_size)?;
499 let scratch_size = intercept.scratch_size();
500 let lin = make_linearization16(self.transfer_function, bit_depth)?;
501 Ok(Arc::new(SplitPlanInterceptor {
502 intercept,
503 splitter: Arc::new(Common16BitSplitter {
504 linearization: lin.linearization,
505 gamma: lin.gamma,
506 has_alpha: false,
507 bit_depth,
508 }),
509 inner_scratch: scratch_size,
510 }))
511 }
512
513 pub fn plan_rgba_resampling16(
514 &self,
515 source_size: ImageSize,
516 target_size: ImageSize,
517 premultiply_alpha: bool,
518 bit_depth: usize,
519 ) -> Result<Arc<Resampling<u16, 4>>, PicScaleError> {
520 let intercept =
521 self.scaler
522 .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
523 let scratch_size = intercept.scratch_size();
524 let lin = make_linearization16(self.transfer_function, bit_depth)?;
525 Ok(Arc::new(SplitPlanInterceptor {
526 intercept,
527 splitter: Arc::new(Common16BitSplitter {
528 linearization: lin.linearization,
529 gamma: lin.gamma,
530 has_alpha: true,
531 bit_depth,
532 }),
533 inner_scratch: scratch_size,
534 }))
535 }
536}