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