1
+ const c1 = 3424 / 4096 ;
2
+ const c2 = 2413 / 128 ;
3
+ const c3 = 2392 / 128 ;
4
+ const m1 = 2610 / 16384 ;
5
+ const m2 = 2523 / 32 ;
6
+ const im1 = 16384 / 2610 ;
7
+ const im2 = 32 / 2523 ;
8
+
9
+ // The matrix below includes the 4% crosstalk components
10
+ // and is from the procedure in the Dolby "What is ICtCp" paper"
11
+ const XYZtoICtCp_LMS_M = [
12
+ [ 0.3592832590121217 , 0.6976051147779502 , - 0.0358915932320290 ] ,
13
+ [ - 0.1920808463704993 , 1.1004767970374321 , 0.0753748658519118 ] ,
14
+ [ 0.0070797844607479 , 0.0748396662186362 , 0.8433265453898765 ] ,
15
+ ] ;
16
+
17
+ // This matrix includes the Ebner LMS coefficients,
18
+ // the rotation, and the scaling to [-0.5,0.5] range
19
+ // rational terms are from Fröhlich p.97
20
+ // and ITU-R BT.2124-0 pp.2-3
21
+ const ICtCp_LMStoIPT_M = [
22
+ [ 2048 / 4096 , 2048 / 4096 , 0 ] ,
23
+ [ 6610 / 4096 , - 13613 / 4096 , 7003 / 4096 ] ,
24
+ [ 17933 / 4096 , - 17390 / 4096 , - 543 / 4096 ] ,
25
+ ] ;
26
+
27
+ // inverted matrices, calculated from the above
28
+ const IPTtoICtCp_LMS_M = [
29
+ [ 0.9999999999999998 , 0.0086090370379328 , 0.1110296250030260 ] ,
30
+ [ 0.9999999999999998 , - 0.0086090370379328 , - 0.1110296250030259 ] ,
31
+ [ 0.9999999999999998 , 0.5600313357106791 , - 0.3206271749873188 ] ,
32
+ ] ;
33
+
34
+ const ICtCp_LMStoXYZ_M = [
35
+ [ 2.0701522183894223 , - 1.3263473389671563 , 0.2066510476294053 ] ,
36
+ [ 0.3647385209748072 , 0.6805660249472273 , - 0.0453045459220347 ] ,
37
+ [ - 0.0497472075358123 , - 0.0492609666966131 , 1.1880659249923042 ] ,
38
+ ] ;
39
+
40
+ function XYZ_to_ICtCp ( XYZ ) {
41
+ // convert an array of absolute, D65 XYZ to the ICtCp form of LMS
42
+
43
+ let LMS = multiplyMatrices ( XYZtoICtCp_LMS_M , XYZ ) ;
44
+ return LMStoICtCp ( LMS ) ;
45
+ }
46
+
47
+ function LMStoICtCp ( LMS ) {
48
+ // apply the PQ EOTF
49
+ // we can't ever be dividing by zero because of the "1 +" in the denominator
50
+ let PQLMS = LMS . map ( function ( val ) {
51
+ let num = c1 + ( c2 * ( ( val / 10000 ) ** m1 ) ) ;
52
+ let denom = 1 + ( c3 * ( ( val / 10000 ) ** m1 ) ) ;
53
+
54
+ return ( num / denom ) ** m2 ;
55
+ } ) ;
56
+
57
+ // LMS to IPT, with rotation for Y'C'bC'r compatibility
58
+ return multiplyMatrices ( LMStoIPT_M , PQLMS ) ;
59
+ }
60
+
61
+ function ICtCp_to_XYZ ( ICtCp ) {
62
+ // convert ICtCp to an array of absolute, D65 XYZ
63
+
64
+ let LMS = ICtCptoLMS ( ICtCp ) ;
65
+ return multiplyMatrices ( ICtCp_LMStoXYZ_M , LMS ) ;
66
+ }
67
+
68
+ function ICtCptoLMS ( ICtCp ) {
69
+ let PQLMS = multiplyMatrices ( IPTtoICtCp_LMS_M , ICtCp ) ;
70
+
71
+ // Undo PQ encoding, From BT.2124-0 Annex 2 Conversion 3
72
+ let LMS = PQLMS . map ( function ( val ) {
73
+ let num = Math . max ( ( val ** im2 ) - c1 , 0 ) ;
74
+ let denom = ( c2 - ( c3 * ( val ** im2 ) ) ) ;
75
+ return 10000 * ( ( num / denom ) ** im1 ) ;
76
+ } ) ;
77
+
78
+ return LMS ;
79
+ }
0 commit comments