1
2
3
4
5 package moreland
6
7 import (
8 "image/color"
9 "math"
10 )
11
12
13 type rgb struct {
14 R, G, B float64
15 }
16
17
18 func (c rgb) cieXYZ() cieXYZ {
19 return cieXYZ{
20 X: 0.4124*c.R + 0.3576*c.G + 0.1805*c.B,
21 Y: 0.2126*c.R + 0.7152*c.G + 0.0722*c.B,
22 Z: 0.0193*c.R + 0.1192*c.G + 0.9505*c.B,
23 }
24 }
25
26
27
28 func (c rgb) sRGBA(alpha float64) sRGBA {
29
30 f := func(v float64) float64 {
31 if v > 0.0031308 {
32 return 1.055*math.Pow(v, 1/2.4) - 0.055
33 }
34 return 12.92 * v
35 }
36
37 return sRGBA{
38 R: f(c.R),
39 G: f(c.G),
40 B: f(c.B),
41 A: alpha,
42 }
43 }
44
45
46
47 type cieXYZ struct {
48 X, Y, Z float64
49 }
50
51
52 func (c cieXYZ) rgb() rgb {
53 return rgb{
54 R: c.X*3.2406 + c.Y*-1.5372 + c.Z*-0.4986,
55 G: c.X*-0.9689 + c.Y*1.8758 + c.Z*0.0415,
56 B: c.X*0.0557 + c.Y*-0.204 + c.Z*1.057,
57 }
58 }
59
60
61 func (c cieXYZ) cieLAB() cieLAB {
62
63 f := func(v float64) float64 {
64 if v > 0.008856 {
65 return math.Pow(v, 1.0/3.0)
66 }
67 return 7.787*v + 16.0/116.0
68 }
69
70 tempX := f(c.X / 0.9505)
71 tempY := f(c.Y)
72 tempZ := f(c.Z / 1.089)
73 return cieLAB{
74 L: (116.0 * tempY) - 16.0,
75 A: 500.0 * (tempX - tempY),
76 B: 200 * (tempY - tempZ),
77 }
78 }
79
80
81
82 type sRGBA struct {
83 R, G, B, A float64
84 }
85
86
87 func (c sRGBA) rgb() rgb {
88
89 f := func(v float64) float64 {
90 if v > 0.04045 {
91 return math.Pow((v+0.055)/1.055, 2.4)
92 }
93 return v / 12.92
94 }
95
96 return rgb{
97 R: f(c.R),
98 G: f(c.G),
99 B: f(c.B),
100 }
101 }
102
103
104 func (c sRGBA) RGBA() (r, g, b, a uint32) {
105 return uint32(c.R * c.A * 0xffff), uint32(c.G * c.A * 0xffff), uint32(c.B * c.A * 0xffff), uint32(c.A * 0xffff)
106 }
107
108
109 func (c sRGBA) cieLAB() cieLAB {
110 return c.rgb().cieXYZ().cieLAB()
111 }
112
113
114 func colorTosRGBA(c color.Color) sRGBA {
115 r, g, b, a := c.RGBA()
116 if a == 0 {
117 return sRGBA{}
118 }
119 return sRGBA{
120 R: float64(r) / float64(a),
121 G: float64(g) / float64(a),
122 B: float64(b) / float64(a),
123 A: float64(a) / 0xffff,
124 }
125 }
126
127
128 func (c *sRGBA) clamp() {
129 if c.R > 1 {
130 c.R = 1
131 }
132 if c.G > 1 {
133 c.G = 1
134 }
135 if c.B > 1 {
136 c.B = 1
137 }
138 if c.A > 1 {
139 c.A = 1
140 }
141 if c.R < 0 {
142 c.R = 0
143 }
144 if c.G < 0 {
145 c.G = 0
146 }
147 if c.B < 0 {
148 c.B = 0
149 }
150 if c.A < 0 {
151 c.A = 0
152 }
153 }
154
155
156
157 type cieLAB struct {
158 L, A, B float64
159 }
160
161
162
163 func (c cieLAB) sRGBA(alpha float64) sRGBA {
164 return c.cieXYZ().rgb().sRGBA(alpha)
165 }
166
167
168 func (c cieLAB) cieXYZ() cieXYZ {
169
170 f := func(v float64) float64 {
171 const (
172 xlim = 0.008856
173 a = 7.787
174 b = 16. / 116.
175 ylim = a*xlim + b
176 )
177 if v > ylim {
178 return v * v * v
179 }
180 return (v - b) / a
181 }
182
183
184 const xn, yn, zn = 0.95047, 1.0, 1.08883
185 return cieXYZ{
186 X: xn * f((c.A/500)+(c.L+16)/116),
187 Y: yn * f((c.L+16)/116),
188 Z: zn * f((c.L+16)/116-(c.B/200)),
189 }
190 }
191
192
193 func (c cieLAB) MSH() msh {
194 m := math.Pow(c.L*c.L+c.A*c.A+c.B*c.B, 0.5)
195 return msh{
196 M: m,
197 S: math.Acos(c.L / m),
198 H: math.Atan2(c.B, c.A),
199 }
200 }
201
202
203 type msh struct {
204 M, S, H float64
205 }
206
207
208
209 func colorToMSH(c color.Color) msh {
210 return colorTosRGBA(c).cieLAB().MSH()
211 }
212
213
214 func (c msh) cieLAB() cieLAB {
215 return cieLAB{
216 L: c.M * math.Cos(c.S),
217 A: c.M * math.Sin(c.S) * math.Cos(c.H),
218 B: c.M * math.Sin(c.S) * math.Sin(c.H),
219 }
220 }
221
222
223 func (c msh) RGBA() (r, g, b, a uint32) {
224 return c.cieLAB().sRGBA(1.0).RGBA()
225 }
226
227
228
229 func hueTwist(c msh, convergeM float64) float64 {
230 signH := c.H / math.Abs(c.H)
231 return signH * c.S * math.Sqrt(convergeM*convergeM-c.M*c.M) / (c.M * math.Sin(c.S))
232 }
233
View as plain text