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