-
Notifications
You must be signed in to change notification settings - Fork 81
Expand file tree
/
Copy pathNSColor+Lab.swift
More file actions
138 lines (110 loc) · 4.4 KB
/
Copy pathNSColor+Lab.swift
File metadata and controls
138 lines (110 loc) · 4.4 KB
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
import Cocoa
// swiftlint:disable identifier_name
extension NSColor {
// Shared linearization for sRGB components used by both LAB and OKLCH.
private func linearizeSRGB(_ c: CGFloat) -> CGFloat {
c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)
}
/*
* OpenGL
*/
/**
Get the rgb values of this color in opengl format.
- returns: An NSColor as an opengl string.
*/
func toOpenGLString(style: CopyFormat = .css) -> String {
let RGB = toRGBAComponents()
let formatString: NSString
switch style {
case .css, .design, .swiftUI:
formatString = "rgba(%.5g, %.5g, %.5g, 1.0)"
case .unformatted:
formatString = "%.5g, %.5g, %.5g, 1.0"
}
let openGLString = NSString(format: formatString, RGB.r, RGB.g, RGB.b)
return openGLString as String
}
/*
* CIE-LAB
*/
private func toXYZComponents() -> (x: CGFloat, y: CGFloat, z: CGFloat) {
let srgb = toRGBAComponents(in: .sRGB)
let r_lin = linearizeSRGB(srgb.r)
let g_lin = linearizeSRGB(srgb.g)
let b_lin = linearizeSRGB(srgb.b)
let x = r_lin * 0.4124564 + g_lin * 0.3575761 + b_lin * 0.1804375
let y = r_lin * 0.2126729 + g_lin * 0.7151522 + b_lin * 0.0721750
let z = r_lin * 0.0193339 + g_lin * 0.1191920 + b_lin * 0.9503041
return (x: x, y: y, z: z)
}
func toLabComponents() -> (l: CGFloat, a: CGFloat, b: CGFloat) {
let xyz = toXYZComponents()
// D65 Reference White
let Xn: CGFloat = 0.95047
let Yn: CGFloat = 1.00000
let Zn: CGFloat = 1.08883
func f(_ t: CGFloat) -> CGFloat {
let delta: CGFloat = 6.0 / 29.0
if t > pow(delta, 3.0) {
return pow(t, 1.0 / 3.0)
} else {
return (t / (3.0 * pow(delta, 2.0))) + (4.0 / 29.0)
}
}
let L_star = (116.0 * f(xyz.y / Yn)) - 16.0
let a_star = 500.0 * (f(xyz.x / Xn) - f(xyz.y / Yn))
let b_star = 200.0 * (f(xyz.y / Yn) - f(xyz.z / Zn))
return (l: L_star, a: a_star, b: b_star)
}
func toLabString(style: CopyFormat = .css) -> String {
let lab = toLabComponents()
let l_str = (round(lab.l * 100) / 100).strippedDecimalString(maxDecimalPlaces: 2)
let a_str = (round(lab.a * 100) / 100).strippedDecimalString(maxDecimalPlaces: 2)
let b_str = (round(lab.b * 100) / 100).strippedDecimalString(maxDecimalPlaces: 2)
switch style {
case .css:
return "lab(\(l_str) \(a_str) \(b_str))"
case .design, .swiftUI:
return "lab(\(l_str), \(a_str), \(b_str))"
case .unformatted:
return "\(l_str),\(a_str),\(b_str)"
}
}
/*
* OKLCH
*/
func toOklchComponents() -> (l: CGFloat, c: CGFloat, h: CGFloat) {
let srgb = toRGBAComponents(in: .sRGB)
let r_lin = linearizeSRGB(srgb.r)
let g_lin = linearizeSRGB(srgb.g)
let b_lin = linearizeSRGB(srgb.b)
let l = 0.4122214708 * r_lin + 0.5363325363 * g_lin + 0.0514459929 * b_lin
let m = 0.2119034982 * r_lin + 0.6806995451 * g_lin + 0.1073969566 * b_lin
let s = 0.0883024619 * r_lin + 0.2817188376 * g_lin + 0.6299787005 * b_lin
let l_ = cbrt(l)
let m_ = cbrt(m)
let s_ = cbrt(s)
let L = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_
let a = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_
let b = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_
let C = sqrt(a * a + b * b)
var H = atan2(b, a) * 180.0 / .pi
if H < 0 { H += 360.0 }
return (l: L, c: C, h: H)
}
func toOklchString(style: CopyFormat = .css) -> String {
let oklch = toOklchComponents()
let l_str = (round(oklch.l * 10000) / 100).strippedDecimalString(maxDecimalPlaces: 2)
let c_str = (round(oklch.c * 10000) / 10000).strippedDecimalString(maxDecimalPlaces: 4)
let h_str = (round(oklch.h * 100) / 100).strippedDecimalString(maxDecimalPlaces: 2)
switch style {
case .css:
return "oklch(\(l_str)% \(c_str) \(h_str))"
case .design, .swiftUI:
return "oklch(\(l_str), \(c_str), \(h_str))"
case .unformatted:
return "\(l_str), \(c_str), \(h_str)"
}
}
}
// swiftlint:enable identifier_name