1use crate::mlaf::{fmla, mlaf};
30use crate::transform::PointeeSizeExpressible;
31use crate::*;
32use num_traits::AsPrimitive;
33use pxfm::*;
34
35#[inline]
36fn srgb_to_linear(gamma: f64) -> f64 {
38 if gamma < 0f64 {
39 0f64
40 } else if gamma < 12.92f64 * 0.0030412825601275209f64 {
41 gamma * (1f64 / 12.92f64)
42 } else if gamma < 1.0f64 {
43 f_pow(
44 (gamma + 0.0550107189475866f64) / 1.0550107189475866f64,
45 2.4f64,
46 )
47 } else {
48 1.0f64
49 }
50}
51
52#[inline]
53#[cfg(feature = "extended_range")]
54fn srgb_to_linearf_extended(gamma: f32) -> f32 {
56 if gamma < 12.92 * 0.0030412825601275209 {
57 gamma * (1. / 12.92f32)
58 } else {
59 dirty_powf((gamma + 0.0550107189475866) / 1.0550107189475866, 2.4)
60 }
61}
62
63#[inline]
64fn srgb_from_linear(linear: f64) -> f64 {
66 if linear < 0.0f64 {
67 0.0f64
68 } else if linear < 0.0030412825601275209f64 {
69 linear * 12.92f64
70 } else if linear < 1.0f64 {
71 fmla(
72 1.0550107189475866f64,
73 f_pow(linear, 1.0f64 / 2.4f64),
74 -0.0550107189475866f64,
75 )
76 } else {
77 1.0f64
78 }
79}
80
81#[cfg(feature = "extended_range")]
83pub(crate) fn srgb_from_linear_extended(linear: f32) -> f32 {
84 if linear < 0.0030412825601275209f32 {
85 linear * 12.92f32
86 } else {
87 fmla(
88 1.0550107189475866f32,
89 dirty_powf(linear, 1.0f32 / 2.4f32),
90 -0.0550107189475866f32,
91 )
92 }
93}
94
95#[inline]
96fn rec709_to_linear(gamma: f64) -> f64 {
98 if gamma < 0.0f64 {
99 0.0f64
100 } else if gamma < 4.5f64 * 0.018053968510807f64 {
101 gamma * (1f64 / 4.5f64)
102 } else if gamma < 1.0f64 {
103 f_pow(
104 (gamma + 0.09929682680944f64) / 1.09929682680944f64,
105 1.0f64 / 0.45f64,
106 )
107 } else {
108 1.0f64
109 }
110}
111
112#[inline]
113#[cfg(feature = "extended_range")]
114fn rec709_to_linearf_extended(gamma: f32) -> f32 {
116 if gamma < 4.5 * 0.018053968510807 {
117 gamma * (1. / 4.5)
118 } else {
119 f_powf((gamma + 0.09929682680944) / 1.09929682680944, 1.0 / 0.45)
120 }
121}
122
123#[inline]
124fn rec709_from_linear(linear: f64) -> f64 {
126 if linear < 0.0f64 {
127 0.0f64
128 } else if linear < 0.018053968510807f64 {
129 linear * 4.5f64
130 } else if linear < 1.0f64 {
131 fmla(
132 1.09929682680944f64,
133 f_pow(linear, 0.45f64),
134 -0.09929682680944f64,
135 )
136 } else {
137 1.0f64
138 }
139}
140
141#[cfg(feature = "extended_range")]
143fn rec709_from_linearf_extended(linear: f32) -> f32 {
144 if linear < 0.018053968510807 {
145 linear * 4.5
146 } else {
147 fmla(
148 1.09929682680944,
149 dirty_powf(linear, 0.45),
150 -0.09929682680944,
151 )
152 }
153}
154
155pub(crate) fn smpte428_to_linear(gamma: f64) -> f64 {
157 const SCALE: f64 = 1. / 0.91655527974030934f64;
158 f_pow(gamma.max(0.).min(1f64), 2.6f64) * SCALE
159}
160
161#[cfg(feature = "extended_range")]
162pub(crate) fn smpte428_to_linearf_extended(gamma: f32) -> f32 {
164 const SCALE: f32 = 1. / 0.91655527974030934;
165 dirty_powf(gamma.max(0.), 2.6) * SCALE
166}
167
168fn smpte428_from_linear(linear: f64) -> f64 {
170 const POWER_VALUE: f64 = 1.0f64 / 2.6f64;
171 f_pow(0.91655527974030934f64 * linear.max(0.), POWER_VALUE)
172}
173
174#[cfg(feature = "extended_range")]
175fn smpte428_from_linearf(linear: f32) -> f32 {
177 const POWER_VALUE: f32 = 1.0 / 2.6;
178 dirty_powf(0.91655527974030934 * linear.max(0.), POWER_VALUE)
179}
180
181pub(crate) fn smpte240_to_linear(gamma: f64) -> f64 {
183 if gamma < 0.0 {
184 0.0
185 } else if gamma < 4.0 * 0.022821585529445 {
186 gamma / 4.0
187 } else if gamma < 1.0 {
188 f_pow((gamma + 0.111572195921731) / 1.111572195921731, 1.0 / 0.45)
189 } else {
190 1.0
191 }
192}
193
194#[cfg(feature = "extended_range")]
195pub(crate) fn smpte240_to_linearf_extended(gamma: f32) -> f32 {
197 if gamma < 4.0 * 0.022821585529445 {
198 gamma / 4.0
199 } else {
200 dirty_powf((gamma + 0.111572195921731) / 1.111572195921731, 1.0 / 0.45)
201 }
202}
203
204fn smpte240_from_linear(linear: f64) -> f64 {
206 if linear < 0.0 {
207 0.0
208 } else if linear < 0.022821585529445 {
209 linear * 4.0
210 } else if linear < 1.0 {
211 fmla(1.111572195921731, f_pow(linear, 0.45), -0.111572195921731)
212 } else {
213 1.0
214 }
215}
216
217#[cfg(feature = "extended_range")]
218fn smpte240_from_linearf_extended(linear: f32) -> f32 {
220 if linear < 0.022821585529445 {
221 linear * 4.0
222 } else {
223 fmla(1.111572195921731, f_powf(linear, 0.45), -0.111572195921731)
224 }
225}
226
227#[inline]
228fn log100_from_linear(linear: f64) -> f64 {
230 if linear <= 0.01f64 {
231 0.
232 } else {
233 1. + f_log10(linear.min(1.)) / 2.0
234 }
235}
236
237#[cfg(feature = "extended_range")]
238fn log100_from_linearf(linear: f32) -> f32 {
240 if linear <= 0.01 {
241 0.
242 } else {
243 1. + f_log10f(linear.min(1.)) / 2.0
244 }
245}
246
247pub(crate) fn log100_to_linear(gamma: f64) -> f64 {
249 const MID_INTERVAL: f64 = 0.01 / 2.;
251 if gamma <= 0. {
252 MID_INTERVAL
253 } else {
254 f_exp10(2. * (gamma.min(1.) - 1.))
255 }
256}
257
258#[cfg(feature = "extended_range")]
259pub(crate) fn log100_to_linearf(gamma: f32) -> f32 {
261 const MID_INTERVAL: f32 = 0.01 / 2.;
263 if gamma <= 0. {
264 MID_INTERVAL
265 } else {
266 f_exp10f(2. * (gamma.min(1.) - 1.))
267 }
268}
269
270#[inline]
271pub(crate) fn log100_sqrt10_to_linear(gamma: f64) -> f64 {
273 const MID_INTERVAL: f64 = 0.00316227766 / 2.;
275 if gamma <= 0. {
276 MID_INTERVAL
277 } else {
278 f_exp10(2.5 * (gamma.min(1.) - 1.))
279 }
280}
281
282#[cfg(feature = "extended_range")]
283pub(crate) fn log100_sqrt10_to_linearf(gamma: f32) -> f32 {
285 const MID_INTERVAL: f32 = 0.00316227766 / 2.;
287 if gamma <= 0. {
288 MID_INTERVAL
289 } else {
290 f_exp10f(2.5 * (gamma.min(1.) - 1.))
291 }
292}
293
294fn log100_sqrt10_from_linear(linear: f64) -> f64 {
296 if linear <= 0.00316227766 {
297 0.0
298 } else {
299 1.0 + f_log10(linear.min(1.)) / 2.5
300 }
301}
302
303#[cfg(feature = "extended_range")]
304fn log100_sqrt10_from_linearf(linear: f32) -> f32 {
306 if linear <= 0.00316227766 {
307 0.0
308 } else {
309 1.0 + f_log10f(linear.min(1.)) / 2.5
310 }
311}
312
313fn bt1361_from_linear(linear: f64) -> f64 {
315 if linear < -0.25 {
316 -0.25
317 } else if linear < 0.0 {
318 fmla(
319 -0.27482420670236,
320 f_pow(-4.0 * linear, 0.45),
321 0.02482420670236,
322 )
323 } else if linear < 0.018053968510807 {
324 linear * 4.5
325 } else if linear < 1.0 {
326 fmla(1.09929682680944, f_pow(linear, 0.45), -0.09929682680944)
327 } else {
328 1.0
329 }
330}
331
332#[cfg(feature = "extended_range")]
333fn bt1361_from_linearf(linear: f32) -> f32 {
335 if linear < -0.25 {
336 -0.25
337 } else if linear < 0.0 {
338 fmla(
339 -0.27482420670236,
340 dirty_powf(-4.0 * linear, 0.45),
341 0.02482420670236,
342 )
343 } else if linear < 0.018053968510807 {
344 linear * 4.5
345 } else if linear < 1.0 {
346 fmla(
347 1.09929682680944,
348 dirty_powf(linear, 0.45),
349 -0.09929682680944,
350 )
351 } else {
352 1.0
353 }
354}
355
356pub(crate) fn bt1361_to_linear(gamma: f64) -> f64 {
358 if gamma < -0.25f64 {
359 -0.25f64
360 } else if gamma < 0.0f64 {
361 f_pow(
362 (gamma - 0.02482420670236f64) / -0.27482420670236f64,
363 1.0f64 / 0.45f64,
364 ) / -4.0f64
365 } else if gamma < 4.5 * 0.018053968510807 {
366 gamma / 4.5
367 } else if gamma < 1.0 {
368 f_pow((gamma + 0.09929682680944) / 1.09929682680944, 1.0 / 0.45)
369 } else {
370 1.0f64
371 }
372}
373
374#[cfg(feature = "extended_range")]
375fn bt1361_to_linearf(gamma: f32) -> f32 {
377 if gamma < -0.25 {
378 -0.25
379 } else if gamma < 0.0 {
380 dirty_powf((gamma - 0.02482420670236) / -0.27482420670236, 1.0 / 0.45) / -4.0
381 } else if gamma < 4.5 * 0.018053968510807 {
382 gamma / 4.5
383 } else if gamma < 1.0 {
384 dirty_powf((gamma + 0.09929682680944) / 1.09929682680944, 1.0 / 0.45)
385 } else {
386 1.0
387 }
388}
389
390#[inline(always)]
391fn pure_gamma_function(x: f64, gamma: f64) -> f64 {
393 if x <= 0f64 {
394 0f64
395 } else if x >= 1f64 {
396 1f64
397 } else {
398 f_pow(x, gamma)
399 }
400}
401
402#[cfg(feature = "extended_range")]
403#[inline(always)]
404fn pure_gamma_function_f(x: f32, gamma: f32) -> f32 {
406 if x <= 0. { 0. } else { dirty_powf(x, gamma) }
407}
408
409pub(crate) fn iec61966_to_linear(gamma: f64) -> f64 {
410 if gamma < -4.5f64 * 0.018053968510807f64 {
411 f_pow(
412 (-gamma + 0.09929682680944f64) / -1.09929682680944f64,
413 1.0 / 0.45,
414 )
415 } else if gamma < 4.5f64 * 0.018053968510807f64 {
416 gamma / 4.5
417 } else {
418 f_pow(
419 (gamma + 0.09929682680944f64) / 1.09929682680944f64,
420 1.0 / 0.45,
421 )
422 }
423}
424
425#[cfg(feature = "extended_range")]
426fn iec61966_to_linearf(gamma: f32) -> f32 {
427 if gamma < -4.5 * 0.018053968510807 {
428 dirty_powf((-gamma + 0.09929682680944) / -1.09929682680944, 1.0 / 0.45)
429 } else if gamma < 4.5 * 0.018053968510807 {
430 gamma / 4.5
431 } else {
432 dirty_powf((gamma + 0.09929682680944) / 1.09929682680944, 1.0 / 0.45)
433 }
434}
435
436fn iec61966_from_linear(v: f64) -> f64 {
437 if v < -0.018053968510807f64 {
438 fmla(-1.09929682680944f64, f_pow(-v, 0.45), 0.09929682680944f64)
439 } else if v < 0.018053968510807f64 {
440 v * 4.5f64
441 } else {
442 fmla(1.09929682680944f64, f_pow(v, 0.45), -0.09929682680944f64)
443 }
444}
445
446#[cfg(feature = "extended_range")]
447fn iec61966_from_linearf(v: f32) -> f32 {
448 if v < -0.018053968510807 {
449 fmla(-1.09929682680944, dirty_powf(-v, 0.45), 0.09929682680944)
450 } else if v < 0.018053968510807 {
451 v * 4.5
452 } else {
453 fmla(1.09929682680944, dirty_powf(v, 0.45), -0.09929682680944)
454 }
455}
456
457#[inline]
458fn gamma2p2_from_linear(linear: f64) -> f64 {
460 pure_gamma_function(linear, 1f64 / 2.2f64)
461}
462
463#[cfg(feature = "extended_range")]
464#[inline]
465fn gamma2p2_from_linear_f(linear: f32) -> f32 {
467 pure_gamma_function_f(linear, 1. / 2.2)
468}
469
470#[inline]
471fn gamma2p2_to_linear(gamma: f64) -> f64 {
473 pure_gamma_function(gamma, 2.2f64)
474}
475
476#[cfg(feature = "extended_range")]
477#[inline]
478fn gamma2p2_to_linear_f(gamma: f32) -> f32 {
480 pure_gamma_function_f(gamma, 2.2)
481}
482
483#[inline]
484fn gamma2p8_from_linear(linear: f64) -> f64 {
486 pure_gamma_function(linear, 1f64 / 2.8f64)
487}
488
489#[cfg(feature = "extended_range")]
490#[inline]
491fn gamma2p8_from_linear_f(linear: f32) -> f32 {
493 pure_gamma_function_f(linear, 1. / 2.8)
494}
495
496#[inline]
497fn gamma2p8_to_linear(gamma: f64) -> f64 {
499 pure_gamma_function(gamma, 2.8f64)
500}
501
502#[cfg(feature = "extended_range")]
503#[inline]
504fn gamma2p8_to_linear_f(gamma: f32) -> f32 {
506 pure_gamma_function_f(gamma, 2.8)
507}
508
509#[inline]
510pub(crate) fn pq_to_linear(gamma: f64) -> f64 {
512 if gamma > 0.0 {
513 let pow_gamma = f_pow(gamma, 1.0 / 78.84375);
514 let num = (pow_gamma - 0.8359375).max(0.);
515 let den = mlaf(18.8515625, -18.6875, pow_gamma).max(f64::MIN);
516 f_pow(num / den, 1.0 / 0.1593017578125)
517 } else {
518 0.0
519 }
520}
521
522pub(crate) fn pq_to_linearf(gamma: f32) -> f32 {
524 if gamma > 0.0 {
525 let pow_gamma = f_powf(gamma, 1.0 / 78.84375);
526 let num = (pow_gamma - 0.8359375).max(0.);
527 let den = mlaf(18.8515625, -18.6875, pow_gamma).max(f32::MIN);
528 f_powf(num / den, 1.0 / 0.1593017578125)
529 } else {
530 0.0
531 }
532}
533
534fn pq_from_linear(linear: f64) -> f64 {
536 if linear > 0.0 {
537 let linear = linear.clamp(0., 1.);
538 let pow_linear = f_pow(linear, 0.1593017578125);
539 let num = fmla(0.1640625, pow_linear, -0.1640625);
540 let den = mlaf(1.0, 18.6875, pow_linear);
541 f_pow(1.0 + num / den, 78.84375)
542 } else {
543 0.0
544 }
545}
546
547#[inline]
548pub(crate) fn pq_from_linearf(linear: f32) -> f32 {
550 if linear > 0.0 {
551 let linear = linear.max(0.);
552 let pow_linear = f_powf(linear, 0.1593017578125);
553 let num = fmla(0.1640625, pow_linear, -0.1640625);
554 let den = mlaf(1.0, 18.6875, pow_linear);
555 f_powf(1.0 + num / den, 78.84375)
556 } else {
557 0.0
558 }
559}
560
561#[inline]
562pub(crate) fn hlg_to_linear(gamma: f64) -> f64 {
564 if gamma < 0.0 {
565 return 0.0;
566 }
567 if gamma <= 0.5 {
568 f_pow((gamma * gamma) * (1.0 / 3.0), 1.2)
569 } else {
570 f_pow(
571 (f_exp((gamma - 0.55991073) / 0.17883277) + 0.28466892) / 12.0,
572 1.2,
573 )
574 }
575}
576
577#[cfg(feature = "extended_range")]
578pub(crate) fn hlg_to_linearf(gamma: f32) -> f32 {
580 if gamma < 0.0 {
581 return 0.0;
582 }
583 if gamma <= 0.5 {
584 f_powf((gamma * gamma) * (1.0 / 3.0), 1.2)
585 } else {
586 f_powf(
587 (f_expf((gamma - 0.55991073) / 0.17883277) + 0.28466892) / 12.0,
588 1.2,
589 )
590 }
591}
592
593fn hlg_from_linear(linear: f64) -> f64 {
595 let mut linear = linear.clamp(0., 1.);
597 linear = f_pow(linear, 1.0 / 1.2);
599 if linear < 0.0 {
600 0.0
601 } else if linear <= (1.0 / 12.0) {
602 (3.0 * linear).sqrt()
603 } else {
604 fmla(
605 0.17883277,
606 f_log(fmla(12.0, linear, -0.28466892)),
607 0.55991073,
608 )
609 }
610}
611
612#[cfg(feature = "extended_range")]
613fn hlg_from_linearf(linear: f32) -> f32 {
615 let mut linear = linear.max(0.);
617 linear = f_powf(linear, 1.0 / 1.2);
619 if linear < 0.0 {
620 0.0
621 } else if linear <= (1.0 / 12.0) {
622 (3.0 * linear).sqrt()
623 } else {
624 0.17883277 * f_logf(12.0 * linear - 0.28466892) + 0.55991073
625 }
626}
627
628#[inline]
629fn trc_linear(v: f64) -> f64 {
630 v.min(1.).max(0.)
631}
632
633impl TransferCharacteristics {
634 pub fn linearize(self, v: f64) -> f64 {
635 match self {
636 TransferCharacteristics::Reserved => 0f64,
637 TransferCharacteristics::Bt709
638 | TransferCharacteristics::Bt601
639 | TransferCharacteristics::Bt202010bit
640 | TransferCharacteristics::Bt202012bit => rec709_to_linear(v),
641 TransferCharacteristics::Unspecified => 0f64,
642 TransferCharacteristics::Bt470M => gamma2p2_to_linear(v),
643 TransferCharacteristics::Bt470Bg => gamma2p8_to_linear(v),
644 TransferCharacteristics::Smpte240 => smpte240_to_linear(v),
645 TransferCharacteristics::Linear => trc_linear(v),
646 TransferCharacteristics::Log100 => log100_to_linear(v),
647 TransferCharacteristics::Log100sqrt10 => log100_sqrt10_to_linear(v),
648 TransferCharacteristics::Iec61966 => iec61966_to_linear(v),
649 TransferCharacteristics::Bt1361 => bt1361_to_linear(v),
650 TransferCharacteristics::Srgb => srgb_to_linear(v),
651 TransferCharacteristics::Smpte2084 => pq_to_linear(v),
652 TransferCharacteristics::Smpte428 => smpte428_to_linear(v),
653 TransferCharacteristics::Hlg => hlg_to_linear(v),
654 }
655 }
656
657 pub fn gamma(self, v: f64) -> f64 {
658 match self {
659 TransferCharacteristics::Reserved => 0f64,
660 TransferCharacteristics::Bt709
661 | TransferCharacteristics::Bt601
662 | TransferCharacteristics::Bt202010bit
663 | TransferCharacteristics::Bt202012bit => rec709_from_linear(v),
664 TransferCharacteristics::Unspecified => 0f64,
665 TransferCharacteristics::Bt470M => gamma2p2_from_linear(v),
666 TransferCharacteristics::Bt470Bg => gamma2p8_from_linear(v),
667 TransferCharacteristics::Smpte240 => smpte240_from_linear(v),
668 TransferCharacteristics::Linear => trc_linear(v),
669 TransferCharacteristics::Log100 => log100_from_linear(v),
670 TransferCharacteristics::Log100sqrt10 => log100_sqrt10_from_linear(v),
671 TransferCharacteristics::Iec61966 => iec61966_from_linear(v),
672 TransferCharacteristics::Bt1361 => bt1361_from_linear(v),
673 TransferCharacteristics::Srgb => srgb_from_linear(v),
674 TransferCharacteristics::Smpte2084 => pq_from_linear(v),
675 TransferCharacteristics::Smpte428 => smpte428_from_linear(v),
676 TransferCharacteristics::Hlg => hlg_from_linear(v),
677 }
678 }
679
680 #[cfg(feature = "extended_range")]
681 pub(crate) fn extended_gamma_tristimulus(self) -> fn(Rgb<f32>) -> Rgb<f32> {
682 match self {
683 TransferCharacteristics::Reserved => |x| Rgb::new(x.r, x.g, x.b),
684 TransferCharacteristics::Bt709
685 | TransferCharacteristics::Bt601
686 | TransferCharacteristics::Bt202010bit
687 | TransferCharacteristics::Bt202012bit => |x| {
688 Rgb::new(
689 rec709_from_linearf_extended(x.r),
690 rec709_from_linearf_extended(x.g),
691 rec709_from_linearf_extended(x.b),
692 )
693 },
694 TransferCharacteristics::Unspecified => |x| Rgb::new(x.r, x.g, x.b),
695 TransferCharacteristics::Bt470M => |x| {
696 Rgb::new(
697 gamma2p2_from_linear_f(x.r),
698 gamma2p2_from_linear_f(x.g),
699 gamma2p2_from_linear_f(x.b),
700 )
701 },
702 TransferCharacteristics::Bt470Bg => |x| {
703 Rgb::new(
704 gamma2p8_from_linear_f(x.r),
705 gamma2p8_from_linear_f(x.g),
706 gamma2p8_from_linear_f(x.b),
707 )
708 },
709 TransferCharacteristics::Smpte240 => |x| {
710 Rgb::new(
711 smpte240_from_linearf_extended(x.r),
712 smpte240_from_linearf_extended(x.g),
713 smpte240_from_linearf_extended(x.b),
714 )
715 },
716 TransferCharacteristics::Linear => |x| Rgb::new(x.r, x.g, x.b),
717 TransferCharacteristics::Log100 => |x| {
718 Rgb::new(
719 log100_from_linearf(x.r),
720 log100_from_linearf(x.g),
721 log100_from_linearf(x.b),
722 )
723 },
724 TransferCharacteristics::Log100sqrt10 => |x| {
725 Rgb::new(
726 log100_sqrt10_from_linearf(x.r),
727 log100_sqrt10_from_linearf(x.g),
728 log100_sqrt10_from_linearf(x.b),
729 )
730 },
731 TransferCharacteristics::Iec61966 => |x| {
732 Rgb::new(
733 iec61966_from_linearf(x.r),
734 iec61966_from_linearf(x.g),
735 iec61966_from_linearf(x.b),
736 )
737 },
738 TransferCharacteristics::Bt1361 => |x| {
739 Rgb::new(
740 bt1361_from_linearf(x.r),
741 bt1361_from_linearf(x.g),
742 bt1361_from_linearf(x.b),
743 )
744 },
745 TransferCharacteristics::Srgb => |x| {
746 Rgb::new(
747 srgb_from_linear_extended(x.r),
748 srgb_from_linear_extended(x.g),
749 srgb_from_linear_extended(x.b),
750 )
751 },
752 TransferCharacteristics::Smpte2084 => |x| {
753 Rgb::new(
754 pq_from_linearf(x.r),
755 pq_from_linearf(x.g),
756 pq_from_linearf(x.b),
757 )
758 },
759 TransferCharacteristics::Smpte428 => |x| {
760 Rgb::new(
761 smpte428_from_linearf(x.r),
762 smpte428_from_linearf(x.g),
763 smpte428_from_linearf(x.b),
764 )
765 },
766 TransferCharacteristics::Hlg => |x| {
767 Rgb::new(
768 hlg_from_linearf(x.r),
769 hlg_from_linearf(x.g),
770 hlg_from_linearf(x.b),
771 )
772 },
773 }
774 }
775
776 #[cfg(feature = "extended_range")]
777 pub(crate) fn extended_gamma_single(self) -> fn(f32) -> f32 {
778 match self {
779 TransferCharacteristics::Reserved => |x| x,
780 TransferCharacteristics::Bt709
781 | TransferCharacteristics::Bt601
782 | TransferCharacteristics::Bt202010bit
783 | TransferCharacteristics::Bt202012bit => |x| rec709_from_linearf_extended(x),
784 TransferCharacteristics::Unspecified => |x| x,
785 TransferCharacteristics::Bt470M => |x| gamma2p2_from_linear_f(x),
786 TransferCharacteristics::Bt470Bg => |x| gamma2p8_from_linear_f(x),
787 TransferCharacteristics::Smpte240 => |x| smpte240_from_linearf_extended(x),
788 TransferCharacteristics::Linear => |x| x,
789 TransferCharacteristics::Log100 => |x| log100_from_linearf(x),
790 TransferCharacteristics::Log100sqrt10 => |x| log100_sqrt10_from_linearf(x),
791 TransferCharacteristics::Iec61966 => |x| iec61966_from_linearf(x),
792 TransferCharacteristics::Bt1361 => |x| bt1361_from_linearf(x),
793 TransferCharacteristics::Srgb => |x| srgb_from_linear_extended(x),
794 TransferCharacteristics::Smpte2084 => |x| pq_from_linearf(x),
795 TransferCharacteristics::Smpte428 => |x| smpte428_from_linearf(x),
796 TransferCharacteristics::Hlg => |x| hlg_from_linearf(x),
797 }
798 }
799
800 #[cfg(feature = "extended_range")]
801 pub(crate) fn extended_linear_tristimulus(self) -> fn(Rgb<f32>) -> Rgb<f32> {
802 match self {
803 TransferCharacteristics::Reserved => |x| Rgb::new(x.r, x.g, x.b),
804 TransferCharacteristics::Bt709
805 | TransferCharacteristics::Bt601
806 | TransferCharacteristics::Bt202010bit
807 | TransferCharacteristics::Bt202012bit => |x| {
808 Rgb::new(
809 rec709_to_linearf_extended(x.r),
810 rec709_to_linearf_extended(x.g),
811 rec709_to_linearf_extended(x.b),
812 )
813 },
814 TransferCharacteristics::Unspecified => |x| Rgb::new(x.r, x.g, x.b),
815 TransferCharacteristics::Bt470M => |x| {
816 Rgb::new(
817 gamma2p2_to_linear_f(x.r),
818 gamma2p2_to_linear_f(x.g),
819 gamma2p2_to_linear_f(x.b),
820 )
821 },
822 TransferCharacteristics::Bt470Bg => |x| {
823 Rgb::new(
824 gamma2p8_to_linear_f(x.r),
825 gamma2p8_to_linear_f(x.g),
826 gamma2p8_to_linear_f(x.b),
827 )
828 },
829 TransferCharacteristics::Smpte240 => |x| {
830 Rgb::new(
831 smpte240_to_linearf_extended(x.r),
832 smpte240_to_linearf_extended(x.g),
833 smpte240_to_linearf_extended(x.b),
834 )
835 },
836 TransferCharacteristics::Linear => |x| Rgb::new(x.r, x.g, x.b),
837 TransferCharacteristics::Log100 => |x| {
838 Rgb::new(
839 log100_to_linearf(x.r),
840 log100_to_linearf(x.g),
841 log100_to_linearf(x.b),
842 )
843 },
844 TransferCharacteristics::Log100sqrt10 => |x| {
845 Rgb::new(
846 log100_sqrt10_to_linearf(x.r),
847 log100_sqrt10_to_linearf(x.g),
848 log100_sqrt10_to_linearf(x.b),
849 )
850 },
851 TransferCharacteristics::Iec61966 => |x| {
852 Rgb::new(
853 iec61966_to_linearf(x.r),
854 iec61966_to_linearf(x.g),
855 iec61966_to_linearf(x.b),
856 )
857 },
858 TransferCharacteristics::Bt1361 => |x| {
859 Rgb::new(
860 bt1361_to_linearf(x.r),
861 bt1361_to_linearf(x.g),
862 bt1361_to_linearf(x.b),
863 )
864 },
865 TransferCharacteristics::Srgb => |x| {
866 Rgb::new(
867 srgb_to_linearf_extended(x.r),
868 srgb_to_linearf_extended(x.g),
869 srgb_to_linearf_extended(x.b),
870 )
871 },
872 TransferCharacteristics::Smpte2084 => {
873 |x| Rgb::new(pq_to_linearf(x.r), pq_to_linearf(x.g), pq_to_linearf(x.b))
874 }
875 TransferCharacteristics::Smpte428 => |x| {
876 Rgb::new(
877 smpte428_to_linearf_extended(x.r),
878 smpte428_to_linearf_extended(x.g),
879 smpte428_to_linearf_extended(x.b),
880 )
881 },
882 TransferCharacteristics::Hlg => |x| {
883 Rgb::new(
884 hlg_to_linearf(x.r),
885 hlg_to_linearf(x.g),
886 hlg_to_linearf(x.b),
887 )
888 },
889 }
890 }
891
892 #[cfg(feature = "extended_range")]
893 pub(crate) fn extended_linear_single(self) -> fn(f32) -> f32 {
894 match self {
895 TransferCharacteristics::Reserved => |x| x,
896 TransferCharacteristics::Bt709
897 | TransferCharacteristics::Bt601
898 | TransferCharacteristics::Bt202010bit
899 | TransferCharacteristics::Bt202012bit => |x| rec709_to_linearf_extended(x),
900 TransferCharacteristics::Unspecified => |x| x,
901 TransferCharacteristics::Bt470M => |x| gamma2p2_to_linear_f(x),
902 TransferCharacteristics::Bt470Bg => |x| gamma2p8_to_linear_f(x),
903 TransferCharacteristics::Smpte240 => |x| smpte240_to_linearf_extended(x),
904 TransferCharacteristics::Linear => |x| x,
905 TransferCharacteristics::Log100 => |x| log100_to_linearf(x),
906 TransferCharacteristics::Log100sqrt10 => |x| log100_sqrt10_to_linearf(x),
907 TransferCharacteristics::Iec61966 => |x| iec61966_to_linearf(x),
908 TransferCharacteristics::Bt1361 => |x| bt1361_to_linearf(x),
909 TransferCharacteristics::Srgb => |x| srgb_to_linearf_extended(x),
910 TransferCharacteristics::Smpte2084 => |x| pq_to_linearf(x),
911 TransferCharacteristics::Smpte428 => |x| smpte428_to_linearf_extended(x),
912 TransferCharacteristics::Hlg => |x| hlg_to_linearf(x),
913 }
914 }
915
916 pub(crate) fn make_linear_table<
917 T: PointeeSizeExpressible,
918 const N: usize,
919 const BIT_DEPTH: usize,
920 >(
921 &self,
922 ) -> Box<[f32; N]> {
923 let mut gamma_table = Box::new([0f32; N]);
924 let max_value = if T::FINITE {
925 (1 << BIT_DEPTH) - 1
926 } else {
927 T::NOT_FINITE_LINEAR_TABLE_SIZE - 1
928 };
929 let cap_values = if T::FINITE {
930 (1u32 << BIT_DEPTH) as usize
931 } else {
932 T::NOT_FINITE_LINEAR_TABLE_SIZE
933 };
934 assert!(cap_values <= N, "Invalid lut table construction");
935 let scale_value = 1f64 / max_value as f64;
936 for (i, g) in gamma_table.iter_mut().enumerate().take(cap_values) {
937 *g = self.linearize(i as f64 * scale_value) as f32;
938 }
939 gamma_table
940 }
941
942 pub(crate) fn make_gamma_table<
943 T: Default + Copy + 'static + PointeeSizeExpressible,
944 const BUCKET: usize,
945 const N: usize,
946 >(
947 &self,
948 bit_depth: usize,
949 ) -> Box<[T; BUCKET]>
950 where
951 f32: AsPrimitive<T>,
952 {
953 let mut table = Box::new([T::default(); BUCKET]);
954 let max_range = 1f64 / (N - 1) as f64;
955 let max_value = ((1 << bit_depth) - 1) as f64;
956 if T::FINITE {
957 for (v, output) in table.iter_mut().take(N).enumerate() {
958 *output = ((self.gamma(v as f64 * max_range) * max_value) as f32)
959 .round()
960 .as_();
961 }
962 } else {
963 for (v, output) in table.iter_mut().take(N).enumerate() {
964 *output = (self.gamma(v as f64 * max_range) as f32).as_();
965 }
966 }
967 table
968 }
969}
970
971#[cfg(test)]
972mod tests {
973 use super::*;
974
975 #[test]
976 fn srgb_test() {
977 let srgb_0 = srgb_to_linear(0.5);
978 let srgb_1 = srgb_from_linear(srgb_0);
979 assert!((0.5 - srgb_1).abs() < 1e-9f64);
980 }
981
982 #[test]
983 fn log100_sqrt10_test() {
984 let srgb_0 = log100_sqrt10_to_linear(0.5);
985 let srgb_1 = log100_sqrt10_from_linear(srgb_0);
986 assert_eq!(0.5, srgb_1);
987 }
988
989 #[test]
990 fn log100_test() {
991 let srgb_0 = log100_to_linear(0.5);
992 let srgb_1 = log100_from_linear(srgb_0);
993 assert_eq!(0.5, srgb_1);
994 }
995
996 #[test]
997 fn iec61966_test() {
998 let srgb_0 = iec61966_to_linear(0.5);
999 let srgb_1 = iec61966_from_linear(srgb_0);
1000 assert!((0.5 - srgb_1).abs() < 1e-9f64);
1001 }
1002
1003 #[test]
1004 fn smpte240_test() {
1005 let srgb_0 = smpte240_to_linear(0.5);
1006 let srgb_1 = smpte240_from_linear(srgb_0);
1007 assert!((0.5 - srgb_1).abs() < 1e-9f64);
1008 }
1009
1010 #[test]
1011 fn smpte428_test() {
1012 let srgb_0 = smpte428_to_linear(0.5);
1013 let srgb_1 = smpte428_from_linear(srgb_0);
1014 assert!((0.5 - srgb_1).abs() < 1e-9f64);
1015 }
1016
1017 #[test]
1018 fn rec709_test() {
1019 let srgb_0 = rec709_to_linear(0.5);
1020 let srgb_1 = rec709_from_linear(srgb_0);
1021 assert!((0.5 - srgb_1).abs() < 1e-9f64);
1022 }
1023
1024 #[cfg(feature = "extended_range")]
1025 #[test]
1026 fn rec709f_test() {
1027 let srgb_0 = rec709_to_linearf_extended(0.5);
1028 let srgb_1 = rec709_from_linearf_extended(srgb_0);
1029 assert!((0.5 - srgb_1).abs() < 1e-5f32);
1030 }
1031
1032 #[cfg(feature = "extended_range")]
1033 #[test]
1034 fn srgbf_test() {
1035 let srgb_0 = srgb_to_linearf_extended(0.5);
1036 let srgb_1 = srgb_from_linear_extended(srgb_0);
1037 assert!((0.5 - srgb_1).abs() < 1e-5f32);
1038 }
1039
1040 #[test]
1041 fn hlg_test() {
1042 let z0 = hlg_to_linear(0.5);
1043 let z1 = hlg_from_linear(z0);
1044 assert!((0.5 - z1).abs() < 1e-5f64);
1045 }
1046
1047 #[test]
1048 fn pq_test() {
1049 let z0 = pq_to_linear(0.5);
1050 let z1 = pq_from_linear(z0);
1051 assert!((0.5 - z1).abs() < 1e-5f64);
1052 }
1053
1054 #[test]
1055 fn pqf_test() {
1056 let z0 = pq_to_linearf(0.5);
1057 let z1 = pq_from_linearf(z0);
1058 assert!((0.5 - z1).abs() < 1e-5f32);
1059 }
1060
1061 #[test]
1062 fn iec_test() {
1063 let z0 = iec61966_to_linear(0.5);
1064 let z1 = iec61966_from_linear(z0);
1065 assert!((0.5 - z1).abs() < 1e-5f64);
1066 }
1067
1068 #[test]
1069 fn bt1361_test() {
1070 let z0 = bt1361_to_linear(0.5);
1071 let z1 = bt1361_from_linear(z0);
1072 assert!((0.5 - z1).abs() < 1e-5f64);
1073 }
1074}