1use crate::mixed_storage::CpuRound;
31use crate::pic_scale_error::{PicScaleError, try_vec};
32use crate::scaler::{Scaling, ScalingF32};
33use crate::support::check_image_size_overflow;
34use crate::{
35 ImageStore, ImageStoreMut, ImageStoreScaling, ResamplingFunction, Scaler, ScalingOptions,
36 ScalingU16, ThreadingPolicy,
37};
38use colorutils_rs::TransferFunction;
39
40#[derive(Debug, Copy, Clone)]
41pub struct LinearScaler {
48 pub(crate) scaler: Scaler,
49 pub(crate) transfer_function: TransferFunction,
50}
51
52impl LinearScaler {
53 pub fn new(filter: ResamplingFunction) -> Self {
55 LinearScaler {
56 scaler: Scaler::new(filter),
57 transfer_function: TransferFunction::Srgb,
58 }
59 }
60
61 pub fn new_with_transfer(
63 filter: ResamplingFunction,
64 transfer_function: TransferFunction,
65 ) -> Self {
66 LinearScaler {
67 scaler: Scaler::new(filter),
68 transfer_function,
69 }
70 }
71}
72
73struct Linearization {
74 linearization: Box<[f32; 256]>,
75 gamma: Box<[u8; 65536]>,
76}
77
78struct Linearization16 {
79 linearization: Box<[f32; 65536]>,
80 gamma: Box<[u16; 262144]>,
81}
82
83fn make_linearization16(
84 transfer_function: TransferFunction,
85 bit_depth: usize,
86) -> Result<Linearization16, PicScaleError> {
87 if bit_depth < 8 {
88 return Err(PicScaleError::UnsupportedBitDepth(bit_depth));
89 }
90 let mut linearizing = Box::new([0f32; 65536]);
91 let max_lin_depth = (1u32 << bit_depth) - 1;
92 let keep_max = 1u32 << bit_depth;
93 let mut gamma = Box::new([0u16; 262144]);
94
95 for (i, dst) in linearizing.iter_mut().take(keep_max as usize).enumerate() {
96 *dst = transfer_function.linearize(i as f32 / max_lin_depth as f32);
97 }
98
99 for (i, dst) in gamma.iter_mut().enumerate() {
100 *dst = (transfer_function.gamma(i as f32 / 262143.) * max_lin_depth as f32)
101 .cpu_round()
102 .min(max_lin_depth as f32) as u16;
103 }
104
105 Ok(Linearization16 {
106 linearization: linearizing,
107 gamma,
108 })
109}
110
111fn make_linearization(transfer_function: TransferFunction) -> Linearization {
112 let mut linearizing = Box::new([0f32; 256]);
113 let mut gamma = Box::new([0u8; 65536]);
114
115 for (i, dst) in linearizing.iter_mut().enumerate() {
116 *dst = transfer_function.linearize(i as f32 / 255.);
117 }
118
119 for (i, dst) in gamma.iter_mut().enumerate() {
120 *dst = (transfer_function.gamma(i as f32 / 65535.) * 255.)
121 .cpu_round()
122 .min(255.) as u8;
123 }
124
125 Linearization {
126 linearization: linearizing,
127 gamma,
128 }
129}
130
131fn resize_typical8<'a, const CN: usize>(
132 resampling_function: ResamplingFunction,
133 transfer_function: TransferFunction,
134 threading_policy: ThreadingPolicy,
135 store: &ImageStore<'a, u8, CN>,
136 into: &mut ImageStoreMut<'a, u8, CN>,
137) -> Result<(), PicScaleError>
138where
139 ImageStore<'a, f32, CN>: ImageStoreScaling<'a, f32, CN>,
140{
141 let new_size = into.get_size();
142 into.validate()?;
143 store.validate()?;
144 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
145 return Err(PicScaleError::ZeroImageDimensions);
146 }
147
148 if check_image_size_overflow(store.width, store.height, store.channels) {
149 return Err(PicScaleError::SourceImageIsTooLarge);
150 }
151
152 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
153 return Err(PicScaleError::DestinationImageIsTooLarge);
154 }
155
156 if store.width == new_size.width && store.height == new_size.height {
157 store.copied_to_mut(into);
158 return Ok(());
159 }
160
161 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
162
163 let mut linear_store =
164 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
165
166 let linearization = make_linearization(transfer_function);
167
168 for (&src, dst) in store
169 .as_bytes()
170 .iter()
171 .zip(linear_store.buffer.borrow_mut())
172 {
173 *dst = linearization.linearization[src as usize];
174 }
175
176 let new_immutable_store = ImageStore::<f32, CN> {
177 buffer: std::borrow::Cow::Owned(target_vertical),
178 channels: CN,
179 width: store.width,
180 height: store.height,
181 stride: store.width * CN,
182 bit_depth: 12,
183 };
184
185 let mut new_store =
186 ImageStoreMut::<f32, CN>::try_alloc_with_depth(into.width, into.height, 12)?;
187
188 new_immutable_store.scale(
189 &mut new_store,
190 ScalingOptions {
191 resampling_function,
192 threading_policy,
193 ..Default::default()
194 },
195 )?;
196
197 for (&src, dst) in new_store.as_bytes().iter().zip(into.buffer.borrow_mut()) {
198 let v = (src * 65535.).cpu_round().min(65535.).max(0.) as u16;
199 *dst = linearization.gamma[v as usize];
200 }
201
202 Ok(())
203}
204
205fn resize_typical16<'a, const CN: usize>(
206 resampling_function: ResamplingFunction,
207 transfer_function: TransferFunction,
208 threading_policy: ThreadingPolicy,
209 store: &ImageStore<'a, u16, CN>,
210 into: &mut ImageStoreMut<'a, u16, CN>,
211) -> Result<(), PicScaleError>
212where
213 ImageStore<'a, f32, CN>: ImageStoreScaling<'a, f32, CN>,
214{
215 let new_size = into.get_size();
216 into.validate()?;
217 store.validate()?;
218 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
219 return Err(PicScaleError::ZeroImageDimensions);
220 }
221
222 if check_image_size_overflow(store.width, store.height, store.channels) {
223 return Err(PicScaleError::SourceImageIsTooLarge);
224 }
225
226 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
227 return Err(PicScaleError::DestinationImageIsTooLarge);
228 }
229
230 if store.width == new_size.width && store.height == new_size.height {
231 store.copied_to_mut(into);
232 return Ok(());
233 }
234
235 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
236
237 let mut linear_store =
238 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
239
240 let linearization = make_linearization16(transfer_function, into.bit_depth)?;
241
242 for (&src, dst) in store
243 .as_bytes()
244 .iter()
245 .zip(linear_store.buffer.borrow_mut())
246 {
247 *dst = linearization.linearization[src as usize];
248 }
249
250 let new_immutable_store = ImageStore::<f32, CN> {
251 buffer: std::borrow::Cow::Owned(target_vertical),
252 channels: CN,
253 width: store.width,
254 height: store.height,
255 stride: store.width * CN,
256 bit_depth: 16,
257 };
258
259 let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
260
261 new_immutable_store.scale(
262 &mut new_store,
263 ScalingOptions {
264 resampling_function,
265 threading_policy,
266 ..Default::default()
267 },
268 )?;
269
270 for (&src, dst) in new_store.as_bytes().iter().zip(into.buffer.borrow_mut()) {
271 let v = ((src * 262143.).cpu_round().max(0.) as u32).min(262143);
272 *dst = linearization.gamma[v as usize];
273 }
274
275 Ok(())
276}
277
278impl Scaling for LinearScaler {
279 fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
280 self.scaler.threading_policy = threading_policy;
281 }
282
283 fn resize_plane<'a>(
284 &'a self,
285 store: &ImageStore<'a, u8, 1>,
286 into: &mut ImageStoreMut<'a, u8, 1>,
287 ) -> Result<(), PicScaleError> {
288 resize_typical8(
289 self.scaler.function,
290 self.transfer_function,
291 self.scaler.threading_policy,
292 store,
293 into,
294 )
295 }
296
297 fn resize_cbcr8<'a>(
298 &'a self,
299 store: &ImageStore<'a, u8, 2>,
300 into: &mut ImageStoreMut<'a, u8, 2>,
301 ) -> Result<(), PicScaleError> {
302 resize_typical8(
303 self.scaler.function,
304 self.transfer_function,
305 self.scaler.threading_policy,
306 store,
307 into,
308 )
309 }
310
311 fn resize_gray_alpha<'a>(
312 &'a self,
313 store: &ImageStore<'a, u8, 2>,
314 into: &mut ImageStoreMut<'a, u8, 2>,
315 premultiply_alpha: bool,
316 ) -> Result<(), PicScaleError> {
317 let new_size = into.get_size();
318 into.validate()?;
319 store.validate()?;
320 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
321 return Err(PicScaleError::ZeroImageDimensions);
322 }
323
324 if check_image_size_overflow(store.width, store.height, store.channels) {
325 return Err(PicScaleError::SourceImageIsTooLarge);
326 }
327
328 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
329 return Err(PicScaleError::DestinationImageIsTooLarge);
330 }
331
332 if store.width == new_size.width && store.height == new_size.height {
333 store.copied_to_mut(into);
334 return Ok(());
335 }
336
337 const CN: usize = 2;
338
339 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
340
341 let mut linear_store =
342 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
343
344 let linearization = make_linearization(self.transfer_function);
345
346 for (src, dst) in store
347 .as_bytes()
348 .chunks_exact(2)
349 .zip(linear_store.buffer.borrow_mut().chunks_exact_mut(2))
350 {
351 dst[0] = linearization.linearization[src[0] as usize];
352 dst[1] = src[1] as f32 / 255.;
353 }
354
355 let new_immutable_store = ImageStore::<f32, CN> {
356 buffer: std::borrow::Cow::Owned(target_vertical),
357 channels: CN,
358 width: store.width,
359 height: store.height,
360 stride: store.width * CN,
361 bit_depth: 12,
362 };
363
364 let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
365
366 self.scaler.resize_gray_alpha_f32(
367 &new_immutable_store,
368 &mut new_store,
369 premultiply_alpha,
370 )?;
371
372 for (src, dst) in new_store
373 .as_bytes()
374 .chunks_exact(2)
375 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
376 {
377 let v0 = (src[0] * 65535.).cpu_round().min(65535.).max(0.) as u16;
378
379 dst[0] = linearization.gamma[v0 as usize];
380 dst[1] = (src[1] * 255.).cpu_round().min(255.).max(0.) as u8;
381 }
382
383 Ok(())
384 }
385
386 fn resize_rgb<'a>(
387 &'a self,
388 store: &ImageStore<'a, u8, 3>,
389 into: &mut ImageStoreMut<'a, u8, 3>,
390 ) -> Result<(), PicScaleError> {
391 resize_typical8(
392 self.scaler.function,
393 self.transfer_function,
394 self.scaler.threading_policy,
395 store,
396 into,
397 )
398 }
399
400 fn resize_rgba<'a>(
401 &'a self,
402 store: &ImageStore<'a, u8, 4>,
403 into: &mut ImageStoreMut<'a, u8, 4>,
404 premultiply_alpha: bool,
405 ) -> Result<(), PicScaleError> {
406 let new_size = into.get_size();
407 into.validate()?;
408 store.validate()?;
409 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
410 return Err(PicScaleError::ZeroImageDimensions);
411 }
412
413 if check_image_size_overflow(store.width, store.height, store.channels) {
414 return Err(PicScaleError::SourceImageIsTooLarge);
415 }
416
417 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
418 return Err(PicScaleError::DestinationImageIsTooLarge);
419 }
420
421 if store.width == new_size.width && store.height == new_size.height {
422 store.copied_to_mut(into);
423 return Ok(());
424 }
425
426 const CN: usize = 4;
427
428 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
429
430 let mut linear_store =
431 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
432
433 let linearization = make_linearization(self.transfer_function);
434
435 for (src, dst) in store
436 .as_bytes()
437 .chunks_exact(4)
438 .zip(linear_store.buffer.borrow_mut().chunks_exact_mut(4))
439 {
440 dst[0] = linearization.linearization[src[0] as usize];
441 dst[1] = linearization.linearization[src[1] as usize];
442 dst[2] = linearization.linearization[src[2] as usize];
443 dst[3] = src[3] as f32 / 255.;
444 }
445
446 let new_immutable_store = ImageStore::<f32, CN> {
447 buffer: std::borrow::Cow::Owned(target_vertical),
448 channels: CN,
449 width: store.width,
450 height: store.height,
451 stride: store.width * CN,
452 bit_depth: 12,
453 };
454
455 let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
456
457 self.scaler
458 .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
459
460 for (src, dst) in new_store
461 .as_bytes()
462 .chunks_exact(4)
463 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
464 {
465 let v0 = (src[0] * 65535.).cpu_round().min(65535.).max(0.) as u16;
466 let v1 = (src[1] * 65535.).cpu_round().min(65535.).max(0.) as u16;
467 let v2 = (src[2] * 65535.).cpu_round().min(65535.).max(0.) as u16;
468
469 dst[0] = linearization.gamma[v0 as usize];
470 dst[1] = linearization.gamma[v1 as usize];
471 dst[2] = linearization.gamma[v2 as usize];
472 dst[3] = (src[3] * 255.).cpu_round().min(255.).max(0.) as u8;
473 }
474
475 Ok(())
476 }
477}
478
479impl ScalingU16 for LinearScaler {
480 fn resize_plane_u16<'a>(
481 &'a self,
482 store: &ImageStore<'a, u16, 1>,
483 into: &mut ImageStoreMut<'a, u16, 1>,
484 ) -> Result<(), PicScaleError> {
485 resize_typical16(
486 self.scaler.function,
487 self.transfer_function,
488 self.scaler.threading_policy,
489 store,
490 into,
491 )
492 }
493
494 fn resize_cbcr_u16<'a>(
495 &'a self,
496 store: &ImageStore<'a, u16, 2>,
497 into: &mut ImageStoreMut<'a, u16, 2>,
498 ) -> Result<(), PicScaleError> {
499 resize_typical16(
500 self.scaler.function,
501 self.transfer_function,
502 self.scaler.threading_policy,
503 store,
504 into,
505 )
506 }
507
508 fn resize_gray_alpha16<'a>(
509 &'a self,
510 store: &ImageStore<'a, u16, 2>,
511 into: &mut ImageStoreMut<'a, u16, 2>,
512 premultiply_alpha: bool,
513 ) -> Result<(), PicScaleError> {
514 let new_size = into.get_size();
515 into.validate()?;
516 store.validate()?;
517 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
518 return Err(PicScaleError::ZeroImageDimensions);
519 }
520
521 if check_image_size_overflow(store.width, store.height, store.channels) {
522 return Err(PicScaleError::SourceImageIsTooLarge);
523 }
524
525 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
526 return Err(PicScaleError::DestinationImageIsTooLarge);
527 }
528
529 if store.width == new_size.width && store.height == new_size.height {
530 store.copied_to_mut(into);
531 return Ok(());
532 }
533
534 const CN: usize = 2;
535
536 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
537
538 let mut linear_store =
539 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
540
541 let linearization = make_linearization16(self.transfer_function, into.bit_depth)?;
542
543 let max_bit_depth_value = ((1u32 << into.bit_depth) - 1) as f32;
544
545 let v_recip = 1. / max_bit_depth_value;
546
547 for (src, dst) in store
548 .as_bytes()
549 .chunks_exact(2)
550 .zip(linear_store.buffer.borrow_mut().chunks_exact_mut(2))
551 {
552 dst[0] = linearization.linearization[src[0] as usize];
553 dst[1] = src[1] as f32 * v_recip;
554 }
555
556 let new_immutable_store = ImageStore::<f32, CN> {
557 buffer: std::borrow::Cow::Owned(target_vertical),
558 channels: CN,
559 width: store.width,
560 height: store.height,
561 stride: store.width * CN,
562 bit_depth: 16,
563 };
564
565 let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
566
567 self.scaler.resize_gray_alpha_f32(
568 &new_immutable_store,
569 &mut new_store,
570 premultiply_alpha,
571 )?;
572
573 for (src, dst) in new_store
574 .as_bytes()
575 .chunks_exact(2)
576 .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
577 {
578 let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
579
580 dst[0] = linearization.gamma[v0 as usize];
581 dst[1] = (src[1] * max_bit_depth_value)
582 .cpu_round()
583 .min(max_bit_depth_value)
584 .max(0.) as u16;
585 }
586
587 Ok(())
588 }
589
590 fn resize_rgb_u16<'a>(
591 &'a self,
592 store: &ImageStore<'a, u16, 3>,
593 into: &mut ImageStoreMut<'a, u16, 3>,
594 ) -> Result<(), PicScaleError> {
595 resize_typical16(
596 self.scaler.function,
597 self.transfer_function,
598 self.scaler.threading_policy,
599 store,
600 into,
601 )
602 }
603
604 fn resize_rgba_u16<'a>(
605 &'a self,
606 store: &ImageStore<'a, u16, 4>,
607 into: &mut ImageStoreMut<'a, u16, 4>,
608 premultiply_alpha: bool,
609 ) -> Result<(), PicScaleError> {
610 let new_size = into.get_size();
611 into.validate()?;
612 store.validate()?;
613 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
614 return Err(PicScaleError::ZeroImageDimensions);
615 }
616
617 if check_image_size_overflow(store.width, store.height, store.channels) {
618 return Err(PicScaleError::SourceImageIsTooLarge);
619 }
620
621 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
622 return Err(PicScaleError::DestinationImageIsTooLarge);
623 }
624
625 if store.width == new_size.width && store.height == new_size.height {
626 store.copied_to_mut(into);
627 return Ok(());
628 }
629
630 const CN: usize = 4;
631
632 let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
633
634 let mut linear_store =
635 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
636
637 let linearization = make_linearization16(self.transfer_function, into.bit_depth)?;
638
639 let max_bit_depth_value = ((1u32 << into.bit_depth) - 1) as f32;
640
641 let v_recip = 1. / max_bit_depth_value;
642
643 for (src, dst) in store
644 .as_bytes()
645 .chunks_exact(4)
646 .zip(linear_store.buffer.borrow_mut().chunks_exact_mut(4))
647 {
648 dst[0] = linearization.linearization[src[0] as usize];
649 dst[1] = linearization.linearization[src[1] as usize];
650 dst[2] = linearization.linearization[src[2] as usize];
651 dst[3] = src[3] as f32 * v_recip;
652 }
653
654 let new_immutable_store = ImageStore::<f32, CN> {
655 buffer: std::borrow::Cow::Owned(target_vertical),
656 channels: CN,
657 width: store.width,
658 height: store.height,
659 stride: store.width * CN,
660 bit_depth: 16,
661 };
662
663 let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
664
665 self.scaler
666 .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
667
668 for (src, dst) in new_store
669 .as_bytes()
670 .chunks_exact(4)
671 .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
672 {
673 let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
674 let v1 = ((src[1] * 262143.).cpu_round().max(0.) as u32).min(262143);
675 let v2 = ((src[2] * 262143.).cpu_round().max(0.) as u32).min(262143);
676
677 dst[0] = linearization.gamma[v0 as usize];
678 dst[1] = linearization.gamma[v1 as usize];
679 dst[2] = linearization.gamma[v2 as usize];
680 dst[3] = (src[3] * max_bit_depth_value)
681 .cpu_round()
682 .min(max_bit_depth_value)
683 .max(0.) as u16;
684 }
685
686 Ok(())
687 }
688}