1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
include Types
let pi = Float.pi
let m =
[|
[| 3.2409699419045214; -1.5373831775700935; -0.49861076029300328 |];
[| -0.96924363628087983; 1.8759675015077207; 0.041555057407175613 |];
[| 0.055630079696993609; -0.20397695888897657; 1.0569715142428786 |];
|]
let m_inv =
[|
[| 0.41239079926595948; 0.35758433938387796; 0.18048078840183429 |];
[| 0.21263900587151036; 0.71516867876775593; 0.072192315360733715 |];
[| 0.019330818715591851; 0.11919477979462599; 0.95053215224966058 |];
|]
let ref_u = 0.19783000664283681
let ref_v = 0.468319994938791
let kappa = 903.2962962962963
let epsilon = 0.0088564516790356308
let mod_float f1 f2 =
let r = mod_float f1 f2 in
if r < 0. then r +. 360. else r
let round f = if abs_float f < 0.5 then 0 else int_of_float (f +. copysign 0.5 f)
let dot_product a b =
let len = Array.length a in
let rec dot_product_aux i acc =
if i >= len then acc else dot_product_aux (i + 1) (acc +. (a.(i) *. b.(i)))
in
dot_product_aux 0 0.0
let from_linear c =
if c <= 0.0031308 then 12.92 *. c else (1.055 *. (c ** (1.0 /. 2.4))) -. 0.055
let to_linear c =
let a = 0.055 in
if c > 0.04045 then ((c +. a) /. (1.0 +. a)) ** 2.4 else c /. 12.92
let intersect_line_line x1 y1 x2 y2 = (y1 -. y2) /. (x2 -. x1)
let distance_from_pole x y = sqrt ((x ** 2.0) +. (y ** 2.0))
let length_of_ray_until_intersect theta x y =
y /. (sin theta -. (x *. cos theta))
let get_bounds l =
let sub1 = ((l +. 16.0) ** 3.0) /. 1560896.0 in
let sub2 = if sub1 > epsilon then sub1 else l /. kappa in
let ret = Array.make_matrix 6 2 0.0 in
Array.iteri
(fun i mi ->
for k = 0 to 1 do
let top1 = ((284517.0 *. mi.(0)) -. (94839.0 *. mi.(2))) *. sub2 in
let top2 =
((838422.0 *. mi.(2)) +. (769860.0 *. mi.(1)) +. (731718.0 *. mi.(0)))
*. l *. sub2
-. (769860.0 *. float_of_int k *. l)
in
let bottom =
(((632260.0 *. mi.(2)) -. (126452.0 *. mi.(1))) *. sub2)
+. (126452.0 *. float_of_int k)
in
ret.((i * 2) + k).(0) <- top1 /. bottom;
ret.((i * 2) + k).(1) <- top2 /. bottom
done)
m;
ret
let max_safe_chroma_for_l l =
let bounds = get_bounds l in
Array.fold_left
(fun min_length line ->
let m1 = line.(0) in
let b1 = line.(1) in
let x = intersect_line_line m1 b1 (-1.0 /. m1) 0.0 in
let dist = distance_from_pole x (b1 +. (x *. m1)) in
if dist < min_length then dist else min_length)
max_float bounds
let max_chroma_for_lh l h =
let h_rad = h /. 360.0 *. 2.0 *. Float.pi in
let bounds = get_bounds l in
let min_length =
Array.fold_left
(fun minLength line ->
let length = length_of_ray_until_intersect h_rad line.(0) line.(1) in
if length > 0.0 && length < minLength then length else minLength)
Float.max_float bounds
in
min_length
let y_to_l y =
if y <= epsilon then y *. kappa else (116.0 *. (y ** (1.0 /. 3.0))) -. 16.0
let l_to_y l = if l <= 8.0 then l /. kappa else ((l +. 16.0) /. 116.0) ** 3.0
let conv_xyz_luv { x; y; z } =
if y = 0.0 then { l = 0.0; u = 0.0; v = 0.0 }
else
let l = y_to_l y in
let var_u = 4.0 *. x /. (x +. (15.0 *. y) +. (3.0 *. z)) in
let var_v = 9.0 *. y /. (x +. (15.0 *. y) +. (3.0 *. z)) in
let u = 13.0 *. l *. (var_u -. ref_u) in
let v = 13.0 *. l *. (var_v -. ref_v) in
{ l; u; v }
let conv_luv_xyz { l; u; v } =
if l = 0.0 then { x = 0.0; y = 0.0; z = 0.0 }
else
let var_u = (u /. (13.0 *. l)) +. ref_u in
let var_v = (v /. (13.0 *. l)) +. ref_v in
let y = l_to_y l in
let x =
-.(9.0 *. y *. var_u) /. (((var_u -. 4.0) *. var_v) -. (var_u *. var_v))
in
let z =
((9.0 *. y) -. (15.0 *. var_v *. y) -. (var_v *. x)) /. (3.0 *. var_v)
in
{ x; y; z }
let conv_luv_lch { l; u; v } =
let c = sqrt ((u ** 2.0) +. (v ** 2.0)) in
let h_rad = atan2 v u in
let h =
if c < 0.00000001 then 0.0
else mod_float (h_rad *. 360.0 /. (2.0 *. pi)) 360.0
in
{ l; c; h }
let conv_lch_luv { l; c; h } =
let h_rad = h /. 360.0 *. 2.0 *. pi in
let u = cos h_rad *. c in
let v = sin h_rad *. c in
{ l; u; v }
let conv_hsluv_lch { h; s; l } =
if l > 99.9999999 || l < 0.00000001 then { l; c = 0.; h }
else
let max = max_chroma_for_lh l h in
{ l; c = max /. 100.0 *. s; h }
let conv_lch_hsluv { l; c; h } =
if l > 99.9999999 || l < 0.00000001 then { h; s = 0.; l }
else
let max = max_chroma_for_lh l h in
{ h; s = c /. max *. 100.0; l }
let conv_hpluv_lch { h; p; l } =
if l > 99.9999999 || l < 0.00000001 then { l; c = 0.; h }
else
let max = max_safe_chroma_for_l l in
{ l; c = max /. 100. *. p; h }
let conv_lch_hpluv { l; c; h } =
if l > 99.9999999 || l < 0.00000001 then { h; p = 0.; l }
else
let max = max_safe_chroma_for_l l in
{ h; p = c /. max *. 100.0; l }
let conv_rgb_hex { r; g; b } =
let rV = round (max 0.0 (min r 1.0) *. 255.0) in
let gV = round (max 0.0 (min g 1.0) *. 255.0) in
let bV = round (max 0.0 (min b 1.0) *. 255.0) in
Printf.sprintf "#%02x%02x%02x" rV gV bV
let conv_hex_rgb hex =
let hex =
if String.length hex > 0 && hex.[0] = '#' then
String.sub hex 1 (String.length hex - 1)
else hex
in
let rV = int_of_string ("0x" ^ String.sub hex 0 2) in
let gV = int_of_string ("0x" ^ String.sub hex 2 2) in
let bV = int_of_string ("0x" ^ String.sub hex 4 2) in
{
r = float_of_int rV /. 255.0;
g = float_of_int gV /. 255.0;
b = float_of_int bV /. 255.0;
}
let conv_xyz_rgb { x; y; z } =
let r = from_linear (dot_product m.(0) [| x; y; z |]) in
let g = from_linear (dot_product m.(1) [| x; y; z |]) in
let b = from_linear (dot_product m.(2) [| x; y; z |]) in
{ r; g; b }
let conv_rgb_xyz { r; g; b } =
let r = to_linear r in
let g = to_linear g in
let b = to_linear b in
let x = dot_product m_inv.(0) [| r; g; b |] in
let y = dot_product m_inv.(1) [| r; g; b |] in
let z = dot_product m_inv.(2) [| r; g; b |] in
{ x; y; z }
let conv_lch_rgb { l; c; h } =
conv_lch_luv { l; c; h } |> conv_luv_xyz |> conv_xyz_rgb
let conv_rgb_lch { r; g; b } =
conv_rgb_xyz { r; g; b } |> conv_xyz_luv |> conv_luv_lch
let conv_hsluv_rgb { h; s; l } = conv_hsluv_lch { h; s; l } |> conv_lch_rgb
let conv_rgb_hsluv { r; g; b } = conv_rgb_lch { r; g; b } |> conv_lch_hsluv
let hsluv_to_hex { h; s; l } = conv_hsluv_rgb { h; s; l } |> conv_rgb_hex
let hsluv_from_hex hex = conv_hex_rgb hex |> conv_rgb_hsluv
let hsluv_to_rgb = conv_hsluv_rgb
let hsluv_from_rgb = conv_rgb_hsluv
let hpluv_to_hex { h; p; l } =
{ h; p; l } |> conv_hpluv_lch |> conv_lch_luv |> conv_luv_xyz |> conv_xyz_rgb
|> conv_rgb_hex
let hpluv_from_hex hex =
hex |> conv_hex_rgb |> conv_rgb_xyz |> conv_xyz_luv |> conv_luv_lch
|> conv_lch_hpluv
let hpluv_to_rgb { h; p; l } =
{ h; p; l } |> conv_hpluv_lch |> conv_lch_luv |> conv_luv_xyz |> conv_xyz_rgb
let hpluv_from_rgb { r; g; b } =
{ r; g; b } |> conv_rgb_xyz |> conv_xyz_luv |> conv_luv_lch |> conv_lch_hpluv
include Pretty_printers